Skip to content

feat(spiffe): JWT-SVID signing endpoint (RFC UAA-SPIFFE-001)#3968

Draft
rkoster wants to merge 9 commits into
cloudfoundry:developfrom
rkoster:spiffe-signer
Draft

feat(spiffe): JWT-SVID signing endpoint (RFC UAA-SPIFFE-001)#3968
rkoster wants to merge 9 commits into
cloudfoundry:developfrom
rkoster:spiffe-signer

Conversation

@rkoster

@rkoster rkoster commented Jun 29, 2026

Copy link
Copy Markdown

Summary

Adds a JWT-SVID signing endpoint to UAA so it can act as a SPIFFE identity
server for Cloud Foundry workloads (offline-OIDC federation).

POST /jwt-svid/sign authenticates a SPIFFE Agent (client credentials), verifies
the caller's CF instance-identity certificate against the configured CA,
parses org/space/app GUIDs from the cert OUs, checks a proof-of-possession
signature for freshness, and mints an RS256 JWT-SVID signed with UAA's active key
and OIDC issuer (so it verifies offline against /token_keys).

Key changes (all under org.cloudfoundry.identity.uaa.spiffe)

  • JwtSvidController, JwtSvidRequest / JwtSvidResponse, JwtSvidSigner
  • InstanceIdentityVerifier, CertificateOuParser, ProofOfPossessionVerifier
  • SpiffeId, CfInstanceIdentity, SpiffeProperties, SpiffeConfiguration,
    SpiffeSecurityConfiguration
  • Full unit-test coverage for each component (9 focused commits).

Context

Part of RFC UAA-SPIFFE-001UAA as a SPIFFE Identity Server for Cloud Foundry.

Cross-repo dependencies

  • cloudfoundry/uaa-release spiffe-signer — exposes uaa.spiffe.* (trust
    domain, instance-identity CA) and the signer client.
  • Consumed by the SPIFFE Agent in cloudfoundry/diego-release spiffe-agent.

Status

Draft / POC. End-to-end verified on bosh-lite: a CF app received a valid JWT-SVID
with cf org/space/app/process_type claims.

rkoster added 9 commits June 26, 2026 19:26
Reject process_type not matching [A-Za-z0-9_-]{1,63} and audience that is blank, over 512 chars, or contains control characters (400). process_type is concatenated into the SPIFFE ID path and every proof-of-possession message field must be newline-free, so this closes SPIFFE-ID injection and PoP message ambiguity. Adds characterization tests pinning existing fail-closed behaviour: malformed PEM, future-dated and malformed-base64 PoP, and a not-yet-valid instance cert.
Comment on lines +49 to +59
SecurityFilterChain chain = http
.securityMatcher("/jwt-svid/**")
.authorizeHttpRequests(auth -> {
auth.requestMatchers("/**").hasAuthority("uaa.resource");
auth.anyRequest().denyAll();
})
.authenticationManager(clientAuthenticationManager)
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.addFilterAt(clientAuthenticationFilter.getFilter(), BasicAuthenticationFilter.class)
.anonymous(AnonymousConfigurer::disable)
.csrf(CsrfConfigurer::disable)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Development

Successfully merging this pull request may close these issues.

2 participants