You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Let an EgressPolicy hostname rule ask the egress gateway to inject a Substrate-issued actor JWT into a request header. The gateway mints the token through the existing Control.MintActorJWT RPC with the audience(s) the policy names, and attaches it on the TLS-terminated MITM leg. The actor never holds the token.
Unlike inject_static_headers, there is no secret to provision per actor. The credential is derived from identity the gateway already verified on the CONNECT leg.
Current state
Control.MintActorJWT(actor, actor_uid, audience[]) exists in cmd/ateapi/internal/controlapi/actor.go.
EgressRuleEffects has one field, inject_static_headers. PR egress: add credential injector #1360 implements it in cmd/atenet/internal/router/egress/credentials.go (Handler.applyEffects) by calling a CredentialProvider, and only on the egress_tls_mitm filter chain.
On the MITM leg the handler knows the actor only from the dev.ate.actor.identity filter state, a SPIFFE URI carrying atespace and name but not the UID.
Proposal
API
Add another effect alongside inject_static_headers:
message EgressRuleEffects {
repeated CredentialHeaderInjection inject_static_headers = 1;
// Injects a Substrate-issued actor JWT bound to the listed audiences.
// +k8s:optional
// +k8s:maxItems=16
// +k8s:listType=map
// +k8s:listMapKey=header
repeated ActorJWTHeaderInjection inject_actor_jwt = 2;
}
message ActorJWTHeaderInjection {
// +k8s:required
string header = 1; // case-insensitive, unique across both effect lists
// +k8s:optional
string prefix = 2; // prepended verbatim, e.g. "Bearer "
// +k8s:required
// +k8s:minItems=1
// +k8s:maxItems=16
repeated string audiences = 3; // passed to MintActorJWT as-is
}
Header uniqueness must be enforced across inject_static_headers and inject_actor_jwt together. The audiences limits mirror MintActorJWTRequest.audience.
Gateway
On the egress_tls_mitm leg, when the first matching rule carries inject_actor_jwt:
Resolve the actor UID. Either look it up with GetActor and cache it next to the compiled policy, or carry the UID from the CONNECT leg (where the certificate's ActorIdentity extension was already verified) into filter state.
Call MintActorJWT(actor, uid, audiences) over the gateway's existing pod-identity mTLS connection to ateapi. No new dependency: the handler already holds a ControlClient.
Cache tokens per (actor UID, audience set) using the same singleflight and TTL pattern as policycache.go, refreshing well inside the 15-minute lifetime.
Set header to prefix + jwt with OVERWRITE_IF_EXISTS_OR_ADD, so a value the actor set itself never survives.
Fail forwarding request if minting JWT fails.
Behavior on the cleartext leg should match whatever #1360 settles on for static injection, so the two effects have one rule.
Make the issuer configurable and publish OIDC discovery plus JWKS (localjwtauthority.Pool.VerificationKeys already exists for this). Without it no relying party can verify the token. This can be a separate prerequisite issue, but it gates any real consumer.
Out of scope
RFC 8693 exchange of the actor JWT for another credential (follow-up issue).
Conditional/sentinel-triggered injection (we likely want to eventually support this over blind injection, but will leave this out of scope for now)
Summary
Let an EgressPolicy hostname rule ask the egress gateway to inject a Substrate-issued actor JWT into a request header. The gateway mints the token through the existing Control.MintActorJWT RPC with the audience(s) the policy names, and attaches it on the TLS-terminated MITM leg. The actor never holds the token.
Motivation
Current state
Proposal
API
Add another effect alongside inject_static_headers:
Header uniqueness must be enforced across inject_static_headers and inject_actor_jwt together. The audiences limits mirror MintActorJWTRequest.audience.
Gateway
On the egress_tls_mitm leg, when the first matching rule carries inject_actor_jwt:
Behavior on the cleartext leg should match whatever #1360 settles on for static injection, so the two effects have one rule.
ateapi
Out of scope