From ef5be49897f2887c4c4836f55659cd36724b6f70 Mon Sep 17 00:00:00 2001 From: Ankush Agarwal Date: Mon, 19 May 2025 10:12:48 -0700 Subject: [PATCH 1/2] Add support for SubjectAltNames from BackendTLSPolicy.validation Signed-off-by: Ankush Agarwal --- internal/gatewayapi/backendtlspolicy.go | 16 ++ .../backendtlspolicy-subjectaltnames.in.yaml | 137 +++++++++++++ .../backendtlspolicy-subjectaltnames.out.yaml | 190 ++++++++++++++++++ internal/ir/xds.go | 15 ++ internal/ir/zz_generated.deepcopy.go | 32 +++ .../in/xds-ir/http-route-with-tlsbundle.yaml | 3 + .../http-route-with-tlsbundle.clusters.yaml | 6 + internal/xds/translator/translator.go | 24 +++ release-notes/current.yaml | 1 + 9 files changed, 424 insertions(+) create mode 100644 internal/gatewayapi/testdata/backendtlspolicy-subjectaltnames.in.yaml create mode 100644 internal/gatewayapi/testdata/backendtlspolicy-subjectaltnames.out.yaml diff --git a/internal/gatewayapi/backendtlspolicy.go b/internal/gatewayapi/backendtlspolicy.go index 143bc749fd..dc5c86b0f1 100644 --- a/internal/gatewayapi/backendtlspolicy.go +++ b/internal/gatewayapi/backendtlspolicy.go @@ -207,9 +207,25 @@ func getBackendTLSPolicy( } func getBackendTLSBundle(backendTLSPolicy *gwapiv1a3.BackendTLSPolicy, resources *resource.Resources) (*ir.TLSUpstreamConfig, error) { + // Translate SubjectAltNames from gwapiv1a3 to ir + var subjectAltNames []ir.SubjectAltName + for _, san := range backendTLSPolicy.Spec.Validation.SubjectAltNames { + var subjectAltName ir.SubjectAltName + switch san.Type { + case "DNS": + subjectAltName.Hostname = ptr.To(string(san.Hostname)) + case "URI": + subjectAltName.URI = ptr.To(string(san.URI)) + default: + continue // skip unknown types + } + subjectAltNames = append(subjectAltNames, subjectAltName) + } + tlsBundle := &ir.TLSUpstreamConfig{ SNI: ptr.To(string(backendTLSPolicy.Spec.Validation.Hostname)), UseSystemTrustStore: ptr.Deref(backendTLSPolicy.Spec.Validation.WellKnownCACertificates, "") == gwapiv1a3.WellKnownCACertificatesSystem, + SubjectAltNames: subjectAltNames, } if tlsBundle.UseSystemTrustStore { tlsBundle.CACertificate = &ir.TLSCACertificate{ diff --git a/internal/gatewayapi/testdata/backendtlspolicy-subjectaltnames.in.yaml b/internal/gatewayapi/testdata/backendtlspolicy-subjectaltnames.in.yaml new file mode 100644 index 0000000000..70caf30844 --- /dev/null +++ b/internal/gatewayapi/testdata/backendtlspolicy-subjectaltnames.in.yaml @@ -0,0 +1,137 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + name: gateway-btls + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + name: httproute-btls + namespace: envoy-gateway + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-btls + sectionName: http + rules: + - matches: + - path: + type: Exact + value: "/exact" + backendRefs: + - name: http-backend + namespace: backends + port: 8080 + +referenceGrants: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: ReferenceGrant + metadata: + name: refg-route-svc + namespace: backends + spec: + from: + - group: gateway.networking.k8s.io + kind: HTTPRoute + namespace: envoy-gateway + - group: gateway.networking.k8s.io + kind: Gateway + namespace: envoy-gateway + to: + - group: "" + kind: Service + +services: + - apiVersion: v1 + kind: Service + metadata: + name: http-backend + namespace: backends + spec: + clusterIP: 10.11.12.13 + ports: + - port: 8080 + name: http + protocol: TCP + targetPort: 8080 + +endpointSlices: + - apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: endpointslice-http-backend + namespace: backends + labels: + kubernetes.io/service-name: http-backend + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - "10.244.0.11" + conditions: + ready: true + +configMaps: + - apiVersion: v1 + kind: ConfigMap + metadata: + name: ca-cmap + namespace: backends + data: + ca.crt: | + -----BEGIN CERTIFICATE----- + MIIDJzCCAg+gAwIBAgIUAl6UKIuKmzte81cllz5PfdN2IlIwDQYJKoZIhvcNAQEL + BQAwIzEQMA4GA1UEAwwHbXljaWVudDEPMA0GA1UECgwGa3ViZWRiMB4XDTIzMTAw + MjA1NDE1N1oXDTI0MTAwMTA1NDE1N1owIzEQMA4GA1UEAwwHbXljaWVudDEPMA0G + A1UECgwGa3ViZWRiMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwSTc + 1yj8HW62nynkFbXo4VXKv2jC0PM7dPVky87FweZcTKLoWQVPQE2p2kLDK6OEszmM + yyr+xxWtyiveremrWqnKkNTYhLfYPhgQkczib7eUalmFjUbhWdLvHakbEgCodn3b + kz57mInX2VpiDOKg4kyHfiuXWpiBqrCx0KNLpxo3DEQcFcsQTeTHzh4752GV04RU + Ti/GEWyzIsl4Rg7tGtAwmcIPgUNUfY2Q390FGqdH4ahn+mw/6aFbW31W63d9YJVq + ioyOVcaMIpM5B/c7Qc8SuhCI1YGhUyg4cRHLEw5VtikioyE3X04kna3jQAj54YbR + bpEhc35apKLB21HOUQIDAQABo1MwUTAdBgNVHQ4EFgQUyvl0VI5vJVSuYFXu7B48 + 6PbMEAowHwYDVR0jBBgwFoAUyvl0VI5vJVSuYFXu7B486PbMEAowDwYDVR0TAQH/ + BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAMLxrgFVMuNRq2wAwcBt7SnNR5Cfz + 2MvXq5EUmuawIUi9kaYjwdViDREGSjk7JW17vl576HjDkdfRwi4E28SydRInZf6J + i8HZcZ7caH6DxR335fgHVzLi5NiTce/OjNBQzQ2MJXVDd8DBmG5fyatJiOJQ4bWE + A7FlP0RdP3CO3GWE0M5iXOB2m1qWkE2eyO4UHvwTqNQLdrdAXgDQlbam9e4BG3Gg + d/6thAkWDbt/QNT+EJHDCvhDRKh1RuGHyg+Y+/nebTWWrFWsktRrbOoHCZiCpXI1 + 3eXE6nt0YkgtDxG22KqnhpAg9gUSs2hlhoxyvkzyF0mu6NhPlwAgnq7+/Q== + -----END CERTIFICATE----- +backendTLSPolicies: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: BackendTLSPolicy + metadata: + name: policy-btls + namespace: backends + spec: + targetRefs: + - group: "" + kind: Service + name: http-backend + sectionName: http + validation: + caCertificateRefs: + - name: ca-cmap + group: "" + kind: ConfigMap + hostname: example.com + subjectAltNames: + - type: URI + uri: spiffe://cluster.local/ns/istio-demo/sa/echo-v1 + - type: DNS + hostname: subdomain.secondexample.com diff --git a/internal/gatewayapi/testdata/backendtlspolicy-subjectaltnames.out.yaml b/internal/gatewayapi/testdata/backendtlspolicy-subjectaltnames.out.yaml new file mode 100644 index 0000000000..215860c4ea --- /dev/null +++ b/internal/gatewayapi/testdata/backendtlspolicy-subjectaltnames.out.yaml @@ -0,0 +1,190 @@ +backendTLSPolicies: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: BackendTLSPolicy + metadata: + creationTimestamp: null + name: policy-btls + namespace: backends + spec: + targetRefs: + - group: "" + kind: Service + name: http-backend + sectionName: http + validation: + caCertificateRefs: + - group: "" + kind: ConfigMap + name: ca-cmap + hostname: example.com + subjectAltNames: + - type: URI + uri: spiffe://cluster.local/ns/istio-demo/sa/echo-v1 + - hostname: subdomain.secondexample.com + type: DNS + status: + ancestors: + - ancestorRef: + name: gateway-btls + namespace: envoy-gateway + sectionName: http + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: gateway.envoyproxy.io/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-btls + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-btls + namespace: envoy-gateway + spec: + parentRefs: + - name: gateway-btls + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: http-backend + namespace: backends + port: 8080 + matches: + - path: + type: Exact + value: /exact + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-btls + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-btls: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-btls/http + ports: + - containerPort: 10080 + name: http-80 + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-btls + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-btls + namespace: envoy-gateway-system +xdsIR: + envoy-gateway/gateway-btls: + accessLog: + json: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + metadata: + kind: Gateway + name: gateway-btls + namespace: envoy-gateway + sectionName: http + name: envoy-gateway/gateway-btls/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - destination: + name: httproute/envoy-gateway/httproute-btls/rule/0 + settings: + - addressType: IP + endpoints: + - host: 10.244.0.11 + port: 8080 + name: httproute/envoy-gateway/httproute-btls/rule/0/backend/0 + protocol: HTTP + tls: + alpnProtocols: null + caCertificate: + certificate: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURKekNDQWcrZ0F3SUJBZ0lVQWw2VUtJdUttenRlODFjbGx6NVBmZE4ySWxJd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0l6RVFNQTRHQTFVRUF3d0hiWGxqYVdWdWRERVBNQTBHQTFVRUNnd0dhM1ZpWldSaU1CNFhEVEl6TVRBdwpNakExTkRFMU4xb1hEVEkwTVRBd01UQTFOREUxTjFvd0l6RVFNQTRHQTFVRUF3d0hiWGxqYVdWdWRERVBNQTBHCkExVUVDZ3dHYTNWaVpXUmlNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXdTVGMKMXlqOEhXNjJueW5rRmJYbzRWWEt2MmpDMFBNN2RQVmt5ODdGd2VaY1RLTG9XUVZQUUUycDJrTERLNk9Fc3ptTQp5eXIreHhXdHlpdmVyZW1yV3FuS2tOVFloTGZZUGhnUWtjemliN2VVYWxtRmpVYmhXZEx2SGFrYkVnQ29kbjNiCmt6NTdtSW5YMlZwaURPS2c0a3lIZml1WFdwaUJxckN4MEtOTHB4bzNERVFjRmNzUVRlVEh6aDQ3NTJHVjA0UlUKVGkvR0VXeXpJc2w0Umc3dEd0QXdtY0lQZ1VOVWZZMlEzOTBGR3FkSDRhaG4rbXcvNmFGYlczMVc2M2Q5WUpWcQppb3lPVmNhTUlwTTVCL2M3UWM4U3VoQ0kxWUdoVXlnNGNSSExFdzVWdGlraW95RTNYMDRrbmEzalFBajU0WWJSCmJwRWhjMzVhcEtMQjIxSE9VUUlEQVFBQm8xTXdVVEFkQmdOVkhRNEVGZ1FVeXZsMFZJNXZKVlN1WUZYdTdCNDgKNlBiTUVBb3dId1lEVlIwakJCZ3dGb0FVeXZsMFZJNXZKVlN1WUZYdTdCNDg2UGJNRUFvd0R3WURWUjBUQVFILwpCQVV3QXdFQi96QU5CZ2txaGtpRzl3MEJBUXNGQUFPQ0FRRUFNTHhyZ0ZWTXVOUnEyd0F3Y0J0N1NuTlI1Q2Z6CjJNdlhxNUVVbXVhd0lVaTlrYVlqd2RWaURSRUdTams3SlcxN3ZsNTc2SGpEa2RmUndpNEUyOFN5ZFJJblpmNkoKaThIWmNaN2NhSDZEeFIzMzVmZ0hWekxpNU5pVGNlL09qTkJRelEyTUpYVkRkOERCbUc1ZnlhdEppT0pRNGJXRQpBN0ZsUDBSZFAzQ08zR1dFME01aVhPQjJtMXFXa0UyZXlPNFVIdndUcU5RTGRyZEFYZ0RRbGJhbTllNEJHM0dnCmQvNnRoQWtXRGJ0L1FOVCtFSkhEQ3ZoRFJLaDFSdUdIeWcrWSsvbmViVFdXckZXc2t0UnJiT29IQ1ppQ3BYSTEKM2VYRTZudDBZa2d0RHhHMjJLcW5ocEFnOWdVU3MyaGxob3h5dmt6eUYwbXU2TmhQbHdBZ25xNysvUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K + name: policy-btls/backends-ca + sni: example.com + subjectAltNames: + - uri: spiffe://cluster.local/ns/istio-demo/sa/echo-v1 + - hostname: subdomain.secondexample.com + weight: 1 + hostname: '*' + isHTTP2: false + metadata: + kind: HTTPRoute + name: httproute-btls + namespace: envoy-gateway + name: httproute/envoy-gateway/httproute-btls/rule/0/match/0/* + pathMatch: + distinct: false + exact: /exact + name: "" + readyListener: + address: 0.0.0.0 + ipFamily: IPv4 + path: /ready + port: 19003 diff --git a/internal/ir/xds.go b/internal/ir/xds.go index b3759e45fd..bd93162be2 100644 --- a/internal/ir/xds.go +++ b/internal/ir/xds.go @@ -445,6 +445,20 @@ type TLSCACertificate struct { Certificate []byte `json:"certificate,omitempty" yaml:"certificate,omitempty"` } +// SubjectAltName holds the subject alternative name for the certificate +// This is the internal representation of the SubjectAltName in the Gateway API +// https://github.com/kubernetes-sigs/gateway-api/blob/6fbbd90954c2a30a6502cbb22ec6e7f3359ed3a0/apis/v1alpha3/backendtlspolicy_types.go#L177 +// +k8s:deepcopy-gen=true +type SubjectAltName struct { + // Only one of the following fields should be set. + // Hostname contains Subject Alternative Name specified in DNS name format. + Hostname *string `json:"hostname,omitempty" yaml:"hostname,omitempty"` + // URI contains Subject Alternative Name specified in a full URI format. + // It MUST include both a scheme (e.g., "http" or "ftp") and a scheme-specific-part. + // Common values include SPIFFE IDs like "spiffe://mycluster.example.com/ns/myns/sa/svc1sa". + URI *string `json:"uri,omitempty" yaml:"uri,omitempty"` +} + func (t TLSCertificate) Validate() error { var errs error if len(t.Certificate) == 0 { @@ -2855,6 +2869,7 @@ type TLSUpstreamConfig struct { UseSystemTrustStore bool `json:"useSystemTrustStore,omitempty" yaml:"useSystemTrustStore,omitempty"` CACertificate *TLSCACertificate `json:"caCertificate,omitempty" yaml:"caCertificate,omitempty"` TLSConfig `json:",inline"` + SubjectAltNames []SubjectAltName `json:"subjectAltNames,omitempty" yaml:"subjectAltNames,omitempty"` } func (t *TLSUpstreamConfig) ToTLSConfig() (*tls.Config, error) { diff --git a/internal/ir/zz_generated.deepcopy.go b/internal/ir/zz_generated.deepcopy.go index eb5f63edb8..73b24e66bb 100644 --- a/internal/ir/zz_generated.deepcopy.go +++ b/internal/ir/zz_generated.deepcopy.go @@ -3213,6 +3213,31 @@ func (in *StringMatch) DeepCopy() *StringMatch { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SubjectAltName) DeepCopyInto(out *SubjectAltName) { + *out = *in + if in.Hostname != nil { + in, out := &in.Hostname, &out.Hostname + *out = new(string) + **out = **in + } + if in.URI != nil { + in, out := &in.URI, &out.URI + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubjectAltName. +func (in *SubjectAltName) DeepCopy() *SubjectAltName { + if in == nil { + return nil + } + out := new(SubjectAltName) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TCPClientTimeout) DeepCopyInto(out *TCPClientTimeout) { *out = *in @@ -3588,6 +3613,13 @@ func (in *TLSUpstreamConfig) DeepCopyInto(out *TLSUpstreamConfig) { (*in).DeepCopyInto(*out) } in.TLSConfig.DeepCopyInto(&out.TLSConfig) + if in.SubjectAltNames != nil { + in, out := &in.SubjectAltNames, &out.SubjectAltNames + *out = make([]SubjectAltName, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSUpstreamConfig. diff --git a/internal/xds/translator/testdata/in/xds-ir/http-route-with-tlsbundle.yaml b/internal/xds/translator/testdata/in/xds-ir/http-route-with-tlsbundle.yaml index 5eff08a35f..c3e7bd51c8 100644 --- a/internal/xds/translator/testdata/in/xds-ir/http-route-with-tlsbundle.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/http-route-with-tlsbundle.yaml @@ -26,6 +26,9 @@ http: certificate: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURKekNDQWcrZ0F3SUJBZ0lVQWw2VUtJdUttenRlODFjbGx6NVBmZE4ySWxJd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0l6RVFNQTRHQTFVRUF3d0hiWGxqYVdWdWRERVBNQTBHQTFVRUNnd0dhM1ZpWldSaU1CNFhEVEl6TVRBdwpNakExTkRFMU4xb1hEVEkwTVRBd01UQTFOREUxTjFvd0l6RVFNQTRHQTFVRUF3d0hiWGxqYVdWdWRERVBNQTBHCkExVUVDZ3dHYTNWaVpXUmlNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXdTVGMKMXlqOEhXNjJueW5rRmJYbzRWWEt2MmpDMFBNN2RQVmt5ODdGd2VaY1RLTG9XUVZQUUUycDJrTERLNk9Fc3ptTQp5eXIreHhXdHlpdmVyZW1yV3FuS2tOVFloTGZZUGhnUWtjemliN2VVYWxtRmpVYmhXZEx2SGFrYkVnQ29kbjNiCmt6NTdtSW5YMlZwaURPS2c0a3lIZml1WFdwaUJxckN4MEtOTHB4bzNERVFjRmNzUVRlVEh6aDQ3NTJHVjA0UlUKVGkvR0VXeXpJc2w0Umc3dEd0QXdtY0lQZ1VOVWZZMlEzOTBGR3FkSDRhaG4rbXcvNmFGYlczMVc2M2Q5WUpWcQppb3lPVmNhTUlwTTVCL2M3UWM4U3VoQ0kxWUdoVXlnNGNSSExFdzVWdGlraW95RTNYMDRrbmEzalFBajU0WWJSCmJwRWhjMzVhcEtMQjIxSE9VUUlEQVFBQm8xTXdVVEFkQmdOVkhRNEVGZ1FVeXZsMFZJNXZKVlN1WUZYdTdCNDgKNlBiTUVBb3dId1lEVlIwakJCZ3dGb0FVeXZsMFZJNXZKVlN1WUZYdTdCNDg2UGJNRUFvd0R3WURWUjBUQVFILwpCQVV3QXdFQi96QU5CZ2txaGtpRzl3MEJBUXNGQUFPQ0FRRUFNTHhyZ0ZWTXVOUnEyd0F3Y0J0N1NuTlI1Q2Z6CjJNdlhxNUVVbXVhd0lVaTlrYVlqd2RWaURSRUdTams3SlcxN3ZsNTc2SGpEa2RmUndpNEUyOFN5ZFJJblpmNkoKaThIWmNaN2NhSDZEeFIzMzVmZ0hWekxpNU5pVGNlL09qTkJRelEyTUpYVkRkOERCbUc1ZnlhdEppT0pRNGJXRQpBN0ZsUDBSZFAzQ08zR1dFME01aVhPQjJtMXFXa0UyZXlPNFVIdndUcU5RTGRyZEFYZ0RRbGJhbTllNEJHM0dnCmQvNnRoQWtXRGJ0L1FOVCtFSkhEQ3ZoRFJLaDFSdUdIeWcrWSsvbmViVFdXckZXc2t0UnJiT29IQ1ppQ3BYSTEKM2VYRTZudDBZa2d0RHhHMjJLcW5ocEFnOWdVU3MyaGxob3h5dmt6eUYwbXU2TmhQbHdBZ25xNysvUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K name: policy-btls/policies-ca SNI: example.com + subjectAltNames: + - uri: spiffe://cluster.local/ns/istio-demo/sa/echo-v1 + - hostname: subdomain.secondexample.com weight: 1 hostname: '*' name: httproute/envoy-gateway/httproute-btls/rule/0/match/0/* diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-with-tlsbundle.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-with-tlsbundle.clusters.yaml index 0ba4829c93..991b3dbbaa 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-with-tlsbundle.clusters.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-with-tlsbundle.clusters.yaml @@ -29,6 +29,12 @@ - matcher: exact: example.com sanType: DNS + - matcher: + exact: spiffe://cluster.local/ns/istio-demo/sa/echo-v1 + sanType: URI + - matcher: + exact: subdomain.secondexample.com + sanType: DNS validationContextSdsSecretConfig: name: policy-btls/policies-ca sdsConfig: diff --git a/internal/xds/translator/translator.go b/internal/xds/translator/translator.go index 5118ffd329..bbe28719d3 100644 --- a/internal/xds/translator/translator.go +++ b/internal/xds/translator/translator.go @@ -1043,6 +1043,30 @@ func buildXdsUpstreamTLSSocketWthCert(tlsConfig *ir.TLSUpstreamConfig) (*corev3. }, }, } + for _, san := range tlsConfig.SubjectAltNames { + var sanType tlsv3.SubjectAltNameMatcher_SanType + var value string + + // Exactly one of san.Hostname or san.URI is guaranteed to be set + if san.Hostname != nil { + sanType = tlsv3.SubjectAltNameMatcher_DNS + value = *san.Hostname + } else if san.URI != nil { + sanType = tlsv3.SubjectAltNameMatcher_URI + value = *san.URI + } + validationContext.DefaultValidationContext.MatchTypedSubjectAltNames = append( + validationContext.DefaultValidationContext.MatchTypedSubjectAltNames, + &tlsv3.SubjectAltNameMatcher{ + SanType: sanType, + Matcher: &matcherv3.StringMatcher{ + MatchPattern: &matcherv3.StringMatcher_Exact{ + Exact: value, + }, + }, + }, + ) + } } tlsCtx := &tlsv3.UpstreamTlsContext{ diff --git a/release-notes/current.yaml b/release-notes/current.yaml index 818799641d..d7a4f65860 100644 --- a/release-notes/current.yaml +++ b/release-notes/current.yaml @@ -10,6 +10,7 @@ security updates: | new features: | Added support for percentage-based request mirroring Add an option to OIDC authentication to bypass it and defer to JWT when the request contains an "Authorization: Bearer ..." header. + Added support for configuring Subject Alternative Names (SANs) for upstream TLS validation via `BackendTLSPolicy.validation.subjectAltNames`. bug fixes: | From 88a551fc30d87ea79709caba6979a953d6455f5e Mon Sep 17 00:00:00 2001 From: Ankush Agarwal Date: Mon, 19 May 2025 10:58:12 -0700 Subject: [PATCH 2/2] Fix yaml lint issues Signed-off-by: Ankush Agarwal --- .../testdata/backendtlspolicy-subjectaltnames.in.yaml | 8 ++++---- .../testdata/in/xds-ir/http-route-with-tlsbundle.yaml | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/gatewayapi/testdata/backendtlspolicy-subjectaltnames.in.yaml b/internal/gatewayapi/testdata/backendtlspolicy-subjectaltnames.in.yaml index 70caf30844..2b349c7783 100644 --- a/internal/gatewayapi/testdata/backendtlspolicy-subjectaltnames.in.yaml +++ b/internal/gatewayapi/testdata/backendtlspolicy-subjectaltnames.in.yaml @@ -131,7 +131,7 @@ backendTLSPolicies: kind: ConfigMap hostname: example.com subjectAltNames: - - type: URI - uri: spiffe://cluster.local/ns/istio-demo/sa/echo-v1 - - type: DNS - hostname: subdomain.secondexample.com + - type: URI + uri: spiffe://cluster.local/ns/istio-demo/sa/echo-v1 + - type: DNS + hostname: subdomain.secondexample.com diff --git a/internal/xds/translator/testdata/in/xds-ir/http-route-with-tlsbundle.yaml b/internal/xds/translator/testdata/in/xds-ir/http-route-with-tlsbundle.yaml index c3e7bd51c8..b0a453e792 100644 --- a/internal/xds/translator/testdata/in/xds-ir/http-route-with-tlsbundle.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/http-route-with-tlsbundle.yaml @@ -27,8 +27,8 @@ http: name: policy-btls/policies-ca SNI: example.com subjectAltNames: - - uri: spiffe://cluster.local/ns/istio-demo/sa/echo-v1 - - hostname: subdomain.secondexample.com + - uri: spiffe://cluster.local/ns/istio-demo/sa/echo-v1 + - hostname: subdomain.secondexample.com weight: 1 hostname: '*' name: httproute/envoy-gateway/httproute-btls/rule/0/match/0/*