Skip to content

Add ate-idp-server to serve the discovery document and key set for actor JWTs - #2099

Open
Max Thompson (thompsonmax) wants to merge 4 commits into
agent-substrate:mainfrom
thompsonmax:ate-idp-server
Open

Max Thompson (thompsonmax) wants to merge 4 commits into
agent-substrate:mainfrom
thompsonmax:ate-idp-server

Conversation

@thompsonmax

@thompsonmax Max Thompson (thompsonmax) commented Oct 1, 2026 •

Copy link
Copy Markdown
Collaborator

Part of #1756. Stacked on #2132; only the last two commits are new.

  • Adds cmd/ate-idp-server, which serves <issuer>/.well-known/openid-configuration and <issuer>/openid/v1/jwks over HTTPS with a servicedns credential bundle, so relying parties can verify actor JWTs.
  • Reads the actor JWT authority pool from --actor-id-jwt-pool with localjwtauthority.RefreshingPool, the reader ate-api-server signs with, and builds both documents from the pool's verification keys on each request.
  • Serves the last good documents if the pool can't be read or published, and reports not ready with 503 until it has built any, so relying parties never cache an empty set.
  • Requires --issuer rather than deriving a default, so it can't disagree with ateapi; the installer will pass both the same value.
  • Records in docs/metrics/substrate.yaml that it emits no metrics.

For reviewers: the pool holds the signing keys, so ate-idp-server will mount the same Secret ate-api-server does, in place of the controller and ConfigMap in #2134. Nothing deploys it yet.

Testing: unit tests for routing (including issuer paths and dot segments), headers, readiness, key changes, last-good serving, and required flags, plus a local run over TLS against a generated pool. E2E can't cover it until it's deployed.

  • Tests pass
  • Appropriate changes to documentation are included in the PR

Comment thread cmd/ate-idp-server/internal/server/server.go Outdated
@wiz-alphabet

wiz-alphabet Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

Wiz Scan Summary

Scanner Findings
Vulnerability Finding Vulnerabilities -
Data Finding Sensitive Data -
Secret Finding Secrets -
IaC Misconfiguration IaC Misconfigurations -
SAST Finding SAST Findings 1 Medium 1 Low
Software Management Finding Software Management Findings -
Total 1 Medium 1 Low

View scan details in Wiz

To detect these findings earlier in the dev lifecycle, try the Wiz Code extension for VS Code, JetBrains, or Visual Studio.

Add JWKS, which serializes verification keys into the JWK set relying
parties fetch from the issuer, sorted by key ID. It encodes with
go-jose's JSONWebKey, as the Kubernetes service account issuer does, and
adds the checks go-jose leaves to the caller: the set is not empty, key
IDs are present and unique, and each algorithm fits its key (ES256 for
P-256 EC, RS256, RS384, or RS512 for RSA), so a malformed pool cannot
publish a set that verifiers would reject.

localjwtauthority.VerificationKey gains Algorithm so callers can publish
it.
The rest of Substrate's JWT code is in-tree, and go-jose was only used
here, for RFC 7638 thumbprints and JWK encoding.

Thumbprint is now the base64url SHA-256 of the key's PKIX
(SubjectPublicKeyInfo) DER encoding. Key IDs are opaque to relying
parties, and pools store each key's ID, so existing keys keep theirs;
only new keys get IDs in the new form.

JWKS encodes RSA and P-256 keys itself, with the RFC 7518 parameters,
padding EC coordinates to the curve size. go-jose stays in go.mod only
as an indirect dependency of go-spiffe.
Add a small read-only HTTPS server for the OpenID discovery document and
JWK set that relying parties use to verify actor JWTs. It serves the key
set from --jwks-file as is, builds the discovery document from the keys'
algorithms with oidcdiscovery.DiscoveryDocument, and reloads when the
file changes, keeping the last good set if a new one does not parse.
Routes sit under the issuer's path and match on the cleaned path.

--issuer is required. The installer passes the same value it gives
ateapi, so the two cannot derive different issuers.

Until a key set loads, /readyz and both documents return 503, so a
relying party never caches an empty set. TLS uses the servicedns
credential bundle through credbundle, which picks up rotated
certificates. The server emits no metrics, which
docs/metrics/substrate.yaml records.
ate-idp-server read a JWK set that a controller was to derive from the
authority pool and copy into a ConfigMap. It now mounts the pool itself
and reads it with localjwtauthority.RefreshingPool, the same reader
ate-api-server signs with, so publishing a key needs no second component
and no internal ConfigMap. --actor-id-jwt-pool replaces --jwks-file and
--reload-interval; the pool is reread at most once a minute.

Each request builds the key set and discovery document from the pool's
verification keys. RefreshingPool fails every call after a bad reread,
so the server keeps the last documents it built and serves those; until
it has built any, it reports not ready and answers with 503.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants