From 16338ce1cfa2a8df58ef8d062f2f5fce92a01b79 Mon Sep 17 00:00:00 2001 From: shaun-nx Date: Tue, 9 Dec 2025 08:51:51 +0000 Subject: [PATCH 01/19] Add initial valid and invalid filter reference scenarios --- docs/proposals/authentication-filter.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/docs/proposals/authentication-filter.md b/docs/proposals/authentication-filter.md index 41b22fb511..8a49fdc332 100644 --- a/docs/proposals/authentication-filter.md +++ b/docs/proposals/authentication-filter.md @@ -906,7 +906,25 @@ This can use the status `RouteConditionPartiallyInvalid` defined in the Gateway ## Testing - Unit tests -- Functional tests to validate behavioural scenarios when referencing filters in different combinations. The details of these tests are out of scope for this document. +- Functional tests to validate behavioural scenarios when referencing filters in different combinations. + +### Functional Test Cases + + + +Valid reference scenarios +- Resolved filter referenced by a single route rule within a single HTTP/GRPCRoute +- Resolved filter referenced by multiple route rules within a single HTTP/GRPCRoute +- Resolved filter reference by multiple HTTP/GRPCRoutes + +Invalid reference scenarios +- Resolved filter referenced multiple times in a single route rule within a single HTTP/GRPCRoute +- Resolved filter referenced multiple times by multiple route rules within a single HTTP/GRPCRoute +- Unresolved filter referenced by a single route rule within a single HTTP/GRPCRoute +- Unresolved filter referenced by multiple route rules within a single HTTP/GRPCRoute ## Security Considerations From fdcc50460fd528a6eec93c85fa0512532d8156c8 Mon Sep 17 00:00:00 2001 From: shaun-nx Date: Tue, 9 Dec 2025 09:03:23 +0000 Subject: [PATCH 02/19] Add details around resolved references --- docs/proposals/authentication-filter.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/docs/proposals/authentication-filter.md b/docs/proposals/authentication-filter.md index 8a49fdc332..dccc77dfce 100644 --- a/docs/proposals/authentication-filter.md +++ b/docs/proposals/authentication-filter.md @@ -910,17 +910,21 @@ This can use the status `RouteConditionPartiallyInvalid` defined in the Gateway ### Functional Test Cases - +Invalid resolved filter secnarios: +- Resolved filter that references a secret that does not exist +- Resolved filter that referenced a secret with the incorrect data key -Valid reference scenarios +Valid reference scenarios: - Resolved filter referenced by a single route rule within a single HTTP/GRPCRoute - Resolved filter referenced by multiple route rules within a single HTTP/GRPCRoute - Resolved filter reference by multiple HTTP/GRPCRoutes -Invalid reference scenarios +Invalid reference scenarios: - Resolved filter referenced multiple times in a single route rule within a single HTTP/GRPCRoute - Resolved filter referenced multiple times by multiple route rules within a single HTTP/GRPCRoute - Unresolved filter referenced by a single route rule within a single HTTP/GRPCRoute From c82527a338a51fcdd0a94bcca3626b7e3cdfed7e Mon Sep 17 00:00:00 2001 From: shaun-nx Date: Tue, 9 Dec 2025 09:06:02 +0000 Subject: [PATCH 03/19] Update API spec for Basic Auth only --- docs/proposals/authentication-filter.md | 310 +++++------------------- 1 file changed, 54 insertions(+), 256 deletions(-) diff --git a/docs/proposals/authentication-filter.md b/docs/proposals/authentication-filter.md index dccc77dfce..925e265696 100644 --- a/docs/proposals/authentication-filter.md +++ b/docs/proposals/authentication-filter.md @@ -74,14 +74,14 @@ This portion also contains: ### Golang API -Below is the Golang API for the `AuthenticationFilter` API: +Below is the Golang API for the `AuthenticationFilter` API. +This is currently designed for Basic Auth. ```go package v1alpha1 import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "github.com/nginx/nginx-gateway-fabric/v2/apis/v1alpha1" ) // +genclient @@ -91,20 +91,17 @@ import ( // +kubebuilder:resource:categories=nginx-gateway-fabric,shortName=authfilter;authenticationfilter // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` -// AuthenticationFilter configures request authentication (Basic or JWT) and is -// referenced by HTTPRoute filters via ExtensionRef. +// AuthenticationFilter configures request authentication and is +// referenced by HTTPRoute and GRPCRoute filters using ExtensionRef. type AuthenticationFilter struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata"` - // Spec defines the desired state of the AuthenticationFilter. - Spec AuthenticationFilterSpec `json:"spec"` + // Spec defines the desired state of the AuthenticationFilter. + Spec AuthenticationFilterSpec `json:"spec"` - // Status defines the state of the AuthenticationFilter, following the same - // pattern as SnippetsFilter: per-controller conditions with an Accepted condition. - // - // +optional - Status AuthenticationFilterStatus `json:"status,omitempty"` + // Status defines the state of the AuthenticationFilter. + Status AuthenticationFilterStatus `json:"status,omitempty"` } // +kubebuilder:object:root=true @@ -112,257 +109,58 @@ type AuthenticationFilter struct { // AuthenticationFilterList contains a list of AuthenticationFilter resources. type AuthenticationFilterList struct { metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` + metav1.ListMeta `json:"metadata"` Items []AuthenticationFilter `json:"items"` } // AuthenticationFilterSpec defines the desired configuration. -// Exactly one of Basic or JWT must be set according to Type. -// +kubebuilder:validation:XValidation:message="for type=Basic, spec.basic must be set and spec.jwt must be empty; for type=JWT, spec.jwt must be set and spec.basic must be empty",rule="self.type == 'Basic' ? self.basic != null && self.jwt == null : self.type == 'JWT' ? self.jwt != null && self.basic == null : false" -// +kubebuilder:validation:XValidation:message="type 'Basic' requires spec.basic to be set. All other spec types must be unset",rule="self.type == 'Basic' ? self.type != null && self.jwt == null : true" -// +kubebuilder:validation:XValidation:message="type 'JWT' requires spec.jwt to be set. All other spec types must be unset",rule="self.type == 'JWT' ? self.type != null && self.basic == null : true" -// +kubebuilder:validation:XValidation:message="when spec.basic is set, type must be 'Basic'",rule="self.basic != null ? self.type == 'Basic' : true" -// +kubebuilder:validation:XValidation:message="when spec.jwt is set, type must be 'JWT'",rule="self.jwt != null ? self.type == 'JWT' : true" +// +kubebuilder:validation:XValidation:message="for type=Basic, spec.basic must be set",rule="!(!has(self.basic) && self.type == 'Basic')" +// +//nolint:lll type AuthenticationFilterSpec struct { - // Type selects the authentication mechanism. - Type AuthType `json:"type"` - - // Basic configures HTTP Basic Authentication. - // Required when Type == Basic. - // - // +optional - Basic *BasicAuth `json:"basic,omitempty"` + // Basic configures HTTP Basic Authentication. + // + // +optional + Basic *BasicAuth `json:"basic,omitempty"` - // JWT configures JSON Web Token authentication (NGINX Plus). - // Required when Type == JWT. - // - // +optional - JWT *JWTAuth `json:"jwt,omitempty"` + // Type selects the authentication mechanism. + Type AuthType `json:"type"` } // AuthType defines the authentication mechanism. -// +kubebuilder:validation:Enum=Basic;JWT +// +// +kubebuilder:validation:Enum=Basic; type AuthType string const ( - AuthTypeBasic AuthType = "Basic" - AuthTypeJWT AuthType = "JWT" + // AuthTypeBasic is the HTTP Basic Authentication mechanism. + AuthTypeBasic AuthType = "Basic" ) // BasicAuth configures HTTP Basic Authentication. type BasicAuth struct { - // SecretRef allows referencing a Secret in the same namespace - SecretRef LocalObjectReference `json:"secretRef"` - - // Realm used by NGINX `auth_basic` directive. - // https://nginx.org/en/docs/http/ngx_http_auth_basic_module.html#auth_basic - // Also configures "realm="" in WWW-Authenticate header in error page location. - Realm string `json:"realm"` - - // OnFailure customizes the 401 response for failed authentication. - // - // +optional - OnFailure *AuthFailureResponse `json:"onFailure,omitempty"` -} - -// JWTKeyMode selects where JWT keys come from. -// +kubebuilder:validation:Enum=File;Remote -type JWTKeyMode string - -const ( - JWTKeyModeFile JWTKeyMode = "File" - JWTKeyModeRemote JWTKeyMode = "Remote" -) - -// JWTAuth configures JWT-based authentication (NGINX Plus). -// +kubebuilder:validation:XValidation:message="mode 'File' requires file set and remote unset",rule="self.mode == 'File' ? self.file != null && self.remote == null : true" -// +kubebuilder:validation:XValidation:message="mode 'Remote' requires remote set and file unset",rule="self.mode == 'Remote' ? self.remote != null && self.file == null : true" -// +kubebuilder:validation:XValidation:message="when file is set, mode must be 'File'",rule="self.file != null ? self.mode == 'File' : true" -// +kubebuilder:validation:XValidation:message="when remote is set, mode must be 'Remote'",rule="self.remote != null ? self.mode == 'Remote' : true" -type JWTAuth struct { - // Realm used by NGINX `auth_jwt` directive - // https://nginx.org/en/docs/http/ngx_http_auth_jwt_module.html#auth_jwt - // Configures "realm="" in WWW-Authenticate header in error page location. - Realm string `json:"realm"` - - // Mode selects how JWT keys are provided: local file or remote JWKS. - Mode JWTKeyMode `json:"mode"` - - // File specifies local JWKS configuration. - // Required when Mode == File. - // - // +optional - File *JWTFileKeySource `json:"file,omitempty"` - - // Remote specifies remote JWKS configuration. - // Required when Mode == Remote. - // - // +optional - Remote *RemoteKeySource `json:"remote,omitempty"` - - // Leeway is the acceptable clock skew for exp/nbf checks. - // Configures `auth_jwt_leeway` directive. - // https://nginx.org/en/docs/http/ngx_http_auth_jwt_module.html#auth_jwt_leeway - // Example: "auth_jwt_leeway 60s". - // - // +optional - Leeway *v1alpha1.Duration `json:"leeway,omitempty"` - - // Type sets token type: signed | encrypted | nested. - // Default: signed. - // Configures `auth_jwt_type` directive. - // https://nginx.org/en/docs/http/ngx_http_auth_jwt_module.html#auth_jwt_type - // Example: "auth_jwt_type signed;". - // - // +optional - // +kubebuilder:default=signed - Type *JWTType `json:"type,omitempty"` - - // KeyCache is the cache duration for keys. - // Configures auth_jwt_key_cache directive. - // https://nginx.org/en/docs/http/ngx_http_auth_jwt_module.html#auth_jwt_key_cache - // Example: "auth_jwt_key_cache 10m". - // - // +optional - KeyCache *v1alpha1.Duration `json:"keyCache,omitempty"` + // SecretRef allows referencing a Secret in the same namespace. + SecretRef LocalObjectReference `json:"secretRef"` - // OnFailure customizes the 401 response for failed authentication. - // - // +optional - OnFailure *AuthFailureResponse `json:"onFailure,omitempty"` -} - -// JWTFileKeySource specifies local JWKS key configuration. -type JWTFileKeySource struct { - // SecretRef references a Secret containing the JWKS. - SecretRef LocalObjectReference `json:"secretRef"` - - // KeyCache is the cache duration for keys. - // Configures `auth_jwt_key_cache` directive. - // https://nginx.org/en/docs/http/ngx_http_auth_jwt_module.html#auth_jwt_key_cache - // Example: "auth_jwt_key_cache 10m;". - // - // +optional - KeyCache *v1alpha1.Duration `json:"keyCache,omitempty"` + // Realm used by NGINX `auth_basic` directive. + // https://nginx.org/en/docs/http/ngx_http_auth_basic_module.html#auth_basic + // Also configures "realm="" in WWW-Authenticate header in error page location. + Realm string `json:"realm"` } -// LocalObjectReference specifies a local Kubernetes object -// with a required `key` field to extract data. +// LocalObjectReference specifies a local Kubernetes object. type LocalObjectReference struct { - Name string: `json:"name"` -} - - - // RemoteKeySource specifies remote JWKS configuration. -type RemoteKeySource struct { - // URL is the JWKS endpoint, e.g. "https://issuer.example.com/.well-known/jwks.json". - URL string `json:"url"` - - // Cache configures NGINX proxy_cache for JWKS fetches made via auth_jwt_key_request. - // When set, NGF will render proxy_cache_path in http{} and attach proxy_cache to the internal JWKS location. - // - // +optional - Cache *JWKSCache `json:"cache,omitempty"` -} - - // JWKSCache controls NGINX `proxy_cache_path` and `proxy_cache` settings used for JWKS responses. -type JWKSCache struct { - // Levels specifies the directory hierarchy for cached files. - // Used in `proxy_cache_path` directive. - // https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_cache_path - // Example: "levels=1:2". - // - // +optional - Levels *string `json:"levels,omitempty"` - - // KeysZoneName is the name of the cache keys zone. - // If omitted, the controller SHOULD derive a unique, stable name per filter instance. - // - // +optional - KeysZoneName *string `json:"keysZoneName,omitempty"` - - // KeysZoneSize is the size of the cache keys zone (e.g. "10m"). - // This is required to avoid unbounded allocations. - KeysZoneSize string `json:"keysZoneSize"` - - // MaxSize limits the total size of the cache (e.g. "50m"). - // - // +optional - MaxSize *string `json:"maxSize,omitempty"` - - // Inactive defines the inactivity timeout before cached items are evicted (e.g. "10m"). - // - // +optional - Inactive *string `json:"inactive,omitempty"` - - // UseTempPath controls whether a temporary file is used for cache writes. - // Maps to use_temp_path=(on|off). Default: false (off). - // - // +optional - UseTempPath *bool `json:"useTempPath,omitempty"` -} - -// JWTType represents NGINX auth_jwt_type. -// +kubebuilder:validation:Enum=signed;encrypted;nested -type JWTType string - -const ( - JWTTypeSigned JWTType = "signed" - JWTTypeEncrypted JWTType = "encrypted" - JWTTypeNested JWTType = "nested" -) - -// AuthScheme enumerates supported WWW-Authenticate schemes. -// +kubebuilder:validation:Enum=Basic;Bearer -type AuthScheme string - -const ( - AuthSchemeBasic AuthScheme = "Basic" - AuthSchemeBearer AuthScheme = "Bearer" -) - -// AuthFailureBodyPolicy controls the failure response body behavior. -// +kubebuilder:validation:Enum=Unauthorized;Forbidden;Empty -type AuthFailureBodyPolicy string - -const ( - AuthFailureBodyPolicyUnauthorized AuthFailureBodyPolicy = "Unauthorized" - AuthFailureBodyPolicyForbidden AuthFailureBodyPolicy = "Forbidden" - AuthFailureBodyPolicyEmpty AuthFailureBodyPolicy = "Empty" -) - -// AuthFailureResponse customizes 401/403 failures. -type AuthFailureResponse struct { - // Allowed: 401, 403. - // Default: 401. - // - // +optional - // +kubebuilder:default=401 - // +kubebuilder:validation:XValidation:message="statusCode must be 401 or 403",rule="self == null || self in [401, 403]" - StatusCode *int32 `json:"statusCode,omitempty"` - - // Challenge scheme. If omitted, inferred from filter Type (Basic|Bearer). - // Configures WWW-Authenticate header in error page location. - // - // +optional - // +kubebuilder:default=Basic - Scheme *AuthScheme `json:"scheme,omitempty"` - - // Controls whether a default canned body is sent or an empty body. - // Default: Unauthorized. - // - // +optional - // +kubebuilder:default=Unauthorized - BodyPolicy *AuthFailureBodyPolicy `json:"bodyPolicy,omitempty"` + // Name is the referenced object. + Name string `json:"name"` } // AuthenticationFilterStatus defines the state of AuthenticationFilter. type AuthenticationFilterStatus struct { - // Controllers is a list of Gateway API controllers that processed the AuthenticationFilter - // and the status of the AuthenticationFilter with respect to each controller. - // - // +kubebuilder:validation:MaxItems=16 - Controllers []ControllerStatus `json:"controllers,omitempty"` + // Controllers is a list of Gateway API controllers that processed the AuthenticationFilter + // and the status of the AuthenticationFilter with respect to each controller. + // + // +kubebuilder:validation:MaxItems=16 + Controllers []ControllerStatus `json:"controllers,omitempty"` } // AuthenticationFilterConditionType is a type of condition associated with AuthenticationFilter. @@ -372,22 +170,22 @@ type AuthenticationFilterConditionType string type AuthenticationFilterConditionReason string const ( - // AuthenticationFilterConditionTypeAccepted indicates that the AuthenticationFilter is accepted. - // - // Possible reasons for this condition to be True: - // * Accepted - // - // Possible reasons for this condition to be False: - // * Invalid - AuthenticationFilterConditionTypeAccepted AuthenticationFilterConditionType = "Accepted" - - // AuthenticationFilterConditionReasonAccepted is used with the Accepted condition type when - // the condition is true. - AuthenticationFilterConditionReasonAccepted AuthenticationFilterConditionReason = "Accepted" - - // AuthenticationFilterConditionReasonInvalid is used with the Accepted condition type when - // the filter is invalid. - AuthenticationFilterConditionReasonInvalid AuthenticationFilterConditionReason = "Invalid" + // AuthenticationFilterConditionTypeAccepted indicates that the AuthenticationFilter is accepted. + // + // Possible reasons for this condition to be True: + // * Accepted + // + // Possible reasons for this condition to be False: + // * Invalid. + AuthenticationFilterConditionTypeAccepted AuthenticationFilterConditionType = "Accepted" + + // AuthenticationFilterConditionReasonAccepted is used with the Accepted condition type when + // the condition is true. + AuthenticationFilterConditionReasonAccepted AuthenticationFilterConditionReason = "Accepted" + + // AuthenticationFilterConditionReasonInvalid is used with the Accepted condition type when + // the filter is invalid. + AuthenticationFilterConditionReasonInvalid AuthenticationFilterConditionReason = "Invalid" ) ``` From 4163a49f1f7fed67199c82da4c929261bb630f72 Mon Sep 17 00:00:00 2001 From: shaun-nx Date: Tue, 9 Dec 2025 09:39:28 +0000 Subject: [PATCH 04/19] Remove AuthFailure from API. Move to stretch goals --- docs/proposals/authentication-filter.md | 344 +++++++++++++++++------- 1 file changed, 253 insertions(+), 91 deletions(-) diff --git a/docs/proposals/authentication-filter.md b/docs/proposals/authentication-filter.md index 925e265696..2ee8acf8e6 100644 --- a/docs/proposals/authentication-filter.md +++ b/docs/proposals/authentication-filter.md @@ -74,14 +74,14 @@ This portion also contains: ### Golang API -Below is the Golang API for the `AuthenticationFilter` API. -This is currently designed for Basic Auth. +Below is the Golang API for the `AuthenticationFilter` API: ```go package v1alpha1 import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "github.com/nginx/nginx-gateway-fabric/v2/apis/v1alpha1" ) // +genclient @@ -91,17 +91,20 @@ import ( // +kubebuilder:resource:categories=nginx-gateway-fabric,shortName=authfilter;authenticationfilter // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` -// AuthenticationFilter configures request authentication and is -// referenced by HTTPRoute and GRPCRoute filters using ExtensionRef. +// AuthenticationFilter configures request authentication (Basic or JWT) and is +// referenced by HTTPRoute filters via ExtensionRef. type AuthenticationFilter struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata"` + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` - // Spec defines the desired state of the AuthenticationFilter. - Spec AuthenticationFilterSpec `json:"spec"` + // Spec defines the desired state of the AuthenticationFilter. + Spec AuthenticationFilterSpec `json:"spec"` - // Status defines the state of the AuthenticationFilter. - Status AuthenticationFilterStatus `json:"status,omitempty"` + // Status defines the state of the AuthenticationFilter, following the same + // pattern as SnippetsFilter: per-controller conditions with an Accepted condition. + // + // +optional + Status AuthenticationFilterStatus `json:"status,omitempty"` } // +kubebuilder:object:root=true @@ -109,32 +112,41 @@ type AuthenticationFilter struct { // AuthenticationFilterList contains a list of AuthenticationFilter resources. type AuthenticationFilterList struct { metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata"` + metav1.ListMeta `json:"metadata,omitempty"` Items []AuthenticationFilter `json:"items"` } // AuthenticationFilterSpec defines the desired configuration. -// +kubebuilder:validation:XValidation:message="for type=Basic, spec.basic must be set",rule="!(!has(self.basic) && self.type == 'Basic')" -// -//nolint:lll +// Exactly one of Basic or JWT must be set according to Type. +// +kubebuilder:validation:XValidation:message="for type=Basic, spec.basic must be set and spec.jwt must be empty; for type=JWT, spec.jwt must be set and spec.basic must be empty",rule="self.type == 'Basic' ? self.basic != null && self.jwt == null : self.type == 'JWT' ? self.jwt != null && self.basic == null : false" +// +kubebuilder:validation:XValidation:message="type 'Basic' requires spec.basic to be set. All other spec types must be unset",rule="self.type == 'Basic' ? self.type != null && self.jwt == null : true" +// +kubebuilder:validation:XValidation:message="type 'JWT' requires spec.jwt to be set. All other spec types must be unset",rule="self.type == 'JWT' ? self.type != null && self.basic == null : true" +// +kubebuilder:validation:XValidation:message="when spec.basic is set, type must be 'Basic'",rule="self.basic != null ? self.type == 'Basic' : true" +// +kubebuilder:validation:XValidation:message="when spec.jwt is set, type must be 'JWT'",rule="self.jwt != null ? self.type == 'JWT' : true" type AuthenticationFilterSpec struct { - // Basic configures HTTP Basic Authentication. - // - // +optional - Basic *BasicAuth `json:"basic,omitempty"` + // Type selects the authentication mechanism. + Type AuthType `json:"type"` + + // Basic configures HTTP Basic Authentication. + // Required when Type == Basic. + // + // +optional + Basic *BasicAuth `json:"basic,omitempty"` - // Type selects the authentication mechanism. - Type AuthType `json:"type"` + // JWT configures JSON Web Token authentication (NGINX Plus). + // Required when Type == JWT. + // + // +optional + JWT *JWTAuth `json:"jwt,omitempty"` } // AuthType defines the authentication mechanism. -// -// +kubebuilder:validation:Enum=Basic; +// +kubebuilder:validation:Enum=Basic;JWT type AuthType string const ( - // AuthTypeBasic is the HTTP Basic Authentication mechanism. - AuthTypeBasic AuthType = "Basic" + AuthTypeBasic AuthType = "Basic" + AuthTypeJWT AuthType = "JWT" ) // BasicAuth configures HTTP Basic Authentication. @@ -154,13 +166,157 @@ type LocalObjectReference struct { Name string `json:"name"` } +// JWTKeyMode selects where JWT keys come from. +// +kubebuilder:validation:Enum=File;Remote +type JWTKeyMode string + +const ( + JWTKeyModeFile JWTKeyMode = "File" + JWTKeyModeRemote JWTKeyMode = "Remote" +) + +// JWTAuth configures JWT-based authentication (NGINX Plus). +// +kubebuilder:validation:XValidation:message="mode 'File' requires file set and remote unset",rule="self.mode == 'File' ? self.file != null && self.remote == null : true" +// +kubebuilder:validation:XValidation:message="mode 'Remote' requires remote set and file unset",rule="self.mode == 'Remote' ? self.remote != null && self.file == null : true" +// +kubebuilder:validation:XValidation:message="when file is set, mode must be 'File'",rule="self.file != null ? self.mode == 'File' : true" +// +kubebuilder:validation:XValidation:message="when remote is set, mode must be 'Remote'",rule="self.remote != null ? self.mode == 'Remote' : true" +type JWTAuth struct { + // Realm used by NGINX `auth_jwt` directive + // https://nginx.org/en/docs/http/ngx_http_auth_jwt_module.html#auth_jwt + // Configures "realm="" in WWW-Authenticate header in error page location. + Realm string `json:"realm"` + + // Mode selects how JWT keys are provided: local file or remote JWKS. + Mode JWTKeyMode `json:"mode"` + + // File specifies local JWKS configuration. + // Required when Mode == File. + // + // +optional + File *JWTFileKeySource `json:"file,omitempty"` + + // Remote specifies remote JWKS configuration. + // Required when Mode == Remote. + // + // +optional + Remote *RemoteKeySource `json:"remote,omitempty"` + + // Leeway is the acceptable clock skew for exp/nbf checks. + // Configures `auth_jwt_leeway` directive. + // https://nginx.org/en/docs/http/ngx_http_auth_jwt_module.html#auth_jwt_leeway + // Example: "auth_jwt_leeway 60s". + // + // +optional + Leeway *v1alpha1.Duration `json:"leeway,omitempty"` + + // Type sets token type: signed | encrypted | nested. + // Default: signed. + // Configures `auth_jwt_type` directive. + // https://nginx.org/en/docs/http/ngx_http_auth_jwt_module.html#auth_jwt_type + // Example: "auth_jwt_type signed;". + // + // +optional + // +kubebuilder:default=signed + Type *JWTType `json:"type,omitempty"` + + // KeyCache is the cache duration for keys. + // Configures auth_jwt_key_cache directive. + // https://nginx.org/en/docs/http/ngx_http_auth_jwt_module.html#auth_jwt_key_cache + // Example: "auth_jwt_key_cache 10m". + // + // +optional + KeyCache *v1alpha1.Duration `json:"keyCache,omitempty"` +} + +// JWTFileKeySource specifies local JWKS key configuration. +type JWTFileKeySource struct { + // SecretRef references a Secret containing the JWKS. + SecretRef LocalObjectReference `json:"secretRef"` + + // KeyCache is the cache duration for keys. + // Configures `auth_jwt_key_cache` directive. + // https://nginx.org/en/docs/http/ngx_http_auth_jwt_module.html#auth_jwt_key_cache + // Example: "auth_jwt_key_cache 10m;". + // + // +optional + KeyCache *v1alpha1.Duration `json:"keyCache,omitempty"` +} + + // RemoteKeySource specifies remote JWKS configuration. +type RemoteKeySource struct { + // URL is the JWKS endpoint, e.g. "https://issuer.example.com/.well-known/jwks.json". + URL string `json:"url"` + + // Cache configures NGINX proxy_cache for JWKS fetches made via auth_jwt_key_request. + // When set, NGF will render proxy_cache_path in http{} and attach proxy_cache to the internal JWKS location. + // + // +optional + Cache *JWKSCache `json:"cache,omitempty"` +} + + // JWKSCache controls NGINX `proxy_cache_path` and `proxy_cache` settings used for JWKS responses. +type JWKSCache struct { + // Levels specifies the directory hierarchy for cached files. + // Used in `proxy_cache_path` directive. + // https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_cache_path + // Example: "levels=1:2". + // + // +optional + Levels *string `json:"levels,omitempty"` + + // KeysZoneName is the name of the cache keys zone. + // If omitted, the controller SHOULD derive a unique, stable name per filter instance. + // + // +optional + KeysZoneName *string `json:"keysZoneName,omitempty"` + + // KeysZoneSize is the size of the cache keys zone (e.g. "10m"). + // This is required to avoid unbounded allocations. + KeysZoneSize string `json:"keysZoneSize"` + + // MaxSize limits the total size of the cache (e.g. "50m"). + // + // +optional + MaxSize *string `json:"maxSize,omitempty"` + + // Inactive defines the inactivity timeout before cached items are evicted (e.g. "10m"). + // + // +optional + Inactive *string `json:"inactive,omitempty"` + + // UseTempPath controls whether a temporary file is used for cache writes. + // Maps to use_temp_path=(on|off). Default: false (off). + // + // +optional + UseTempPath *bool `json:"useTempPath,omitempty"` +} + +// JWTType represents NGINX auth_jwt_type. +// +kubebuilder:validation:Enum=signed;encrypted;nested +type JWTType string + +const ( + JWTTypeSigned JWTType = "signed" + JWTTypeEncrypted JWTType = "encrypted" + JWTTypeNested JWTType = "nested" +) + +// AuthScheme enumerates supported WWW-Authenticate schemes. +// +kubebuilder:validation:Enum=Basic;Bearer +type AuthScheme string + +const ( + AuthSchemeBasic AuthScheme = "Basic" + AuthSchemeBearer AuthScheme = "Bearer" +) + // AuthenticationFilterStatus defines the state of AuthenticationFilter. type AuthenticationFilterStatus struct { - // Controllers is a list of Gateway API controllers that processed the AuthenticationFilter - // and the status of the AuthenticationFilter with respect to each controller. - // - // +kubebuilder:validation:MaxItems=16 - Controllers []ControllerStatus `json:"controllers,omitempty"` + // Controllers is a list of Gateway API controllers that processed the AuthenticationFilter + // and the status of the AuthenticationFilter with respect to each controller. + // + // +kubebuilder:validation:MaxItems=16 + Controllers []ControllerStatus `json:"controllers,omitempty"` } // AuthenticationFilterConditionType is a type of condition associated with AuthenticationFilter. @@ -170,22 +326,22 @@ type AuthenticationFilterConditionType string type AuthenticationFilterConditionReason string const ( - // AuthenticationFilterConditionTypeAccepted indicates that the AuthenticationFilter is accepted. - // - // Possible reasons for this condition to be True: - // * Accepted - // - // Possible reasons for this condition to be False: - // * Invalid. - AuthenticationFilterConditionTypeAccepted AuthenticationFilterConditionType = "Accepted" - - // AuthenticationFilterConditionReasonAccepted is used with the Accepted condition type when - // the condition is true. - AuthenticationFilterConditionReasonAccepted AuthenticationFilterConditionReason = "Accepted" - - // AuthenticationFilterConditionReasonInvalid is used with the Accepted condition type when - // the filter is invalid. - AuthenticationFilterConditionReasonInvalid AuthenticationFilterConditionReason = "Invalid" + // AuthenticationFilterConditionTypeAccepted indicates that the AuthenticationFilter is accepted. + // + // Possible reasons for this condition to be True: + // * Accepted + // + // Possible reasons for this condition to be False: + // * Invalid + AuthenticationFilterConditionTypeAccepted AuthenticationFilterConditionType = "Accepted" + + // AuthenticationFilterConditionReasonAccepted is used with the Accepted condition type when + // the condition is true. + AuthenticationFilterConditionReasonAccepted AuthenticationFilterConditionReason = "Accepted" + + // AuthenticationFilterConditionReasonInvalid is used with the Accepted condition type when + // the filter is invalid. + AuthenticationFilterConditionReasonInvalid AuthenticationFilterConditionReason = "Invalid" ) ``` @@ -202,9 +358,6 @@ spec: secretRef: name: basic-auth-users # Secret containing auth data. realm: "Restricted" - onFailure: # Optional. These setting may be defaults. - statusCode: 401 - scheme: Basic ``` In the case of Basic Auth, the deployed Secret and HTTPRoute may look like this: @@ -304,10 +457,6 @@ http { # Path is generated by NGF using the name and key from the secret auth_basic_user_file /etc/nginx/secrets/basic-auth-users/auth; - # Optional: customize failure per filter onFailure - # Ensures a consistent body and explicit WWW-Authenticate header - error_page 401 = @basic_auth_failure; - # Optional: do not forward client Authorization header to upstream proxy_set_header Authorization ""; @@ -320,12 +469,6 @@ http { # Pass traffic to upstream proxy_pass http://backend_default; } - - # Internal location for custom 401 response - location @basic_auth_failure { - add_header WWW-Authenticate 'Basic realm="Restricted"' always; - return 401 'Unauthorized'; - } } } ``` @@ -358,9 +501,6 @@ spec: leeway: 60s # Configures auth_jwt_leeway # Sets auth_jwt_type type: signed # signed | encrypted | nested - onFailure: - statusCode: 403 # Set to 403 for example purposes. Defaults to 401. - scheme: Bearer ``` #### Example JWT AuthenticationFilter with Remote JWKs @@ -384,9 +524,6 @@ spec: type: signed # signed | encrypted | nested # Optional cache duration for keys (auth_jwt_key_cache) keyCache: 10m - onFailure: - statusCode: 403 # Set to 403 for example purposes. Defaults to 401. - scheme: Bearer ``` #### Secret referenced by filter @@ -493,9 +630,6 @@ http { add_header X-User-Email $jwt_claim_email always; add_header X-Auth-Mechanism "jwt" always; - # Optional: customize failure per filter onFailure - error_page 401 = @jwt_auth_failure; - # Optional: do not forward client Authorization header to upstream proxy_set_header Authorization ""; @@ -508,12 +642,6 @@ http { # Pass traffic to upstream proxy_pass http://backend_default; } - - # Internal location for custom 401 response - location @jwt_auth_failure { - add_header WWW-Authenticate 'Bearer realm="Restricted", error="insufficient_scope"' always; - return 403 'Forbidden'; - } } } ``` @@ -574,9 +702,6 @@ http { add_header X-User-Email $jwt_claim_email always; add_header X-Auth-Mechanism "jwt" always; - # Optional: customize failure per filter onFailure - error_page 401 = @jwt_auth_failure; - # Optional: do not forward client Authorization header to upstream proxy_set_header Authorization ""; @@ -597,12 +722,6 @@ http { proxy_cache jwks_jwt_auth; proxy_pass https://issuer.example.com/.well-known/jwks.json; } - - # Internal location for custom 401 response - location @jwt_auth_failure { - add_header WWW-Authenticate 'Bearer realm="Restricted", error="invalid_token"' always; - return 401 'Unauthorized'; - } } } ``` @@ -710,19 +829,24 @@ This can use the status `RouteConditionPartiallyInvalid` defined in the Gateway Note: The keyword "resolved" is used to refer to a filter that the controller has found, and matches the reference of the route rule. For a filter to be considered "resolved", it must: + 1. Exist in the same namespace as the HTTP/GRPCRoute 2. The group and kind referenced must match -Invalid resolved filter secnarios: +Invalid resolved filter scenarios: + - Resolved filter that references a secret that does not exist - Resolved filter that referenced a secret with the incorrect data key Valid reference scenarios: + - Resolved filter referenced by a single route rule within a single HTTP/GRPCRoute - Resolved filter referenced by multiple route rules within a single HTTP/GRPCRoute - Resolved filter reference by multiple HTTP/GRPCRoutes Invalid reference scenarios: + +- Resolved filter referenced multiple times in a single route rule within a single HTTP/GRPCRoute - Resolved filter referenced multiple times in a single route rule within a single HTTP/GRPCRoute - Resolved filter referenced multiple times by multiple route rules within a single HTTP/GRPCRoute - Unresolved filter referenced by a single route rule within a single HTTP/GRPCRoute @@ -749,12 +873,6 @@ Proxy cache TTL should be configurable and set to a reasonable default, reducing Users should be advised to regularly rotate their JWKS keys in cases where they chose to reference a local JWKS via a `secrefRef` -### Auth failure behaviour - -3xx response codes should not be allowed and AuthenticationFilter.onFailure must not support redirect targets. This is to prevent to prevent open-redirect abuse. - -401 and 403 should be the only allowable auth failure codes. - ### Optional headers Below are a list of optional defensive headers that user's may choose to include. @@ -789,8 +907,6 @@ We should validated that only one `AuthenticationFilter` is referenced per-rule. This scenario can use the status `RouteConditionPartiallyInvalid` defined in the Gateway API here: https://github.com/nginx/nginx-gateway-fabric/blob/3934c5c8c60b5aea91be4337d63d4e1d8640baa8/internal/controller/state/conditions/conditions.go#L402 -An `AuthenticationFilter` that sets a `onFailure.statusCode` to anything other than `401` or `403` should be rejected. This relates to the "Auth failure behaviour" section in the Security Considerations section. - ## Alternatives The Gateway API defines a means to standardise authentication through use of the [HTTPExternalAuthFilter](https://gateway-api.sigs.k8s.io/reference/spec/#httpexternalauthfilter) available in the HTTPRoute specification. @@ -819,6 +935,52 @@ document that behavior. ## Stretch Goals +### Custom authentiation failure response + +By default, authentication failures return a 401 response. +If a used wanted to change this response code, or include additioanl headers in this response, we can include a custom named location that can be called by the [error_page](https://nginx.org/en/docs/http/ngx_http_core_module.html#error_page) directive. + +Example AuthenticationFilter configuration: + +```yaml +apiVersion: gateway.nginx.org/v1alpha1 +kind: AuthenticationFilter +metadata: + name: basic-auth +spec: + type: Basic + basic: + secretRef: + name: basic-auth-users + realm: "Restricted" + onFailure: + statusCode: 401 + scheme: Basic +``` + +Example NGINX configuration: + +```nginx +server{ + location /api { + auth_basic "Restricted"; + auth_basic_user_file /etc/nginx/secrets/basic-auth-users/auth; + + # Calls named location + error_page 401 = @basic_auth_failure; + } + + location @basic_auth_failure { + add_header WWW-Authenticate 'Basic realm="Restricted"' always; + return 401 'Unauthorized'; + } +} +``` + +If we support this configuration, 3xx response codes should not be allowed and AuthenticationFilter.onFailure must not support redirect targets. This is to prevent to prevent open-redirect abuse. + +We should only allow 401 and 403 response codes. + ### Cross namespace access When referencing secrets for Basic Auth and JWT Auth, the initial implementation will use `LocalObjectReference`. From eb0b8eee45035fac120e6c1258eb420b46ce9e77 Mon Sep 17 00:00:00 2001 From: shaun-nx Date: Tue, 9 Dec 2025 10:06:09 +0000 Subject: [PATCH 05/19] Fix typos and indentation --- docs/proposals/authentication-filter.md | 121 ++++++++++++------------ 1 file changed, 59 insertions(+), 62 deletions(-) diff --git a/docs/proposals/authentication-filter.md b/docs/proposals/authentication-filter.md index 2ee8acf8e6..f618865dcf 100644 --- a/docs/proposals/authentication-filter.md +++ b/docs/proposals/authentication-filter.md @@ -14,7 +14,7 @@ This new filter should eventually expose all forms of authentication available t - Design Authentication CRD with Basic Auth and JWT Auth in mind - Determine initial resource specification - Evaluate filter early in request processing, occurring before URLRewrite, header modifiers and backend selection -- Authentication failures returns 401 Unauthorized by default +- Authentication failures return 401 Unauthorized by default - Ensure response codes are configurable ## Non-Goals @@ -25,9 +25,9 @@ This new filter should eventually expose all forms of authentication available t ## Introduction -This document focuses explicitly on Authentication (AuthN) and not Authorization (AuthZ). Authentication (AuthN) defines the verification of identity. It asks the question, "Who are you?". This is different from Authorization (AuthZ), which preceeds Authentication. It asks the question, "What are you allowed to do". +This document focuses explicitly on Authentication (AuthN) and not Authorization (AuthZ). Authentication (AuthN) defines the verification of identity. It asks the question, "Who are you?". This is different from Authorization (AuthZ), which follows Authentication. It asks the question, "What are you allowed to do?" -This document also focus on HTTP Basic Authentication and JWT Authentication. Other authentication methods such as OpenID Connect (OIDC) are mentioned, but are not part of the CRD design. These will be covered in future design and implementation tasks. +This document also focuses on HTTP Basic Authentication and JWT Authentication. Other authentication methods such as OpenID Connect (OIDC) are mentioned, but are not part of the CRD design. These will be covered in future design and implementation tasks. ## Use Cases @@ -68,7 +68,7 @@ This portion also contains: - Example HTTPRoutes and NGINX configuration 3. Example spec for JWT Auth - Example HTTPRoutes - - Examples for Local & Remote JWKS configration + - Examples for Local & Remote JWKS configuration - Example NGINX configuration for both Local & Remote JWKS - Example of additional optional fields @@ -80,7 +80,7 @@ Below is the Golang API for the `AuthenticationFilter` API: package v1alpha1 import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/nginx/nginx-gateway-fabric/v2/apis/v1alpha1" ) @@ -111,9 +111,9 @@ type AuthenticationFilter struct { // AuthenticationFilterList contains a list of AuthenticationFilter resources. type AuthenticationFilterList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []AuthenticationFilter `json:"items"` + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []AuthenticationFilter `json:"items"` } // AuthenticationFilterSpec defines the desired configuration. @@ -151,19 +151,19 @@ const ( // BasicAuth configures HTTP Basic Authentication. type BasicAuth struct { - // SecretRef allows referencing a Secret in the same namespace. - SecretRef LocalObjectReference `json:"secretRef"` + // SecretRef allows referencing a Secret in the same namespace. + SecretRef LocalObjectReference `json:"secretRef"` - // Realm used by NGINX `auth_basic` directive. - // https://nginx.org/en/docs/http/ngx_http_auth_basic_module.html#auth_basic - // Also configures "realm="" in WWW-Authenticate header in error page location. - Realm string `json:"realm"` + // Realm used by NGINX `auth_basic` directive. + // https://nginx.org/en/docs/http/ngx_http_auth_basic_module.html#auth_basic + // Also configures "realm="" in WWW-Authenticate header in error page location. + Realm string `json:"realm"` } // LocalObjectReference specifies a local Kubernetes object. type LocalObjectReference struct { - // Name is the referenced object. - Name string `json:"name"` + // Name is the referenced object. + Name string `json:"name"` } // JWTKeyMode selects where JWT keys come from. @@ -296,9 +296,9 @@ type JWKSCache struct { type JWTType string const ( - JWTTypeSigned JWTType = "signed" - JWTTypeEncrypted JWTType = "encrypted" - JWTTypeNested JWTType = "nested" + JWTTypeSigned JWTType = "signed" + JWTTypeEncrypted JWTType = "encrypted" + JWTTypeNested JWTType = "nested" ) // AuthScheme enumerates supported WWW-Authenticate schemes. @@ -364,20 +364,20 @@ In the case of Basic Auth, the deployed Secret and HTTPRoute may look like this: #### Secret referenced by filter -To create this kind of secreet for Basic Auth first run this command: +To create this kind of secret for Basic Auth first run this command: ```bash htpasswd -c auth user ``` -This will create a file called `auth` with the user name and an MD5 hashes password: +This will create a file called `auth` with the username and an MD5-hashed password: ```bash cat auth user:$apr1$prQ3Bh4t$A6bmTv7VgmemGe5eqR61j0 ``` -Use these options in the `htpasswd` command for stronger hashing algorithims: +Use these options in the `htpasswd` command for stronger hashing algorithms: ```bash -2 Force SHA-256 hashing of the password (secure). @@ -391,7 +391,7 @@ You can then run this command to generate the secret from the `auth` file: kubectl create secret generic basic-auth --from-file=auth ``` -Note: `auth` will be the default key for secrets referenced `AuthenticationFilters` of `Type: Basic`. +Note: `auth` will be the default key for secrets referenced by `AuthenticationFilters` of `Type: Basic`. Example secret: @@ -437,7 +437,7 @@ spec: Note: For Basic Auth, NGF will store the file used by `auth_basic_user_file` in `/etc/nginx/secrets/` The full path will use the `name` and `key` of the secret referenced by `AuthenticationFilter` -In this case, the full path will be `/etc/nginx/secrets/basic-auth-users/htpasswd` +In this case, the full path will be `/etc/nginx/secrets/basic-auth-users/auth` ```nginx http { @@ -475,9 +475,9 @@ http { ### Example spec for JWT Auth -For JWT Auth, there is two options. +For JWT Auth, there are two options. -1. Local JWKS file stored as as a Secret +1. Local JWKS file stored as a Secret 2. Remote JWKS from an IdP provider like Keycloak #### Example JWT AuthenticationFilter with Local JWKS @@ -534,7 +534,7 @@ To create the example secret, run the following command: kubectl create secret generic basic-auth --from-file=jwks.json ``` -Note: `jwks.json` will be the default key for secrets referenced `AuthenticationFilters` of `Type: JWT`. +Note: `jwks.json` will be the default key for secrets referenced by `AuthenticationFilters` of `Type: JWT`. ```yaml apiVersion: v1 @@ -610,7 +610,7 @@ http { # File-based JWKS # Path is generated by NGF using the name and key from the secret - auth_jwt_key_file /etc/nginx/keys/jwt-keys-secure/jwks; + auth_jwt_key_file /etc/nginx/keys/jwt-keys-secure/jwks.json; # Optional: key cache duration auth_jwt_key_cache 10m; @@ -650,8 +650,8 @@ http { These are some directives the `Remote` mode uses over the `File` mode: -- `auth_jwt_key_request`: When using the `Remote` mode, this is used in place of `auth_jwt_key_file`. This will call the `internal` NGINX location `/_ngf-internal_jwks_uri` to redirect the request to the external auth provider (e.g. KeyCloak) -- `proxy_cache_path`: This is used to configuring caching of the JWKS after an initial request allowing subsequent requests to not request re-authenticaiton for a time +- `auth_jwt_key_request`: When using the `Remote` mode, this is used in place of `auth_jwt_key_file`. This will call the `internal` NGINX location `/_ngf-internal_jwks_uri` to redirect the request to the external auth provider (e.g. Keycloak) +- `proxy_cache_path`: This is used to configure caching of the JWKS after an initial request, allowing subsequent requests to avoid re-authentication for a time ```nginx http { @@ -753,8 +753,8 @@ spec: ### Attachment -Filters must be attached to a HTTPRoute at the `rules.matches` level. -This means that a single `AuthenticationFilter` may be attached mutliple times to a single HTTPRoute. +Filters must be attached to an HTTPRoute at the `rules.matches` level. +This means that a single `AuthenticationFilter` may be attached multiple times to a single HTTPRoute. #### Basic example @@ -766,10 +766,10 @@ This example shows a single HTTPRoute, with a single `filter` defined in a `rule #### Referencing multiple AuthenticationFilter resources in a single rule -Only a single `AuthenticationFilter` may be referened in a single rule. +Only a single `AuthenticationFilter` may be referenced in a single rule. -The `Status` the HTTPRoute/GRPCRoute in this scenario should be set to `Invalid`, and the resource should be `Rejected`. -In this scenario, the route rule that is referencing multiple `AuthenticationFilter` resources will be `Rejected`/ +The Status of the HTTPRoute/GRPCRoute in this scenario should be set to `Invalid`, and the resource should be `Rejected`. +In this scenario, the route rule that is referencing multiple `AuthenticationFilter` resources will be `Rejected`. All other route rules will remain working. The HTTPRoute/GRPCRoute resource will display an `UnresolvedRef` message to inform the user that the rule has been `Rejected`. @@ -812,7 +812,7 @@ spec: #### Referencing an AuthenticationFilter resource that is invalid Note: With appropriate use of CEL validation, we are less likely to encounter a scenario where an AuthenticationFilter has been deployed to the cluster with an invalid configuration. -If this does happen, and a route rule references this AuthenticationFilter, the route rule will be set to `Invalid` and the the HTTPRoute/GRPCRoute will display the `UnresolvedRef` status. +If this does happen, and a route rule references this AuthenticationFilter, the route rule will be set to `Invalid` and the HTTPRoute/GRPCRoute will display the `UnresolvedRef` status. #### Attaching a JWT AuthenticationFilter to a route when using NGINX OSS @@ -836,13 +836,13 @@ For a filter to be considered "resolved", it must: Invalid resolved filter scenarios: - Resolved filter that references a secret that does not exist -- Resolved filter that referenced a secret with the incorrect data key +- Resolved filter that references a secret with the incorrect data key Valid reference scenarios: - Resolved filter referenced by a single route rule within a single HTTP/GRPCRoute - Resolved filter referenced by multiple route rules within a single HTTP/GRPCRoute -- Resolved filter reference by multiple HTTP/GRPCRoutes +- Resolved filter referenced by multiple HTTP/GRPCRoutes Invalid reference scenarios: @@ -871,12 +871,12 @@ Proxy cache TTL should be configurable and set to a reasonable default, reducing ### Key rotation -Users should be advised to regularly rotate their JWKS keys in cases where they chose to reference a local JWKS via a `secrefRef` +Users should be advised to regularly rotate their JWKS keys in cases where they choose to reference a local JWKS via a `secretRef`. ### Optional headers -Below are a list of optional defensive headers that user's may choose to include. -In certain scenarios, these headers may be deployed to improve overall security from client reponses. +Below are a list of optional defensive headers that users may choose to include. +In certain scenarios, these headers may be deployed to improve overall security from client responses. ```nginx add_header Content-Type "text/plain; charset=utf-8" always; @@ -887,23 +887,23 @@ add_header Cache-Control "no-store" always; Detailed header breakdown: - Content-Type: "text/plain; charset=utf-8" - - This header explicitly set the body as plain text. This prevents browsers from treating the response as HTML or JavaScript, and is effective at mitigating Cross-side scrpting (XSS) through error pages + - This header explicitly sets the body as plain text. This prevents browsers from treating the response as HTML or JavaScript, and is effective at mitigating Cross-site scripting (XSS) through error pages - X-Content-Type-Options: "nosniff" - - This header prevents content type confusion. This occurrs when browsers guesses HTML & JavaScript, and executes it despite a benign type. + - This header prevents content type confusion. This occurs when browsers guess HTML and JavaScript, and execute it despite a benign type. - Cache-Control: "no-store" - - This header informs browsers and proxies not to cache the response. Avoids sensitive, auth-related content, from being being stored and served later to unintended recipients. + - This header informs browsers and proxies not to cache the response. Avoids sensitive, auth-related content from being stored and served later to unintended recipients. ### Validation -When referencing an `AuthenticationFilter` in either a HTTPRoute or GRPCRoute, it is important that we ensure all configurable fields are validated, and that the resulting NGINX configuration is correct and secure. +When referencing an `AuthenticationFilter` in either an HTTPRoute or GRPCRoute, it is important that we ensure all configurable fields are validated, and that the resulting NGINX configuration is correct and secure. -All fields in the `AuthenticationFilter` will be validated with Open API Schema. +All fields in the `AuthenticationFilter` will be validated with OpenAPI Schema. We should also include [CEL](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#validation-rules) validation where required. -We should validated that only one `AuthenticationFilter` is referenced per-rule. Multiple references to an `AuthenticationFilter` in a single rule should result in an `Invalid` HTTPRoute/GRPCRoute, and the rule should be `Rejected`. +We should validate that only one `AuthenticationFilter` is referenced per-rule. Multiple references to an `AuthenticationFilter` in a single rule should result in an `Invalid` HTTPRoute/GRPCRoute, and the rule should be `Rejected`. This scenario can use the status `RouteConditionPartiallyInvalid` defined in the Gateway API here: https://github.com/nginx/nginx-gateway-fabric/blob/3934c5c8c60b5aea91be4337d63d4e1d8640baa8/internal/controller/state/conditions/conditions.go#L402 @@ -911,18 +911,18 @@ This scenario can use the status `RouteConditionPartiallyInvalid` defined in the The Gateway API defines a means to standardise authentication through use of the [HTTPExternalAuthFilter](https://gateway-api.sigs.k8s.io/reference/spec/#httpexternalauthfilter) available in the HTTPRoute specification. -This allows users to reference an external authentication services, such as Keycloak, to handle the authentication requests. +This allows users to reference an external authentication service, such as Keycloak, to handle the authentication requests. While this API is available in the experimental channel, it is subject to change. Our decision to go forward with our own `AuthenticationFilter` was to ensure we could quickly provide authentication to our users while allowing us to closely monitor progress of the ExternalAuthFilter. -It is certainly possible for us to provide an External Authentication Services that leverages NGINX and is something we can further investigate as the API progresses. +It is certainly possible for us to provide an External Authentication Service that leverages NGINX and is something we can further investigate as the API progresses. ## Additional considerations -### Documenting filter behavour +### Documenting filter behavior -In regards to documentation of filter behaviour with the `AuthenticationFilter`, the Gateway API documentation on filters states the following: +In regards to documentation of filter behavior with the `AuthenticationFilter`, the Gateway API documentation on filters states the following: ```text Wherever possible, implementations SHOULD implement filters in the order they are specified. @@ -935,10 +935,10 @@ document that behavior. ## Stretch Goals -### Custom authentiation failure response +### Custom authentication failure response By default, authentication failures return a 401 response. -If a used wanted to change this response code, or include additioanl headers in this response, we can include a custom named location that can be called by the [error_page](https://nginx.org/en/docs/http/ngx_http_core_module.html#error_page) directive. +If a user wanted to change this response code, or include additional headers in this response, we can include a custom named location that can be called by the [error_page](https://nginx.org/en/docs/http/ngx_http_core_module.html#error_page) directive. Example AuthenticationFilter configuration: @@ -977,7 +977,7 @@ server{ } ``` -If we support this configuration, 3xx response codes should not be allowed and AuthenticationFilter.onFailure must not support redirect targets. This is to prevent to prevent open-redirect abuse. +If we support this configuration, 3xx response codes should not be allowed and AuthenticationFilter.onFailure must not support redirect targets. This is to prevent open-redirect abuse. We should only allow 401 and 403 response codes. @@ -985,7 +985,7 @@ We should only allow 401 and 403 response codes. When referencing secrets for Basic Auth and JWT Auth, the initial implementation will use `LocalObjectReference`. -Future updates to this will use the `NamespacedSecretKeyReference` in conjunction with `ReferenceGrants` to support access to secrets in different namespace` +Future updates to this will use the `NamespacedSecretKeyReference` in conjunction with `ReferenceGrants` to support access to secrets in different namespaces. Struct for `NamespacedSecretKeyReference`: @@ -1001,9 +1001,8 @@ type NamespacedSecretKeyReference struct { } ``` -For initial implementaion, both Basic Auth and Local JWKS should will only have access to Secrets in the same namespace. +For the initial implementation, both Basic Auth and Local JWKS will only have access to Secrets in the same namespace. -Example: Grant BasicAuth in app-ns to read a Secret in security-ns Example: Grant BasicAuth in app-ns to read a Secret in security-ns ```yaml @@ -1023,7 +1022,6 @@ spec: name: basic-auth-users ``` -AuthenticationFilter referencing the cross-namespace Secret AuthenticationFilter referencing the cross-namespace Secret ```yaml @@ -1043,10 +1041,9 @@ spec: ### Additional Fields for JWT -`require`, `tokenSource` and `propagation` are some additional fields that may be incldued in future updates to the API. +`require`, `tokenSource` and `propagation` are some additional fields that may be included in future updates to the API. These fields allow for more customization of how the JWT auth behaves, but aren't required for the minimal delivery of JWT Auth. -Example of what implementation of these fields might look like: Example of what implementation of these fields might look like: ```yaml @@ -1073,7 +1070,7 @@ spec: - "cli" # Where client presents the token - # By defaults to reading from Authorization header (Bearer) + # By default, reading from Authorization header (Bearer) tokenSource: type: Header # Alternative: read from a cookie named tokenName @@ -1194,7 +1191,7 @@ type HeaderValue struct { - [Gateway API ExternalAuthFilter GEP](https://gateway-api.sigs.k8s.io/geps/gep-1494/) - [HTTPExternalAuthFilter Specification](https://gateway-api.sigs.k8s.io/reference/spec/#httpexternalauthfilter) -- [Kubernetes documentation on CEL validaton](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#validation-rules) +- [Kubernetes documentation on CEL validation](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#validation-rules) - [NGINX HTTP Basic Auth Module](https://nginx.org/en/docs/http/ngx_http_auth_basic_module.html) - [NGINX JWT Auth Module](https://nginx.org/en/docs/http/ngx_http_auth_jwt_module.html) - [NGINX OIDC Module](https://nginx.org/en/docs/http/ngx_http_oidc_module.html) From bc31f0d3d2e81c5ed1a68133a8d637e0016b7932 Mon Sep 17 00:00:00 2001 From: shaun-nx Date: Tue, 9 Dec 2025 10:10:08 +0000 Subject: [PATCH 06/19] Fix reference to KeyCloak --- docs/proposals/authentication-filter.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/proposals/authentication-filter.md b/docs/proposals/authentication-filter.md index f618865dcf..8dfbf6884e 100644 --- a/docs/proposals/authentication-filter.md +++ b/docs/proposals/authentication-filter.md @@ -478,7 +478,7 @@ http { For JWT Auth, there are two options. 1. Local JWKS file stored as a Secret -2. Remote JWKS from an IdP provider like Keycloak +2. Remote JWKS from an IdP provider like KeyCloak #### Example JWT AuthenticationFilter with Local JWKS @@ -650,7 +650,7 @@ http { These are some directives the `Remote` mode uses over the `File` mode: -- `auth_jwt_key_request`: When using the `Remote` mode, this is used in place of `auth_jwt_key_file`. This will call the `internal` NGINX location `/_ngf-internal_jwks_uri` to redirect the request to the external auth provider (e.g. Keycloak) +- `auth_jwt_key_request`: When using the `Remote` mode, this is used in place of `auth_jwt_key_file`. This will call the `internal` NGINX location `/_ngf-internal_jwks_uri` to redirect the request to the external auth provider (e.g. KeyCloak) - `proxy_cache_path`: This is used to configure caching of the JWKS after an initial request, allowing subsequent requests to avoid re-authentication for a time ```nginx @@ -911,7 +911,7 @@ This scenario can use the status `RouteConditionPartiallyInvalid` defined in the The Gateway API defines a means to standardise authentication through use of the [HTTPExternalAuthFilter](https://gateway-api.sigs.k8s.io/reference/spec/#httpexternalauthfilter) available in the HTTPRoute specification. -This allows users to reference an external authentication service, such as Keycloak, to handle the authentication requests. +This allows users to reference an external authentication service, such as KeyCloak, to handle the authentication requests. While this API is available in the experimental channel, it is subject to change. Our decision to go forward with our own `AuthenticationFilter` was to ensure we could quickly provide authentication to our users while allowing us to closely monitor progress of the ExternalAuthFilter. From 32c609bff347d634ddf8e7c9faf2758197c7aeee Mon Sep 17 00:00:00 2001 From: shaun-nx Date: Tue, 9 Dec 2025 13:32:26 +0000 Subject: [PATCH 07/19] Add expected outcomes for each tests case --- docs/proposals/authentication-filter.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/proposals/authentication-filter.md b/docs/proposals/authentication-filter.md index 8dfbf6884e..6f84f302f9 100644 --- a/docs/proposals/authentication-filter.md +++ b/docs/proposals/authentication-filter.md @@ -836,21 +836,29 @@ For a filter to be considered "resolved", it must: Invalid resolved filter scenarios: - Resolved filter that references a secret that does not exist + - Expected outcome: Filter is marked as Invalid. The route rule that references this filter is also marked as Invalid - Resolved filter that references a secret with the incorrect data key + - Expected outcome: Filter is marked as Invalid. The route rule that references this filter is also marked as Invalid Valid reference scenarios: - Resolved filter referenced by a single route rule within a single HTTP/GRPCRoute + - Expected outcome: Requests to this route rule will successfully process authentication requests - Resolved filter referenced by multiple route rules within a single HTTP/GRPCRoute -- Resolved filter referenced by multiple HTTP/GRPCRoutes + - Expected outcome: Requests to all route rules referencing the filter successfully process authentication requests +- Resolved filter referenced by rules in multiple HTTP/GRPCRoutes + - Expected outcome: Requests to all route rules across each HTTP/GRPCRoute successfully process authentication requests Invalid reference scenarios: - Resolved filter referenced multiple times in a single route rule within a single HTTP/GRPCRoute -- Resolved filter referenced multiple times in a single route rule within a single HTTP/GRPCRoute + - Expected outcome: The route rule referencing multiple filters will be marked as Invalid - Resolved filter referenced multiple times by multiple route rules within a single HTTP/GRPCRoute + - Expected outcome: Each route rule referencing multiple filters will be marked as Invalid - Unresolved filter referenced by a single route rule within a single HTTP/GRPCRoute + - Expected outcome: The route rule referencing multiple filters will be marked as Invalid - Unresolved filter referenced by multiple route rules within a single HTTP/GRPCRoute + - Expected outcome: Each route rule referencing multiple filters will be marked as Invalid ## Security Considerations From c90f2acb329bc10a0e6f1a43559d49739cb97894 Mon Sep 17 00:00:00 2001 From: Shaun Date: Tue, 9 Dec 2025 18:23:03 +0000 Subject: [PATCH 08/19] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/proposals/authentication-filter.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/proposals/authentication-filter.md b/docs/proposals/authentication-filter.md index 6f84f302f9..45d4d9d4f2 100644 --- a/docs/proposals/authentication-filter.md +++ b/docs/proposals/authentication-filter.md @@ -478,7 +478,7 @@ http { For JWT Auth, there are two options. 1. Local JWKS file stored as a Secret -2. Remote JWKS from an IdP provider like KeyCloak +2. Remote JWKS from an IdP provider like Keycloak #### Example JWT AuthenticationFilter with Local JWKS @@ -919,7 +919,7 @@ This scenario can use the status `RouteConditionPartiallyInvalid` defined in the The Gateway API defines a means to standardise authentication through use of the [HTTPExternalAuthFilter](https://gateway-api.sigs.k8s.io/reference/spec/#httpexternalauthfilter) available in the HTTPRoute specification. -This allows users to reference an external authentication service, such as KeyCloak, to handle the authentication requests. +This allows users to reference an external authentication service, such as Keycloak, to handle the authentication requests. While this API is available in the experimental channel, it is subject to change. Our decision to go forward with our own `AuthenticationFilter` was to ensure we could quickly provide authentication to our users while allowing us to closely monitor progress of the ExternalAuthFilter. @@ -1030,7 +1030,7 @@ spec: name: basic-auth-users ``` -AuthenticationFilter referencing the cross-namespace Secret + ```yaml apiVersion: gateway.nginx.org/v1alpha1 From 3bb040e5caed04154a1a702d1adbe78b20760412 Mon Sep 17 00:00:00 2001 From: shaun-nx Date: Wed, 10 Dec 2025 17:50:24 +0000 Subject: [PATCH 09/19] Mention testing of correct NGINX configuration --- docs/proposals/authentication-filter.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/proposals/authentication-filter.md b/docs/proposals/authentication-filter.md index 45d4d9d4f2..e74286c37f 100644 --- a/docs/proposals/authentication-filter.md +++ b/docs/proposals/authentication-filter.md @@ -827,12 +827,14 @@ This can use the status `RouteConditionPartiallyInvalid` defined in the Gateway ### Functional Test Cases -Note: The keyword "resolved" is used to refer to a filter that the controller has found, and matches the reference of the route rule. +The keyword "resolved" is used to refer to a filter that the controller has found, and matches the reference of the route rule. For a filter to be considered "resolved", it must: 1. Exist in the same namespace as the HTTP/GRPCRoute 2. The group and kind referenced must match +For each test, we aim to ensure the NGINX configuration is always correct for each scenario. + Invalid resolved filter scenarios: - Resolved filter that references a secret that does not exist From 137b22d47799a7b5e690f4878f1eaf943bf0ef5c Mon Sep 17 00:00:00 2001 From: shaun-nx Date: Fri, 12 Dec 2025 14:50:22 +0000 Subject: [PATCH 10/19] Update functional test cases --- docs/proposals/authentication-filter.md | 57 +++++++++++++------------ 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/docs/proposals/authentication-filter.md b/docs/proposals/authentication-filter.md index e74286c37f..9cdf8e5264 100644 --- a/docs/proposals/authentication-filter.md +++ b/docs/proposals/authentication-filter.md @@ -825,42 +825,45 @@ This can use the status `RouteConditionPartiallyInvalid` defined in the Gateway - Unit tests - Functional tests to validate behavioural scenarios when referencing filters in different combinations. -### Functional Test Cases +## Functional Test Cases -The keyword "resolved" is used to refer to a filter that the controller has found, and matches the reference of the route rule. -For a filter to be considered "resolved", it must: +### Valid scenarios -1. Exist in the same namespace as the HTTP/GRPCRoute -2. The group and kind referenced must match +This sections covers deployment scenarios that are considered valid -For each test, we aim to ensure the NGINX configuration is always correct for each scenario. +- Single route rule with a single path in an HTTPRoute/GRPCRoute referencing a valid AuthenticationFilter +- Single route rule with two or more paths in an HTTPRoute/GRPCRoute referencing a valid AuthenticationFilter +- Two or more route rules each with a single path in an HTTPRoute/GRPCRoute referencing a valid AuthenticationFilter +- Two or more route rules each with two or more paths in an HTTPRoute/GRPCRoute referencing a valid AuthenticationFilter +- Two or more HTTPRoute/GRPCRoute resource each with single route rule with a single path referencing a valid AuthenticationFilter. +- Two or more HTTPRoute/GRPCRoute resource each with single route rule, each with two or more paths referencing a valid AuthenticationFilter. +- Two or more HTTPRoute/GRPCRoute resource each with two or more route rules each with a single path referencing a valid AuthenticationFilter. -Invalid resolved filter scenarios: +### Invalid scenarios -- Resolved filter that references a secret that does not exist - - Expected outcome: Filter is marked as Invalid. The route rule that references this filter is also marked as Invalid -- Resolved filter that references a secret with the incorrect data key - - Expected outcome: Filter is marked as Invalid. The route rule that references this filter is also marked as Invalid +This sections covers deployment scenarios that are considered valid -Valid reference scenarios: +- Single route rule with a single path in an HTTPRoute/GRPCRoute referencing an invalid AuthenticationFilter +- Single route rule with two or more paths in an HTTPRoute/GRPCRoute referencing an invalid AuthenticationFilter +- Two or more route rules each with a single path in an HTTPRoute/GRPCRoute referencing an invalid AuthenticationFilter +- Two or more route rules each with two or more paths in an HTTPRoute/GRPCRoute referencing an invalid AuthenticationFilter +- Two or more HTTPRoute/GRPCRoute resource each with single route rule with a single path referencing an invalid AuthenticationFilter. +- Two or more HTTPRoute/GRPCRoute resource each with single route rule, each with two or more paths referencing an invalid AuthenticationFilter. +- Two or more HTTPRoute/GRPCRoute resource each with two or more route rules each with a single path referencing an invalid AuthenticationFilter. +- Two or more route rules each with a single path in an HTTPRoute/GRPCRoute, where one rule references a valid AuthenticationFilter, and the other references an invalid AuthenticationFilter. +- Two or more route rules each with two or more paths in an HTTPRoute/GRPCRoute where one rule references a valid AuthenticationFilter, and the other references an invalid AuthenticationFilter. +- Two or more valid or invalid AuthenticationFilters referenced in a route rule. -- Resolved filter referenced by a single route rule within a single HTTP/GRPCRoute - - Expected outcome: Requests to this route rule will successfully process authentication requests -- Resolved filter referenced by multiple route rules within a single HTTP/GRPCRoute - - Expected outcome: Requests to all route rules referencing the filter successfully process authentication requests -- Resolved filter referenced by rules in multiple HTTP/GRPCRoutes - - Expected outcome: Requests to all route rules across each HTTP/GRPCRoute successfully process authentication requests +### Invalid AuthenticationFilter scenarios -Invalid reference scenarios: +This section covers configuation scenarios for an AuthenticationFilter resource that would be considered invalid -- Resolved filter referenced multiple times in a single route rule within a single HTTP/GRPCRoute - - Expected outcome: The route rule referencing multiple filters will be marked as Invalid -- Resolved filter referenced multiple times by multiple route rules within a single HTTP/GRPCRoute - - Expected outcome: Each route rule referencing multiple filters will be marked as Invalid -- Unresolved filter referenced by a single route rule within a single HTTP/GRPCRoute - - Expected outcome: The route rule referencing multiple filters will be marked as Invalid -- Unresolved filter referenced by multiple route rules within a single HTTP/GRPCRoute - - Expected outcome: Each route rule referencing multiple filters will be marked as Invalid +- An AuthenticationFilter deployed with an empty `Realm` value +- An AuthenticationFilter deployed with an empty `secretRef.Name` value +- An AuthenticationFilter referencing a secret that does not exist +- An AuthenticationFilter referencing a secret in a different namespace +- An AuthenticationFilter referencing a secret with an incorrect type (e.g Opaque) +- An AuthenticationFilter referencing a secret with an incorrect keyd ## Security Considerations From 5f8baf523535670d9bd2ba2cb1c744b5d09b1f1a Mon Sep 17 00:00:00 2001 From: shaun-nx Date: Mon, 15 Dec 2025 08:00:42 +0000 Subject: [PATCH 11/19] Add details on custom secret types for Basic and JWT auth --- docs/proposals/authentication-filter.md | 28 +++++++++++++++---------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/docs/proposals/authentication-filter.md b/docs/proposals/authentication-filter.md index 9cdf8e5264..423e281d2f 100644 --- a/docs/proposals/authentication-filter.md +++ b/docs/proposals/authentication-filter.md @@ -364,6 +364,9 @@ In the case of Basic Auth, the deployed Secret and HTTPRoute may look like this: #### Secret referenced by filter +For Basic Auth, we will process a custom secret type of `nginx.org/htpasswd`. +This will allow us to be more confident that the user is providing us with the appropriate kind of secret for this use case. + To create this kind of secret for Basic Auth first run this command: ```bash @@ -388,7 +391,7 @@ Use these options in the `htpasswd` command for stronger hashing algorithms: You can then run this command to generate the secret from the `auth` file: ```bash -kubectl create secret generic basic-auth --from-file=auth +kubectl create secret generic auth-basic-test --type='nginx.org/htpasswd' --from-file=auth ``` Note: `auth` will be the default key for secrets referenced by `AuthenticationFilters` of `Type: Basic`. @@ -400,7 +403,7 @@ apiVersion: v1 kind: Secret metadata: name: basic-auth-users -type: Opaque +type: nginx.org/htpasswd data: auth: YWRtaW46JGFwcjEkWnhZMTIzNDUkYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4Lwp1c2VyOiRhcHIxJEFiQzk4NzY1JG1ub3BxcnN0dXZ3eHl6YWJjZGVmZ2hpSktMLwo= ``` @@ -436,8 +439,8 @@ spec: #### Generated NGINX config Note: For Basic Auth, NGF will store the file used by `auth_basic_user_file` in `/etc/nginx/secrets/` -The full path will use the `name` and `key` of the secret referenced by `AuthenticationFilter` -In this case, the full path will be `/etc/nginx/secrets/basic-auth-users/auth` +The full path will use the `name` , `namespace` and `key` of the secret referenced by `AuthenticationFilter` +In this case, the full path will be `/etc/nginx/secrets/default/basic-auth-users/auth` ```nginx http { @@ -455,7 +458,7 @@ http { auth_basic "Restricted"; # Path is generated by NGF using the name and key from the secret - auth_basic_user_file /etc/nginx/secrets/basic-auth-users/auth; + auth_basic_user_file /etc/nginx/secrets/default/basic-auth-users/auth; # Optional: do not forward client Authorization header to upstream proxy_set_header Authorization ""; @@ -528,10 +531,13 @@ spec: #### Secret referenced by filter +For JWT Auth, we will process a custom secret type of `nginx.org/jwt`. +This will allow us to be more confident that the user is providing us with the appropriate kind of secret for this use case. + To create the example secret, run the following command: ```bash -kubectl create secret generic basic-auth --from-file=jwks.json +kubectl create secret generic jwt-keys-secure --type='nginx.org/htpasswd' --from-file=jwks.json ``` Note: `jwks.json` will be the default key for secrets referenced by `AuthenticationFilters` of `Type: JWT`. @@ -541,7 +547,7 @@ apiVersion: v1 kind: Secret metadata: name: jwt-keys-secure -type: Opaque +type: nginx.org/jwt data: jwks.json: ewogICJrZXlzIjogWwogICAgewogICAgICAia3R5IjogIlJTQSIsCiAgICAgICJ1c2UiOiAic2lnIiwKICAgICAgImtpZCI6ICJleGFtcGxlLWtleS1pZCIsCiAgICAgICJhbGciOiAiUlMyNTYiLAogICAgICAibiI6ICJiYXNlNjR1cmwtbW9kdWx1cyIsCiAgICAgICJlIjogIkFRQUIiCiAgICB9CiAgXQp9Cg== ``` @@ -581,8 +587,8 @@ Below are `two` potential NGINX configurations based on the mode used. 1. NGINX Config when using `Mode: File` (i.e. locally referenced JWKS key) Note: For JWT Auth, NGF will store the file used by `auth_jwt_key_file` in `/etc/nginx/keys/` -The full path will use the `name` and `key` of the secret referenced by `AuthenticationFilter` -In this case, the full path will be `/etc/nginx/keys/jwt-keys-secure/jwks.json` +The full path will use the `name`, `namespace`, and `key` of the secret referenced by `AuthenticationFilter` +In this case, the full path will be `/etc/nginx/keys/default/jwt-keys-secure/jwks.json` ```nginx http { @@ -610,7 +616,7 @@ http { # File-based JWKS # Path is generated by NGF using the name and key from the secret - auth_jwt_key_file /etc/nginx/keys/jwt-keys-secure/jwks.json; + auth_jwt_key_file /etc/nginx/keys/default/jwt-keys-secure/jwks.json; # Optional: key cache duration auth_jwt_key_cache 10m; @@ -977,7 +983,7 @@ Example NGINX configuration: server{ location /api { auth_basic "Restricted"; - auth_basic_user_file /etc/nginx/secrets/basic-auth-users/auth; + auth_basic_user_file /etc/nginx/secrets/default/basic-auth-users/auth; # Calls named location error_page 401 = @basic_auth_failure; From 364b29496ee8eb5c637feaf97e6851552089d429 Mon Sep 17 00:00:00 2001 From: shaun-nx Date: Mon, 15 Dec 2025 08:01:56 +0000 Subject: [PATCH 12/19] Change stretch goals to future updates --- docs/proposals/authentication-filter.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/proposals/authentication-filter.md b/docs/proposals/authentication-filter.md index 423e281d2f..b9d4cfe64d 100644 --- a/docs/proposals/authentication-filter.md +++ b/docs/proposals/authentication-filter.md @@ -952,7 +952,7 @@ If implementations choose a strict interpretation of filter ordering, they MUST document that behavior. ``` -## Stretch Goals +## Future updates ### Custom authentication failure response From f9a31ccc9ad66fd36784222ff73e0f6485dbee4b Mon Sep 17 00:00:00 2001 From: shaun-nx Date: Mon, 15 Dec 2025 08:14:03 +0000 Subject: [PATCH 13/19] Fix punctuation --- docs/proposals/authentication-filter.md | 38 ++++++++++++------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/docs/proposals/authentication-filter.md b/docs/proposals/authentication-filter.md index b9d4cfe64d..7c8c9c90fb 100644 --- a/docs/proposals/authentication-filter.md +++ b/docs/proposals/authentication-filter.md @@ -439,7 +439,7 @@ spec: #### Generated NGINX config Note: For Basic Auth, NGF will store the file used by `auth_basic_user_file` in `/etc/nginx/secrets/` -The full path will use the `name` , `namespace` and `key` of the secret referenced by `AuthenticationFilter` +The full path will use the `name`, `namespace`, and `key` of the secret referenced by `AuthenticationFilter` In this case, the full path will be `/etc/nginx/secrets/default/basic-auth-users/auth` ```nginx @@ -494,7 +494,7 @@ spec: type: JWT jwt: realm: "Restricted" - # Key verification mode. Local file or Remote JWKs + # Key verification mode. Local file or Remote JWKS mode: File file: secretRef: @@ -506,7 +506,7 @@ spec: type: signed # signed | encrypted | nested ``` -#### Example JWT AuthenticationFilter with Remote JWKs +#### Example JWT AuthenticationFilter with Remote JWKS ```yaml apiVersion: gateway.nginx.org/v1alpha1 @@ -517,7 +517,7 @@ spec: type: JWT jwt: realm: "Restricted" - # Key verification mode. Local file or Remote JWKs + # Key verification mode. Local file or Remote JWKS mode: Remote remote: url: https://issuer.example.com/.well-known/jwks.json @@ -537,7 +537,7 @@ This will allow us to be more confident that the user is providing us with the a To create the example secret, run the following command: ```bash -kubectl create secret generic jwt-keys-secure --type='nginx.org/htpasswd' --from-file=jwks.json +kubectl create secret generic jwt-keys-secure --type='nginx.org/jwt' --from-file=jwks.json ``` Note: `jwks.json` will be the default key for secrets referenced by `AuthenticationFilters` of `Type: JWT`. @@ -656,7 +656,7 @@ http { These are some directives the `Remote` mode uses over the `File` mode: -- `auth_jwt_key_request`: When using the `Remote` mode, this is used in place of `auth_jwt_key_file`. This will call the `internal` NGINX location `/_ngf-internal_jwks_uri` to redirect the request to the external auth provider (e.g. KeyCloak) +- `auth_jwt_key_request`: When using the `Remote` mode, this is used in place of `auth_jwt_key_file`. This will call the `internal` NGINX location `/_ngf-internal_jwks_uri` to redirect the request to the external auth provider (e.g. Keycloak) - `proxy_cache_path`: This is used to configure caching of the JWKS after an initial request, allowing subsequent requests to avoid re-authentication for a time ```nginx @@ -835,41 +835,41 @@ This can use the status `RouteConditionPartiallyInvalid` defined in the Gateway ### Valid scenarios -This sections covers deployment scenarios that are considered valid +This section covers deployment scenarios that are considered valid - Single route rule with a single path in an HTTPRoute/GRPCRoute referencing a valid AuthenticationFilter - Single route rule with two or more paths in an HTTPRoute/GRPCRoute referencing a valid AuthenticationFilter - Two or more route rules each with a single path in an HTTPRoute/GRPCRoute referencing a valid AuthenticationFilter - Two or more route rules each with two or more paths in an HTTPRoute/GRPCRoute referencing a valid AuthenticationFilter -- Two or more HTTPRoute/GRPCRoute resource each with single route rule with a single path referencing a valid AuthenticationFilter. -- Two or more HTTPRoute/GRPCRoute resource each with single route rule, each with two or more paths referencing a valid AuthenticationFilter. -- Two or more HTTPRoute/GRPCRoute resource each with two or more route rules each with a single path referencing a valid AuthenticationFilter. +- Two or more HTTPRoute/GRPCRoute resources each with a single route rule with a single path referencing a valid AuthenticationFilter. +- Two or more HTTPRoute/GRPCRoute resources each with a single route rule, each with two or more paths referencing a valid AuthenticationFilter. +- Two or more HTTPRoute/GRPCRoute resources each with two or more route rules, each with a single path referencing a valid AuthenticationFilter. ### Invalid scenarios -This sections covers deployment scenarios that are considered valid +This section covers deployment scenarios that are considered invalid - Single route rule with a single path in an HTTPRoute/GRPCRoute referencing an invalid AuthenticationFilter - Single route rule with two or more paths in an HTTPRoute/GRPCRoute referencing an invalid AuthenticationFilter - Two or more route rules each with a single path in an HTTPRoute/GRPCRoute referencing an invalid AuthenticationFilter - Two or more route rules each with two or more paths in an HTTPRoute/GRPCRoute referencing an invalid AuthenticationFilter -- Two or more HTTPRoute/GRPCRoute resource each with single route rule with a single path referencing an invalid AuthenticationFilter. -- Two or more HTTPRoute/GRPCRoute resource each with single route rule, each with two or more paths referencing an invalid AuthenticationFilter. -- Two or more HTTPRoute/GRPCRoute resource each with two or more route rules each with a single path referencing an invalid AuthenticationFilter. +- Two or more HTTPRoute/GRPCRoute resources each with a single route rule with a single path referencing an invalid AuthenticationFilter. +- Two or more HTTPRoute/GRPCRoute resources each with a single route rule, each with two or more paths referencing an invalid AuthenticationFilter. +- Two or more HTTPRoute/GRPCRoute resources each with two or more route rules, each with a single path referencing an invalid AuthenticationFilter. - Two or more route rules each with a single path in an HTTPRoute/GRPCRoute, where one rule references a valid AuthenticationFilter, and the other references an invalid AuthenticationFilter. - Two or more route rules each with two or more paths in an HTTPRoute/GRPCRoute where one rule references a valid AuthenticationFilter, and the other references an invalid AuthenticationFilter. - Two or more valid or invalid AuthenticationFilters referenced in a route rule. ### Invalid AuthenticationFilter scenarios -This section covers configuation scenarios for an AuthenticationFilter resource that would be considered invalid +This section covers configuration scenarios for an AuthenticationFilter resource that would be considered invalid - An AuthenticationFilter deployed with an empty `Realm` value - An AuthenticationFilter deployed with an empty `secretRef.Name` value - An AuthenticationFilter referencing a secret that does not exist - An AuthenticationFilter referencing a secret in a different namespace -- An AuthenticationFilter referencing a secret with an incorrect type (e.g Opaque) -- An AuthenticationFilter referencing a secret with an incorrect keyd +- An AuthenticationFilter referencing a secret with an incorrect type (e.g., Opaque) +- An AuthenticationFilter referencing a secret with an incorrect key ## Security Considerations @@ -1000,7 +1000,7 @@ If we support this configuration, 3xx response codes should not be allowed and A We should only allow 401 and 403 response codes. -### Cross namespace access +### Cross-namespace access When referencing secrets for Basic Auth and JWT Auth, the initial implementation will use `LocalObjectReference`. @@ -1109,7 +1109,7 @@ spec: stripAuthorization: true # Optionally remove client Authorization header before proxy_pass ``` -Example GoLang API changes: +Example Golang API changes: ```go type JWTAuth struct { From 72765f3f714b8b52da7fac04c3b9846faaab0414 Mon Sep 17 00:00:00 2001 From: Shaun Date: Mon, 15 Dec 2025 08:16:27 +0000 Subject: [PATCH 14/19] Update docs/proposals/authentication-filter.md Co-authored-by: Saloni Choudhary <146118978+salonichf5@users.noreply.github.com> --- docs/proposals/authentication-filter.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/proposals/authentication-filter.md b/docs/proposals/authentication-filter.md index 7c8c9c90fb..18acac9b9d 100644 --- a/docs/proposals/authentication-filter.md +++ b/docs/proposals/authentication-filter.md @@ -52,7 +52,7 @@ This document also focuses on HTTP Basic Authentication and JWT Authentication. ```text The realm value is a free-form string that can only be compared for equality with other realms on that -server. The server will service the request only if it can validate +server. The server will service the request only if it can validate the user-id and password for the protection space applying to the requested resource. ``` From 0ca1e035ec66c69ed8cba7f81a6f67b2218563e2 Mon Sep 17 00:00:00 2001 From: shaun-nx Date: Mon, 15 Dec 2025 16:37:47 +0000 Subject: [PATCH 15/19] Update tests to cover expected outcomes for each scenario --- docs/proposals/authentication-filter.md | 101 +++++++++++++----- .../nginx/agent/grpc/context/doc.go | 2 +- 2 files changed, 77 insertions(+), 26 deletions(-) diff --git a/docs/proposals/authentication-filter.md b/docs/proposals/authentication-filter.md index 18acac9b9d..6f3d108949 100644 --- a/docs/proposals/authentication-filter.md +++ b/docs/proposals/authentication-filter.md @@ -833,43 +833,94 @@ This can use the status `RouteConditionPartiallyInvalid` defined in the Gateway ## Functional Test Cases +### Invalid AuthenticationFilter scenarios + +This section covers configuration deployment scenarios for an AuthenticationFilter resource that would be considered invalid. +When an AuthenticationFilter is described as invalid, it could be for these reasons: + +- An AuthenticationFilter deployed with an empty `Realm` value +- An AuthenticationFilter deployed with an empty `secretRef.Name` value +- An AuthenticationFilter referencing a secret that does not exist +- An AuthenticationFilter referencing a secret in a different namespace +- An AuthenticationFilter referencing a secret with an incorrect type (e.g., Opaque) +- An AuthenticationFilter referencing a secret with an incorrect key + ### Valid scenarios This section covers deployment scenarios that are considered valid -- Single route rule with a single path in an HTTPRoute/GRPCRoute referencing a valid AuthenticationFilter -- Single route rule with two or more paths in an HTTPRoute/GRPCRoute referencing a valid AuthenticationFilter -- Two or more route rules each with a single path in an HTTPRoute/GRPCRoute referencing a valid AuthenticationFilter -- Two or more route rules each with two or more paths in an HTTPRoute/GRPCRoute referencing a valid AuthenticationFilter -- Two or more HTTPRoute/GRPCRoute resources each with a single route rule with a single path referencing a valid AuthenticationFilter. -- Two or more HTTPRoute/GRPCRoute resources each with a single route rule, each with two or more paths referencing a valid AuthenticationFilter. -- Two or more HTTPRoute/GRPCRoute resources each with two or more route rules, each with a single path referencing a valid AuthenticationFilter. +Single route rule with a single path in an HTTPRoute/GRPCRoute referencing a valid AuthenticationFilter +- Expected outcomes: + The route rule is makred as valid. + Request to the path will return a 200 response when correctly authenticated. + Request to the path will return a 401 reponse when incorrectly authenticated. + +Single route rule with two or more paths in an HTTPRoute/GRPCRoute referencing a valid AuthenticationFilter +- Expected outcomes: + The route rule is makred as valid. + Requests to any path in the valid route rule return a 200 response when correctly authenticated. + Requests to any path in the valid route rule return a 401 reponse when incorrectly authenticated. + +Two or more route rules each with a single path in an HTTPRoute/GRPCRoute referencing a valid AuthenticationFilter +- Expected outcomes: + All route rules are marked as valid. + Request to a path in each route rule will return a 200 response when correctly authenticated. + Request to a path in each route rule will return a 401 reponse when incorrectly authenticated. + +Two or more route rules each with two or more paths in an HTTPRoute/GRPCRoute referencing a valid AuthenticationFilter +- Expected outcomes: + All route rules are marked as valid. + Requests to any path in the valid route rule return a 200 response when correctly authenticated. + Requests to any path in the valid route rule return a 401 reponse when incorrectly authenticated. ### Invalid scenarios This section covers deployment scenarios that are considered invalid -- Single route rule with a single path in an HTTPRoute/GRPCRoute referencing an invalid AuthenticationFilter -- Single route rule with two or more paths in an HTTPRoute/GRPCRoute referencing an invalid AuthenticationFilter -- Two or more route rules each with a single path in an HTTPRoute/GRPCRoute referencing an invalid AuthenticationFilter -- Two or more route rules each with two or more paths in an HTTPRoute/GRPCRoute referencing an invalid AuthenticationFilter -- Two or more HTTPRoute/GRPCRoute resources each with a single route rule with a single path referencing an invalid AuthenticationFilter. -- Two or more HTTPRoute/GRPCRoute resources each with a single route rule, each with two or more paths referencing an invalid AuthenticationFilter. -- Two or more HTTPRoute/GRPCRoute resources each with two or more route rules, each with a single path referencing an invalid AuthenticationFilter. -- Two or more route rules each with a single path in an HTTPRoute/GRPCRoute, where one rule references a valid AuthenticationFilter, and the other references an invalid AuthenticationFilter. -- Two or more route rules each with two or more paths in an HTTPRoute/GRPCRoute where one rule references a valid AuthenticationFilter, and the other references an invalid AuthenticationFilter. -- Two or more valid or invalid AuthenticationFilters referenced in a route rule. +Single route rule with a single path in an HTTPRoute/GRPCRoute referencing an invalid AuthenticationFilter +- Expected outcomes: + The route rule is maked as invalid. + Request to the path will return a 500 error. -### Invalid AuthenticationFilter scenarios +Single route rule with two or more paths in an HTTPRoute/GRPCRoute each referencing an invalid AuthenticationFilter +- Expected outcomes: + The route rules are maked as invalid. + Requets to both paths in will return a 500 error. -This section covers configuration scenarios for an AuthenticationFilter resource that would be considered invalid +Two or more route rules each with a single path in an HTTPRoute/GRPCRoute referencing an invalid AuthenticationFilter +- Expected outcomes: + Both route rules are marked as invalid. + Requets to each paths in each route rule will return a 500 error. + +Two or more route rules each with two or more paths in an HTTPRoute/GRPCRoute referencing an invalid AuthenticationFilter +- Expected outcomes: + Both route rules are marked as invalid. + Requets to each paths in each route rule will return a 500 error. + + +Two or more route rules each with a single path in an HTTPRoute/GRPCRoute, where one rule references a valid AuthenticationFilter, and the other references an invalid AuthenticationFilter +- Expected outcomes: + The route rule referencing the invald AuthentiationFilter is marked as invalid. + Requests to the path in the invalid route rule will return a 500 error. + The roue rule referencing the valid AuthenticationFilter is marked as valid. + Requests to the path in the valid route rule will return a 200 reponse when correctly authenticated. + Requests to the path in the valid route rule will return a 401 reponse when incorrectly authenticated. + + +Two or more route rules each with two or more paths in an HTTPRoute/GRPCRoute where one rule references a valid AuthenticationFilter, and the other references an invalid AuthenticationFilter +- Expected outcomes: + The route rules referencing the invald AuthentiationFilter is marked as invalid. + Requests to any path in the invalid route rule will return a 500 error. + The roue rules referencing the valid AuthenticationFilter is marked as valid. + Requests to any path in the valid route rule will return a 200 reponse when correctly authenticated. + Requests to any path in the valid route rule will return a 401 reponse when incorrectly authenticated. + + +Two or more AuthenticationFilters referenced in a route rule. +- Expected outcomes: + The route rule referencing multiple AuthenticationFilters is marked as invalid. + Requests to any path in the invalid route rule will return a 500 error. -- An AuthenticationFilter deployed with an empty `Realm` value -- An AuthenticationFilter deployed with an empty `secretRef.Name` value -- An AuthenticationFilter referencing a secret that does not exist -- An AuthenticationFilter referencing a secret in a different namespace -- An AuthenticationFilter referencing a secret with an incorrect type (e.g., Opaque) -- An AuthenticationFilter referencing a secret with an incorrect key ## Security Considerations diff --git a/internal/controller/nginx/agent/grpc/context/doc.go b/internal/controller/nginx/agent/grpc/context/doc.go index 4091b10d69..689a126cf7 100644 --- a/internal/controller/nginx/agent/grpc/context/doc.go +++ b/internal/controller/nginx/agent/grpc/context/doc.go @@ -1,4 +1,4 @@ /* Package context contains the functions for storing extra information in the gRPC context. */ -package context //nolint:revive // ignoring conflicting package name +package context From d69ee1a8532f2f8319b498f1f5ea5341377e67c6 Mon Sep 17 00:00:00 2001 From: shaun-nx Date: Tue, 16 Dec 2025 08:22:51 +0000 Subject: [PATCH 16/19] Correct typos and grammer --- docs/proposals/authentication-filter.md | 42 ++++++++++++------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/docs/proposals/authentication-filter.md b/docs/proposals/authentication-filter.md index 6f3d108949..36b83f41e1 100644 --- a/docs/proposals/authentication-filter.md +++ b/docs/proposals/authentication-filter.md @@ -27,7 +27,7 @@ This new filter should eventually expose all forms of authentication available t This document focuses explicitly on Authentication (AuthN) and not Authorization (AuthZ). Authentication (AuthN) defines the verification of identity. It asks the question, "Who are you?". This is different from Authorization (AuthZ), which follows Authentication. It asks the question, "What are you allowed to do?" -This document also focuses on HTTP Basic Authentication and JWT Authentication. Other authentication methods such as OpenID Connect (OIDC) are mentioned, but are not part of the CRD design. These will be covered in future design and implementation tasks. +This document also focuses on HTTP Basic Authentication and JWT Authentication. Other authentication methods such as OpenID Connect (OIDC) are mentioned but are not part of the CRD design. These will be covered in future design and implementation tasks. ## Use Cases @@ -829,7 +829,7 @@ This can use the status `RouteConditionPartiallyInvalid` defined in the Gateway ## Testing - Unit tests -- Functional tests to validate behavioural scenarios when referencing filters in different combinations. +- Functional tests to validate behavioral scenarios when referencing filters in different combinations. ## Functional Test Cases @@ -851,27 +851,27 @@ This section covers deployment scenarios that are considered valid Single route rule with a single path in an HTTPRoute/GRPCRoute referencing a valid AuthenticationFilter - Expected outcomes: - The route rule is makred as valid. + The route rule is marked as valid. Request to the path will return a 200 response when correctly authenticated. - Request to the path will return a 401 reponse when incorrectly authenticated. + Request to the path will return a 401 response when incorrectly authenticated. Single route rule with two or more paths in an HTTPRoute/GRPCRoute referencing a valid AuthenticationFilter - Expected outcomes: - The route rule is makred as valid. + The route rule is marked as valid. Requests to any path in the valid route rule return a 200 response when correctly authenticated. - Requests to any path in the valid route rule return a 401 reponse when incorrectly authenticated. + Requests to any path in the valid route rule return a 401 response when incorrectly authenticated. Two or more route rules each with a single path in an HTTPRoute/GRPCRoute referencing a valid AuthenticationFilter - Expected outcomes: All route rules are marked as valid. Request to a path in each route rule will return a 200 response when correctly authenticated. - Request to a path in each route rule will return a 401 reponse when incorrectly authenticated. + Request to a path in each route rule will return a 401 response when incorrectly authenticated. Two or more route rules each with two or more paths in an HTTPRoute/GRPCRoute referencing a valid AuthenticationFilter - Expected outcomes: All route rules are marked as valid. Requests to any path in the valid route rule return a 200 response when correctly authenticated. - Requests to any path in the valid route rule return a 401 reponse when incorrectly authenticated. + Requests to any path in the valid route rule return a 401 response when incorrectly authenticated. ### Invalid scenarios @@ -879,32 +879,32 @@ This section covers deployment scenarios that are considered invalid Single route rule with a single path in an HTTPRoute/GRPCRoute referencing an invalid AuthenticationFilter - Expected outcomes: - The route rule is maked as invalid. + The route rule is marked as invalid. Request to the path will return a 500 error. Single route rule with two or more paths in an HTTPRoute/GRPCRoute each referencing an invalid AuthenticationFilter - Expected outcomes: - The route rules are maked as invalid. - Requets to both paths in will return a 500 error. + The route rules are marked as invalid. + Requests to both paths in will return a 500 error. Two or more route rules each with a single path in an HTTPRoute/GRPCRoute referencing an invalid AuthenticationFilter - Expected outcomes: Both route rules are marked as invalid. - Requets to each paths in each route rule will return a 500 error. + Requests to each paths in each route rule will return a 500 error. Two or more route rules each with two or more paths in an HTTPRoute/GRPCRoute referencing an invalid AuthenticationFilter - Expected outcomes: Both route rules are marked as invalid. - Requets to each paths in each route rule will return a 500 error. + Requests to each paths in each route rule will return a 500 error. Two or more route rules each with a single path in an HTTPRoute/GRPCRoute, where one rule references a valid AuthenticationFilter, and the other references an invalid AuthenticationFilter - Expected outcomes: - The route rule referencing the invald AuthentiationFilter is marked as invalid. + The route rule referencing the invalid AuthentiationFilter is marked as invalid. Requests to the path in the invalid route rule will return a 500 error. The roue rule referencing the valid AuthenticationFilter is marked as valid. - Requests to the path in the valid route rule will return a 200 reponse when correctly authenticated. - Requests to the path in the valid route rule will return a 401 reponse when incorrectly authenticated. + Requests to the path in the valid route rule will return a 200 response when correctly authenticated. + Requests to the path in the valid route rule will return a 401 response when incorrectly authenticated. Two or more route rules each with two or more paths in an HTTPRoute/GRPCRoute where one rule references a valid AuthenticationFilter, and the other references an invalid AuthenticationFilter @@ -912,8 +912,8 @@ Two or more route rules each with two or more paths in an HTTPRoute/GRPCRoute wh The route rules referencing the invald AuthentiationFilter is marked as invalid. Requests to any path in the invalid route rule will return a 500 error. The roue rules referencing the valid AuthenticationFilter is marked as valid. - Requests to any path in the valid route rule will return a 200 reponse when correctly authenticated. - Requests to any path in the valid route rule will return a 401 reponse when incorrectly authenticated. + Requests to any path in the valid route rule will return a 200 response when correctly authenticated. + Requests to any path in the valid route rule will return a 401 response when incorrectly authenticated. Two or more AuthenticationFilters referenced in a route rule. @@ -929,7 +929,7 @@ Two or more AuthenticationFilters referenced in a route rule. Basic Auth sends credentials in an Authorization header that is base64-encoded. JWT Auth requires users to provide a bearer token through the Authorization header. -Both of these methods can be easily intercepted over HTTP. +Both methods can be easily intercepted over HTTP. Users that attach an `AuthenticationFilter` to an HTTPRoute/GRPCRoute should be advised to enable HTTPS traffic at the Gateway level for the routes. @@ -979,7 +979,7 @@ This scenario can use the status `RouteConditionPartiallyInvalid` defined in the ## Alternatives -The Gateway API defines a means to standardise authentication through use of the [HTTPExternalAuthFilter](https://gateway-api.sigs.k8s.io/reference/spec/#httpexternalauthfilter) available in the HTTPRoute specification. +The Gateway API defines a means to standardize authentication through use of the [HTTPExternalAuthFilter](https://gateway-api.sigs.k8s.io/reference/spec/#httpexternalauthfilter) available in the HTTPRoute specification. This allows users to reference an external authentication service, such as Keycloak, to handle the authentication requests. While this API is available in the experimental channel, it is subject to change. @@ -992,7 +992,7 @@ It is certainly possible for us to provide an External Authentication Service th ### Documenting filter behavior -In regards to documentation of filter behavior with the `AuthenticationFilter`, the Gateway API documentation on filters states the following: +In regard to documentation of filter behavior with the `AuthenticationFilter`, the Gateway API documentation on filters states the following: ```text Wherever possible, implementations SHOULD implement filters in the order they are specified. From d8b66b061db1722fb3e34334be0a62cd9ee826d5 Mon Sep 17 00:00:00 2001 From: shaun-nx Date: Tue, 16 Dec 2025 08:23:56 +0000 Subject: [PATCH 17/19] Remove scenarios related to invalid AuthenticationFilter deployments --- docs/proposals/authentication-filter.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/proposals/authentication-filter.md b/docs/proposals/authentication-filter.md index 36b83f41e1..9d97552f03 100644 --- a/docs/proposals/authentication-filter.md +++ b/docs/proposals/authentication-filter.md @@ -838,8 +838,6 @@ This can use the status `RouteConditionPartiallyInvalid` defined in the Gateway This section covers configuration deployment scenarios for an AuthenticationFilter resource that would be considered invalid. When an AuthenticationFilter is described as invalid, it could be for these reasons: -- An AuthenticationFilter deployed with an empty `Realm` value -- An AuthenticationFilter deployed with an empty `secretRef.Name` value - An AuthenticationFilter referencing a secret that does not exist - An AuthenticationFilter referencing a secret in a different namespace - An AuthenticationFilter referencing a secret with an incorrect type (e.g., Opaque) From 98ceae37ebbfd96eb5501e398051cb135dba354a Mon Sep 17 00:00:00 2001 From: shaun-nx Date: Tue, 16 Dec 2025 08:37:36 +0000 Subject: [PATCH 18/19] Update details on secret path and filename creation --- docs/proposals/authentication-filter.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/proposals/authentication-filter.md b/docs/proposals/authentication-filter.md index 9d97552f03..1c98dd437b 100644 --- a/docs/proposals/authentication-filter.md +++ b/docs/proposals/authentication-filter.md @@ -438,9 +438,9 @@ spec: #### Generated NGINX config -Note: For Basic Auth, NGF will store the file used by `auth_basic_user_file` in `/etc/nginx/secrets/` -The full path will use the `name`, `namespace`, and `key` of the secret referenced by `AuthenticationFilter` -In this case, the full path will be `/etc/nginx/secrets/default/basic-auth-users/auth` +For Basic Auth, NGF will store the file used by `auth_basic_user_file` in `/etc/nginx/secrets/` +The full path to the file will be `/etc/nginx/secrets/basic_auth__` +In this case, the full path will be `/etc/nginx/secrets/basic_auth_default_basic-auth-user` ```nginx http { @@ -458,7 +458,7 @@ http { auth_basic "Restricted"; # Path is generated by NGF using the name and key from the secret - auth_basic_user_file /etc/nginx/secrets/default/basic-auth-users/auth; + auth_basic_user_file /etc/nginx/secrets/basic_auth_default_basic_auth_user; # Optional: do not forward client Authorization header to upstream proxy_set_header Authorization ""; @@ -586,9 +586,9 @@ Below are `two` potential NGINX configurations based on the mode used. 1. NGINX Config when using `Mode: File` (i.e. locally referenced JWKS key) -Note: For JWT Auth, NGF will store the file used by `auth_jwt_key_file` in `/etc/nginx/keys/` -The full path will use the `name`, `namespace`, and `key` of the secret referenced by `AuthenticationFilter` -In this case, the full path will be `/etc/nginx/keys/default/jwt-keys-secure/jwks.json` +or JWT Auth, NGF will store the file used by `auth_jwt_key_file` in `/etc/nginx/keys/` +The full path to the file will be `/etc/nginx/secrets/jwt_auth__` +In this case, the full path will be `/etc/nginx/keys/jwt_auth_default_jwt-keys-secure` ```nginx http { @@ -616,7 +616,7 @@ http { # File-based JWKS # Path is generated by NGF using the name and key from the secret - auth_jwt_key_file /etc/nginx/keys/default/jwt-keys-secure/jwks.json; + auth_jwt_key_file /etc/nginx/keys/jwt_auth_default_jwt-keys-secure; # Optional: key cache duration auth_jwt_key_cache 10m; @@ -1032,7 +1032,7 @@ Example NGINX configuration: server{ location /api { auth_basic "Restricted"; - auth_basic_user_file /etc/nginx/secrets/default/basic-auth-users/auth; + auth_basic_user_file /etc/nginx/secrets/basic_auth_default_basic_auth_user; # Calls named location error_page 401 = @basic_auth_failure; From bb91ce14def51413e2d4eb85c859554789b2cce1 Mon Sep 17 00:00:00 2001 From: shaun-nx Date: Tue, 16 Dec 2025 08:56:02 +0000 Subject: [PATCH 19/19] Update heading casing --- docs/proposals/authentication-filter.md | 96 ++++++++++++------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/docs/proposals/authentication-filter.md b/docs/proposals/authentication-filter.md index 1c98dd437b..bae557f166 100644 --- a/docs/proposals/authentication-filter.md +++ b/docs/proposals/authentication-filter.md @@ -35,7 +35,7 @@ This document also focuses on HTTP Basic Authentication and JWT Authentication. - As an Application Developer, I want to secure access to my APIs and Backend Applications. - As an Application Developer, I want to enforce authentication on specific routes and matches. -### Understanding NGINX authentication methods +### Understanding NGINX Authentication Methods | **Authentication Method** | **OSS** | **Plus** | **NGINX Module** | **Details** | | ------------------------------- | -------------- | ---------------- | ---------------------------------- | -------------------------------------------------------------------- | @@ -43,7 +43,7 @@ This document also focuses on HTTP Basic Authentication and JWT Authentication. | **JWT (JSON Web Token)** | ❌ | ✅ | [ngx_http_auth_jwt_module](https://nginx.org/en/docs/http/ngx_http_auth_jwt_module.html) | Tokens are used for stateless authentication between client and server. | | **OpenID Connect** | ❌ | ✅ | [ngx_http_oidc_module](https://nginx.org/en/docs/http/ngx_http_oidc_module.html) | Allows authentication through third-party providers like Google. | -### Understanding authentication terminology +### Understanding Authentication Terminology #### Realms @@ -345,7 +345,7 @@ const ( ) ``` -### Example spec for Basic Auth +### Example Spec for Basic Auth ```yaml apiVersion: gateway.nginx.org/v1alpha1 @@ -362,7 +362,7 @@ spec: In the case of Basic Auth, the deployed Secret and HTTPRoute may look like this: -#### Secret referenced by filter +#### Secret Referenced by Filter For Basic Auth, we will process a custom secret type of `nginx.org/htpasswd`. This will allow us to be more confident that the user is providing us with the appropriate kind of secret for this use case. @@ -436,7 +436,7 @@ spec: port: 80 ``` -#### Generated NGINX config +#### Generated NGINX Config For Basic Auth, NGF will store the file used by `auth_basic_user_file` in `/etc/nginx/secrets/` The full path to the file will be `/etc/nginx/secrets/basic_auth__` @@ -529,7 +529,7 @@ spec: keyCache: 10m ``` -#### Secret referenced by filter +#### Secret Referenced by Filter For JWT Auth, we will process a custom secret type of `nginx.org/jwt`. This will allow us to be more confident that the user is providing us with the appropriate kind of secret for this use case. @@ -552,7 +552,7 @@ data: jwks.json: ewogICJrZXlzIjogWwogICAgewogICAgICAia3R5IjogIlJTQSIsCiAgICAgICJ1c2UiOiAic2lnIiwKICAgICAgImtpZCI6ICJleGFtcGxlLWtleS1pZCIsCiAgICAgICJhbGciOiAiUlMyNTYiLAogICAgICAibiI6ICJiYXNlNjR1cmwtbW9kdWx1cyIsCiAgICAgICJlIjogIkFRQUIiCiAgICB9CiAgXQp9Cg== ``` -#### HTTPRoute that will reference this filter +#### HTTPRoute that Will Reference this Filter ```yaml apiVersion: gateway.networking.k8s.io/v1 @@ -732,7 +732,7 @@ http { } ``` -### Caching configuration +### Caching Configuration Users may also choose to change the caching configuration set by `proxy_cache_path`. This can be made available in the `cache` configuration under `jwt.remote.cache` @@ -770,7 +770,7 @@ This example shows a single HTTPRoute, with a single `filter` defined in a `rule ### Status -#### Referencing multiple AuthenticationFilter resources in a single rule +#### Referencing multiple AuthenticationFilter Resources in a Single Rule Only a single `AuthenticationFilter` may be referenced in a single rule. @@ -780,7 +780,7 @@ All other route rules will remain working. The HTTPRoute/GRPCRoute resource will display an `UnresolvedRef` message to inform the user that the rule has been `Rejected`. -This behaviour falls in line with the expected behaviour of filters in the Gateway API, which generally allows only one type of a specific filter (authentication, rewriting, etc.) within a rule. +This behavior falls in line with the expected behavior of filters in the Gateway API, which generally allows only one type of a specific filter (authentication, rewriting, etc.) within a rule. Below is an example of an **invalid** HTTPRoute that references multiple `AuthenticationFilter` resources in a single rule: @@ -815,14 +815,14 @@ spec: port: 80 ``` -#### Referencing an AuthenticationFilter resource that is invalid +#### Referencing an AuthenticationFilter Resource that Is Invalid -Note: With appropriate use of CEL validation, we are less likely to encounter a scenario where an AuthenticationFilter has been deployed to the cluster with an invalid configuration. -If this does happen, and a route rule references this AuthenticationFilter, the route rule will be set to `Invalid` and the HTTPRoute/GRPCRoute will display the `UnresolvedRef` status. +Note: With appropriate use of CEL validation, we are less likely to encounter a scenario where an `AuthenticationFilter` has been deployed to the cluster with an invalid configuration. +If this does happen, and a route rule references this `AuthenticationFilter`, the route rule will be set to `Invalid` and the HTTPRoute/GRPCRoute will display the `UnresolvedRef` status. -#### Attaching a JWT AuthenticationFilter to a route when using NGINX OSS +#### Attaching a JWT AuthenticationFilter to a Route When Using NGINX OSS -If a user attempts to attach a JWT type AuthenticationFilter while using NGINX OSS, the rule referencing the filter will be `Rejected`. +If a user attempts to attach a JWT type `AuthenticationFilter` while using NGINX OSS, the rule referencing the filter will be `Rejected`. This can use the status `RouteConditionPartiallyInvalid` defined in the Gateway API here: https://github.com/nginx/nginx-gateway-fabric/blob/3934c5c8c60b5aea91be4337d63d4e1d8640baa8/internal/controller/state/conditions/conditions.go#L402 @@ -833,39 +833,39 @@ This can use the status `RouteConditionPartiallyInvalid` defined in the Gateway ## Functional Test Cases -### Invalid AuthenticationFilter scenarios +### Invalid AuthenticationFilter Scenarios -This section covers configuration deployment scenarios for an AuthenticationFilter resource that would be considered invalid. -When an AuthenticationFilter is described as invalid, it could be for these reasons: +This section covers configuration deployment scenarios for an `AuthenticationFilter` resource that would be considered invalid. +When an `AuthenticationFilter` is described as invalid, it could be for these reasons: -- An AuthenticationFilter referencing a secret that does not exist -- An AuthenticationFilter referencing a secret in a different namespace -- An AuthenticationFilter referencing a secret with an incorrect type (e.g., Opaque) -- An AuthenticationFilter referencing a secret with an incorrect key +- An `AuthenticationFilter` referencing a secret that does not exist +- An `AuthenticationFilter` referencing a secret in a different namespace +- An `AuthenticationFilter` referencing a secret with an incorrect type (e.g., Opaque) +- An `AuthenticationFilter` referencing a secret with an incorrect key -### Valid scenarios +### Valid Scenarios This section covers deployment scenarios that are considered valid -Single route rule with a single path in an HTTPRoute/GRPCRoute referencing a valid AuthenticationFilter +Single route rule with a single path in an HTTPRoute/GRPCRoute referencing a valid `AuthenticationFilter` - Expected outcomes: The route rule is marked as valid. Request to the path will return a 200 response when correctly authenticated. Request to the path will return a 401 response when incorrectly authenticated. -Single route rule with two or more paths in an HTTPRoute/GRPCRoute referencing a valid AuthenticationFilter +Single route rule with two or more paths in an HTTPRoute/GRPCRoute referencing a valid `AuthenticationFilter` - Expected outcomes: The route rule is marked as valid. Requests to any path in the valid route rule return a 200 response when correctly authenticated. Requests to any path in the valid route rule return a 401 response when incorrectly authenticated. -Two or more route rules each with a single path in an HTTPRoute/GRPCRoute referencing a valid AuthenticationFilter +Two or more route rules each with a single path in an HTTPRoute/GRPCRoute referencing a valid `AuthenticationFilter` - Expected outcomes: All route rules are marked as valid. Request to a path in each route rule will return a 200 response when correctly authenticated. Request to a path in each route rule will return a 401 response when incorrectly authenticated. -Two or more route rules each with two or more paths in an HTTPRoute/GRPCRoute referencing a valid AuthenticationFilter +Two or more route rules each with two or more paths in an HTTPRoute/GRPCRoute referencing a valid `AuthenticationFilter` - Expected outcomes: All route rules are marked as valid. Requests to any path in the valid route rule return a 200 response when correctly authenticated. @@ -875,48 +875,48 @@ Two or more route rules each with two or more paths in an HTTPRoute/GRPCRoute re This section covers deployment scenarios that are considered invalid -Single route rule with a single path in an HTTPRoute/GRPCRoute referencing an invalid AuthenticationFilter +Single route rule with a single path in an HTTPRoute/GRPCRoute referencing an invalid `AuthenticationFilter` - Expected outcomes: The route rule is marked as invalid. Request to the path will return a 500 error. -Single route rule with two or more paths in an HTTPRoute/GRPCRoute each referencing an invalid AuthenticationFilter +Single route rule with two or more paths in an HTTPRoute/GRPCRoute where each route rule references an invalid Authe`nticationFilter - Expected outcomes: The route rules are marked as invalid. Requests to both paths in will return a 500 error. -Two or more route rules each with a single path in an HTTPRoute/GRPCRoute referencing an invalid AuthenticationFilter +Two or more route rules each with a single path in an HTTPRoute/GRPCRoute referencing an invalid `AuthenticationFilter` - Expected outcomes: Both route rules are marked as invalid. Requests to each paths in each route rule will return a 500 error. -Two or more route rules each with two or more paths in an HTTPRoute/GRPCRoute referencing an invalid AuthenticationFilter +Two or more route rules each with two or more paths in an HTTPRoute/GRPCRoute referencing an invalid `AuthenticationFilter` - Expected outcomes: Both route rules are marked as invalid. Requests to each paths in each route rule will return a 500 error. -Two or more route rules each with a single path in an HTTPRoute/GRPCRoute, where one rule references a valid AuthenticationFilter, and the other references an invalid AuthenticationFilter +Two or more route rules each with a single path in an HTTPRoute/GRPCRoute, where one rule references a valid `AuthenticationFilter`, and the other references an invalid `AuthenticationFilter` - Expected outcomes: - The route rule referencing the invalid AuthentiationFilter is marked as invalid. + The route rule referencing the invalid `AuthentiationFilter` is marked as invalid. Requests to the path in the invalid route rule will return a 500 error. - The roue rule referencing the valid AuthenticationFilter is marked as valid. + The roue rule referencing the valid `AuthenticationFilter` is marked as valid. Requests to the path in the valid route rule will return a 200 response when correctly authenticated. Requests to the path in the valid route rule will return a 401 response when incorrectly authenticated. -Two or more route rules each with two or more paths in an HTTPRoute/GRPCRoute where one rule references a valid AuthenticationFilter, and the other references an invalid AuthenticationFilter +Two or more route rules each with two or more paths in an HTTPRoute/GRPCRoute where one rule references a valid `AuthenticationFilter`, and the other references an invalid `AuthenticationFilter` - Expected outcomes: - The route rules referencing the invald AuthentiationFilter is marked as invalid. + The route rules referencing the invalid `AuthentiationFilter` is marked as invalid. Requests to any path in the invalid route rule will return a 500 error. - The roue rules referencing the valid AuthenticationFilter is marked as valid. + The roue rules referencing the valid `AuthenticationFilter` is marked as valid. Requests to any path in the valid route rule will return a 200 response when correctly authenticated. Requests to any path in the valid route rule will return a 401 response when incorrectly authenticated. -Two or more AuthenticationFilters referenced in a route rule. +Two or more `AuthenticationFilters` referenced in a route rule. - Expected outcomes: - The route rule referencing multiple AuthenticationFilters is marked as invalid. + The route rule referencing multiple `AuthenticationFilters` is marked as invalid. Requests to any path in the invalid route rule will return a 500 error. @@ -924,7 +924,7 @@ Two or more AuthenticationFilters referenced in a route rule. ### Basic Auth and Local JWKS -Basic Auth sends credentials in an Authorization header that is base64-encoded. +Basic Auth sends credentials in an Authorization header which is `base64` encoded. JWT Auth requires users to provide a bearer token through the Authorization header. Both methods can be easily intercepted over HTTP. @@ -937,11 +937,11 @@ Any example configurations and deployments for the `AuthenticationFilter` should Proxy cache TTL should be configurable and set to a reasonable default, reducing periods of stale cached JWKs. -### Key rotation +### Key Rotation Users should be advised to regularly rotate their JWKS keys in cases where they choose to reference a local JWKS via a `secretRef`. -### Optional headers +### Optional Headers Below are a list of optional defensive headers that users may choose to include. In certain scenarios, these headers may be deployed to improve overall security from client responses. @@ -986,9 +986,9 @@ Our decision to go forward with our own `AuthenticationFilter` was to ensure we It is certainly possible for us to provide an External Authentication Service that leverages NGINX and is something we can further investigate as the API progresses. -## Additional considerations +## Additional Considerations -### Documenting filter behavior +### Documenting Filter Behavior In regard to documentation of filter behavior with the `AuthenticationFilter`, the Gateway API documentation on filters states the following: @@ -1001,9 +1001,9 @@ If implementations choose a strict interpretation of filter ordering, they MUST document that behavior. ``` -## Future updates +## Future Updates -### Custom authentication failure response +### Custom Authentication Failure Response By default, authentication failures return a 401 response. If a user wanted to change this response code, or include additional headers in this response, we can include a custom named location that can be called by the [error_page](https://nginx.org/en/docs/http/ngx_http_core_module.html#error_page) directive. @@ -1045,11 +1045,11 @@ server{ } ``` -If we support this configuration, 3xx response codes should not be allowed and AuthenticationFilter.onFailure must not support redirect targets. This is to prevent open-redirect abuse. +If we support this configuration, 3xx response codes should not be allowed and `AuthenticationFilter.onFailure` must not support redirect targets. This is to prevent open-redirect abuse. We should only allow 401 and 403 response codes. -### Cross-namespace access +### Cross-Namespace Access When referencing secrets for Basic Auth and JWT Auth, the initial implementation will use `LocalObjectReference`.