hsheinblatt opened a new pull request, #1398:
URL: https://github.com/apache/knox/pull/1398
KNOX-3441 - Add delegated token exchange (actor-token and headless) with
policy enforcement and audit
## What changes were proposed in this pull request?
Implements RFC 8693 delegated token exchange in TokenExchangeHandler:
a request is classified as same-subject (dispatched unchanged) or a
delegation attempt, expressed either interactively via actor_token or,
headlessly, via a requested_subject that differs from the subject_token's
own subject. All delegation behavior is gated behind five new topology
flags on JWTFederationFilter (delegation.server.enabled,
delegation.requested.subject.enabled,
delegation.enforce.requested.audience.required,
delegation.enforce.requested.audience.exactly.one,
delegation.requested.scope.enabled),
each defaulting to false and resolved once at init() alongside the filter's
existing DelegationPolicyService resolution, exposed to TokenExchangeHandler
through a new evaluateDelegationPolicy() callback so the handler retains no
direct GatewayServices/GatewayConfig access.
Delegation requests are rejected uniformly when delegation.server.enabled is
off, and the invalid combination of actor_token present with a differing
requested_subject is rejected once the gate is on. For headless delegation,
createSubjectForHeadlessDelegation() treats the subject_token's identity as
the actor (PrimaryPrincipal) and requested_subject as the impersonated
party, carried via TokenExchangePrincipal with a null subjectIssuer; no
ActorChainPrincipal is added, since subject_token here is the actor's own
token, not the impersonated user's. requested_subject is format-validated
(length bound, no control characters) and, per flag, audience/resource
values can be required (at least one) and/or capped (at most one distinct
combined audience/resource value).
DelegationPolicyService.evaluate() is called exactly once for an otherwise
valid delegated exchange, for both the actor_token and requested_subject
forms. The actor identity passed to the policy check is derived from the
actor's validated JWT: a Kubernetes service-account subject
(system:serviceaccount:<namespace>:<sa-name>) is tagged K8S_SA with actorId
"<issuer>:<namespace>:<sa-name>" (avoiding collisions across namespaces/
clusters); every other subject is tagged USER with the subject value itself
as actorId. A policy denial maps to HTTP 400 invalid_request with a generic
description that does not identify which value or scope failed; the
pre-existing canActFor.groups UnsupportedOperationException still maps to
HTTP 501, unaffected. Requested scopes are passed through as empty for now;
scope parsing/validation is deferred. Actor-chain depth is parsed but not
yet capped, and PolicyDecision's effectiveTtlSec is computed but not yet
used to override the issued token's TTL -- both deferred to later work.
PolicyCheckRequest.requestedResource (String) became requestedResources
(Set<String>, same null-safe defensive-copy pattern as requestedScopes), and
JdbcDelegationPolicyService.evaluate() now checks membership and scope
compliance independently per requested resource. A request with scopes but
no resources is denied with "scope_not_allowed" (a scope can only be
validated against a specific resource's allowed set); a request with
neither resources nor scopes imposes no resource/scope restriction.
Added a new Action.TOKEN_EXCHANGE audit action, emitted for both outcomes at
the point evaluate() returns, with a ResourceType.PRINCIPAL resourceName of
"<actorAuthority>/<actorId>" and a message carrying the deny reason (when
denied), the actor identity, the subject token's issuer/subject, the
requested_subject (when present), and the requested resources. The minted
token's jti/expiry/subject are not included (unavailable at the
policy-decision point) but the audit record can be correlated with the
pre-existing token-minting log line via the request's trace_id/reqID.
Also fixes a stale PolicyDecision.getDenyReason() javadoc reference to
invalid_grant (should read invalid_request).
## How was this patch tested?
- JWTFederationFilterDelegationConfigTest (new): each of the five delegation
flags defaults to false, is independently configurable, and all five can
be enabled together; evaluateDelegationPolicy() delegates to the resolved
DelegationPolicyService. TestFilterConfig extended with a
DelegationPolicyService constructor overload for this.
- TokenExchangeHandlerTest: extensive new coverage for the delegation gate
(on/off, actor_token vs requested_subject, blank/whitespace
requested_subject treated as absent), the actor_token/requested_subject
conflict rejection, headless Subject construction (principal shape, act
claim on subject_token not carried forward, requested_issuer boundary
values), requested_subject format validation (control character,
overlong, at-the-bound), audience required/max-one enforcement (including
blank-value and same-value-dedupe cases) for both delegation forms and all
combined-flag cells, actor-identity derivation for both K8S_SA and USER
outcomes, policy-check routing and requested-resources population for
both delegation forms (and confirmation same-subject exchanges never
invoke policy evaluation), HTTP 400/501 error mapping, and audit emission
end-to-end (FAILURE per delegation form, SUCCESS per actor-authority
value).
- JdbcDelegationPolicyServiceTest: updated all pre-existing evaluate() tests
to the multi-resource constructor shape and added cases for multi-resource
authorization/denial, per-resource scope enforcement, and the
empty-requestedResources/requestedScopes interaction cases.
- PolicyCheckRequestTest: updated to the new constructor shape and added
coverage for the requestedResources field's defensive copy and
post-construction immutability.
- DelegationPolicyServiceFactoryTest: updated to the new constructor shape.
- Pre-existing TokenExchangeHandlerTest and
JWTFederationFilterTokenExchangeTest
tests that send an actor_token now set delegation.server.enabled=true so
they keep passing with the gate defaulting to reject delegation.
## Integration Tests
Integration tests will be added as a follow on
## UI changes
N/A
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]