Skip to content

Commit cc6a68d

Browse files
feat: single pane of glass — unified API gateway with auth proxy (#103)
* feat: all admin apis are available unter the same base path * administration API: add auth proxy, remove some unneeded paths * update API paths and bruno collection * fix tests (wip) * update bruno requests * exclude siglet API from auth proxy * update dataplane base URL * update readme * build and upload auth-proxy * refactor method name * reorder workflow actions * authenticate siglet's token API * rewrite auth-proxy in rust * fix e2e test and docker publish * simplify e2e tests * authproxy: tests, sourcedoc * add e2e tests for SPG * rename auth-proxy -> clearglass
1 parent 1048cdb commit cc6a68d

61 files changed

Lines changed: 3513 additions & 261 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/build-docker.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ jobs:
4141
- name: dataplane
4242
context: launchers/dataplane
4343
dockerfile: launchers/dataplane/src/main/docker/Dockerfile
44+
- name: clearglass
45+
context: clearglass
46+
dockerfile: clearglass/Dockerfile
4447

4548
steps:
4649
- name: Checkout code

.github/workflows/e2e-tests.yaml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,20 +48,22 @@ jobs:
4848
uses: azure/setup-helm@v5
4949

5050
- name: "Build runtime images"
51-
run: |
51+
run: |-
5252
./gradlew dockerize
53+
docker buildx build -f clearglass/Dockerfile -t ghcr.io/metaform/jad/clearglass:latest clearglass
5354
5455
- name: "Create k8s Kind Cluster"
5556
uses: helm/kind-action@v1.14.0
5657
with:
5758
cluster_name: jad
5859

5960
- name: "Load runtime images into KinD"
60-
run: |
61+
run: |-
6162
kind load docker-image -n jad ghcr.io/metaform/jad/controlplane:latest \
6263
ghcr.io/metaform/jad/dataplane:latest \
6364
ghcr.io/metaform/jad/identity-hub:latest \
64-
ghcr.io/metaform/jad/issuerservice:latest
65+
ghcr.io/metaform/jad/issuerservice:latest \
66+
ghcr.io/metaform/jad/clearglass:latest
6567
6668
6769
- name: "Install Traefik Gateway controller"
@@ -94,6 +96,7 @@ jobs:
9496
--selector=type=edcv-infra \
9597
--timeout=300s
9698
99+
97100
- name: "Deploy JAD applications"
98101
run: |-
99102
# this is crucial - without it, KinD would take the prebuilt images from GHCR

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,6 @@ out/
88
.idea/
99

1010
.terraform*
11-
*terraform.tfstate*
11+
*terraform.tfstate*
12+
**/target
13+
/authproxy/target/

README.md

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,23 @@ and now want to see it in action, please follow the following steps to build and
205205

206206
For this, both the EDC-V and CFM docker images must be built locally!!
207207

208+
#### 2.3 Build and deploy clearglass
209+
210+
Clearglass is a small Rust application that acts as a reverse proxy for the JAD services and is described in more
211+
detail in a [later chapter](#jads-apis--a-single-pane-of-glass). It is being deployed as part of the base (or
212+
infrastructure)
213+
layer.
214+
215+
For now, we have to build and load it manually using the following commands:
216+
217+
```shell
218+
docker buildx build -f clearglass/Dockerfile -t ghcr.io/metaform/jad/clearglass:latest clearglass
219+
kind load docker-image -n jad ghcr.io/metaform/jad/clearglass:latest
220+
```
221+
222+
_Note that in a later evolution of JAD clearglass will be moved into its own repository which will make this step
223+
obsolete._
224+
208225
### 3. Deploy the services
209226

210227
JAD uses plain Kubernetes manifests to deploy the services. All the manifests are located in the [k8s](./k8s) folder.
@@ -399,6 +416,76 @@ For example, if a participant onboarding went only through half-way, we recommen
399416

400417
In some cases, even deleting and re-creating the KinD cluster may be required.
401418

419+
## JAD's APIs – A single pane of glass
420+
421+
All JAD services are exposed through a single Traefik gateway (`edcv-gateway`) on `jad.localhost`, acting as a single
422+
pane of glass. Each service is reachable via a path prefix that is rewritten before forwarding to the backend.
423+
424+
Authentication is enforced at the gateway level using Traefik `ForwardAuth` middlewares. Each middleware forwards the
425+
`Authorization` header to the `clearglass` service, which validates the Bearer token against Keycloak via RFC 7662
426+
token introspection and checks for the required OAuth2 scopes. Services without a `middleware` entry listed are
427+
unauthenticated at the gateway level.
428+
429+
### Application routes (`jad.localhost`)
430+
431+
| Service | Exposed path | Rewrites to | Backend port | Auth middleware |
432+
|---------------------|---------------------|-------------------------|--------------|----------------------------------------|
433+
| Control Plane | `/api/management` | `/api/mgmt` | `8081` | `jwt-auth-management-api` |
434+
| Identity Hub | `/api/identity` | `/api/identity/v1alpha` | `7081` | `jwt-auth-identity-api` |
435+
| Issuer Service | `/api/issuer/admin` | `/api/admin/v1alpha` | `10013` | `jwt-auth-issuer-admin-api` |
436+
| Provision Manager | `/api/pm` | `/api/v1alpha` | `8080` | `jwt-auth-provision-manager-api` |
437+
| Tenant Manager | `/api/tm` | `/api/v1alpha1` | `8080` | `jwt-auth-tenant-manager-api` |
438+
| Dataplane (public) | `/api/dp/public` | `/` | `11002` ||
439+
| Dataplane (control) | `/api/dp/control` | `/` | `8083` ||
440+
| Dataplane (certs) | `/api/dp/certs` | `/` | `8186` ||
441+
| Siglet | `/api/siglet` | `/` | `8080` ||
442+
| Redline | `/redline` | `/` | `8081` ||
443+
| Keycloak | `/auth` | `/` | `8080` | — (is the auth server) |
444+
| Web UI | `/ui` | `/` | `80` | — (obtains its own token via Keycloak) |
445+
446+
### Auth middleware scopes
447+
448+
Each `jwt-auth-*` middleware enforces a specific pair of OAuth2 scopes (`read` and `write`):
449+
450+
| Middleware | Required scopes |
451+
|----------------------------------|-------------------------------------------------------------|
452+
| `jwt-auth-management-api` | `management-api:read`, `management-api:write` |
453+
| `jwt-auth-identity-api` | `identity-api:read`, `identity-api:write` |
454+
| `jwt-auth-issuer-admin-api` | `issuer-admin-api:read`, `issuer-admin-api:write` |
455+
| `jwt-auth-provision-manager-api` | `provision-manager-api:read`, `provision-manager-api:write` |
456+
| `jwt-auth-tenant-manager-api` | `tenant-manager-api:read`, `tenant-manager-api:write` |
457+
458+
### Infrastructure routes (each on their own hostname)
459+
460+
Infrastructure services are not protected by the auth middleware and are only intended for local development access.
461+
462+
| Service | Hostname | Remark |
463+
|------------|------------------------|----------------------------------------------------------------|
464+
| Grafana | `grafana.localhost` | |
465+
| Prometheus | `prometheus.localhost` | |
466+
| Jaeger | `jaeger.localhost` | |
467+
| Loki | `loki.localhost` | |
468+
| Vault | `vault.localhost` | access from outside the cluster is only intended for e2e tests |
469+
470+
### Clearglass
471+
472+
`clearglass` is a small sidecar service (`ghcr.io/metaform/jad/clearglass`) that acts as the authentication and
473+
authorization enforcement point for all protected APIs. Traefik's `ForwardAuth` mechanism intercepts every inbound
474+
request and calls `clearglass`'s `/validate` endpoint before forwarding it to the backend.
475+
476+
The proxy performs two checks:
477+
478+
1. **Token validation** — it calls Keycloak's RFC 7662 token introspection endpoint
479+
(`/realms/edcv/protocol/openid-connect/token/introspect`) using its own client credentials (`clearglass` /
480+
`clearglass-secret`) to verify that the Bearer token in the `Authorization` header is active.
481+
2. **Scope check** — the required OAuth2 scopes are passed as `?scope=` query parameters by each Traefik middleware.
482+
The proxy checks that the token carries at least those scopes. If either check fails, the request is rejected with
483+
`401 Unauthorized` before it ever reaches the backend service.
484+
485+
This design keeps authentication logic out of the individual services and centralizes it in one place, making it easy
486+
to add or modify access rules by updating the middleware definitions in
487+
[`k8s/base/jwt-middleware.yaml`](k8s/base/jwt-middleware.yaml).
488+
402489
## Deploying JAD on a bare-metal/cloud-hosted Kubernetes
403490

404491
KinD is geared towards local development and testing. For example, it comes with a bunch of useful defaults, such as

0 commit comments

Comments
 (0)