Tasko Olevski
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    1
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # Handling of credentials in Renku This document is a summary of the main authentication and authorization flows in Renku. It explains how user credentials are handled in Renku by the application and its user-facing clients. The last section also summarizes how deployment credentials are managed. ## Table of contents: * Description of key services * API Authorization * Interactive session access * DevOps flow ## Key services These are the most important services in Renku that handle user credentials. ### Keycloak [Keycloak](https://www.keycloak.org/) is an open source identity and access management application. Renku uses it as its main source of user identities and the Renku services use tokens provided by Keycloak to authenticate and authorize requests. ### GitLab [GitLab](https://gitlab.com) is an open-source repository management solution. Renku relies on GitLab to manage user projects. GitLab can be provided either as a part of the Renku instance, or linked to a Renku instance as an external service. In either mode, Renku requires users to authorize Renku clients to use their GitLab credentials on their behalf for operations such as repository cloning and pushing. ### Gateway The Renku Gateway is a component of the Renku platform, through which backend APIs are exposed. The Gateway takes care of ensuring that API requests are authenticated and that they contain the credentials needed by the backend to perform the required tasks. ## API authorization Renku services expose a variety of APIs to external clients via the Renku Gateway. The API Gateway is itself a service that provides a unified interface for clients to access backends like GitLab, the Renku core service, the notebooks service etc. Its primary function is authorizing clients to access various backend services and authenticating clients with those services. ### Log in to Renku Upon receiving a login request from a client, the gateway initiates an authentication request to Keycloak which prompts the user to log in if required. Some clients (e.g. Renku CLI client) might need to be authorized to access Keycloak; Keycloak prompts the user to authorize them. Upon success, Keycloak access/refresh/ID tokens are retrieved from Keycloak by the gateway and stored in a Redis storage. The gateway then redirects the client to initiate another authentication request to the Gitlab; it retrieves Gitlab access tokens and stores them in the Redis storage. Clients that do not have an active session to the server (e.g. Renku CLI client) need to store a Keycloak access token locally, to include it in future requests; those clients send a request to receive this token. For interactive clients (e.g. Renku UI) the access token will be saved in their session cookie by the gateway. The following diagram shows different login steps for the Renku UI client: ```mermaid sequenceDiagram participant UI participant Gateway participant Keycloak participant GitLab participant Redis UI ->>+ Gateway: /api/auth/login?reidrect_url activate UI Gateway ->> Redis: store state, redirect_url Gateway -->>- UI: redirect UI ->>+ Keycloak: /auth/realms/Renku/protocol/openid-connect/auth Keycloak ->> Keycloak: login page if needed Keycloak -->>- UI: redirect UI ->>+ Gateway: /api/auth/token Redis ->> Gateway: retreive state, redirect_url Gateway ->>+ Keycloak: /auth/realms/Renku/protocol/openid-connect/token Keycloak -->>- Gateway: access/refresh/ID tokens Gateway ->> Redis: store Keycloak tokens Gateway -->>- UI: redirect UI ->>+ Gateway: /api/auth/gitlab/login?reidrect_url Gateway ->> Redis: store state, redirect_url Gateway -->>- UI: redirect UI ->>+ GitLab: /gitlab/auth/authorize GitLab ->> GitLab: redirect to Keycloak if needed GitLab -->>- UI: redirect UI ->>+ Gateway: /api/auth/gitlab/token Redis ->> Gateway: retreive state, redirect_url Gateway ->>+ GitLab: /gitlab/oauth/token GitLab -->>- Gateway: access/refresh/ID tokens Gateway ->> Redis: store GitLab tokens Gateway -->>- UI: redirect to reidrect_url deactivate UI ``` ### Accessing the Renku API Requests to access backend APIs are all routed through the gateway. When a client sends a request to use one of the backend APIs, the gateway uses the client's access token to find and inject the correct user credentials that are required by the target service. As mentioned in the previous section, access tokens for clients are either stored in the session (for interactive clients) or are included with the request (for non-interactive clients). The gateway does the following steps to authenticate a request: 1. It looks for an access token, first in the request's authorization header and then in the session cookie. 2. If an access token is found, the gateway uses it as a key to the Redis store to get the Keycloak access token. 3. If the Keycloak access token is not found in Redis or if it's expired, then the session ended and the client cannot access the service. 4. If the requested backend service requires a GitLab access token, the gateway uses the Keycloak access token as a key to retrieve the GitLab access token from Redis. 6. Depending on the service that is being accessed, various request headers are updated and the access tokens are stored in them. 7. The request with the modified header is forwarded to the proper service. The following diagram shows the sequence for a request that requires GitLab access: ```mermaid sequenceDiagram participant Client participant Gateway participant GitLab participant Redis Client ->>+ Gateway: /api/<resource> (session cookie/request parameter) activate Client Gateway ->> Gateway: extract access token from request parameters/session cookie Redis ->> Gateway: retreive Keycloak tokens Gateway ->> Gateway: validate Keycloak access token Redis ->> Gateway: retreive GitLab tokens Gateway ->> Gateway: include the GitLab access token in the request's Autorization header Gateway ->>+ GitLab: /api/gitlab/<resource> (modified request parameters) GitLab ->> GitLab: process request GitLab -->>- Gateway: API response Gateway -->>- Client: API response deactivate Client ``` Similar flows are implemented for other backends, but only providing the required credentials. ### Logout When a client sends a logout request, the gateway removes its associated access tokens from the Redis store and terminates the session. ## Interactive session access There are two types of sessions: - for anonymous users that are not logged in - for normal registered (and logged in) users These have a slightly different structure because anonymous users get assigned and are identified by a random ID, whereas registered users are all registered in Renku's `Keycloak` or any identity provider that Renku's `Keycloak` is connected to. Anonymous user sessions are optional and need to be explicitly enabled by an administrator. By default only registered and logged-in users can launch interactive sessions. ### Registered user sessions Each session comes with its own ingress that leads only to the `Oauth2 proxy` container. Any requests for the session coming from outside the cluster have to go through the `Oauth2 proxy`. The `Oauth2 proxy` is a client to Renku's `Keycloak` and it can determine whether a user is logged in or not and whether they are authorized to access the session. The authorization is done based on the email contained in the user's identity token from `Keycloak`. If the request is coming from a logged-in and authorized user, the request is forwarded to the session container (running a JuptyerLab server). If the user is not logged in they are redirected to the login sequence; if they fail or are not authorized to access the session, the request is rejected. ```mermaid flowchart LR User[User] subgraph Kubernetes[ ] SI[Session<br>ingress] subgraph Session statefulset Jupyterlab[Jupyterlab] auth[Oauth2 proxy] end Keycloak[Keycloak] end User --> |GET /sessions/session_name| SI SI --> auth auth --> Keycloak Keycloak --> auth auth --> Jupyterlab ``` ### Anonymous user sessions When anonymous users first access renku they are assigned an ID that is stored in a cookie in the user's browser. This id is used also as an access token in the session. In this case there is no need to use `Keycloak` or the `Oauth2 proxy`, Jupyterlab has a simple token based authentication that is utilized. If this token (which has the same value as the anonymous user ID) is not present or provided by the user they cannot access the session. We use a simple `traefik` proxy in every session to forward the request to the Jupyterlab container. The traefik proxy does not do any modifications to the request. This proxy may be removed in future versions because it is not strictly necessary. Anonymous user sessions are quite limited relative to registered user sessions. We do not have any credentials for anonymous users and therefore they cannot do any git operations that require authentication (i.e. push, create branches, etc.). ```mermaid flowchart LR User[User] subgraph Kubernetes[ ] SI[Session<br>ingress] subgraph Session statefulset Jupyterlab[Jupyterlab<br>container] proxy[Traefik proxy] end end User --> |GET /sessions/session_name?token| SI SI --> proxy proxy --> Jupyterlab ``` ### Git credentials in registered user sessions The JupyterLab container in a user session is running an image created by the user. Since we do not have full control over this image we cannot trust the container to have direct access to credentials. However, registered users need to be able to access GitLab from within a session without being asked for credentials. To solve this problem, all registered user sessions are configured to use a proxy for git requests. The proxy injects the user's git credentials for outgoing git requests, but only for the repository from which the session was started. This way we can avoid a user's credentials being hijacked to access other (potentially private or sensitive) repositories if a user is tricked into launching a sessions with a malicious image. ```mermaid flowchart LR subgraph Session statefulset Jupyterlab[Jupyterlab<br>container] gitProxy[Git proxy] end GitLab Jupyterlab --> gitProxy gitProxy --> GitLab ``` Cloning the repository is done in a Kubernetes `initContainer` that runs an image that we create. Therefore this container has the user git credentials. But the credentials are removed as soon as the clone operation finishes (regardless of whether it succeeded or failed). ### Network access to and from user sessions Egress from user sessions is restricted to hosts that are outside of the cluster. This prevents users inside the interactive sessions from trying to access e.g. the Postgres database that is running in the same cluster. Similarly, the ingress to user sessions is limited (via a network policy) to only occur through the `Oauth proxy`, in order to prevent attempts to bypass the proxy and access the running JupyterServer directly. In the case of anonymous sessions the only ingress to the session is through the Traefik proxy. This is applied to ingress that originates inside or outside of the cluster. Therefore, this network policy essentially forces all requests through a single "path" that always requires authentication. ### External storage access from interactive sessions A recently added feature to Renku is the ability to mount S3 buckets to user sessions. This is possible with any S3 compatible storage (not just with AWS). This feature is optional and must be enabled by an administrator of the Renku instance. The user is expected to provide credentials for every bucket they want to mount every time they lunch a session. If the bucket is public then credentials are not required, only the endpoint of the S3 storage service and the name of the bucket. In a bit more detail, S3 credentials are handled as follows: 1. A user enters the credentials in their browser when launching a session (the credentials are not stored in the browser at all) 2. The credentials are sent with a POST request to the Renku notebook service, which is responsible for managing interactive sessions 3. The credentials are used to verify that the bucket exists in the notebook service (they are not stored in the notebook service) 4. The notebook service creates a manifest for a custom Kubernetes resource that is used by the controller process to create all the required resources needed by a session (the credentials are stored in this manifest) 5. The controller uses the manifest from the previous step to create a Kubernetes secret with the credentials (the credentials are stored in this secret). The secret is deleted when the user session that is using the S3 bucket that needs said credentials is deleted. The S3 credentials are not stored anywhere in the user session because the S3 bucket is mounted by a [custom Kubernetes storage driver](https://datashim-io.github.io/datashim/) that operates outside of the user session and can handle S3 buckets. ## DevOps workflow The Renku deployment is managed through Helm; the Helm chart and its values file are operated by Flux, our chosen Gitops tool. The values file contains the administrator credentials of the different services included in Renku: Keycloak, Postgres, GitLab (if deployed). Those credentials are encrypted using AES-256, through SOPS/GPG and stored in a private Git repository accessible by members of the Renku team. The private key used for the encryption is only available to the administrators within the Renku team and are rotated when a member leaves the team Once deployed in the cluster the credentials are stored as Kubernetes secrets in the Renku namespace. Only the Renku-team administrators have access to the credentials to the Kubernetes cluster.

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully