hsheinblatt opened a new pull request, #1432:
URL: https://github.com/apache/knox/pull/1432

   KNOX-3491 - Add pluggable request audience validation, with a Kubernetes 
destination validator
   
   ## What changes were proposed in this pull request?
   
   Adds a pluggable mechanism for validating a JWT's aud claim, and one
   implementation that checks a delegation token's audience against the
   request's actual destination rather than against a fixed configured
   list.
   
   RequestAudienceValidator is the pluggable check, modeled on the
   existing PreAuthValidator pattern and discovered via ServiceLoader
   (RequestAudienceValidatorService), resolved by a filter's
   request.audience.validator init parameter. AbstractJWTFilter gains a
   validator-accepting overload of doFullTokenValidation()/
   validateToken(); the existing methods become one-line forwarders onto
   the previous fixed-list behavior (matchesConfiguredAudiences()), so
   every call site is unaffected unless it opts in. JWTFederationFilter
   is the only call site wired to it; with request.audience.validator
   unset its behavior is unchanged.
   
   The one implementation added, K8sDestinationAudienceValidator
   (request.audience.k8s.destination.validation), only engages for a
   token carrying a delegation act claim. Each aud entry is expected to
   be a URL of the form
   https://cluster-domain[:port]/namespace/service-name[/resource-path],
   parsed by the new AudienceResource record. An optional
   request.audience.k8s.audience.path.prefix lets an entry carry extra
   leading path segments before namespace and service-name, e.g. for
   network routing; left unset, namespace and service-name must begin
   straight after the authority. Which segments are actually compared
   is driven by which trusted-header init params are configured:
   namespace from a destination SPIFFE-id header
   (SpiffeId, relocated from gateway-provider-security-k8s into
   gateway-spi) and/or a Kubernetes server-name header (parsed by the
   new DestinationServiceName record); service-name only from the
   server-name header; resource-path only from a dedicated path header.
   cluster-domain, checked against an allow-list, is always enforced.
   At least one header param must be configured or init fails fast; when
   both namespace sources are configured, they must agree or the request
   is rejected fail-closed.
   
   Every header this validator reads is trusted at face value with no
   independent verification -- each must be populated only by a
   component upstream of Knox, never one an untrusted caller could
   influence. Values pulled from these headers (namespace, service name,
   port) are parsed only as far as their shape requires and are not
   otherwise validated (e.g. a non-numeric or absent port suffix is
   simply stripped, not rejected): an incorrect value just fails to
   match the aud entry later, at comparison, rather than being rejected
   at parse time. Namespace and service-name are compared
   case-insensitively; the resource path is compared case-sensitively
   and never percent-decoded, with both a raw and a percent-encoded
   candidate accepted on the aud side.
   request.audience.k8s.require-all-audiences-match controls whether one
   matching aud entry is sufficient (default) or every entry must match;
   independent of, and not a substitute for, JWTFederationFilter's
   existing delegation.enforce.requested.audience.* minting-side
   controls.
   
   What changed:
   - gateway-spi: AudienceValidationResult; SpiffeId/SpiffeIdTest moved
     from gateway-provider-security-k8s into org.apache.knox.gateway.util.
   - gateway-provider-security-k8s: ServiceAccountValidator's import
     updated to follow the SpiffeId move; no behavior change.
   - gateway-provider-security-jwt: RequestAudienceValidator interface;
     RequestAudienceValidatorService; AbstractJWTFilter
     (matchesConfiguredAudiences() extraction, new validator-accepting
     overloads); JWTFederationFilter (validator resolution, lifecycle
     wiring, doFilter integration); DestinationServiceName and
     AudienceResource (aud/header parsing records, including
     query-string/fragment stripping and the path-prefix search);
     K8sDestinationAudienceValidator and its META-INF/services
     registration; new jetty-util dependency (URIUtil, for percent-
     encoding the resource path).
   - knox-site: config_request_audience_validator.md (new provider doc,
     including the path-prefix parameter) and its mkdocs.yml nav entry.
   
   ## How was this patch tested?
   
   - AudienceValidationResultTest (3), SpiffeIdTest (7, moved unchanged),
     RequestAudienceValidatorServiceTest (5): construction/lookup
     behavior, including init-param unset/known/unknown and the
     production META-INF/services registration.
   - AbstractJWTFilter/CommonJWTFilterTest, JWTFederationFilterTest:
     matchesConfiguredAudiences() extraction is behavior-preserving;
     no-validator-configured regression; init() rejecting an unknown
     validator name; a configured validator invoked from doFilter and
     its destroy() propagated.
   - DestinationServiceNameTest (13): valid FQDN parsing with/without
     port and with lower-casing/whitespace handling; a trailing
     ":<anything>" suffix always stripped rather than validated as a
     port; suffix/label-count mismatches and null/empty input rejected;
     a non-label (whitespace-containing) segment parsing through rather
     than being rejected.
   - AudienceResourceTest (40): URL shape parsing (scheme, authority,
     userinfo, path segment count, dot/dot-dot/empty segments); query
     string and fragment stripped from the path; port normalization
     (absent/explicit-default/bare-colon); an empty namespace or
     service-name segment parsing through rather than being rejected;
     raw vs. percent-encoded resource path candidates; null/blank input,
     trimming, and case-insensitive scheme/host/namespace/service-name
     handling; and the audience.path.prefix search (found immediately or
     after skipped segments, first-occurrence-wins, case- and
     segment-boundary-sensitive matching, a multi-segment prefix, and
     a missing or too-short match failing to parse).
   - K8sDestinationAudienceValidatorTest (55): init fail-fast (no header
     param configured; each of the three alone sufficient; cluster-domains
     entries with a path, userinfo, scheme, or unparseable port rejected,
     via java.net.URI-based parsing); no-act-claim fallthrough; a
     delegation token with a null/empty aud claim rejected; each header
     required and rejecting the request when missing; namespace-only,
     service-name-plus-namespace, and path-only checks passing on a match
     and failing on a mismatch; an unconfigured segment gap accepted; both
     namespace sources agreeing/disagreeing; aud entries with the wrong
     shape never matching; malformed/suffix-mismatched/wrong-label-count
     server-name headers rejected, trailing port and custom cluster suffix
     honored; cluster-domain port equivalence rules; dot/dot-dot/empty
     request-path segments rejected and a trailing slash not causing a
     mismatch; raw vs. encoded path candidates on both sides; path-header-
     from-url extraction; require-all-audiences-match accepting/rejecting
     on partial vs. full agreement; audience.path.prefix matching and its
     opt-in default; and getName().
   
   ## 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]

Reply via email to