Skip to content

Commit e17fc30

Browse files
authored
Add support for jwt validation action at the ingress level (#4445)
1 parent 4908ffd commit e17fc30

File tree

18 files changed

+809
-33
lines changed

18 files changed

+809
-33
lines changed

docs/guide/ingress/annotations.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ You can add annotations to kubernetes Ingress and Service objects to customize t
6161
| [alb.ingress.kubernetes.io/auth-scope](#auth-scope) | string |openid| Ingress,Service | N/A |
6262
| [alb.ingress.kubernetes.io/auth-session-cookie](#auth-session-cookie) | string |AWSELBAuthSessionCookie| Ingress,Service | N/A |
6363
| [alb.ingress.kubernetes.io/auth-session-timeout](#auth-session-timeout) | integer |'604800'| Ingress,Service | N/A |
64+
| [alb.ingress.kubernetes.io/jwt-validation](#jwt-validation) | json |N/A| Ingress | N/A |
6465
| [alb.ingress.kubernetes.io/actions.${action-name}](#actions) | json |N/A| Ingress | N/A |
6566
| [alb.ingress.kubernetes.io/transforms.${transforms-name}](#transforms) | json |N/A| Ingress | N/A |
6667
| [alb.ingress.kubernetes.io/conditions.${conditions-name}](#conditions) | json |N/A| Ingress | N/A |
@@ -847,6 +848,19 @@ ALB supports authentication with Cognito or OIDC. See [Authenticate Users Using
847848
alb.ingress.kubernetes.io/auth-session-timeout: '86400'
848849
```
849850

851+
## Token Validation
852+
ALB supports validating JSON Web Tokens (JWTs) for secure service-to-service communication. The `exp` and `iss` claims are always validated by default. If present, the `nbf` and `iat` claims will also be automatically validated. JWT validation can be controlled with the following annotation:
853+
854+
!!!warning "HTTPS only"
855+
JWT validation is only supported for HTTPS listeners. See [TLS](#tls) for configuring HTTPS listeners.
856+
857+
- <a name="jwt-validation">`alb.ingress.kubernetes.io/jwt-validation`</a> specifies the configuration for JWT validation. This includes the JSON Web Key Set (JWKS) endpoint and issuer, along with any additional claims to validate. For each additional claim added, the name of the claim, the format of the values in the JWT, and the values of the claim should all be specified.
858+
859+
!!!example
860+
```
861+
alb.ingress.kubernetes.io/jwt-validation: '{"jwksEndpoint":"https://example-endpoint.com","issuer":"https://example-issuer.com","additionalClaims":[{"name":"admin","format":"single-string","values":["true"]},{"name":"ver","format":"string-array","values":["6","19"]},{"name":"scope","format":"space-separated-values","values":["read:api","write","email"]}]}'
862+
```
863+
850864
## Health Check
851865
Health check on target groups can be controlled with following annotations:
852866

go.mod

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,21 @@ module sigs.k8s.io/aws-load-balancer-controller
33
go 1.24.9
44

55
require (
6-
github.com/aws/aws-sdk-go-v2 v1.39.2
6+
github.com/aws/aws-sdk-go-v2 v1.39.6
77
github.com/aws/aws-sdk-go-v2/config v1.27.27
88
github.com/aws/aws-sdk-go-v2/credentials v1.17.27
99
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11
1010
github.com/aws/aws-sdk-go-v2/service/acm v1.28.4
1111
github.com/aws/aws-sdk-go-v2/service/appmesh v1.27.7
1212
github.com/aws/aws-sdk-go-v2/service/ec2 v1.173.0
13-
github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2 v1.51.0
13+
github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2 v1.52.0
1414
github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi v1.23.3
1515
github.com/aws/aws-sdk-go-v2/service/servicediscovery v1.31.7
1616
github.com/aws/aws-sdk-go-v2/service/shield v1.27.3
1717
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3
1818
github.com/aws/aws-sdk-go-v2/service/wafregional v1.23.3
1919
github.com/aws/aws-sdk-go-v2/service/wafv2 v1.51.4
20-
github.com/aws/smithy-go v1.23.0
20+
github.com/aws/smithy-go v1.23.2
2121
github.com/evanphx/json-patch v5.9.11+incompatible
2222
github.com/gavv/httpexpect/v2 v2.9.0
2323
github.com/go-logr/logr v1.4.3
@@ -60,8 +60,8 @@ require (
6060
github.com/ajg/form v1.5.1 // indirect
6161
github.com/andybalholm/brotli v1.0.4 // indirect
6262
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
63-
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.9 // indirect
64-
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.9 // indirect
63+
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13 // indirect
64+
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.13 // indirect
6565
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
6666
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 // indirect
6767
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 // indirect

go.sum

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,18 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd
2929
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
3030
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
3131
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
32-
github.com/aws/aws-sdk-go-v2 v1.39.2 h1:EJLg8IdbzgeD7xgvZ+I8M1e0fL0ptn/M47lianzth0I=
33-
github.com/aws/aws-sdk-go-v2 v1.39.2/go.mod h1:sDioUELIUO9Znk23YVmIk86/9DOpkbyyVb1i/gUNFXY=
32+
github.com/aws/aws-sdk-go-v2 v1.39.6 h1:2JrPCVgWJm7bm83BDwY5z8ietmeJUbh3O2ACnn+Xsqk=
33+
github.com/aws/aws-sdk-go-v2 v1.39.6/go.mod h1:c9pm7VwuW0UPxAEYGyTmyurVcNrbF6Rt/wixFqDhcjE=
3434
github.com/aws/aws-sdk-go-v2/config v1.27.27 h1:HdqgGt1OAP0HkEDDShEl0oSYa9ZZBSOmKpdpsDMdO90=
3535
github.com/aws/aws-sdk-go-v2/config v1.27.27/go.mod h1:MVYamCg76dFNINkZFu4n4RjDixhVr51HLj4ErWzrVwg=
3636
github.com/aws/aws-sdk-go-v2/credentials v1.17.27 h1:2raNba6gr2IfA0eqqiP2XiQ0UVOpGPgDSi0I9iAP+UI=
3737
github.com/aws/aws-sdk-go-v2/credentials v1.17.27/go.mod h1:gniiwbGahQByxan6YjQUMcW4Aov6bLC3m+evgcoN4r4=
3838
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 h1:KreluoV8FZDEtI6Co2xuNk/UqI9iwMrOx/87PBNIKqw=
3939
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11/go.mod h1:SeSUYBLsMYFoRvHE0Tjvn7kbxaUhl75CJi1sbfhMxkU=
40-
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.9 h1:se2vOWGD3dWQUtfn4wEjRQJb1HK1XsNIt825gskZ970=
41-
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.9/go.mod h1:hijCGH2VfbZQxqCDN7bwz/4dzxV+hkyhjawAtdPWKZA=
42-
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.9 h1:6RBnKZLkJM4hQ+kN6E7yWFveOTg8NLPHAkqrs4ZPlTU=
43-
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.9/go.mod h1:V9rQKRmK7AWuEsOMnHzKj8WyrIir1yUJbZxDuZLFvXI=
40+
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13 h1:a+8/MLcWlIxo1lF9xaGt3J/u3yOZx+CdSveSNwjhD40=
41+
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13/go.mod h1:oGnKwIYZ4XttyU2JWxFrwvhF6YKiK/9/wmE3v3Iu9K8=
42+
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.13 h1:HBSI2kDkMdWz4ZM7FjwE7e/pWDEZ+nR95x8Ztet1ooY=
43+
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.13/go.mod h1:YE94ZoDArI7awZqJzBAZ3PDD2zSfuP7w6P2knOzIn8M=
4444
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU=
4545
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY=
4646
github.com/aws/aws-sdk-go-v2/service/acm v1.28.4 h1:wiW1Y6/1lysA0eJZRq0I53YYKuV9MNAzL15z2eZRlEE=
@@ -49,8 +49,8 @@ github.com/aws/aws-sdk-go-v2/service/appmesh v1.27.7 h1:q44a6kysAfej9zZwRnraOg9s
4949
github.com/aws/aws-sdk-go-v2/service/appmesh v1.27.7/go.mod h1:ZYSmrgAMp0rTCHH+SGsoxZo+PPbgsDqBzewTp3tSJ60=
5050
github.com/aws/aws-sdk-go-v2/service/ec2 v1.173.0 h1:ta62lid9JkIpKZtZZXSj6rP2AqY5x1qYGq53ffxqD9Q=
5151
github.com/aws/aws-sdk-go-v2/service/ec2 v1.173.0/go.mod h1:o6QDjdVKpP5EF0dp/VlvqckzuSDATr1rLdHt3A5m0YY=
52-
github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2 v1.51.0 h1:Zy1yjx+R6cR4pAwzFFJ8nWJh4ri8I44H76PDJ77tcJo=
53-
github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2 v1.51.0/go.mod h1:RuZwE3p8IrWqK1kZhwH2TymlHLPuiI/taBMb8vrD39Q=
52+
github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2 v1.52.0 h1:ZEXlVNHNM5R4BCrK79Y/cXEEmkGFDhBBKz4YlEfgNRk=
53+
github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2 v1.52.0/go.mod h1:Uyo8wjqYyZaHVqoe+APHe4+THRGv4pctJzItYYnRe5Q=
5454
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 h1:dT3MqvGhSoaIhRseqw2I0yH81l7wiR2vjs57O51EAm8=
5555
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3/go.mod h1:GlAeCkHwugxdHaueRr4nhPuY+WW+gR8UjlcqzPr1SPI=
5656
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 h1:HGErhhrxZlQ044RiM+WdoZxp0p+EGM62y3L6pwA4olE=
@@ -71,8 +71,8 @@ github.com/aws/aws-sdk-go-v2/service/wafregional v1.23.3 h1:7dr6En0/6KRFoz8VmnYk
7171
github.com/aws/aws-sdk-go-v2/service/wafregional v1.23.3/go.mod h1:24TtlRsv4LKAE3VnRJQhpatr8cpX0yj8NSzg8/lxOCw=
7272
github.com/aws/aws-sdk-go-v2/service/wafv2 v1.51.4 h1:1khBA5uryBRJoCb4G2iR5RT06BkfPEjjDCHAiRb8P3Q=
7373
github.com/aws/aws-sdk-go-v2/service/wafv2 v1.51.4/go.mod h1:QpFImaPGKNwa+MiZ+oo6LbV1PVQBapc0CnrAMRScoxM=
74-
github.com/aws/smithy-go v1.23.0 h1:8n6I3gXzWJB2DxBDnfxgBaSX6oe0d/t10qGz7OKqMCE=
75-
github.com/aws/smithy-go v1.23.0/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI=
74+
github.com/aws/smithy-go v1.23.2 h1:Crv0eatJUQhaManss33hS5r40CG3ZFH+21XSkqMrIUM=
75+
github.com/aws/smithy-go v1.23.2/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0=
7676
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
7777
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
7878
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=

pkg/annotations/constants.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ const (
5252
IngressSuffixAuthScope = "auth-scope"
5353
IngressSuffixAuthSessionCookie = "auth-session-cookie"
5454
IngressSuffixAuthSessionTimeout = "auth-session-timeout"
55+
IngressSuffixJwtValidation = "jwt-validation"
5556
IngressSuffixTargetNodeLabels = "target-node-labels"
5657
IngressSuffixManageSecurityGroupRules = "manage-backend-security-group-rules"
5758
IngressSuffixMutualAuthentication = "mutual-authentication"

pkg/deploy/elbv2/listener_utils.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ package elbv2
22

33
import (
44
"context"
5+
"time"
6+
57
awssdk "github.com/aws/aws-sdk-go-v2/aws"
68
elbv2types "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2/types"
79
"github.com/aws/smithy-go"
810
"github.com/pkg/errors"
911
"sigs.k8s.io/aws-load-balancer-controller/pkg/config"
1012
elbv2model "sigs.k8s.io/aws-load-balancer-controller/pkg/model/elbv2"
11-
"time"
1213
)
1314

1415
const (
@@ -55,6 +56,9 @@ func buildSDKAction(modelAction elbv2model.Action, featureGates config.FeatureGa
5556
if modelAction.AuthenticateOIDCConfig != nil {
5657
sdkObj.AuthenticateOidcConfig = buildSDKAuthenticateOidcActionConfig(*modelAction.AuthenticateOIDCConfig)
5758
}
59+
if modelAction.JwtValidationConfig != nil {
60+
sdkObj.JwtValidationConfig = buildSDKJwtValidationConfig(*modelAction.JwtValidationConfig)
61+
}
5862
if modelAction.FixedResponseConfig != nil {
5963
sdkObj.FixedResponseConfig = buildSDKFixedResponseActionConfig(*modelAction.FixedResponseConfig)
6064
}
@@ -108,6 +112,23 @@ func buildSDKAuthenticateOidcActionConfig(modelCfg elbv2model.AuthenticateOIDCAc
108112
}
109113
}
110114

115+
func buildSDKJwtValidationConfig(modelCfg elbv2model.JwtValidationConfig) *elbv2types.JwtValidationActionConfig {
116+
var additionalClaims []elbv2types.JwtValidationActionAdditionalClaim
117+
for _, additionalClaim := range modelCfg.AdditionalClaims {
118+
additionalClaims = append(additionalClaims, elbv2types.JwtValidationActionAdditionalClaim{
119+
Format: elbv2types.JwtValidationActionAdditionalClaimFormatEnum(additionalClaim.Format),
120+
Name: awssdk.String(additionalClaim.Name),
121+
Values: append([]string{}, additionalClaim.Values...),
122+
})
123+
}
124+
125+
return &elbv2types.JwtValidationActionConfig{
126+
JwksEndpoint: awssdk.String(modelCfg.JwksEndpoint),
127+
Issuer: awssdk.String(modelCfg.Issuer),
128+
AdditionalClaims: additionalClaims,
129+
}
130+
}
131+
111132
func buildSDKFixedResponseActionConfig(modelCfg elbv2model.FixedResponseActionConfig) *elbv2types.FixedResponseActionConfig {
112133
return &elbv2types.FixedResponseActionConfig{
113134
ContentType: modelCfg.ContentType,

pkg/deploy/elbv2/listener_utils_test.go

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
package elbv2
22

33
import (
4+
"testing"
5+
6+
awssdk "github.com/aws/aws-sdk-go-v2/aws"
7+
elbv2types "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2/types"
48
"github.com/aws/smithy-go"
59
"github.com/pkg/errors"
610
"github.com/stretchr/testify/assert"
7-
"testing"
11+
elbv2model "sigs.k8s.io/aws-load-balancer-controller/pkg/model/elbv2"
812
)
913

1014
func Test_isListenerNotFoundError(t *testing.T) {
@@ -45,3 +49,81 @@ func Test_isListenerNotFoundError(t *testing.T) {
4549
})
4650
}
4751
}
52+
53+
func Test_buildSDKJwtValidationConfig(t *testing.T) {
54+
type args struct {
55+
jwtValidationConfig elbv2model.JwtValidationConfig
56+
}
57+
tests := []struct {
58+
name string
59+
args args
60+
want *elbv2types.JwtValidationActionConfig
61+
}{
62+
{
63+
name: "jwt validation with no additional claims",
64+
args: args{
65+
jwtValidationConfig: elbv2model.JwtValidationConfig{
66+
JwksEndpoint: "https://issuer.example.com/.well-known/jwks.json",
67+
Issuer: "https://issuer.com",
68+
},
69+
},
70+
want: &elbv2types.JwtValidationActionConfig{
71+
JwksEndpoint: awssdk.String("https://issuer.example.com/.well-known/jwks.json"),
72+
Issuer: awssdk.String("https://issuer.com"),
73+
},
74+
},
75+
{
76+
name: "jwt validation with additional claims",
77+
args: args{
78+
jwtValidationConfig: elbv2model.JwtValidationConfig{
79+
JwksEndpoint: "https://issuer.example.com/.well-known/jwks.json",
80+
Issuer: "https://issuer.com",
81+
AdditionalClaims: []elbv2model.JwtAdditionalClaim{
82+
{
83+
Format: "string-array",
84+
Name: "scope",
85+
Values: []string{"read:api", "write:api"},
86+
},
87+
{
88+
Format: "single-string",
89+
Name: "iat",
90+
Values: []string{"12456"},
91+
},
92+
{
93+
Format: "space-separated-values",
94+
Name: "aud",
95+
Values: []string{"https://example.com", "https://another-site.com"},
96+
},
97+
},
98+
},
99+
},
100+
want: &elbv2types.JwtValidationActionConfig{
101+
JwksEndpoint: awssdk.String("https://issuer.example.com/.well-known/jwks.json"),
102+
Issuer: awssdk.String("https://issuer.com"),
103+
AdditionalClaims: []elbv2types.JwtValidationActionAdditionalClaim{
104+
{
105+
Format: "string-array",
106+
Name: awssdk.String("scope"),
107+
Values: []string{"read:api", "write:api"},
108+
},
109+
{
110+
Format: "single-string",
111+
Name: awssdk.String("iat"),
112+
Values: []string{"12456"},
113+
},
114+
{
115+
Format: "space-separated-values",
116+
Name: awssdk.String("aud"),
117+
Values: []string{"https://example.com", "https://another-site.com"},
118+
},
119+
},
120+
},
121+
},
122+
}
123+
for _, tt := range tests {
124+
t.Run(tt.name, func(t1 *testing.T) {
125+
got := buildSDKJwtValidationConfig(tt.args.jwtValidationConfig)
126+
assert.Equal(t, tt.want, got)
127+
})
128+
}
129+
}

pkg/equality/elbv2/compare_option_for_actions.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ func CompareOptionForActions(_, _ []elbv2types.Action) cmp.Option {
9595
cmpopts.EquateEmpty(),
9696
cmpopts.IgnoreUnexported(elbv2types.AuthenticateCognitoActionConfig{}),
9797
cmpopts.IgnoreUnexported(elbv2types.AuthenticateOidcActionConfig{}),
98+
cmpopts.IgnoreUnexported(elbv2types.JwtValidationActionConfig{}),
99+
cmpopts.IgnoreUnexported(elbv2types.JwtValidationActionAdditionalClaim{}),
98100
cmpopts.IgnoreUnexported(elbv2types.FixedResponseActionConfig{}),
99101
cmpopts.SortSlices(func(lhs elbv2types.Action, rhs elbv2types.Action) bool {
100102
if lhs.Order == nil || rhs.Order == nil {

pkg/ingress/config_types.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,3 +500,33 @@ func (t *Transform) Validate() error {
500500
}
501501
return nil
502502
}
503+
504+
// The format of an additional claim's value(s) used in JWT validation.
505+
type jwtAdditionalClaimFormat string
506+
507+
const (
508+
FormatSingleString jwtAdditionalClaimFormat = "single-string"
509+
FormatStringArray jwtAdditionalClaimFormat = "string-array"
510+
FormatSpaceSeparatedValues jwtAdditionalClaimFormat = "space-separated-values"
511+
)
512+
513+
// An additional claim to validate during JWT validation.
514+
type JwtAdditionalClaim struct {
515+
// The format of the claim value(s).
516+
Format jwtAdditionalClaimFormat `json:"format"`
517+
// The claim name.
518+
Name string `json:"name"`
519+
// The claim values.
520+
Values []string `json:"values"`
521+
}
522+
523+
// Information about an action that performs JSON Web Token (JWT) validation prior to the routing action.
524+
type JwtValidationConfig struct {
525+
// The JSON Web Key Set (JWKS) endpoint containing the public keys used to verify the JWT.
526+
JwksEndpoint string `json:"jwksEndpoint"`
527+
// The issuer of the JWT.
528+
Issuer string `json:"issuer"`
529+
// Any additional claims in the JWT that should be validated.
530+
// +optional
531+
AdditionalClaims []JwtAdditionalClaim `json:"additionalClaims,omitempty"`
532+
}

pkg/ingress/enhanced_backend_builder.go

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,11 @@ const (
3030
// It contains additional routing conditions and authentication configurations we parsed from annotations.
3131
// Also, when magic string `use-annotation` is specified as backend, the actions will be parsed from annotations as well.
3232
type EnhancedBackend struct {
33-
Conditions []RuleCondition
34-
Transforms []Transform
35-
Action Action
36-
AuthConfig AuthConfig
33+
Conditions []RuleCondition
34+
Transforms []Transform
35+
Action Action
36+
AuthConfig AuthConfig
37+
JwtValidationConfig *JwtValidationConfig
3738
}
3839

3940
type EnhancedBackendBuildOptions struct {
@@ -151,11 +152,18 @@ func (b *defaultEnhancedBackendBuilder) Build(ctx context.Context, ing *networki
151152
}
152153
}
153154

155+
var jwtValidationConfig *JwtValidationConfig
156+
jwtValidationConfig, err = b.buildJwtValidationConfig(ctx, ing.Annotations)
157+
if err != nil {
158+
return EnhancedBackend{}, err
159+
}
160+
154161
return EnhancedBackend{
155-
Conditions: conditions,
156-
Transforms: transforms,
157-
Action: action,
158-
AuthConfig: authCfg,
162+
Conditions: conditions,
163+
Transforms: transforms,
164+
Action: action,
165+
AuthConfig: authCfg,
166+
JwtValidationConfig: jwtValidationConfig,
159167
}, nil
160168
}
161169

@@ -307,6 +315,41 @@ func (b *defaultEnhancedBackendBuilder) buildAuthConfig(ctx context.Context, act
307315
return b.authConfigBuilder.Build(ctx, svcAndIngAnnotations)
308316
}
309317

318+
// Parse annotations to construct JWT validation config model
319+
func (b *defaultEnhancedBackendBuilder) buildJwtValidationConfig(_ context.Context, ingAnnotation map[string]string) (*JwtValidationConfig, error) {
320+
jwtValidationConfig := JwtValidationConfig{}
321+
exists, err := b.annotationParser.ParseJSONAnnotation(annotations.IngressSuffixJwtValidation, &jwtValidationConfig, ingAnnotation)
322+
if err != nil {
323+
return nil, err
324+
}
325+
if !exists {
326+
return nil, nil
327+
}
328+
329+
// Ensure required fields are present
330+
if jwtValidationConfig.JwksEndpoint == "" {
331+
return nil, errors.Errorf("jwksEndpoint is a required field for jwt validation")
332+
}
333+
if jwtValidationConfig.Issuer == "" {
334+
return nil, errors.Errorf("issuer is a required field for jwt validation")
335+
}
336+
337+
// If additional claims are present, ensure each additional claim's required fields are present as well
338+
for _, additionalClaim := range jwtValidationConfig.AdditionalClaims {
339+
if additionalClaim.Format == "" {
340+
return nil, errors.Errorf("format is a required field for additional claims for jwt validation")
341+
}
342+
if additionalClaim.Name == "" {
343+
return nil, errors.Errorf("name is a required field for additional claims for jwt validation")
344+
}
345+
if len(additionalClaim.Values) == 0 {
346+
return nil, errors.Errorf("values must not be empty for additional claims for jwt validation")
347+
}
348+
}
349+
350+
return &jwtValidationConfig, nil
351+
}
352+
310353
// build503ResponseAction generates a 503 fixed response action when forward to a single non-existent Kubernetes Service.
311354
func (b *defaultEnhancedBackendBuilder) build503ResponseAction(messageBody string) Action {
312355
return Action{

0 commit comments

Comments
 (0)