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.

Reply via email to