Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

api: recomputeRoute field in JWT #2612

Merged
merged 5 commits into from
Feb 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions api/v1alpha1/jwt_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type JWT struct {
}

// JWTProvider defines how a JSON Web Token (JWT) can be verified.
// +kubebuilder:validation:XValidation:rule="(has(self.recomputeRoute) && self.recomputeRoute) ? size(self.claimToHeaders) > 0 : true", message="claimToHeaders must be specified if recomputeRoute is enabled"
type JWTProvider struct {
// Name defines a unique name for the JWT provider. A name can have a variety of forms,
// including RFC1123 subdomains, RFC 1123 labels, or RFC 1035 labels.
Expand Down Expand Up @@ -52,8 +53,17 @@ type JWTProvider struct {
// For examples, following config:
// The claim must be of type; string, int, double, bool. Array type claims are not supported
//
// +optional
ClaimToHeaders []ClaimToHeader `json:"claimToHeaders,omitempty"`

// RecomputeRoute clears the route cache and recalculates the routing decision.
// This field must be enabled if the headers generated from the claim are used for
// route matching decisions. If the recomputation selects a new route, features targeting
// the new matched route will be applied.
//
// +optional
Copy link
Contributor

@guydc guydc Feb 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to state something about the impact on other features? e.g. filters that fired prior to jwt (CORS, ExtAuth, Basic, ...) will only execute in the context of the initial match. Also, later filters (Fault, RL, ...) will only execute in the context of the new route.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added a comment around this

RecomputeRoute *bool `json:"recomputeRoute,omitempty"`
zhaohuabing marked this conversation as resolved.
Show resolved Hide resolved

// ExtractFrom defines different ways to extract the JWT token from HTTP request.
// If empty, it defaults to extract JWT token from the Authorization HTTP request header using Bearer schema
// or access_token from query parameters.
Expand Down
5 changes: 5 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,13 @@ spec:
maxLength: 253
minLength: 1
type: string
recomputeRoute:
description: RecomputeRoute clears the route cache and recalculates
the routing decision. This field must be enabled if the
headers generated from the claim are used for route matching
decisions. If the recomputation selects a new route, features
targeting the new matched route will be applied.
type: boolean
remoteJWKS:
description: RemoteJWKS defines how to fetch and cache JSON
Web Key Sets (JWKS) from a remote HTTP/HTTPS endpoint.
Expand All @@ -479,6 +486,11 @@ spec:
- name
- remoteJWKS
type: object
x-kubernetes-validations:
- message: claimToHeaders must be specified if recomputeRoute
is enabled
rule: '(has(self.recomputeRoute) && self.recomputeRoute) ?
size(self.claimToHeaders) > 0 : true'
maxItems: 4
minItems: 1
type: array
Expand Down
3 changes: 2 additions & 1 deletion site/content/en/latest/api/extension_types.md
Original file line number Diff line number Diff line change
Expand Up @@ -1374,7 +1374,8 @@ _Appears in:_
| `issuer` | _string_ | false | Issuer is the principal that issued the JWT and takes the form of a URL or email address. For additional details, see https://tools.ietf.org/html/rfc7519#section-4.1.1 for URL format and https://rfc-editor.org/rfc/rfc5322.html for email format. If not provided, the JWT issuer is not checked. |
| `audiences` | _string array_ | false | Audiences is a list of JWT audiences allowed access. For additional details, see https://tools.ietf.org/html/rfc7519#section-4.1.3. If not provided, JWT audiences are not checked. |
| `remoteJWKS` | _[RemoteJWKS](#remotejwks)_ | true | RemoteJWKS defines how to fetch and cache JSON Web Key Sets (JWKS) from a remote HTTP/HTTPS endpoint. |
| `claimToHeaders` | _[ClaimToHeader](#claimtoheader) array_ | true | ClaimToHeaders is a list of JWT claims that must be extracted into HTTP request headers For examples, following config: The claim must be of type; string, int, double, bool. Array type claims are not supported |
| `claimToHeaders` | _[ClaimToHeader](#claimtoheader) array_ | false | ClaimToHeaders is a list of JWT claims that must be extracted into HTTP request headers For examples, following config: The claim must be of type; string, int, double, bool. Array type claims are not supported |
| `recomputeRoute` | _boolean_ | false | RecomputeRoute clears the route cache and recalculates the routing decision. This field must be enabled if the headers generated from the claim are used for route matching decisions. If the recomputation selects a new route, features targeting the new matched route will be applied. |
| `extractFrom` | _[JWTExtractor](#jwtextractor)_ | false | ExtractFrom defines different ways to extract the JWT token from HTTP request. If empty, it defaults to extract JWT token from the Authorization HTTP request header using Bearer schema or access_token from query parameters. |


Expand Down
115 changes: 115 additions & 0 deletions test/cel-validation/securitypolicy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,121 @@ func TestSecurityPolicyTarget(t *testing.T) {
"spec.extAuth: Invalid value: \"object\": kind is invalid, only Service (specified by omitting the kind field or setting it to 'Service') is supported",
},
},
// JWT
{
desc: "valid jwt",
mutate: func(sp *egv1a1.SecurityPolicy) {
sp.Spec = egv1a1.SecurityPolicySpec{
JWT: &egv1a1.JWT{
Providers: []egv1a1.JWTProvider{
{
Name: "example",
RemoteJWKS: egv1a1.RemoteJWKS{
URI: "https://example.com/jwt/jwks.json",
},
},
},
},
TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{
PolicyTargetReference: gwapiv1a2.PolicyTargetReference{
Group: "gateway.networking.k8s.io",
Kind: "Gateway",
Name: "eg",
},
},
}
},
wantErrors: []string{},
},
{
desc: "jwt with claim to headers",
mutate: func(sp *egv1a1.SecurityPolicy) {
sp.Spec = egv1a1.SecurityPolicySpec{
JWT: &egv1a1.JWT{
Providers: []egv1a1.JWTProvider{
{
Name: "example",
RemoteJWKS: egv1a1.RemoteJWKS{
URI: "https://example.com/jwt/jwks.json",
},
ClaimToHeaders: []egv1a1.ClaimToHeader{
{
Claim: "name",
Header: "x-claim-name",
},
},
},
},
},
TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{
PolicyTargetReference: gwapiv1a2.PolicyTargetReference{
Group: "gateway.networking.k8s.io",
Kind: "Gateway",
Name: "eg",
},
},
}
},
wantErrors: []string{},
},
{
desc: "jwt with recomputeRoute",
mutate: func(sp *egv1a1.SecurityPolicy) {
sp.Spec = egv1a1.SecurityPolicySpec{
JWT: &egv1a1.JWT{
Providers: []egv1a1.JWTProvider{
{
Name: "example",
RemoteJWKS: egv1a1.RemoteJWKS{
URI: "https://example.com/jwt/jwks.json",
},
RecomputeRoute: ptr.To(true),
},
},
},
TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{
PolicyTargetReference: gwapiv1a2.PolicyTargetReference{
Group: "gateway.networking.k8s.io",
Kind: "Gateway",
Name: "eg",
},
},
}
},
wantErrors: []string{"Invalid value: \"object\": no such key: claimToHeaders evaluating rule: claimToHeaders must be specified if recomputeRoute is enabled"},
},
{
desc: "jwt with claim to headers and recomputeRoute",
mutate: func(sp *egv1a1.SecurityPolicy) {
sp.Spec = egv1a1.SecurityPolicySpec{
JWT: &egv1a1.JWT{
Providers: []egv1a1.JWTProvider{
{
Name: "example",
RemoteJWKS: egv1a1.RemoteJWKS{
URI: "https://example.com/jwt/jwks.json",
},
ClaimToHeaders: []egv1a1.ClaimToHeader{
{
Claim: "name",
Header: "x-claim-name",
},
},
RecomputeRoute: ptr.To(true),
},
},
},
TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{
PolicyTargetReference: gwapiv1a2.PolicyTargetReference{
Group: "gateway.networking.k8s.io",
Kind: "Gateway",
Name: "eg",
},
},
}
},
wantErrors: []string{},
},
}

for _, tc := range cases {
Expand Down
Loading