diff --git a/kubernetes/customresourcedefinitions.gen.yaml b/kubernetes/customresourcedefinitions.gen.yaml index 33e5c7f41d..b1ee1950ba 100644 --- a/kubernetes/customresourcedefinitions.gen.yaml +++ b/kubernetes/customresourcedefinitions.gen.yaml @@ -16410,6 +16410,12 @@ spec: description: This field specifies the header name to output a successfully verified JWT payload to the backend. type: string + requireJwt: + description: If set to true, requests without a valid JWT token + will be rejected with a 401 Unauthorized status code along + with a `WWW-Authenticate` header indicating the Bearer authentication + scheme is required. + type: boolean spaceDelimitedClaims: description: List of JWT claim names that should be treated as space-delimited strings. @@ -16699,6 +16705,12 @@ spec: description: This field specifies the header name to output a successfully verified JWT payload to the backend. type: string + requireJwt: + description: If set to true, requests without a valid JWT token + will be rejected with a 401 Unauthorized status code along + with a `WWW-Authenticate` header indicating the Bearer authentication + scheme is required. + type: boolean spaceDelimitedClaims: description: List of JWT claim names that should be treated as space-delimited strings. diff --git a/security/v1/request_authentication_alias.gen.go b/security/v1/request_authentication_alias.gen.go index a0ea1dc617..e555405889 100644 --- a/security/v1/request_authentication_alias.gen.go +++ b/security/v1/request_authentication_alias.gen.go @@ -68,6 +68,18 @@ type RequestAuthentication = v1beta1.RequestAuthentication // // With this configuration, a JWT containing `"custom_scope": "read write admin"` will allow // authorization policies to match against individual values like "read", "write", or "admin". +// +// This example shows how to require JWT tokens and return 401 for missing tokens: +// +// ```yaml +// issuer: https://example.com +// jwksUri: https://example.com/.well-known/jwks.json +// requireJwt: true +// ``` +// +// With `requireJwt: true`, requests without a JWT will receive a 401 Unauthorized response with a +// `WWW-Authenticate: Bearer` header directly from the authentication filter, eliminating the need +// for a separate AuthorizationPolicy when you simply want to require authentication. // +kubebuilder:validation:XValidation:message="only one of jwks or jwksUri can be set",rule="oneof(self.jwksUri, self.jwks_uri, self.jwks)" type JWTRule = v1beta1.JWTRule diff --git a/security/v1beta1/request_authentication.pb.go b/security/v1beta1/request_authentication.pb.go index 492508333c..359413ebc0 100644 --- a/security/v1beta1/request_authentication.pb.go +++ b/security/v1beta1/request_authentication.pb.go @@ -154,6 +154,30 @@ // paths: ["/healthz"] // ``` // +// Alternatively, you can use `requireJwt: true` to enforce JWT requirement directly in the RequestAuthentication +// policy without needing a separate AuthorizationPolicy. This approach returns 401 Unauthorized with a +// `WWW-Authenticate: Bearer` header for missing JWTs instead of 403 Forbidden: +// +// ```yaml +// apiVersion: security.istio.io/v1 +// kind: RequestAuthentication +// metadata: +// name: httpbin +// namespace: foo +// spec: +// selector: +// matchLabels: +// app: httpbin +// jwtRules: +// - issuer: "issuer-foo" +// jwksUri: https://example.com/.well-known/jwks.json +// requireJwt: true +// ``` +// +// With `requireJwt: true`, requests without a JWT will be rejected with 401 Unauthorized and a +// `WWW-Authenticate: Bearer` header, making it clearer that authentication is required. This is semantically +// more accurate than the 403 Forbidden returned by AuthorizationPolicy and complies with RFC 7235. +// // [Experimental] Routing based on derived [metadata](https://istio.io/latest/docs/reference/config/security/conditions/) // is now supported. A prefix '@' is used to denote a match against internal metadata instead of the headers in the request. // Currently this feature is only supported for the following metadata: @@ -410,6 +434,18 @@ func (x *RequestAuthentication) GetJwtRules() []*JWTRule { // // With this configuration, a JWT containing `"custom_scope": "read write admin"` will allow // authorization policies to match against individual values like "read", "write", or "admin". +// +// This example shows how to require JWT tokens and return 401 for missing tokens: +// +// ```yaml +// issuer: https://example.com +// jwksUri: https://example.com/.well-known/jwks.json +// requireJwt: true +// ``` +// +// With `requireJwt: true`, requests without a JWT will receive a 401 Unauthorized response with a +// `WWW-Authenticate: Bearer` header directly from the authentication filter, eliminating the need +// for a separate AuthorizationPolicy when you simply want to require authentication. // +kubebuilder:validation:XValidation:message="only one of jwks or jwksUri can be set",rule="oneof(self.jwksUri, self.jwks_uri, self.jwks)" type JWTRule struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -549,8 +585,37 @@ type JWTRule struct { // +protoc-gen-crd:list-value-validation:MinLength=1 // +kubebuilder:validation:MaxItems=64 SpaceDelimitedClaims []string `protobuf:"bytes,14,rep,name=space_delimited_claims,json=spaceDelimitedClaims,proto3" json:"space_delimited_claims,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + // If set to true, requests without a valid JWT token will be rejected with a 401 Unauthorized status code + // along with a `WWW-Authenticate` header indicating the Bearer authentication scheme is required. + // If set to false or unset (default), requests without a JWT token are allowed to pass through but will not have + // an authenticated identity. In the default case, to enforce that requests must have authentication, + // you should use an AuthorizationPolicy with requestPrincipals. + // + // Note: Setting this to true changes the HTTP status code for missing JWT from 403 (via AuthorizationPolicy) + // to 401 (via RequestAuthentication), which is semantically more accurate for authentication failures and + // includes the proper `WWW-Authenticate` header as required by RFC 7235. + // + // Example usage: + // ```yaml + // jwtRules: + // - issuer: "https://example.com" + // jwksUri: https://example.com/.well-known/jwks.json + // requireJwt: true + // + // ``` + // + // With `requireJwt: true`: + // - Request with missing JWT -> 401 Unauthorized (with WWW-Authenticate: Bearer header) + // - Request with invalid/expired JWT -> 401 Unauthorized (with WWW-Authenticate: Bearer header) + // - Request with valid JWT -> Accepted + // + // With `requireJwt: false` (default): + // - Request with missing JWT -> Accepted (no authenticated identity) + // - Request with invalid/expired JWT -> 401 Unauthorized (with WWW-Authenticate: Bearer header) + // - Request with valid JWT -> Accepted (with authenticated identity) + RequireJwt bool `protobuf:"varint,15,opt,name=require_jwt,json=requireJwt,proto3" json:"require_jwt,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *JWTRule) Reset() { @@ -667,6 +732,13 @@ func (x *JWTRule) GetSpaceDelimitedClaims() []string { return nil } +func (x *JWTRule) GetRequireJwt() bool { + if x != nil { + return x.RequireJwt + } + return false +} + // This message specifies a header location to extract JWT token. type JWTHeader struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -795,7 +867,7 @@ const file_security_v1beta1_request_authentication_proto_rawDesc = "" + "\n" + "targetRefs\x18\x04 \x03(\v2).istio.type.v1beta1.PolicyTargetReferenceR\n" + "targetRefs\x12<\n" + - "\tjwt_rules\x18\x02 \x03(\v2\x1f.istio.security.v1beta1.JWTRuleR\bjwtRules\"\xb0\x04\n" + + "\tjwt_rules\x18\x02 \x03(\v2\x1f.istio.security.v1beta1.JWTRuleR\bjwtRules\"\xd1\x04\n" + "\aJWTRule\x12\x16\n" + "\x06issuer\x18\x01 \x01(\tR\x06issuer\x12\x1c\n" + "\taudiences\x18\x02 \x03(\tR\taudiences\x12\x19\n" + @@ -810,7 +882,9 @@ const file_security_v1beta1_request_authentication_proto_rawDesc = "" + "\x16forward_original_token\x18\t \x01(\bR\x14forwardOriginalToken\x12\\\n" + "\x17output_claim_to_headers\x18\v \x03(\v2%.istio.security.v1beta1.ClaimToHeaderR\x14outputClaimToHeaders\x123\n" + "\atimeout\x18\r \x01(\v2\x19.google.protobuf.DurationR\atimeout\x124\n" + - "\x16space_delimited_claims\x18\x0e \x03(\tR\x14spaceDelimitedClaims\"=\n" + + "\x16space_delimited_claims\x18\x0e \x03(\tR\x14spaceDelimitedClaims\x12\x1f\n" + + "\vrequire_jwt\x18\x0f \x01(\bR\n" + + "requireJwt\"=\n" + "\tJWTHeader\x12\x18\n" + "\x04name\x18\x01 \x01(\tB\x04\xe2A\x01\x02R\x04name\x12\x16\n" + "\x06prefix\x18\x02 \x01(\tR\x06prefix\"I\n" + diff --git a/security/v1beta1/request_authentication.pb.html b/security/v1beta1/request_authentication.pb.html index 31d08f6a64..20e8d8bb1a 100644 --- a/security/v1beta1/request_authentication.pb.html +++ b/security/v1beta1/request_authentication.pb.html @@ -125,6 +125,26 @@ - operation: paths: ["/healthz"] +
Alternatively, you can use requireJwt: true to enforce JWT requirement directly in the RequestAuthentication
+policy without needing a separate AuthorizationPolicy. This approach returns 401 Unauthorized with a
+WWW-Authenticate: Bearer header for missing JWTs instead of 403 Forbidden:
apiVersion: security.istio.io/v1
+kind: RequestAuthentication
+metadata:
+ name: httpbin
+ namespace: foo
+spec:
+ selector:
+ matchLabels:
+ app: httpbin
+ jwtRules:
+ - issuer: "issuer-foo"
+ jwksUri: https://example.com/.well-known/jwks.json
+ requireJwt: true
+
+With requireJwt: true, requests without a JWT will be rejected with 401 Unauthorized and a
+WWW-Authenticate: Bearer header, making it clearer that authentication is required. This is semantically
+more accurate than the 403 Forbidden returned by AuthorizationPolicy and complies with RFC 7235.
[Experimental] Routing based on derived metadata is now supported. A prefix ‘@’ is used to denote a match against internal metadata instead of the headers in the request. Currently this feature is only supported for the following metadata:
@@ -294,6 +314,14 @@With this configuration, a JWT containing "custom_scope": "read write admin" will allow
authorization policies to match against individual values like “read”, “write”, or “admin”.
This example shows how to require JWT tokens and return 401 for missing tokens:
+issuer: https://example.com
+jwksUri: https://example.com/.well-known/jwks.json
+requireJwt: true
+
+With requireJwt: true, requests without a JWT will receive a 401 Unauthorized response with a
+WWW-Authenticate: Bearer header directly from the authentication filter, eliminating the need
+for a separate AuthorizationPolicy when you simply want to require authentication.