Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/build-docker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ jobs:
- name: dataplane
context: launchers/dataplane
dockerfile: launchers/dataplane/src/main/docker/Dockerfile
- name: clearglass
context: clearglass
dockerfile: clearglass/Dockerfile

steps:
- name: Checkout code
Expand Down
9 changes: 6 additions & 3 deletions .github/workflows/e2e-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,20 +48,22 @@ jobs:
uses: azure/setup-helm@v5

- name: "Build runtime images"
run: |
run: |-
./gradlew dockerize
docker buildx build -f clearglass/Dockerfile -t ghcr.io/metaform/jad/clearglass:latest clearglass

- name: "Create k8s Kind Cluster"
uses: helm/kind-action@v1.14.0
with:
cluster_name: jad

- name: "Load runtime images into KinD"
run: |
run: |-
kind load docker-image -n jad ghcr.io/metaform/jad/controlplane:latest \
ghcr.io/metaform/jad/dataplane:latest \
ghcr.io/metaform/jad/identity-hub:latest \
ghcr.io/metaform/jad/issuerservice:latest
ghcr.io/metaform/jad/issuerservice:latest \
ghcr.io/metaform/jad/clearglass:latest


- name: "Install Traefik Gateway controller"
Expand Down Expand Up @@ -94,6 +96,7 @@ jobs:
--selector=type=edcv-infra \
--timeout=300s


- name: "Deploy JAD applications"
run: |-
# this is crucial - without it, KinD would take the prebuilt images from GHCR
Expand Down
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@ out/
.idea/

.terraform*
*terraform.tfstate*
*terraform.tfstate*
**/target
/authproxy/target/
87 changes: 87 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,23 @@ and now want to see it in action, please follow the following steps to build and

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

#### 2.3 Build and deploy clearglass

Clearglass is a small Rust application that acts as a reverse proxy for the JAD services and is described in more
detail in a [later chapter](#jads-apis--a-single-pane-of-glass). It is being deployed as part of the base (or
infrastructure)
layer.

For now, we have to build and load it manually using the following commands:

```shell
docker buildx build -f clearglass/Dockerfile -t ghcr.io/metaform/jad/clearglass:latest clearglass
kind load docker-image -n jad ghcr.io/metaform/jad/clearglass:latest
```

_Note that in a later evolution of JAD clearglass will be moved into its own repository which will make this step
obsolete._

### 3. Deploy the services

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

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

## JAD's APIs – A single pane of glass

All JAD services are exposed through a single Traefik gateway (`edcv-gateway`) on `jad.localhost`, acting as a single
pane of glass. Each service is reachable via a path prefix that is rewritten before forwarding to the backend.

Authentication is enforced at the gateway level using Traefik `ForwardAuth` middlewares. Each middleware forwards the
`Authorization` header to the `clearglass` service, which validates the Bearer token against Keycloak via RFC 7662
token introspection and checks for the required OAuth2 scopes. Services without a `middleware` entry listed are
unauthenticated at the gateway level.

### Application routes (`jad.localhost`)

| Service | Exposed path | Rewrites to | Backend port | Auth middleware |
|---------------------|---------------------|-------------------------|--------------|----------------------------------------|
| Control Plane | `/api/management` | `/api/mgmt` | `8081` | `jwt-auth-management-api` |
| Identity Hub | `/api/identity` | `/api/identity/v1alpha` | `7081` | `jwt-auth-identity-api` |
| Issuer Service | `/api/issuer/admin` | `/api/admin/v1alpha` | `10013` | `jwt-auth-issuer-admin-api` |
| Provision Manager | `/api/pm` | `/api/v1alpha` | `8080` | `jwt-auth-provision-manager-api` |
| Tenant Manager | `/api/tm` | `/api/v1alpha1` | `8080` | `jwt-auth-tenant-manager-api` |
| Dataplane (public) | `/api/dp/public` | `/` | `11002` | β€” |
| Dataplane (control) | `/api/dp/control` | `/` | `8083` | β€” |
| Dataplane (certs) | `/api/dp/certs` | `/` | `8186` | β€” |
| Siglet | `/api/siglet` | `/` | `8080` | β€” |
| Redline | `/redline` | `/` | `8081` | β€” |
| Keycloak | `/auth` | `/` | `8080` | β€” (is the auth server) |
| Web UI | `/ui` | `/` | `80` | β€” (obtains its own token via Keycloak) |

### Auth middleware scopes

Each `jwt-auth-*` middleware enforces a specific pair of OAuth2 scopes (`read` and `write`):

| Middleware | Required scopes |
|----------------------------------|-------------------------------------------------------------|
| `jwt-auth-management-api` | `management-api:read`, `management-api:write` |
| `jwt-auth-identity-api` | `identity-api:read`, `identity-api:write` |
| `jwt-auth-issuer-admin-api` | `issuer-admin-api:read`, `issuer-admin-api:write` |
| `jwt-auth-provision-manager-api` | `provision-manager-api:read`, `provision-manager-api:write` |
| `jwt-auth-tenant-manager-api` | `tenant-manager-api:read`, `tenant-manager-api:write` |

### Infrastructure routes (each on their own hostname)

Infrastructure services are not protected by the auth middleware and are only intended for local development access.

| Service | Hostname | Remark |
|------------|------------------------|----------------------------------------------------------------|
| Grafana | `grafana.localhost` | |
| Prometheus | `prometheus.localhost` | |
| Jaeger | `jaeger.localhost` | |
| Loki | `loki.localhost` | |
| Vault | `vault.localhost` | access from outside the cluster is only intended for e2e tests |

### Clearglass

`clearglass` is a small sidecar service (`ghcr.io/metaform/jad/clearglass`) that acts as the authentication and
authorization enforcement point for all protected APIs. Traefik's `ForwardAuth` mechanism intercepts every inbound
request and calls `clearglass`'s `/validate` endpoint before forwarding it to the backend.

The proxy performs two checks:

1. **Token validation** β€” it calls Keycloak's RFC 7662 token introspection endpoint
(`/realms/edcv/protocol/openid-connect/token/introspect`) using its own client credentials (`clearglass` /
`clearglass-secret`) to verify that the Bearer token in the `Authorization` header is active.
2. **Scope check** β€” the required OAuth2 scopes are passed as `?scope=` query parameters by each Traefik middleware.
The proxy checks that the token carries at least those scopes. If either check fails, the request is rejected with
`401 Unauthorized` before it ever reaches the backend service.

This design keeps authentication logic out of the individual services and centralizes it in one place, making it easy
to add or modify access rules by updating the middleware definitions in
[`k8s/base/jwt-middleware.yaml`](k8s/base/jwt-middleware.yaml).

## Deploying JAD on a bare-metal/cloud-hosted Kubernetes

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