Skip to content

Commit ed454f5

Browse files
authored
Merge pull request #4679 from shuqz/i2g-annotation
[feat i2g]support auth translate
2 parents 5c9f95a + 55a2c9b commit ed454f5

File tree

12 files changed

+561
-40
lines changed

12 files changed

+561
-40
lines changed

.go-version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.26.1
1+
1.26.2

go.mod

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module sigs.k8s.io/aws-load-balancer-controller
22

3-
go 1.26.1
3+
go 1.26.2
44

55
require (
66
github.com/aws/aws-sdk-go-v2 v1.40.0
@@ -30,6 +30,7 @@ require (
3030
github.com/onsi/gomega v1.39.1
3131
github.com/pkg/errors v0.9.1
3232
github.com/prometheus/client_golang v1.23.2
33+
github.com/spf13/cobra v1.10.0
3334
github.com/spf13/pflag v1.0.10
3435
github.com/stretchr/testify v1.11.1
3536
go.uber.org/zap v1.27.1
@@ -38,6 +39,7 @@ require (
3839
gomodules.xyz/jsonpatch/v2 v2.4.0
3940
google.golang.org/grpc v1.78.0
4041
google.golang.org/protobuf v1.36.11
42+
gopkg.in/yaml.v3 v3.0.1
4143
helm.sh/helm/v3 v3.18.5
4244
k8s.io/api v0.35.1
4345
k8s.io/apimachinery v0.35.1
@@ -148,7 +150,6 @@ require (
148150
github.com/shopspring/decimal v1.4.0 // indirect
149151
github.com/sirupsen/logrus v1.9.3 // indirect
150152
github.com/spf13/cast v1.7.0 // indirect
151-
github.com/spf13/cobra v1.10.0 // indirect
152153
github.com/stretchr/objx v0.5.2 // indirect
153154
github.com/valyala/bytebufferpool v1.0.0 // indirect
154155
github.com/valyala/fasthttp v1.34.0 // indirect
@@ -174,7 +175,6 @@ require (
174175
google.golang.org/genproto/googleapis/rpc v0.0.0-20251029180050-ab9386a59fda // indirect
175176
gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect
176177
gopkg.in/inf.v0 v0.9.1 // indirect
177-
gopkg.in/yaml.v3 v3.0.1 // indirect
178178
k8s.io/apiextensions-apiserver v0.35.1 // indirect
179179
k8s.io/apiserver v0.35.1 // indirect
180180
k8s.io/component-base v0.35.1 // indirect

pkg/ingress2gateway/translate/annotation_coverage_test.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,15 @@ func TestAllIngressAnnotationsCovered(t *testing.T) {
7979
annotations.IngressSuffixUseRegexPathMatch,
8080
annotations.IngressSuffixSSLRedirect,
8181
},
82+
Authentication: {
83+
annotations.IngressSuffixAuthType,
84+
annotations.IngressSuffixAuthIDPCognito,
85+
annotations.IngressSuffixAuthIDPOIDC,
86+
annotations.IngressSuffixAuthOnUnauthenticatedRequest,
87+
annotations.IngressSuffixAuthScope,
88+
annotations.IngressSuffixAuthSessionCookie,
89+
annotations.IngressSuffixAuthSessionTimeout,
90+
},
8291
}
8392

8493
// Planned: annotations not yet implemented.
@@ -97,13 +106,6 @@ func TestAllIngressAnnotationsCovered(t *testing.T) {
97106
// transforms.* translation is implemented in translate_transform_helper.go.
98107
},
99108
Authentication: {
100-
annotations.IngressSuffixAuthType,
101-
annotations.IngressSuffixAuthIDPCognito,
102-
annotations.IngressSuffixAuthIDPOIDC,
103-
annotations.IngressSuffixAuthOnUnauthenticatedRequest,
104-
annotations.IngressSuffixAuthScope,
105-
annotations.IngressSuffixAuthSessionCookie,
106-
annotations.IngressSuffixAuthSessionTimeout,
107109
annotations.IngressSuffixJwtValidation,
108110
},
109111
FrontendNLB: {
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
package translate
2+
3+
import (
4+
"fmt"
5+
6+
gatewayv1beta1 "sigs.k8s.io/aws-load-balancer-controller/apis/gateway/v1beta1"
7+
annotations "sigs.k8s.io/aws-load-balancer-controller/pkg/annotations"
8+
"sigs.k8s.io/aws-load-balancer-controller/pkg/ingress"
9+
)
10+
11+
const (
12+
authTypeNone = "none"
13+
authTypeCognito = "cognito"
14+
authTypeOIDC = "oidc"
15+
)
16+
17+
// buildAuthAction reads auth-type and related annotations and returns a
18+
// ListenerRuleConfiguration Action for authenticate-cognito or authenticate-oidc.
19+
// Returns (nil, nil) when auth-type is "none" or absent.
20+
func buildAuthAction(annos map[string]string) (*gatewayv1beta1.Action, error) {
21+
authType := getString(annos, annotations.IngressSuffixAuthType)
22+
if authType == "" || authType == authTypeNone {
23+
return nil, nil
24+
}
25+
26+
switch authType {
27+
case authTypeCognito:
28+
return buildAuthCognitoAction(annos)
29+
case authTypeOIDC:
30+
return buildOIDCAction(annos)
31+
default:
32+
return nil, fmt.Errorf("unsupported auth-type %q", authType)
33+
}
34+
}
35+
36+
// buildAuthCognitoAction parses auth-idp-cognito JSON and shared auth annotations
37+
// into an authenticate-cognito Action.
38+
func buildAuthCognitoAction(annos map[string]string) (*gatewayv1beta1.Action, error) {
39+
var idp ingress.AuthIDPConfigCognito
40+
exists, err := ingressAnnotationParser.ParseJSONAnnotation(annotations.IngressSuffixAuthIDPCognito, &idp, annos)
41+
if err != nil {
42+
return nil, fmt.Errorf("failed to parse %s annotation: %w", annotations.IngressSuffixAuthIDPCognito, err)
43+
}
44+
if !exists {
45+
return nil, fmt.Errorf("auth-type is %q but %s annotation is missing", authTypeCognito, annotations.IngressSuffixAuthIDPCognito)
46+
}
47+
48+
scope := getOptionalAuthScope(annos)
49+
onUnauth := getOptionalAuthOnUnauthenticatedRequest(annos)
50+
sessionCookie := getOptionalAuthSessionCookieName(annos)
51+
sessionTimeout := getOptionalAuthSessionTimeout(annos)
52+
53+
cfg := &gatewayv1beta1.AuthenticateCognitoActionConfig{
54+
UserPoolArn: idp.UserPoolARN,
55+
UserPoolClientID: idp.UserPoolClientID,
56+
UserPoolDomain: idp.UserPoolDomain,
57+
}
58+
59+
if scope != nil {
60+
cfg.Scope = scope
61+
}
62+
if onUnauth != nil {
63+
cognitoEnum := gatewayv1beta1.AuthenticateCognitoActionConditionalBehaviorEnum(*onUnauth)
64+
cfg.OnUnauthenticatedRequest = &cognitoEnum
65+
}
66+
if sessionCookie != nil {
67+
cfg.SessionCookieName = sessionCookie
68+
}
69+
if sessionTimeout != nil {
70+
cfg.SessionTimeout = sessionTimeout
71+
}
72+
73+
if len(idp.AuthenticationRequestExtraParams) > 0 {
74+
params := make(map[string]string, len(idp.AuthenticationRequestExtraParams))
75+
for k, v := range idp.AuthenticationRequestExtraParams {
76+
params[k] = v
77+
}
78+
cfg.AuthenticationRequestExtraParams = &params
79+
}
80+
81+
return &gatewayv1beta1.Action{
82+
Type: gatewayv1beta1.ActionTypeAuthenticateCognito,
83+
AuthenticateCognitoConfig: cfg,
84+
}, nil
85+
}
86+
87+
// buildOIDCAction parses auth-idp-oidc JSON and shared auth annotations
88+
// into an authenticate-oidc Action. The Secret reference (secretName) from
89+
// the annotation JSON is preserved as Secret.Name on the CRD.
90+
func buildOIDCAction(annos map[string]string) (*gatewayv1beta1.Action, error) {
91+
var idp ingress.AuthIDPConfigOIDC
92+
exists, err := ingressAnnotationParser.ParseJSONAnnotation(annotations.IngressSuffixAuthIDPOIDC, &idp, annos)
93+
if err != nil {
94+
return nil, fmt.Errorf("failed to parse %s annotation: %w", annotations.IngressSuffixAuthIDPOIDC, err)
95+
}
96+
if !exists {
97+
return nil, fmt.Errorf("auth-type is %q but %s annotation is missing", authTypeOIDC, annotations.IngressSuffixAuthIDPOIDC)
98+
}
99+
100+
scope := getOptionalAuthScope(annos)
101+
onUnauth := getOptionalAuthOnUnauthenticatedRequest(annos)
102+
sessionCookie := getOptionalAuthSessionCookieName(annos)
103+
sessionTimeout := getOptionalAuthSessionTimeout(annos)
104+
105+
cfg := &gatewayv1beta1.AuthenticateOidcActionConfig{
106+
Issuer: idp.Issuer,
107+
AuthorizationEndpoint: idp.AuthorizationEndpoint,
108+
TokenEndpoint: idp.TokenEndpoint,
109+
UserInfoEndpoint: idp.UserInfoEndpoint,
110+
Secret: &gatewayv1beta1.Secret{Name: idp.SecretName},
111+
}
112+
113+
if scope != nil {
114+
cfg.Scope = scope
115+
}
116+
if onUnauth != nil {
117+
oidcEnum := gatewayv1beta1.AuthenticateOidcActionConditionalBehaviorEnum(*onUnauth)
118+
cfg.OnUnauthenticatedRequest = &oidcEnum
119+
}
120+
if sessionCookie != nil {
121+
cfg.SessionCookieName = sessionCookie
122+
}
123+
if sessionTimeout != nil {
124+
cfg.SessionTimeout = sessionTimeout
125+
}
126+
127+
if len(idp.AuthenticationRequestExtraParams) > 0 {
128+
params := make(map[string]string, len(idp.AuthenticationRequestExtraParams))
129+
for k, v := range idp.AuthenticationRequestExtraParams {
130+
params[k] = v
131+
}
132+
cfg.AuthenticationRequestExtraParams = &params
133+
}
134+
135+
return &gatewayv1beta1.Action{
136+
Type: gatewayv1beta1.ActionTypeAuthenticateOIDC,
137+
AuthenticateOIDCConfig: cfg,
138+
}, nil
139+
}
140+
141+
// getOptionalAuthScope returns the auth-scope annotation value, or nil if not set.
142+
// The CRD has kubebuilder:default="openid" so omitting it lets the webhook fill the default.
143+
func getOptionalAuthScope(annos map[string]string) *string {
144+
v := getString(annos, annotations.IngressSuffixAuthScope)
145+
if v == "" {
146+
return nil
147+
}
148+
return &v
149+
}
150+
151+
// getOptionalAuthOnUnauthenticatedRequest returns the annotation value, or nil if not set.
152+
// The CRD has kubebuilder:default="authenticate".
153+
func getOptionalAuthOnUnauthenticatedRequest(annos map[string]string) *string {
154+
v := getString(annos, annotations.IngressSuffixAuthOnUnauthenticatedRequest)
155+
if v == "" {
156+
return nil
157+
}
158+
return &v
159+
}
160+
161+
// getOptionalAuthSessionCookieName returns the annotation value, or nil if not set.
162+
// The CRD has kubebuilder:default="AWSELBAuthSessionCookie".
163+
func getOptionalAuthSessionCookieName(annos map[string]string) *string {
164+
v := getString(annos, annotations.IngressSuffixAuthSessionCookie)
165+
if v == "" {
166+
return nil
167+
}
168+
return &v
169+
}
170+
171+
// getOptionalAuthSessionTimeout returns the annotation value, or nil if not set.
172+
// The CRD has kubebuilder:default=604800.
173+
func getOptionalAuthSessionTimeout(annos map[string]string) *int64 {
174+
return getInt64(annos, annotations.IngressSuffixAuthSessionTimeout)
175+
}

0 commit comments

Comments
 (0)