Giter Club home page Giter Club logo

renku-gateway's Introduction

Renku API gateway

The Renku API gateway connects the different Renku clients to the various Renku backend services (GitLab, Renku components etc). It consists of two parts: a traefik reverse-proxy (gateway) and a flask application acting predominantly as traefik forward-auth middleware (gateway-auth).

Developing the gateway-auth component

The renku gateway-auth component is best developped in the context of a full renku deployment. In order to get an instance of Renku up and running, follow these instructions.

Once you have an instance of Renku running, you could modify the gateway code, build the image, re-build the chart, redeploy, etc... This will make for a poor development experience with very long feedback cycles.

Instead we recommend intercepting traffic to the gateway-auth component and routing it to your local machine through telepresence (note that currently you MUST use version 2.4.X, mac users see in particular tele-troubleshooting). Once telepresence is installed, create a python environment and install the necessary python dependencies by running poetry install. Then, create a telepresence intercept using the dedicated ./telepresence-intercept.sh script and follow the instructions. This will forward all requests to the gateway-auth service deployed in the cluster to a flask development server running on your local machine (with hot reloading, etc). You can now use your favourite IDE and develop the component completely locally. Stopping the development server through ctrl-C and then stopping the shell process invoked with the intercept by typing exit will terminate the intercept.

Tests

You can run tests with

$ poetry run pytest

Configuration

The simplest way to deploy the gateway is using Helm charts and setting the needed values. But if you prefer to run directly the docker image here is the list of all environment variables that can be set, with their default values.

Variable name Description Default value
HOST_NAME The URL of this service. http://gateway.renku.build
GATEWAY_SECRET_KEY Must be exactly 64 hex characters! Used to encrypt session cookies and redis content. Must be set, no default!
GATEWAY_ALLOW_ORIGIN CORS configuration listing all domains allowed to use the gateway. Use "*" to allow all. ""
GATEWAY_REDIS_HOST The hostname/ip of the Redis instance used for persisting sessions. renku-gw-redis
GITLAB_URL The URL of the Gitlab instance to proxy. http://gitlab.renku.build
GITLAB_CLIENT_ID The client ID for the gateway in Gitlab. renku-ui
GITLAB_CLIENT_SECRET The corresponding secret. no-secret-needed
JUPYTERHUB_URL The URL of the JupyterHub. {{HOST_NAME}}/jupyterhub
JUPYTERHUB_CLIENT_ID The client ID for the gateway in JupyterHub. This corresponds to the service oauth_client_id. gateway
JUPYTERHUB_CLIENT_SECRET The client secret for the gateway in JupyterHub. This corresponds to the service api_token. dummy-secret
KEYCLOAK_URL The URL of the Keycloak instance. http://keycloak.renku.build:8080
OIDC_CLIENT_ID The client ID for the gateway in Keycloak. gateway
OIDC_CLIENT_SECRET The client secret for the gateway in Keycloak. dummy-secret
GATEWAY_SERVICE_PREFIX The URL prefix for the gateway. /
GATEWAY_ENDPOINT_CONFIG_FILE The JSON definition of the API proxying endpoints. endpoints.json
CLI_CLIENT_ID The client ID for the gateway's CLI client in Keycloak. renku-cli
CLI_CLIENT_SECRET The client secret for the gateway's CLI client in Keycloak. dummy-secret
CLI_LOGIN_TIMEOUT The validity of CLI login nonce in seconds. 300

Login workflow

To collect the user's token from the various backend services, the gateway uses the OAuth2/OIDC protocol and redirects the users to each of them.

image

Redis storage

To allow server-side sessions, the gateway relies on Redis.

key value remarks
sessions{{session key}} a dictionary with some temporary states (redirect_urls, login states, cli_token) and the user's Keycloak access token. The session key is managed by Flask-KVsession and kept in a secured, http-only cookie.
cache{{id sub}}_{{backend}}_{{token type}} The corresponding token Id sub is taken from the Keycloak access token in the session or Authorizazion header (after validation of the token). Current backends are Keycloak (kc), Gitlab (gl) and JupyterHub (jh). Token types can be access_token, refresh_token or id_token.

Templates

All static files (css, fonts, images) used in the templates are placed in renku-ui under the static/public path.

renku-gateway's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

renku-gateway's Issues

Improve test coverage

The current test coverage is minimal and arguably not very meaningful. Also,, tests should rely on monkey-patching rather than checking for pytest in the module path.

Gateway to provide an API for exchanging user email to an oauth access token

There's a need for backend services working independently to UI to have access to users' GitLab oauth tokens. As renku-gateway seems to be currently in charge of managing tokens, it sounds reasonable to create such an endpoint in that service.

Proposal for the API

GET <renku-gateway-url>/oauth-tokens/{email}

Headers: renku-graph-token -> <some token>

Response:

  • UNAUTHORIZED (401) if the renku-graph-token is absent or invalid
  • OK (200) with the body: { oauth-token: "<user oauth token>" }

Considerations

This endpoint must be secured and accessible only to known services.

Improve logging strategy

Currently, there is no coherent strategy about what should be logged in development and production mode respectively. It would be nice to have one parameter which consistently sets the log level throughout the entire application (also that of the imported libraries such as oauthlib).


Update: #140 partially addressed this issue for the traefik part of the gateway. For the auth part this remains to be done. Furthermore it should be possible override the log level assumed by the development/production setting through a chart value.

Store user-specific access tokens

In the longer run the gateway has to handle state, i.e. store user-specific access tokens for all backend services which do not accept a keycloak token for their API. This will allow us to integrate backend services for which we do not have admin rights (for example not sudo-token for GitLab). The tokens could for example be stored in a redis store.

Integrate the proxy into renga

  • Remove all hardcoded tokens, read from environment variables instead like it's done already here
  • Create a Dockerfile for the proxy. The previous proxy should again be a good starting point.
  • Create a Makefile inside proxy-repo which will build the proxy docker image.
  • Define new environment variable (the value of the sudo token) in renga Makefile. Add proxy service to docker-compose.yml file of renga repo.
  • Add the sudo token to the GitLab database on through the renga Makefile (very similar to register-gitlab-oauth-applications).
  • Change traefik settings to make the proxy available under api.renga.local

Chart cleanup

There are obsolete values set in values.yaml and there are necessary values which are not documented in values.yaml. This should be fixed.

Make Proxy Public

  • Solve Issue #6
  • Solve Issue #16
  • Add and check headers
  • Squash commits
  • Branch protection
  • Make public

Realtime notifications to clients

The proxy/gateway should give clients the possibility to subscribe to events. The API should be something like:

  • create_notification_channel() : create a websocket for communication and return the url
  • subscribe_to_event(notification_channel, event) : subscribe to events

The generate events, the proxy can initially use the gitlab web hooks. When notifications come in over the web hooks, these will be forwarded to the web socket.

See:

Fix forwarding of logger.info() logging in k8s

All of the logger.info() logging is missing in the k8s logs while logger.debug() logs are present. This can lead to situations where the gateway pod is dying without leaving any logs at all.


Original description by @rokroskar:
At the moment, there are no logs provided when things go wrong during bootstrapping of the gateway. For example, if keycloak isn't reachable, there is no message just an unresponsive service. This makes debugging of new deployments difficult.

Gateway is URL-Unencoding requests to gitlab groups API endpoint

To find out information about a group the UI uses the endpoint /groups/[group-path]. Group-path needs to be URL-encoded (since a group can be a subgroup of another group), but the gateway is unencoding this path and as a result, Gitlab cannot process the request correctly.

API versioning

Introduce two versions of the API endpoints:

  • /api/v0/resource should proxy to GitLabs API v4
  • /api/v1/resource should parse the information according to the Renku schema and use Renku terminology (ku instead of api, etc)

alternatively, the api version could be specified in the request header.

use k8s secrets

The gitlab sudo token and the oidc client secret are passed as standard environment variables. Would be better to use the k8s secrets for these values.

Fix behaviour on expired token

A request to /api/v4/user with an expired token results in the following response:
{id: 1, name: "Administrator", username: "root", state: "active",โ€ฆ}

Make the gateway compatible with newer keycloak versions

When upgrading keycloak to version 4.8.3 we get 500 Internal Server Error, right after clicking login button on Renku.

Gateway pod logs:

Traceback (most recent call last): 
  File "/usr/local/lib/python3.7/site-packages/quart/app.py", line 1440, in handle_request 
    return await self.full_dispatch_request(request_context) 
  File "/usr/local/lib/python3.7/site-packages/quart/app.py", line 1462, in full_dispatch_request 
    result = await self.handle_user_exception(error) 
  File "/usr/local/lib/python3.7/site-packages/quart/flask_patch/app.py", line 23, in new_handle_user_exception 
    return await old_handle_user_exception(self, error) 
  File "/usr/local/lib/python3.7/site-packages/quart/app.py", line 893, in handle_user_exception 
    raise error 
  File "/usr/local/lib/python3.7/site-packages/quart/app.py", line 1460, in full_dispatch_request 
    result = await self.dispatch_request(request_context) 
  File "/usr/local/lib/python3.7/site-packages/quart/app.py", line 1508, in dispatch_request 
    return await handler(**request_.view_args) 
  File "/code/app/auth/web.py", line 206, in get_tokens 
    audience=app.config['OIDC_CLIENT_ID'] 
  File "/usr/local/lib/python3.7/site-packages/jwt/api_jwt.py", line 105, in decode 
    self._validate_claims(payload, merged_options, **kwargs) 
  File "/usr/local/lib/python3.7/site-packages/jwt/api_jwt.py", line 141, in _validate_claims 
    self._validate_aud(payload, audience) 
  File "/usr/local/lib/python3.7/site-packages/jwt/api_jwt.py", line 205, in _validate_aud 
    raise InvalidAudienceError('Invalid audience') 
jwt.exceptions.InvalidAudienceError: Invalid audience 

Line where this audience value is set : web.py

Refactor token handling

Implement the server-side changes to match SwissDataScienceCenter/renku-ui#307:

  • Set access tokens as secure HttpOnly cookies
  • Allow the access token to be sent as cookie header but enforce the presence of a custom header in this case to ensure the request was initiated by JS.
  • Check the Host/Referrer header and compare to the allowed origins specified in the access token
  • Properly set the Access-Control-Allow-Origin header

Fix happy flow test

This test does not work, maybe related to master not working ?

In proxy.py, if in
def authorize(headers):
line 122 del headers['Authorization'] is commented out, the test will run properly.
If the line is not commented out, def authorize(headers) will return an empty list.

sparql query is broken

the query for /api/graph/<namespace>/<project>/lineage is:

PREFIX prov: <http://www.w3.org/ns/prov#> 
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> 
PREFIX wfdesc: <http://purl.org/wf4ever/wfdesc#> 
PREFIX wf: <http://www.w3.org/2005/01/wf/flow#> 
PREFIX wfprov: <http://purl.org/wf4ever/wfprov#> 
PREFIX foaf: <http://xmlns.com/foaf/0.1/>  
SELECT ?v ?w 
WHERE {   
  {     
    SELECT ?entity     
    WHERE {       
      ?entity prov:atLocation ?project .           
      FILTER (?project = <https://dev.renku.ch/gitlab/rokroskar/zurich-bikes-data>)           
      ?entity (prov:qualifiedGeneration/prov:activity | ^prov:entity/^prov:qualifiedUsage) ?qactivity .           
      FILTER (?qactivity = <file:///85a49605d9a579221841720540ec6248c6775f49#>)     
    }     
    GROUP BY ?entity   
  }   
  {     
    ?entity prov:qualifiedGeneration/prov:activity ?activity .     
    BIND (?entity AS ?v)     
    BIND (?activity AS ?w)   
  } 
  UNION 
  {     
    ?activity prov:qualifiedUsage/prov:entity ?entity .     
    BIND (?activity AS ?v)     
    BIND (?entity AS ?w)   
  } 
}

The project URL should be without gitlab/ and the commit sha without a # at the end.

Strip all browser cookies before forwarding requests

It seems like some of the browser cookies are forwarded to the backend services where they might be used to authenticate API requests instead of the oAuth access token. This might lead to unexpected results.

Logout from Gitlab shouldn't rely on admin settings of Gitlab

A global setting of gitlab tells where it has to redirect after logout. On instances of gitlab that are not dedicated to Renku, like gitlab.com, this means the administrator has to choose which users will get a "wrong" redirection.
A solution is to copy the workaround for JupyterHub and use the logout iframe trick.

Fix infinte redirection on login

Depending on the order in which services come up during deployment, there are cases where the UI enters an infinite redirection loop when trying to log in.

Solve import issues

  • import app should work without side effects
  • import app.config should never sys.exit but rather raise an exception if necessary, better to the checks in the create_app

Create GitLab user if it doesn't exist

So far, we have assumed that a user corresponding to the identified keycloak user would already exist in GitLab. In the future this will not be the case. My suggestion is to just try with the keycloak username and - in case of a {"message":"404 User with ID or username \'demo\' Not Found" response - post a new user and try again.

update docs for new endpoint

  • README should be updated with relevant new chart values
  • endpoints.json
  • REST docs

Update: After the introduction of traefik we don't really need REST docs anymore, but the gateway-auth endpoints should be documented properly.

enrich the information returned by the graph query

At the moment, the graph query only returns the links between files and commits. We want to be able to use more information:

  • type of usage (usedBy, generatedBy)
  • type of node (activity, artifact)
  • any labels e.g. output_1

Forward pagination headers

The GitLab api returns several pagination-related X-... and link headers. Make sure these are forwarded by the gateway.

Provide API for lineage queries

MVP

  • Define relevant queries for forward/backward lineage query to Jena
  • Provide an API endpoint the lineage query

post - MVP

  • Add filtering capabilities
  • Query gitlab to check user access rights to private projects

set up CI

the sooner the better, especially with work being done on integration with other services.

remove need for gitlab sudo token

It seems that the gitlab sudo token isn't needed or used anymore. We should remove it from the charts and the related uses of sudo from the code.

Error polluting traefik logs

There are tons of error messages from traefik showing up in the gateway logs.

time="2019-04-18T12:42:01Z" level=error msg="Error while Peeking first byte: EOF"

Call to create webhook sends no or invalid oauth token

It looks at in some circumstances when Gatway does the POST to webhook-services' /projects/:id/webhooks it does send OAUTH-TOKEN header with some invalid token. We tried a few tests on the UI-team environment and when we did curl webhook-services' endpoint from its container console it works properly. So effectively these two calls where succeeding while failing with 401 from gitlab, when the same endpoint was hit by the Gateway.

curl -XPOST --header "OAUTH-TOKEN: 07fbaa9b39f0e208ab1cf48a819888dfe8740613cbd25bc0d2cb58700872e874" http://localhost:9001/projects/72/webhooks/validation -v

curl -XPOST --header "OAUTH-TOKEN: 07fbaa9b39f0e208ab1cf48a819888dfe8740613cbd25bc0d2cb58700872e874" http://localhost:9001/projects/72/webhooks -v

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.