This is an automated email from the ASF dual-hosted git repository. smolnar82 pushed a commit to branch knox_idf in repository https://gitbox.apache.org/repos/asf/knox.git
commit 0d42c67fd8294a999cfd0bc8e127ba45c1f555f1 Author: hanicz <[email protected]> AuthorDate: Mon Jun 29 11:35:27 2026 +0200 KNOX-3365: New docs for k8s ServiceAccount validator (#1283) (cherry picked from commit d9283aa2a48698c1c9ad8be60e1203c53d624e6a) --- knox-site/docs/config_preauth_sso_provider.md | 113 +++++++++++++++++++++++++- 1 file changed, 112 insertions(+), 1 deletion(-) diff --git a/knox-site/docs/config_preauth_sso_provider.md b/knox-site/docs/config_preauth_sso_provider.md index 97022d009..5476713c1 100644 --- a/knox-site/docs/config_preauth_sso_provider.md +++ b/knox-site/docs/config_preauth_sso_provider.md @@ -57,7 +57,7 @@ The following table describes the configuration options for the web app security Name | Description | Default ---------|-----------|-------- -preauth.validation.method | Optional parameter that indicates the types of trust validation to perform on incoming requests. There could be one or more comma-separated validators defined in this property. If there are multiple validators, Apache Knox validates each validator in the same sequence as it is configured. This works similar to short-circuit AND operation i.e. if any validator fails, Knox does not perform further validation and returns overall failure immediately. Possible va [...] +preauth.validation.method | Optional parameter that indicates the types of trust validation to perform on incoming requests. There could be one or more comma-separated validators defined in this property. If there are multiple validators, Apache Knox validates each validator in the same sequence as it is configured. This works similar to short-circuit AND operation i.e. if any validator fails, Knox does not perform further validation and returns overall failure immediately. Possible va [...] preauth.ip.addresses | Optional parameter that indicates the list of trusted ip addresses. When preauth.ip.validation is indicated as the validation method this parameter must be provided to indicate the trusted ip address set. Wildcarded IPs may be used to indicate subnet level trust. ie. 127.0.* | null - which means that no validation will be performed. preauth.custom.header | Required parameter for indicating a custom header to use for extracting the preauthenticated principal. The value extracted from this header is utilized as the PrimaryPrincipal within the established Subject. An incoming request that is missing the configured header will be refused with a 401 unauthorized HTTP status. | SM_USER for SiteMinder usecase preauth.custom.group.header | Optional parameter for indicating a HTTP header name that contains a comma separated list of groups. These are added to the authenticated Subject as group principals. A missing group header will result in no groups being extracted from the incoming request and a log entry but processing will continue. | null - which means that there will be no group principals extracted from the request and added to the established Subject. @@ -101,3 +101,114 @@ The following curl command can be used to request a directory listing from HDFS curl -k -i --header "iv_user: guest" --header "iv_group: admin" -v https://localhost:8443/gateway/sandbox/webhdfs/v1/tmp?op=LISTSTATUS Omitting the `--header "iv_user: guest"` above will result in a rejected request. + +#### Kubernetes ServiceAccount Validator #### + +The Kubernetes ServiceAccount Validator (`preauth.k8s.service.account.validation`) is a `PreAuthValidator` implementation shipped in the `gateway-provider-security-k8s` module. It is intended for deployments where Knox runs inside a Kubernetes cluster and accepts pre-authenticated requests from workloads whose identity is asserted via a [SPIFFE](https://spiffe.io/) ID. + +The validator enforces a binding between: + +1. the calling workload's SPIFFE identity (its Kubernetes namespace and ServiceAccount), and +2. the user the caller is asserting in the `HeaderPreAuth` custom user header. + +The asserted user is only accepted if the corresponding ServiceAccount in Kubernetes carries an annotation that explicitly authorizes that ServiceAccount to assert that user. This prevents a compromised or misconfigured workload from spoofing arbitrary identities through the pre-auth header. + +##### How it works ##### + +For each incoming request the validator: + +1. Reads the SPIFFE header (default `x-spiffe-id`). Its value must be a Kubernetes-style SPIFFE ID of the form `spiffe://<trust-domain>/ns/<namespace>/sa/<service-account>`. Anything else is rejected. +2. Reads the asserted user header (default `x-knoxidf-obo.username`). A missing or empty value is rejected. +3. Looks up the `ServiceAccount` named `<service-account>` in namespace `<namespace>` via the Kubernetes API and reads the annotation configured by `preauth.k8s.sa.user.annotation` (default `knox.apache.org/owner-username`). +4. Accepts the request only if the annotation is present and its value equals the asserted user. Otherwise the request is rejected and `HeaderPreAuth` returns 403. + +ServiceAccount annotation lookups are cached in-memory (Caffeine, write-expiring). Successful lookups (including "annotation missing") are cached; transient errors talking to the Kubernetes API are not cached so they will be retried on the next request. + +##### Prerequisites ##### + +- Knox must run inside a Kubernetes pod (or otherwise have a kubeconfig discoverable by the fabric8 `KubernetesClient`). +- The ServiceAccount Knox runs as must have permission to `get` `serviceaccounts` in any namespace whose workloads it must authorize. Grant this with a `ClusterRole`/`ClusterRoleBinding` (or namespace-scoped `Role`/`RoleBinding`). +- A trust path must inject the SPIFFE header into incoming requests. The network/mesh configuration MUST prevent clients from setting this header themselves; otherwise this validator can be bypassed. +- Each ServiceAccount permitted to assert a user MUST be annotated with the configured annotation key, e.g.: + + apiVersion: v1 + kind: ServiceAccount + metadata: + name: my-workload-sa + namespace: analytics + annotations: + knox.apache.org/owner-username: alice + +##### Configuration parameters ##### + +All parameters are set as `<param>` entries on the `HeaderPreAuth` provider. + +Name | Description | Default +---------|-----------|-------- +preauth.validation.method | Must include `preauth.k8s.service.account.validation` to enable this validator. May be combined with other validators (comma-separated, short-circuit AND). | n/a +preauth.k8s.sa.spiffe.header | HTTP header carrying the caller's SPIFFE ID. Must be set/overwritten by a trusted hop — never by clients directly. | `x-spiffe-id` +preauth.k8s.sa.custom.header | HTTP header carrying the asserted user that the caller is requesting to act as. This is the value validated against the ServiceAccount annotation. | `x-knoxidf-obo.username` +preauth.k8s.sa.user.annotation | Annotation key on the `ServiceAccount` whose value, when equal to the asserted user, authorizes the assertion. | `knox.apache.org/owner-username` +preauth.k8s.sa.cache.ttl.seconds | Write-expiry TTL (in seconds) for cached ServiceAccount annotation lookups. Must be `> 0`. Lower values pick up annotation changes faster at the cost of more API calls. | `60` +preauth.k8s.sa.cache.max.size | Maximum number of distinct `(namespace, service-account)` entries kept in the lookup cache. Must be `> 0`. | `1000` + +Note: the user header default (`x-knoxidf-obo.username`) is independent of the `HeaderPreAuth` `preauth.custom.header` parameter — `preauth.custom.header` controls which header `HeaderPreAuth` extracts as the primary principal, while `preauth.k8s.sa.custom.header` controls which header this validator checks against the ServiceAccount annotation. Configure both to the same header name in deployments where the same value plays both roles. + +##### Example topology ##### + + <provider> + <role>federation</role> + <name>HeaderPreAuth</name> + <enabled>true</enabled> + <param> + <name>preauth.validation.method</name> + <value>preauth.k8s.service.account.validation</value> + </param> + <param> + <name>preauth.custom.header</name> + <value>x-knoxidf-obo.username</value> + </param> + <param> + <name>preauth.k8s.sa.spiffe.header</name> + <value>x-spiffe-id</value> + </param> + <param> + <name>preauth.k8s.sa.custom.header</name> + <value>x-knoxidf-obo.username</value> + </param> + <param> + <name>preauth.k8s.sa.user.annotation</name> + <value>knox.apache.org/owner-username</value> + </param> + <param> + <name>preauth.k8s.sa.cache.ttl.seconds</name> + <value>60</value> + </param> + <param> + <name>preauth.k8s.sa.cache.max.size</name> + <value>1000</value> + </param> + </provider> + +##### Example request ##### + +Assuming the namespace `analytics` contains a ServiceAccount `my-workload-sa` annotated with `knox.apache.org/owner-username: alice`, and the service mesh injects `x-spiffe-id` for that workload: + + curl -k -i \ + --header "x-spiffe-id: spiffe://example.org/ns/analytics/sa/my-workload-sa" \ + --header "x-knoxidf-obo.username: alice" \ + -v https://knox.example.org:8443/gateway/sandbox/webhdfs/v1/tmp?op=LISTSTATUS + +The request is accepted and processed as user `alice`. Sending the same request with `x-knoxidf-obo.username: bob` (or with a SPIFFE ID pointing to a ServiceAccount that is not annotated for `alice`) yields a 403 response. + +##### Failure modes ##### + +The validator rejects the request (causing `HeaderPreAuth` to return 403) and emits a WARN log entry when: + +- the SPIFFE header is missing or empty; +- the user header is missing or empty; +- the SPIFFE header value is not a parseable Kubernetes SPIFFE ID (`spiffe://.../ns/<ns>/sa/<sa>`); +- the target `ServiceAccount` does not exist or carries no annotation under the configured key; +- the annotation value does not match the asserted user. + +Errors talking to the Kubernetes API are logged at ERROR and result in rejection but are not cached, so transient API outages do not produce sticky 403s once the API recovers.
