-
Notifications
You must be signed in to change notification settings - Fork 20
feat(auth): implement RFC 8693 workload on-behalf-of token exchange #1178
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,9 +6,10 @@ | |
| This directory contains a local Authentik-backed NeMo Platform example. Use it | ||
| to validate three user-visible flows: | ||
|
|
||
| - log in to NeMo with Authentik | ||
| - call NeMo APIs through the Authentik gateway | ||
| - run a NeMo job whose workload exchanges a real Authentik workload subject token | ||
| - log in to NeMo Platform with Authentik | ||
| - call NeMo Platform APIs through the Authentik gateway | ||
| - run a NeMo Platform job whose workload exchanges a managed workload proof token for a | ||
| delegated NeMo Platform access token | ||
|
|
||
| All credentials in this example are for local development only. | ||
|
|
||
|
|
@@ -88,8 +89,10 @@ The 2-minute CLI access-token lifetime is a local demo/testing setting so token | |
| refresh is easy to observe. Do not use it as a production default; use a longer | ||
| value such as `hours=1` outside the refresh demonstration. | ||
|
|
||
| In the Docker Compose runtime, Authentik issues the demo workload subject token, | ||
| but it does not accept the RFC 8693 token exchange grant directly. The Docker | ||
| backend refreshes the Authentik subject token file, the SDK posts that token to | ||
| the NeMo auth service, and the gateway trusts the NeMo auth service JWKS for | ||
| exchanged workload access tokens. | ||
| In the Docker Compose runtime, Authentik authenticates users and controller | ||
| service principals, but managed Docker job OBO uses a NeMo Platform-owned | ||
| opaque workload proof token. The Docker backend writes that proof token into the | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe for my docker comment above, this is only for the authentik flow, and not for the "regular" flow? |
||
| job token file, the SDK posts it to the NeMo Platform auth service, and the | ||
| gateway trusts the NeMo Platform auth service JWKS for exchanged workload access | ||
| tokens. Docker OBO does not depend on IdP `jti` claims or IdP-issued workload | ||
| subject tokens. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -20,12 +20,12 @@ the parent directory: | |
|
|
||
| - `../config/platform-compose-authentik.yaml` as the NeMo Platform config. | ||
| - `../gateway/envoy.yaml` as the local gateway config. | ||
| - `../helm/files/blueprints` as the |product-name| blueprint source. | ||
| - `../helm/files/blueprints` as the NeMo Platform blueprint source. | ||
| - `../.generated` for local generated keys and certificates. | ||
|
|
||
| The shared tutorial does not build NeMo images for Compose. It runs | ||
| `${IMAGE_REGISTRY:-my-registry}/nmp-api:${BAKE_TAG:-local}` for both the NeMo | ||
| API service and workload jobs submitted by the tutorial. | ||
| The shared tutorial does not build NeMo Platform images for Compose. It runs | ||
| `${IMAGE_REGISTRY:-my-registry}/nmp-api:${BAKE_TAG:-local}` for both the | ||
| NeMo Platform API service and workload jobs submitted by the tutorial. | ||
|
|
||
| ## Services | ||
|
|
||
|
|
@@ -37,12 +37,12 @@ The stack contains: | |
| - `gateway-tls-init`: a small init container that copies local TLS material into | ||
| the named `gateway-tls` volume with permissions suitable for Envoy. | ||
| - `authentik-blueprint-init`: a one-shot init container that applies the shared | ||
| |product-name| blueprint before the gateway starts. | ||
| NeMo Platform blueprint before the gateway starts. | ||
| - `authentik-postgres`: PostgreSQL for Authentik. | ||
| - `authentik-redis`: Redis for Authentik. | ||
| - `authentik-server` and `authentik-worker`: Authentik itself. | ||
|
|
||
| `nemo` is only on the internal network. Host and workload traffic reaches NeMo | ||
| `nemo` is only on the internal network. Host and workload traffic reaches NeMo Platform | ||
| through the `gateway` service, which also joins the workload network as | ||
| `nemo-gateway`. | ||
|
|
||
|
|
@@ -59,15 +59,15 @@ share local keys: | |
| The workload-token private key is mounted into `nemo` at | ||
| `/var/run/secrets/nemo-platform/workload-token-signing/private-key.pem`. | ||
| `platform-compose-authentik.yaml` points | ||
| `auth.token_signing.private_key_file` at that mounted path. The NeMo auth | ||
| service uses the private key to sign workload-exchange access tokens and Scoped | ||
| Access Key JWTs, and Envoy validates those tokens through the NeMo auth service | ||
| JWKS endpoints. | ||
| `auth.token_signing.private_key_file` at that mounted path. The NeMo Platform | ||
| auth service uses the private key to sign workload-exchange access tokens and | ||
| Scoped Access Key JWTs, and Envoy validates those tokens through the | ||
| NeMo Platform auth service JWKS endpoints. | ||
|
|
||
| The gateway TLS files are copied into the `gateway-tls` named volume by | ||
| `gateway-tls-init`. The `gateway` service uses that volume to serve HTTPS, and | ||
| the `nemo` service mounts the same volume read-only so Python HTTP clients | ||
| inside NeMo trust the demo gateway certificate. | ||
| inside NeMo Platform trust the demo gateway certificate. | ||
|
|
||
| All generated keys and certificates in this example are for local development | ||
| only. | ||
|
|
@@ -94,7 +94,7 @@ The `nemo-setup` service account and app-password in the blueprint exist solely | |
| for automated auth-idp contract tests. They are not part of the browser login | ||
| flow or the workload identity pattern. | ||
|
|
||
| ## NeMo Compose Configuration | ||
| ## NeMo Platform Compose Configuration | ||
|
|
||
| `platform-compose-authentik.yaml` configures NeMo Platform for this topology: | ||
|
|
||
|
|
@@ -104,8 +104,11 @@ flow or the workload identity pattern. | |
| the `nemo` container. | ||
| - Host-side CLI login uses the port-forward-like public gateway URL | ||
| `https://127.0.0.1:18080`. | ||
| - Workload subject tokens come from Authentik's workload OIDC provider. | ||
| - Exchanged workload access tokens come from NeMo's `/apis/auth/token` endpoint. | ||
| - Authentik provides user and controller service-principal authentication. The | ||
| Docker managed-job OBO binding is stored in NeMo Platform auth delegation | ||
| state, not in Authentik. | ||
| - Exchanged workload access tokens come from NeMo Platform's `/apis/auth/token` | ||
| endpoint. | ||
|
|
||
| The Docker jobs executor mounts the `gateway-tls` volume into workload | ||
| containers and sets `SSL_CERT_FILE` and `REQUESTS_CA_BUNDLE` so workload code | ||
|
|
@@ -115,23 +118,25 @@ trusts the local gateway certificate. | |
|
|
||
| Envoy is the public entrypoint for the Compose example. It routes: | ||
|
|
||
| - NeMo paths such as `/.well-known/nemo-platform/`, `/apis/`, `/health/`, | ||
| `/status`, and `/studio/` to `nemo`. | ||
| - NeMo Platform paths such as `/.well-known/nemo-platform/`, `/apis/`, | ||
| `/health/`, `/status`, and `/studio/` to `nemo`. | ||
| - `/health/gateway/ready` to an Envoy-owned readiness check that verifies both | ||
| NeMo and Authentik through their upstream clusters. | ||
| NeMo Platform and Authentik through their upstream clusters. | ||
| - Authentik paths to `authentik-server`. | ||
|
|
||
| Before authentication, Envoy removes incoming `X-NMP-Principal-*` and | ||
| `X-NMP-Scopes` headers so a client cannot spoof identity or scopes. For | ||
| protected `/apis/` requests, Envoy calls NeMo's | ||
| `/apis/auth/authenticate` endpoint with the presented bearer token. The auth | ||
| service validates Authentik OIDC tokens, NeMo workload-exchange access tokens, | ||
| and NeMo Scoped Access Keys, then returns trusted `X-NMP-Principal-*` and | ||
| `X-NMP-Scopes` headers for Envoy to forward upstream. | ||
| protected `/apis/` requests, Envoy calls NeMo Platform's `/apis/auth/ext-authz` | ||
| endpoint with the presented bearer token. The auth service validates Authentik | ||
| OIDC tokens, NeMo Platform workload-exchange access tokens, and NeMo Platform | ||
| Scoped Access Keys, then returns trusted `X-NMP-Principal-*` and `X-NMP-Scopes` | ||
| headers for Envoy to forward upstream. The public `/apis/auth/authenticate` | ||
| endpoint remains a JSON diagnostic/API endpoint for direct callers and is not | ||
| the configured Envoy callout. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. envoy callout? |
||
|
|
||
| The gateway callout is required for dynamic or revocable Scoped Access Keys | ||
| because Envoy JWKS validation can only prove token signature, issuer, audience, | ||
| and time claims. It cannot check NeMo's access-key lifecycle state. Compose | ||
| and time claims. It cannot check NeMo Platform's access-key lifecycle state. Compose | ||
| keeps `auth.access_keys.enabled=true` so Scoped Access Keys can be created and | ||
| validated; Envoy performs the bearer-to-header mapping before the request | ||
| reaches service middleware. | ||
|
|
@@ -145,7 +150,7 @@ job request should not include `NMP_WORKLOAD_IDENTITY_TOKEN_FILE`, | |
| `NEMO_WORKLOAD_TOKEN`, or `NEMO_WORKLOAD_TOKEN_FILE`. | ||
|
|
||
| When a managed Docker workload starts, the backend creates a dedicated workload | ||
| identity volume, writes an Authentik subject token to: | ||
| identity volume and writes a NeMo Platform-owned Docker workload proof token to: | ||
|
|
||
| ```text | ||
| /var/run/secrets/nemo-platform/workload/token | ||
|
|
@@ -158,9 +163,21 @@ NMP_WORKLOAD_IDENTITY_TOKEN_FILE=/var/run/secrets/nemo-platform/workload/token | |
| ``` | ||
|
|
||
| The SDK reads that file and sends an RFC 8693 token exchange request to the | ||
| NeMo auth service through the gateway. The NeMo auth service validates the | ||
| Authentik subject token, mints a NeMo-signed access token, and returns it to the | ||
| workload. The workload uses that exchanged token for normal NeMo API calls. | ||
| NeMo Platform auth service through the gateway. The Docker backend registered | ||
| an internal workload delegation row before the container started. The | ||
| NeMo Platform auth service validates the proof token, checks the matching row, | ||
| mints a NeMo Platform-signed delegated access token, and returns it to the | ||
| workload. The access token uses the captured job submitter as the top-level | ||
| subject and the Docker workload as the RFC 8693 `act.sub` actor. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "Docker workload" is specific to local right? should this just say "container" here and below? since in kube it won't be docker |
||
|
|
||
| Docker supports one proof-token mechanism in this flow. The file contains a | ||
| private opaque proof token whose secret is stored only as a hash in the | ||
| delegation row. | ||
|
|
||
| Docker job OBO therefore does not require Authentik to issue a workload token | ||
| and does not depend on an IdP `jti` claim. The Authentik workload-provider | ||
| configuration in the manifest is retained for direct provider-token contract | ||
| tests, not for the managed Docker job exchange loop. | ||
|
|
||
| The useful end-to-end validation is the workload job in the shared tutorial: | ||
| the job uses the exchanged token to call the NeMo Platform API and read the | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hehe
Authentik authenticatessounds awkward