Skip to content

Install the Gateway

The Gateway is the entry point that Andromeda uses to talk to PERSEUS. Every request from Andromeda goes through the Gateway, which authenticates the user via Keycloak (OIDC), enforces an allowlist of permitted PERSEUS endpoints, manages sessions, applies rate limiting and response caching, and proxies the request to the PERSEUS API.

PERSEUS holds sensitive data for the operation of a data centre, so it is often deployed only on an internal network with no direct access from the outside. Andromeda and other external sites, however, still need to read and write certain data.

The Gateway bridges this gap. It is the single component that is exposed externally, while PERSEUS itself stays on the internal network. Because every request passes through the Gateway, it can make exactly the data that should be available externally reachable — and nothing else: it requires authentication, only forwards calls to an explicit allowlist of PERSEUS endpoints, and rate-limits clients before any request reaches PERSEUS.

This guide explains how to run the Gateway with Docker.

  • Docker Engine.
  • A running PERSEUS instance reachable from the Gateway, plus a PERSEUS API token for it.
  • A Keycloak server with a realm and a client for the Gateway (see below).
  • The public URL of Andromeda, used for login and callback redirects.

The Gateway delegates authentication to a Keycloak server, which is an external component. Configuring and operating Keycloak is out of scope for this guide — please refer to the Keycloak documentation. The Gateway only needs a realm and an OIDC client in it, configured as follows:

  • Client ID matching KEYCLOAK_CLIENT_ID (e.g. perseus-gateway).
  • Client authentication enabled (a confidential client), so the client has a client secret — this is the value you set as KEYCLOAK_CLIENT_SECRET.
  • Standard flow enabled (the OpenID Connect authorization code flow), used to log users in.
  • A valid redirect URI of https://<host>/gateway/auth/callback. You can use an exact URI per environment or a pattern such as https://<host>/gateway/*.
  • Service accounts enabled. The Gateway also calls Keycloak’s admin REST API (to read the available identity providers and to link or unlink user accounts), authenticating with the client’s service account via the client-credentials flow.
  • The service account needs the following realm-management client roles, otherwise those admin calls are rejected:
    • view-realm
    • view-users and manage-users
    • view-identity-providers

You point the Gateway at these via the KEYCLOAK_* variables below. Which identity providers the realm offers (LDAP, GitHub, ORCID, …) is entirely up to your Keycloak setup.

The Gateway authenticates against PERSEUS with a single API token (PERSEUS_API_TOKEN). In PERSEUS, API tokens are scoped to specific endpoints, so the token you create must be authorized for every endpoint the Gateway forwards Andromeda’s requests to. See API usage for how to create and scope a token.

At minimum the token needs the following endpoints:

GET /country
GET /service/Andromeda/*
POST /service/Andromeda/*
DELETE /service/Andromeda/*
GET /service/UserPortal/*
POST /service/UserPortal/*
GET /service/AffiliationManager/*
GET /service/ResourceManager/*
GET /service/PersonSearch/*
POST /service/PersonCreate/*
POST /service/PersonIdentity/*
POST /service/PersonEdit/*
GET /service/SystemStatus/*
GET /service/PriorityManager/*
Terminal window
docker pull docker.io/pc2upb/perseus-gateway:latest

The Gateway is configured entirely through environment variables. Create a .env file with the values for your deployment:

Terminal window
# PERSEUS API
PERSEUS_API_TOKEN=<your-perseus-api-token>
PERSEUS_BASE_URL=https://perseus.example.org/api
# Routing — the Gateway is mounted under /gateway
FASTAPI_ROOT_PATH=/gateway
ANDROMEDA_URL=https://perseus.example.org
# Keycloak
KEYCLOAK_URL=https://keycloak.example.org
KEYCLOAK_REALM=perseus
KEYCLOAK_CLIENT_ID=perseus-gateway
KEYCLOAK_CLIENT_SECRET=<client-secret-from-keycloak>
USERNAME_SAFE_IDP=ldap
# Sessions — set a long random secret and keep cookies secure in production
SESSION_SECRET_KEY=<long-random-secret>
SESSION_COOKIE_SECURE=true
SESSION_COOKIE_DOMAIN=perseus.example.org
# Persistence (SQLite file inside the container, see "Run with Docker")
DATABASE_URL=sqlite:///./perseus_gateway.db
Environment variableDescriptionDefault
PERSEUS_API_TOKENShared secret used to authenticate against the PERSEUS API.perseus_api_key
PERSEUS_BASE_URLBase URL of the PERSEUS API.http://perseus.localhost
PERSEUS_REQUEST_TIMEOUTTimeout in seconds for requests to the PERSEUS API.15
FASTAPI_ROOT_PATHRoot path the application is mounted under. Must match your reverse proxy./gateway
ANDROMEDA_URLBase URL of the Andromeda frontend used for login and callback redirects.http://andromeda.localhost:6173
ANDROMEDA_PROFILE_PATHProfile page path used after account-link flows./profile
KEYCLOAK_URLBase URL of the Keycloak server.http://localhost:8080
KEYCLOAK_REALMKeycloak realm name.perseus
KEYCLOAK_CLIENT_IDOIDC client ID for the Gateway.perseus-gateway
KEYCLOAK_CLIENT_SECRETOIDC client secret for the Gateway.perseus-gateway-secret
USERNAME_SAFE_IDP

kc_idp_hint of the identity provider trusted to provide stable, unique usernames; users authenticating via it are matched by username before falling back to email.

ldap
SESSION_COOKIE_NAMEName of the session cookie.perseus_session
SESSION_COOKIE_SAMESITESameSite policy for the session cookie.lax
SESSION_COOKIE_SECUREWhether the session cookie is only sent over HTTPS. Use true in production.true
SESSION_COOKIE_DOMAINDomain scope for the session cookie.unset
SESSION_TTL_SECONDSSession lifetime in seconds.86400
SESSION_SECRET_KEYSecret key for signing session cookies. Set a long random value.perseus_session_secret
DATABASE_URLConnection string for the Gateway’s persistence layer.sqlite:///./perseus_gateway.db
RATE_LIMIT_PER_MINUTERequests allowed per minute for a client.5
CACHE_EXPIRE_SECONDSTTL for cached responses in seconds.60
VALIDATE_CACHE_SECONDSTTL in seconds for cached authentication validation responses.5
LOGIN_OPTIONSSource for /auth/login-options; supported modes are KEYCLOAK and FILE.KEYCLOAK
LOGIN_OPTIONS_PATHJSON file used for login options when LOGIN_OPTIONS=FILE.login_options.json
ALLOWED_ENDPOINTS_CONFIG_PATHJSON file containing the allowed PERSEUS endpoints.allowed_endpoints.json

The Gateway only forwards requests to PERSEUS endpoints that are explicitly allowed in allowed_endpoints.json. Each entry defines the relative path (no leading slash), the permitted HTTP methods, and whether authentication is required. Paths accept Unix shell-style wildcards (for dynamic segments such as OIDs), and "*" in methods allows every verb.

{
"endpoints": [
{ "path": "country", "methods": ["GET"], "require_login": false },
{ "path": "service/UserPortal/user", "methods": ["GET", "POST"], "require_login": true }
]
}

When LOGIN_OPTIONS=FILE, the available login providers are served from login_options.json instead of being fetched from Keycloak.

The image ships with default versions of both files which are suiteable for usage with Andromeda. To use your own, mount them into the container and point ALLOWED_ENDPOINTS_CONFIG_PATH / LOGIN_OPTIONS_PATH at them (see below). Restart the Gateway after changing these files so the new rules are loaded.

Terminal window
docker run -d \
--name perseus-gateway \
--env-file .env \
-p 8099:5000 \
-v perseus-gateway-data:/usr/src/app/data \
-v "$(pwd)/allowed_endpoints.json:/usr/src/app/allowed_endpoints.json:ro" \
--restart unless-stopped \
docker.io/pc2upb/perseus-gateway:latest

The container listens on port 5000; the example maps it to 8099 on the host.

Terminal window
docker pull docker.io/pc2upb/perseus-gateway:latest
docker rm -f perseus-gateway
# re-run the docker run command above

Expose the Gateway publicly under BASEURL/gateway. The path must match FASTAPI_ROOT_PATH. Here is an example configuration for Nginx.

server {
listen 80;
server_name perseus.example.org;
location /gateway/ {
proxy_pass http://127.0.0.1:8099/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
}
}

Once the container is running and the proxy is in place, the OpenAPI/Swagger UI is available at:

https://perseus.example.org/gateway/docs