diff --git a/docs/guide/ingress/annotations.md b/docs/guide/ingress/annotations.md index 32ddf6531..b31078547 100644 --- a/docs/guide/ingress/annotations.md +++ b/docs/guide/ingress/annotations.md @@ -14,51 +14,51 @@ You can add annotations to kubernetes Ingress and Service objects to customize t - Merge: such annotation can be specified on all Ingresses within IngressGroup, and will be merged together. ## Annotations -| Name | Type |Default|Location|MergeBehavior| -|-------------------------------------------------------------------------------------------------------|-----------------------------|-------|--------|------| -| [alb.ingress.kubernetes.io/load-balancer-name](#load-balancer-name) | string |N/A|Ingress|Exclusive| -| [alb.ingress.kubernetes.io/group.name](#group.name) | string |N/A|Ingress|N/A| -| [alb.ingress.kubernetes.io/group.order](#group.order) | integer |0|Ingress|N/A| -| [alb.ingress.kubernetes.io/tags](#tags) | stringMap |N/A|Ingress,Service|Merge| -| [alb.ingress.kubernetes.io/ip-address-type](#ip-address-type) | ipv4 \| dualstack \| dualstack-without-public-ipv4 |ipv4|Ingress|Exclusive| -| [alb.ingress.kubernetes.io/scheme](#scheme) | internal \| internet-facing |internal|Ingress|Exclusive| -| [alb.ingress.kubernetes.io/subnets](#subnets) | stringList |N/A|Ingress|Exclusive| -| [alb.ingress.kubernetes.io/security-groups](#security-groups) | stringList |N/A|Ingress|Exclusive| -| [alb.ingress.kubernetes.io/manage-backend-security-group-rules](#manage-backend-security-group-rules) | boolean |N/A|Ingress|Exclusive| -| [alb.ingress.kubernetes.io/customer-owned-ipv4-pool](#customer-owned-ipv4-pool) | string |N/A|Ingress|Exclusive| -| [alb.ingress.kubernetes.io/load-balancer-attributes](#load-balancer-attributes) | stringMap |N/A|Ingress|Exclusive| -| [alb.ingress.kubernetes.io/wafv2-acl-arn](#wafv2-acl-arn) | string |N/A|Ingress|Exclusive| -| [alb.ingress.kubernetes.io/waf-acl-id](#waf-acl-id) | string |N/A|Ingress|Exclusive| -| [alb.ingress.kubernetes.io/shield-advanced-protection](#shield-advanced-protection) | boolean |N/A|Ingress|Exclusive| -| [alb.ingress.kubernetes.io/listen-ports](#listen-ports) | json |'[{"HTTP": 80}]' \| '[{"HTTPS": 443}]'|Ingress|Merge| -| [alb.ingress.kubernetes.io/ssl-redirect](#ssl-redirect) | integer |N/A|Ingress|Exclusive| -| [alb.ingress.kubernetes.io/inbound-cidrs](#inbound-cidrs) | stringList |0.0.0.0/0, ::/0|Ingress|Exclusive| -| [alb.ingress.kubernetes.io/security-group-prefix-lists](#security-group-prefix-lists) | stringList |pl-00000000, pl-1111111|Ingress|Exclusive| -| [alb.ingress.kubernetes.io/certificate-arn](#certificate-arn) | stringList |N/A|Ingress|Merge| -| [alb.ingress.kubernetes.io/ssl-policy](#ssl-policy) | string |ELBSecurityPolicy-2016-08|Ingress|Exclusive| -| [alb.ingress.kubernetes.io/target-type](#target-type) | instance \| ip |instance|Ingress,Service|N/A| -| [alb.ingress.kubernetes.io/backend-protocol](#backend-protocol) | HTTP \| HTTPS |HTTP|Ingress,Service|N/A| -| [alb.ingress.kubernetes.io/backend-protocol-version](#backend-protocol-version) | string | HTTP1 |Ingress,Service|N/A| -| [alb.ingress.kubernetes.io/target-group-attributes](#target-group-attributes) | stringMap |N/A|Ingress,Service|N/A| -| [alb.ingress.kubernetes.io/healthcheck-port](#healthcheck-port) | integer \| traffic-port |traffic-port|Ingress,Service|N/A| -| [alb.ingress.kubernetes.io/healthcheck-protocol](#healthcheck-protocol) | HTTP \| HTTPS |HTTP|Ingress,Service|N/A| -| [alb.ingress.kubernetes.io/healthcheck-path](#healthcheck-path) | string |/ \| /AWS.ALB/healthcheck |Ingress,Service|N/A| -| [alb.ingress.kubernetes.io/healthcheck-interval-seconds](#healthcheck-interval-seconds) | integer |'15'|Ingress,Service|N/A| -| [alb.ingress.kubernetes.io/healthcheck-timeout-seconds](#healthcheck-timeout-seconds) | integer |'5'|Ingress,Service|N/A| -| [alb.ingress.kubernetes.io/healthy-threshold-count](#healthy-threshold-count) | integer |'2'|Ingress,Service|N/A| -| [alb.ingress.kubernetes.io/unhealthy-threshold-count](#unhealthy-threshold-count) | integer |'2'|Ingress,Service|N/A| -| [alb.ingress.kubernetes.io/success-codes](#success-codes) | string |'200' \| '12' |Ingress,Service|N/A| -| [alb.ingress.kubernetes.io/auth-type](#auth-type) | none\|oidc\|cognito |none|Ingress,Service|N/A| -| [alb.ingress.kubernetes.io/auth-idp-cognito](#auth-idp-cognito) | json |N/A|Ingress,Service|N/A| -| [alb.ingress.kubernetes.io/auth-idp-oidc](#auth-idp-oidc) | json |N/A|Ingress,Service|N/A| -| [alb.ingress.kubernetes.io/auth-on-unauthenticated-request](#auth-on-unauthenticated-request) | authenticate\|allow\|deny |authenticate|Ingress,Service|N/A| -| [alb.ingress.kubernetes.io/auth-scope](#auth-scope) | string |openid|Ingress,Service|N/A| -| [alb.ingress.kubernetes.io/auth-session-cookie](#auth-session-cookie) | string |AWSELBAuthSessionCookie|Ingress,Service|N/A| -| [alb.ingress.kubernetes.io/auth-session-timeout](#auth-session-timeout) | integer |'604800'|Ingress,Service|N/A| -| [alb.ingress.kubernetes.io/actions.${action-name}](#actions) | json |N/A|Ingress|N/A| -| [alb.ingress.kubernetes.io/conditions.${conditions-name}](#conditions) | json |N/A|Ingress|N/A| -| [alb.ingress.kubernetes.io/target-node-labels](#target-node-labels) | stringMap |N/A|Ingress,Service|N/A| -| [alb.ingress.kubernetes.io/mutual-authentication](#mutual-authentication) | json |'[{"port": 443, "mode": "off"}]'|Ingress|Exclusive| +| Name | Type |Default| Location | MergeBehavior | +|-------------------------------------------------------------------------------------------------------|-----------------------------|------|-----------------|-----------| +| [alb.ingress.kubernetes.io/load-balancer-name](#load-balancer-name) | string |N/A| Ingress | Exclusive | +| [alb.ingress.kubernetes.io/group.name](#group.name) | string |N/A| Ingress | N/A | +| [alb.ingress.kubernetes.io/group.order](#group.order) | integer |0| Ingress | N/A | +| [alb.ingress.kubernetes.io/tags](#tags) | stringMap |N/A| Ingress,Service | Merge | +| [alb.ingress.kubernetes.io/ip-address-type](#ip-address-type) | ipv4 \| dualstack \| dualstack-without-public-ipv4 |ipv4| Ingress | Exclusive | +| [alb.ingress.kubernetes.io/scheme](#scheme) | internal \| internet-facing |internal| Ingress | Exclusive | +| [alb.ingress.kubernetes.io/subnets](#subnets) | stringList |N/A| Ingress | Exclusive | +| [alb.ingress.kubernetes.io/security-groups](#security-groups) | stringList |N/A| Ingress | Exclusive | +| [alb.ingress.kubernetes.io/manage-backend-security-group-rules](#manage-backend-security-group-rules) | boolean |N/A| Ingress | Exclusive | +| [alb.ingress.kubernetes.io/customer-owned-ipv4-pool](#customer-owned-ipv4-pool) | string |N/A| Ingress | Exclusive | +| [alb.ingress.kubernetes.io/load-balancer-attributes](#load-balancer-attributes) | stringMap |N/A| Ingress | Exclusive | +| [alb.ingress.kubernetes.io/wafv2-acl-arn](#wafv2-acl-arn) | string |N/A| Ingress | Exclusive | +| [alb.ingress.kubernetes.io/waf-acl-id](#waf-acl-id) | string |N/A| Ingress | Exclusive | +| [alb.ingress.kubernetes.io/shield-advanced-protection](#shield-advanced-protection) | boolean |N/A| Ingress | Exclusive | +| [alb.ingress.kubernetes.io/listen-ports](#listen-ports) | json |'[{"HTTP": 80}]' \| '[{"HTTPS": 443}]'| Ingress | Merge | +| [alb.ingress.kubernetes.io/ssl-redirect](#ssl-redirect) | integer |N/A| Ingress | Exclusive | +| [alb.ingress.kubernetes.io/inbound-cidrs](#inbound-cidrs) | stringList |0.0.0.0/0, ::/0| Ingress | Exclusive | +| [alb.ingress.kubernetes.io/security-group-prefix-lists](#security-group-prefix-lists) | stringList |pl-00000000, pl-1111111| Ingress | Exclusive | +| [alb.ingress.kubernetes.io/certificate-arn](#certificate-arn) | stringList |N/A| Ingress | Merge | +| [alb.ingress.kubernetes.io/ssl-policy](#ssl-policy) | string |ELBSecurityPolicy-2016-08| Ingress | Exclusive | +| [alb.ingress.kubernetes.io/target-type](#target-type) | instance \| ip |instance| Ingress,Service | N/A | +| [alb.ingress.kubernetes.io/backend-protocol](#backend-protocol) | HTTP \| HTTPS |HTTP| Ingress,Service | N/A | +| [alb.ingress.kubernetes.io/backend-protocol-version](#backend-protocol-version) | string | HTTP1 | Ingress,Service | N/A | +| [alb.ingress.kubernetes.io/target-group-attributes](#target-group-attributes) | stringMap |N/A| Ingress,Service | N/A | +| [alb.ingress.kubernetes.io/healthcheck-port](#healthcheck-port) | integer \| traffic-port |traffic-port| Ingress,Service | N/A | +| [alb.ingress.kubernetes.io/healthcheck-protocol](#healthcheck-protocol) | HTTP \| HTTPS |HTTP| Ingress,Service | N/A | +| [alb.ingress.kubernetes.io/healthcheck-path](#healthcheck-path) | string |/ \| /AWS.ALB/healthcheck | Ingress,Service | N/A | +| [alb.ingress.kubernetes.io/healthcheck-interval-seconds](#healthcheck-interval-seconds) | integer |'15'| Ingress,Service | N/A | +| [alb.ingress.kubernetes.io/healthcheck-timeout-seconds](#healthcheck-timeout-seconds) | integer |'5'| Ingress,Service | N/A | +| [alb.ingress.kubernetes.io/healthy-threshold-count](#healthy-threshold-count) | integer |'2'| Ingress,Service | N/A | +| [alb.ingress.kubernetes.io/unhealthy-threshold-count](#unhealthy-threshold-count) | integer |'2'| Ingress,Service | N/A | +| [alb.ingress.kubernetes.io/success-codes](#success-codes) | string |'200' \| '12' | Ingress,Service | N/A | +| [alb.ingress.kubernetes.io/auth-type](#auth-type) | none\|oidc\|cognito |none| Ingress,Service | N/A | +| [alb.ingress.kubernetes.io/auth-idp-cognito](#auth-idp-cognito) | json |N/A| Ingress,Service | N/A | +| [alb.ingress.kubernetes.io/auth-idp-oidc](#auth-idp-oidc) | json |N/A| Ingress,Service | N/A | +| [alb.ingress.kubernetes.io/auth-on-unauthenticated-request](#auth-on-unauthenticated-request) | authenticate\|allow\|deny |authenticate| Ingress,Service | N/A | +| [alb.ingress.kubernetes.io/auth-scope](#auth-scope) | string |openid| Ingress,Service | N/A | +| [alb.ingress.kubernetes.io/auth-session-cookie](#auth-session-cookie) | string |AWSELBAuthSessionCookie| Ingress,Service | N/A | +| [alb.ingress.kubernetes.io/auth-session-timeout](#auth-session-timeout) | integer |'604800'| Ingress,Service | N/A | +| [alb.ingress.kubernetes.io/actions.${action-name}](#actions) | json |N/A| Ingress | N/A | +| [alb.ingress.kubernetes.io/conditions.${conditions-name}](#conditions) | json |N/A| Ingress | N/A | +| [alb.ingress.kubernetes.io/target-node-labels](#target-node-labels) | stringMap |N/A| Ingress,Service | N/A | +| [alb.ingress.kubernetes.io/mutual-authentication](#mutual-authentication) | json |N/A| Ingress |Exclusive| ## IngressGroup IngressGroup feature enables you to group multiple Ingress resources together. @@ -790,16 +790,19 @@ TLS support can be controlled with the following annotations: - `alb.ingress.kubernetes.io/mutual-authentication` specifies the mutual authentication configuration that should be assigned to the Application Load Balancer secure listener ports. See [Mutual authentication with TLS](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/mutual-authentication.html) in the AWS documentation for more details. - !!!note "Configuration Options" - - `port: listen port ` - - Must be a HTTPS port specified by [listen-ports](#listen-ports). - - `mode: "off" (default) | "passthrough" | "verify"` - - `verify` mode requires an existing trust store resource. - - See [Create a trust store](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/mutual-authentication.html#create-trust-store) in the AWS documentation for more details. - - `trustStore: ARN (arn:aws:elasticloadbalancing:trustStoreArn) | Name (my-trust-store)` - - Both ARN and Name of trustStore are supported values. - - `trustStore` is required when mode is `verify`. - - `ignoreClientCertificateExpiry : true | false (default)` + !!!note + - This annotation is not applicable for Outposts, Local Zones or Wavelength zones. + - "Configuration Options" + - `port: listen port ` + - Must be a HTTPS port specified by [listen-ports](#listen-ports). + - `mode: "off" (default) | "passthrough" | "verify"` + - `verify` mode requires an existing trust store resource. + - See [Create a trust store](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/mutual-authentication.html#create-trust-store) in the AWS documentation for more details. + - `trustStore: ARN (arn:aws:elasticloadbalancing:trustStoreArn) | Name (my-trust-store)` + - Both ARN and Name of trustStore are supported values. + - `trustStore` is required when mode is `verify`. + - `ignoreClientCertificateExpiry : true | false (default)` + - Once the Mutual Authentication is set, to turn it off, you will have to explicitly pass in this annotation with `mode : "off"`. !!!example - [listen-ports](#listen-ports) specifies four HTTPS ports: `80, 443, 8080, 8443` diff --git a/pkg/deploy/elbv2/listener_manager.go b/pkg/deploy/elbv2/listener_manager.go index 22400da9d..44b2308d5 100644 --- a/pkg/deploy/elbv2/listener_manager.go +++ b/pkg/deploy/elbv2/listener_manager.go @@ -267,7 +267,7 @@ func isSDKListenerSettingsDrifted(lsSpec elbv2model.ListenerSpec, sdkLS Listener if len(lsSpec.ALPNPolicy) != 0 && !cmp.Equal(lsSpec.ALPNPolicy, awssdk.StringValueSlice(sdkLS.Listener.AlpnPolicy), cmpopts.EquateEmpty()) { return true } - if !reflect.DeepEqual(desiredDefaultMutualAuthentication, sdkLS.Listener.MutualAuthentication) { + if desiredDefaultMutualAuthentication != nil && !reflect.DeepEqual(desiredDefaultMutualAuthentication, sdkLS.Listener.MutualAuthentication) { return true } diff --git a/pkg/ingress/model_build_listener.go b/pkg/ingress/model_build_listener.go index 2504dd201..892176585 100644 --- a/pkg/ingress/model_build_listener.go +++ b/pkg/ingress/model_build_listener.go @@ -273,14 +273,10 @@ type MutualAuthenticationConfig struct { func (t *defaultModelBuildTask) computeIngressMutualAuthentication(ctx context.Context, ing *ClassifiedIngress) (map[int64]*elbv2model.MutualAuthenticationAttributes, error) { var rawMtlsConfigString string - - // If both Ingress annotation is missing mutual-authentication config, return default mutualAuthentication mode if exists := t.annotationParser.ParseStringAnnotation(annotations.IngressSuffixMutualAuthentication, &rawMtlsConfigString, ing.Ing.Annotations); !exists { - return map[int64]*elbv2model.MutualAuthenticationAttributes{443: { - Mode: string(elbv2model.MutualAuthenticationOffMode), - }}, nil - + return nil, nil } + var ingressAnnotationEntries []MutualAuthenticationConfig if err := json.Unmarshal([]byte(rawMtlsConfigString), &ingressAnnotationEntries); err != nil { diff --git a/pkg/ingress/model_build_listener_test.go b/pkg/ingress/model_build_listener_test.go new file mode 100644 index 000000000..223ee2d40 --- /dev/null +++ b/pkg/ingress/model_build_listener_test.go @@ -0,0 +1,105 @@ +package ingress + +import ( + "context" + "github.com/stretchr/testify/assert" + networking "k8s.io/api/networking/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/aws-load-balancer-controller/pkg/annotations" + "sigs.k8s.io/aws-load-balancer-controller/pkg/model/elbv2" + "testing" +) + +func Test_computeIngressListenPortConfigByPort_MutualAuthentication(t *testing.T) { + type fields struct { + ingGroup Group + } + type WantStruct struct { + port int64 + mutualAuth *elbv2.MutualAuthenticationAttributes + } + + tests := []struct { + name string + fields fields + + wantErr error + mutualAuthMode string + want []WantStruct + }{ + { + name: "Listener Config when MutualAuthentication annotation is specified", + fields: fields{ + ingGroup: Group{ + ID: GroupID{Name: "explicit-group"}, + Members: []ClassifiedIngress{ + { + Ing: &networking.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "awesome-ns", + Name: "ing-1", + Annotations: map[string]string{ + "alb.ingress.kubernetes.io/listen-ports": `[{"HTTPS": 443}, {"HTTPS": 80}]`, + "alb.ingress.kubernetes.io/mutual-authentication": `[{"port":443,"mode":"off"}, {"port":80,"mode":"passthrough"}]`, + "alb.ingress.kubernetes.io/certificate-arn": "arn:aws:iam::123456789:server-certificate/new-clb-cert", + }, + }, + }, + }, + }, + }, + }, + want: []WantStruct{{port: 443, mutualAuth: &(elbv2.MutualAuthenticationAttributes{Mode: "off", TrustStoreArn: nil, IgnoreClientCertificateExpiry: nil})}, {port: 80, mutualAuth: &(elbv2.MutualAuthenticationAttributes{Mode: "passthrough", TrustStoreArn: nil, IgnoreClientCertificateExpiry: nil})}}, + }, + + { + + name: "Listener Config when MutualAuthentication annotation is not specified", + fields: fields{ + ingGroup: Group{ + ID: GroupID{Name: "explicit-group"}, + Members: []ClassifiedIngress{ + { + Ing: &networking.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "awesome-ns", + Name: "ing-1", + Annotations: map[string]string{ + "alb.ingress.kubernetes.io/listen-ports": `[{"HTTPS": 443}, {"HTTPS": 80}]`, + "alb.ingress.kubernetes.io/certificate-arn": "arn:aws:iam::123456789:server-certificate/new-clb-cert", + }, + }, + }, + }, + }, + }, + }, + want: []WantStruct{{port: 443, mutualAuth: nil}, {port: 80, mutualAuth: nil}}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + task := &defaultModelBuildTask{ + ingGroup: tt.fields.ingGroup, + annotationParser: annotations.NewSuffixAnnotationParser("alb.ingress.kubernetes.io"), + } + got, err := task.computeIngressListenPortConfigByPort(context.Background(), &tt.fields.ingGroup.Members[0]) + if err != nil { + assert.EqualError(t, err, tt.wantErr.Error()) + } else { + + for i := 0; i < len(tt.want); i++ { + port := tt.want[i].port + mutualAuth := tt.want[i].mutualAuth + if mutualAuth != nil { + assert.Equal(t, mutualAuth.Mode, got[port].mutualAuthentication.Mode) + } else { + assert.Equal(t, mutualAuth, got[port].mutualAuthentication) + } + + } + + } + }) + } +} diff --git a/pkg/ingress/model_builder.go b/pkg/ingress/model_builder.go index 9c50c2a14..ec753247d 100644 --- a/pkg/ingress/model_builder.go +++ b/pkg/ingress/model_builder.go @@ -381,12 +381,6 @@ func (t *defaultModelBuildTask) mergeListenPortConfigs(_ context.Context, listen mergedSSLPolicy = awssdk.String(t.defaultSSLPolicy) } - if mergedProtocol == elbv2model.ProtocolHTTPS && mergedMtlsAttributes == nil { - mergedMtlsAttributes = &elbv2model.MutualAuthenticationAttributes{ - Mode: string(elbv2model.MutualAuthenticationOffMode), - } - } - return listenPortConfig{ protocol: mergedProtocol, inboundCIDRv4s: mergedInboundCIDRv4s.List(), diff --git a/pkg/ingress/model_builder_test.go b/pkg/ingress/model_builder_test.go index b85403e20..8ed6cc13f 100644 --- a/pkg/ingress/model_builder_test.go +++ b/pkg/ingress/model_builder_test.go @@ -1020,8 +1020,9 @@ func Test_defaultModelBuilder_Build(t *testing.T) { Namespace: "ns-1", Name: "ing-1", Annotations: map[string]string{ - "alb.ingress.kubernetes.io/scheme": "internet-facing", - "alb.ingress.kubernetes.io/certificate-arn": "arn:aws:acm:us-east-1:9999999:certificate/22222222,arn:aws:acm:us-east-1:9999999:certificate/33333333,arn:aws:acm:us-east-1:9999999:certificate/11111111,,arn:aws:acm:us-east-1:9999999:certificate/11111111", + "alb.ingress.kubernetes.io/scheme": "internet-facing", + "alb.ingress.kubernetes.io/certificate-arn": "arn:aws:acm:us-east-1:9999999:certificate/22222222,arn:aws:acm:us-east-1:9999999:certificate/33333333,arn:aws:acm:us-east-1:9999999:certificate/11111111,,arn:aws:acm:us-east-1:9999999:certificate/11111111", + "alb.ingress.kubernetes.io/mutual-authentication": `[{"port":443,"mode":"off"}]`, }, }, Spec: networking.IngressSpec{ @@ -1134,8 +1135,9 @@ func Test_defaultModelBuilder_Build(t *testing.T) { "port": 443, "protocol": "HTTPS", "sslPolicy": "ELBSecurityPolicy-2016-08", - "mutualAuthentication" : { - "mode" : "off" + "mutualAuthentication" : { + "mode" : "off", + "trustStoreArn": "" } } }, @@ -1742,10 +1744,7 @@ func Test_defaultModelBuilder_Build(t *testing.T) { }, "port": 443, "protocol": "HTTPS", - "sslPolicy": "ingress-class-policy", - "mutualAuthentication": { - "mode" : "off" - } + "sslPolicy": "ingress-class-policy" } }, "80": null @@ -2010,10 +2009,7 @@ func Test_defaultModelBuilder_Build(t *testing.T) { }, "port": 443, "protocol": "HTTPS", - "sslPolicy": "ELBSecurityPolicy-2016-08", - "mutualAuthentication": { - "mode" : "off" - } + "sslPolicy": "ELBSecurityPolicy-2016-08" } }, "80": null