Read-through caching forward proxy for attestation collateral (AMD KDS, Intel PCS, NVIDIA RIM).
Clients request collateral using the same paths the upstream vendors use, but against the proxy's address instead of the vendor host. The proxy maps the path prefix to the upstream, serves a fresh copy from its cache when it has one, and otherwise fetches, stores, and returns the upstream response.
Requests are routed to an upstream by path prefix. Only GET is accepted (anything else returns 405).
Unknown paths return 404.
| Path prefix | Upstream host | Vendor |
|---|---|---|
/vcek/, /vlek/ |
kdsintf.amd.com |
AMD KDS |
/sgx/, /tdx/ |
api.trustedservices.intel.com |
Intel PCS |
/IntelSGX… |
certificates.trustedservices.intel.com |
Intel SGX Root CA |
/v1/rim/ |
rim.attestation.nvidia.com |
NVIDIA RIM |
For a matched request the proxy reconstructs the upstream URL as https://<host><path>?<query>.
The path and query string are passed through unchanged, only the host (and scheme) are set by the proxy.
- Look up the reconstructed upstream URL in the cache.
- Fresh cache entry exists: return the cached response directly.
- Stale cache entry exists: fetch the upstream.
- On success, the response is returned to the caller.
- On upstream failure, return stale entry as fallback.
- No cache entry exists: fetch the upstream.
- On success, the response is returned to the caller.
- On upstream failure, return
502 Bad Gateway.
Relevant response headers are forwarded to the client.
Only 200 OK responses from upstream are cached.
Each entry's freshness lifetime is computed at store time, in priority order:
- CRLs: the
nextUpdatefield parsed from the CRL itself. - Otherwise the upstream response's
Cache-Control: max-age(honoringno-cache/no-store/must-understand). - Otherwise a default TTL of 1 hour.
GET /healthz: returnsok, use as a readiness probe.GET /metrics: Prometheus metrics:collateral_proxy_requests_total{result, document}:resultis one ofhit,miss,stale,error,rejecteddocumentis one ofcrl,ak-cert,collateral,unknown
collateral_proxy_upstream_responses_total{code, document}: upstream outcomes by HTTP status code (orerrorwhen the fetch itself failed).
| Flag | Default | Description |
|---|---|---|
-addr |
:80 |
Listen address. |
-state-dir |
/var/lib/collateral-proxy |
Directory for on-disk cache state. |
-upstream-timeout |
10s |
Per-request timeout for upstream fetches. |
The published image is ghcr.io/edgelesssys/collateral-proxy:latest:
docker run -p 8080:80 -v collateral-proxy-state:/var/lib/collateral-proxy ghcr.io/edgelesssys/collateral-proxy:latestMount a persistent volume at -state-dir so the cache survives restarts.
Each release attaches a collateral-proxy.yaml asset to its GitHub Release.
curl -fLO https://github.com/edgelesssys/collateral-proxy/releases/latest/download/collateral-proxy.yml
kubectl apply -f collateral-proxy.ymlClients fetch collateral from the proxy using the vendor's own paths. For example, a VCEK certificate normally fetched from
https://kdsintf.amd.com/vcek/v1/Milan/<hwid>?blSPL=...&teeSPL=...
is instead fetched from the proxy:
http://<proxy-host>/vcek/v1/Milan/<hwid>?blSPL=...&teeSPL=...
The proxy preserves the path and query and rewrites only the host, so clients only need their collateral base URL repointed at the proxy.
- Prerequisites: Nix (flakes) and/or a Go toolchain;
direnv/.envrc. - Build the binary:
nix build .#collateral-proxy. - Build the container image:
nix build .#container. - Push the image:
nix run .#push -- [tag](defaults to the:dev). - Push the image and render the pinned deployment manifest:
nix run .#render-k8s-resources -- [tag]. - Format:
nix fmt. - Lint:
nix run .#lint. - Vuln scan:
nix run .#govulncheck. - Run formatters and tests:
nix flake check.
-
Bump version in
version.txt. -
Push to
maingit commit -am "release: v0.X.0" git push origin release-v0.X.0 -
Open a PR and merge to
main. -
CI running on main publishes
ghcr.io/edgelesssys/collateral-proxy:v0.X.0and moves:latest. -
Push the
v0.X.0tag. CI then publishes a GitHub Release and attachescollateral-proxy.yaml, the deployment manifest pinned tov0.X.0@sha256:<digest>.