diff --git a/api/v1alpha1/tls_types.go b/api/v1alpha1/tls_types.go index bf2a1f5056..e5c19ad014 100644 --- a/api/v1alpha1/tls_types.go +++ b/api/v1alpha1/tls_types.go @@ -141,6 +141,52 @@ type ClientValidationContext struct { // +kubebuilder:validation:MaxItems=8 // +optional CACertificateRefs []gwapiv1.SecretObjectReference `json:"caCertificateRefs,omitempty"` + + // An optional list of base64-encoded SHA-256 hashes. If specified, Envoy will + // verify that the SHA-256 of the DER-encoded Subject Public Key Information + // (SPKI) of the presented certificate matches one of the specified values. + // +optional + SPKIHashes []string `json:"spkiHashes,omitempty"` + + // An optional list of hex-encoded SHA-256 hashes. If specified, Envoy will + // verify that the SHA-256 of the DER-encoded presented certificate matches + // one of the specified values. + // +optional + CertificateHashes []string `json:"certificateHashes,omitempty"` + + // An optional list of Subject Alternative name matchers. If specified, Envoy + // will verify that the Subject Alternative Name of the presented certificate + // matches one of the specified matchers + // +optional + SubjectAltNames *SubjectAltNames `json:"subjectAltNames,omitempty"` +} + +type SubjectAltNames struct { + // DNS names matchers + // +optional + DNSNames []StringMatch `json:"dnsNames,omitempty"` + + // Email addresses matchers + // +optional + EmailAddresses []StringMatch `json:"emailAddresses,omitempty"` + + // IP addresses matchers + // +optional + IPAddresses []StringMatch `json:"ipAddresses,omitempty"` + + // URIs matchers + // +optional + URIs []StringMatch `json:"uris,omitempty"` + + // Other names matchers + // +optional + OtherNames []OtherSANMatch `json:"otherNames,omitempty"` +} + +type OtherSANMatch struct { + // OID Value + Oid string `json:"oid"` + StringMatch `json:",inline"` } // Session defines settings related to TLS session management. diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index a15a3ed938..13f613f0b8 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -1142,6 +1142,21 @@ func (in *ClientValidationContext) DeepCopyInto(out *ClientValidationContext) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.SPKIHashes != nil { + in, out := &in.SPKIHashes, &out.SPKIHashes + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.CertificateHashes != nil { + in, out := &in.CertificateHashes, &out.CertificateHashes + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.SubjectAltNames != nil { + in, out := &in.SubjectAltNames, &out.SubjectAltNames + *out = new(SubjectAltNames) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClientValidationContext. @@ -4763,6 +4778,22 @@ func (in *Operation) DeepCopy() *Operation { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OtherSANMatch) DeepCopyInto(out *OtherSANMatch) { + *out = *in + in.StringMatch.DeepCopyInto(&out.StringMatch) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OtherSANMatch. +func (in *OtherSANMatch) DeepCopy() *OtherSANMatch { + if in == nil { + return nil + } + out := new(OtherSANMatch) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PassiveHealthCheck) DeepCopyInto(out *PassiveHealthCheck) { *out = *in @@ -6231,6 +6262,56 @@ 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 *SubjectAltNames) DeepCopyInto(out *SubjectAltNames) { + *out = *in + if in.DNSNames != nil { + in, out := &in.DNSNames, &out.DNSNames + *out = make([]StringMatch, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.EmailAddresses != nil { + in, out := &in.EmailAddresses, &out.EmailAddresses + *out = make([]StringMatch, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.IPAddresses != nil { + in, out := &in.IPAddresses, &out.IPAddresses + *out = make([]StringMatch, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.URIs != nil { + in, out := &in.URIs, &out.URIs + *out = make([]StringMatch, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.OtherNames != nil { + in, out := &in.OtherNames, &out.OtherNames + *out = make([]OtherSANMatch, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubjectAltNames. +func (in *SubjectAltNames) DeepCopy() *SubjectAltNames { + if in == nil { + return nil + } + out := new(SubjectAltNames) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TCPActiveHealthChecker) DeepCopyInto(out *TCPActiveHealthChecker) { *out = *in diff --git a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml index 3a48215f6a..57ca85e44f 100644 --- a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml +++ b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml @@ -854,11 +854,174 @@ spec: type: object maxItems: 8 type: array + certificateHashes: + description: |- + An optional list of hex-encoded SHA-256 hashes. If specified, Envoy will + verify that the SHA-256 of the DER-encoded presented certificate matches + one of the specified values. + items: + type: string + type: array optional: description: |- Optional set to true accepts connections even when a client doesn't present a certificate. Defaults to false, which rejects connections without a valid client certificate. type: boolean + spkiHashes: + description: |- + An optional list of base64-encoded SHA-256 hashes. If specified, Envoy will + verify that the SHA-256 of the DER-encoded Subject Public Key Information + (SPKI) of the presented certificate matches one of the specified values. + items: + type: string + type: array + subjectAltNames: + description: |- + An optional list of Subject Alternative name matchers. If specified, Envoy + will verify that the Subject Alternative Name of the presented certificate + matches one of the specified matchers + properties: + dnsNames: + description: DNS names matchers + items: + description: |- + StringMatch defines how to match any strings. + This is a general purpose match condition that can be used by other EG APIs + that need to match against a string. + properties: + type: + default: Exact + description: Type specifies how to match against + a string. + enum: + - Exact + - Prefix + - Suffix + - RegularExpression + type: string + value: + description: Value specifies the string value that + the match must have. + maxLength: 1024 + minLength: 1 + type: string + required: + - value + type: object + type: array + emailAddresses: + description: Email addresses matchers + items: + description: |- + StringMatch defines how to match any strings. + This is a general purpose match condition that can be used by other EG APIs + that need to match against a string. + properties: + type: + default: Exact + description: Type specifies how to match against + a string. + enum: + - Exact + - Prefix + - Suffix + - RegularExpression + type: string + value: + description: Value specifies the string value that + the match must have. + maxLength: 1024 + minLength: 1 + type: string + required: + - value + type: object + type: array + ipAddresses: + description: IP addresses matchers + items: + description: |- + StringMatch defines how to match any strings. + This is a general purpose match condition that can be used by other EG APIs + that need to match against a string. + properties: + type: + default: Exact + description: Type specifies how to match against + a string. + enum: + - Exact + - Prefix + - Suffix + - RegularExpression + type: string + value: + description: Value specifies the string value that + the match must have. + maxLength: 1024 + minLength: 1 + type: string + required: + - value + type: object + type: array + otherNames: + description: Other names matchers + items: + properties: + oid: + description: OID Value + type: string + type: + default: Exact + description: Type specifies how to match against + a string. + enum: + - Exact + - Prefix + - Suffix + - RegularExpression + type: string + value: + description: Value specifies the string value that + the match must have. + maxLength: 1024 + minLength: 1 + type: string + required: + - oid + - value + type: object + type: array + uris: + description: URIs matchers + items: + description: |- + StringMatch defines how to match any strings. + This is a general purpose match condition that can be used by other EG APIs + that need to match against a string. + properties: + type: + default: Exact + description: Type specifies how to match against + a string. + enum: + - Exact + - Prefix + - Suffix + - RegularExpression + type: string + value: + description: Value specifies the string value that + the match must have. + maxLength: 1024 + minLength: 1 + type: string + required: + - value + type: object + type: array + type: object type: object ecdhCurves: description: |- diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml index fd37c74bdf..b3d3218b6b 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml @@ -853,11 +853,174 @@ spec: type: object maxItems: 8 type: array + certificateHashes: + description: |- + An optional list of hex-encoded SHA-256 hashes. If specified, Envoy will + verify that the SHA-256 of the DER-encoded presented certificate matches + one of the specified values. + items: + type: string + type: array optional: description: |- Optional set to true accepts connections even when a client doesn't present a certificate. Defaults to false, which rejects connections without a valid client certificate. type: boolean + spkiHashes: + description: |- + An optional list of base64-encoded SHA-256 hashes. If specified, Envoy will + verify that the SHA-256 of the DER-encoded Subject Public Key Information + (SPKI) of the presented certificate matches one of the specified values. + items: + type: string + type: array + subjectAltNames: + description: |- + An optional list of Subject Alternative name matchers. If specified, Envoy + will verify that the Subject Alternative Name of the presented certificate + matches one of the specified matchers + properties: + dnsNames: + description: DNS names matchers + items: + description: |- + StringMatch defines how to match any strings. + This is a general purpose match condition that can be used by other EG APIs + that need to match against a string. + properties: + type: + default: Exact + description: Type specifies how to match against + a string. + enum: + - Exact + - Prefix + - Suffix + - RegularExpression + type: string + value: + description: Value specifies the string value that + the match must have. + maxLength: 1024 + minLength: 1 + type: string + required: + - value + type: object + type: array + emailAddresses: + description: Email addresses matchers + items: + description: |- + StringMatch defines how to match any strings. + This is a general purpose match condition that can be used by other EG APIs + that need to match against a string. + properties: + type: + default: Exact + description: Type specifies how to match against + a string. + enum: + - Exact + - Prefix + - Suffix + - RegularExpression + type: string + value: + description: Value specifies the string value that + the match must have. + maxLength: 1024 + minLength: 1 + type: string + required: + - value + type: object + type: array + ipAddresses: + description: IP addresses matchers + items: + description: |- + StringMatch defines how to match any strings. + This is a general purpose match condition that can be used by other EG APIs + that need to match against a string. + properties: + type: + default: Exact + description: Type specifies how to match against + a string. + enum: + - Exact + - Prefix + - Suffix + - RegularExpression + type: string + value: + description: Value specifies the string value that + the match must have. + maxLength: 1024 + minLength: 1 + type: string + required: + - value + type: object + type: array + otherNames: + description: Other names matchers + items: + properties: + oid: + description: OID Value + type: string + type: + default: Exact + description: Type specifies how to match against + a string. + enum: + - Exact + - Prefix + - Suffix + - RegularExpression + type: string + value: + description: Value specifies the string value that + the match must have. + maxLength: 1024 + minLength: 1 + type: string + required: + - oid + - value + type: object + type: array + uris: + description: URIs matchers + items: + description: |- + StringMatch defines how to match any strings. + This is a general purpose match condition that can be used by other EG APIs + that need to match against a string. + properties: + type: + default: Exact + description: Type specifies how to match against + a string. + enum: + - Exact + - Prefix + - Suffix + - RegularExpression + type: string + value: + description: Value specifies the string value that + the match must have. + maxLength: 1024 + minLength: 1 + type: string + required: + - value + type: object + type: array + type: object type: object ecdhCurves: description: |- diff --git a/internal/gatewayapi/clienttrafficpolicy.go b/internal/gatewayapi/clienttrafficpolicy.go index 9db58a47fa..46c41c026a 100644 --- a/internal/gatewayapi/clienttrafficpolicy.go +++ b/internal/gatewayapi/clienttrafficpolicy.go @@ -865,6 +865,7 @@ func (t *Translator) buildListenerTLSParameters(policy *egv1a1.ClientTrafficPoli if len(irCACert.Certificate) > 0 { irTLSConfig.CACertificate = irCACert irTLSConfig.RequireClientCertificate = !tlsParams.ClientValidation.Optional + setTLSClientValidationContext(tlsParams.ClientValidation, irTLSConfig) } } @@ -880,6 +881,32 @@ func (t *Translator) buildListenerTLSParameters(policy *egv1a1.ClientTrafficPoli return irTLSConfig, nil } +func setTLSClientValidationContext(tlsClientValidation *egv1a1.ClientValidationContext, irTLSConfig *ir.TLSConfig) { + if len(tlsClientValidation.SPKIHashes) > 0 { + irTLSConfig.VerifyCertificateSpki = append(irTLSConfig.VerifyCertificateSpki, tlsClientValidation.SPKIHashes...) + } + if len(tlsClientValidation.CertificateHashes) > 0 { + irTLSConfig.VerifyCertificateHash = append(irTLSConfig.VerifyCertificateHash, tlsClientValidation.CertificateHashes...) + } + if tlsClientValidation.SubjectAltNames != nil { + for _, match := range tlsClientValidation.SubjectAltNames.DNSNames { + irTLSConfig.MatchTypedSubjectAltNames = append(irTLSConfig.MatchTypedSubjectAltNames, irStringMatch("DNS", match)) + } + for _, match := range tlsClientValidation.SubjectAltNames.EmailAddresses { + irTLSConfig.MatchTypedSubjectAltNames = append(irTLSConfig.MatchTypedSubjectAltNames, irStringMatch("EMAIL", match)) + } + for _, match := range tlsClientValidation.SubjectAltNames.IPAddresses { + irTLSConfig.MatchTypedSubjectAltNames = append(irTLSConfig.MatchTypedSubjectAltNames, irStringMatch("IP_ADDRESS", match)) + } + for _, match := range tlsClientValidation.SubjectAltNames.URIs { + irTLSConfig.MatchTypedSubjectAltNames = append(irTLSConfig.MatchTypedSubjectAltNames, irStringMatch("URI", match)) + } + for _, otherName := range tlsClientValidation.SubjectAltNames.OtherNames { + irTLSConfig.MatchTypedSubjectAltNames = append(irTLSConfig.MatchTypedSubjectAltNames, irStringMatch(otherName.Oid, otherName.StringMatch)) + } + } +} + func buildConnection(connection *egv1a1.ClientConnection) (*ir.ClientConnection, error) { if connection == nil { return nil, nil diff --git a/internal/gatewayapi/helpers.go b/internal/gatewayapi/helpers.go index 8e8e4dae04..933a1fb943 100644 --- a/internal/gatewayapi/helpers.go +++ b/internal/gatewayapi/helpers.go @@ -708,3 +708,22 @@ func getCaCertFromSecret(s *corev1.Secret) ([]byte, bool) { return nil, false } } + +func irStringMatch(name string, match egv1a1.StringMatch) *ir.StringMatch { + matchType := egv1a1.StringMatchExact + if match.Type != nil { + matchType = *match.Type + } + switch matchType { + case egv1a1.StringMatchExact: + return &ir.StringMatch{Name: name, Exact: &match.Value} + case egv1a1.StringMatchPrefix: + return &ir.StringMatch{Name: name, Prefix: &match.Value} + case egv1a1.StringMatchSuffix: + return &ir.StringMatch{Name: name, Suffix: &match.Value} + case egv1a1.StringMatchRegularExpression: + return &ir.StringMatch{Name: name, SafeRegex: &match.Value} + default: + return nil + } +} diff --git a/internal/gatewayapi/helpers_test.go b/internal/gatewayapi/helpers_test.go index f39bacf83d..72df6c66c9 100644 --- a/internal/gatewayapi/helpers_test.go +++ b/internal/gatewayapi/helpers_test.go @@ -25,6 +25,7 @@ import ( gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" + "github.com/envoyproxy/gateway/internal/ir" ) func TestValidateGRPCFilterRef(t *testing.T) { @@ -809,3 +810,86 @@ func TestGetCaCertFromSecret(t *testing.T) { }) } } + +func TestIrStringMatch(t *testing.T) { + const stringMatchUnknown egv1a1.StringMatchType = "Unknown" + matchName := "Name" + matchValue := "Value" + + testCases := []struct { + name string + match egv1a1.StringMatch + expected *ir.StringMatch + }{ + { + name: "Exact by default", + match: egv1a1.StringMatch{ + Type: nil, + Value: matchValue, + }, + expected: &ir.StringMatch{ + Name: matchName, + Exact: &matchValue, + }, + }, + { + name: "Exact", + match: egv1a1.StringMatch{ + Type: ptr.To(egv1a1.StringMatchExact), + Value: matchValue, + }, + expected: &ir.StringMatch{ + Name: matchName, + Exact: &matchValue, + }, + }, + { + name: "Prefix", + match: egv1a1.StringMatch{ + Type: ptr.To(egv1a1.StringMatchPrefix), + Value: matchValue, + }, + expected: &ir.StringMatch{ + Name: matchName, + Prefix: &matchValue, + }, + }, + { + name: "Suffix", + match: egv1a1.StringMatch{ + Type: ptr.To(egv1a1.StringMatchSuffix), + Value: matchValue, + }, + expected: &ir.StringMatch{ + Name: matchName, + Suffix: &matchValue, + }, + }, + { + name: "RegularExpression", + match: egv1a1.StringMatch{ + Type: ptr.To(egv1a1.StringMatchRegularExpression), + Value: matchValue, + }, + expected: &ir.StringMatch{ + Name: matchName, + SafeRegex: &matchValue, + }, + }, + { + name: "Unknown", + match: egv1a1.StringMatch{ + Type: ptr.To(stringMatchUnknown), + Value: matchValue, + }, + expected: nil, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + result := irStringMatch(matchName, tc.match) + require.Equal(t, tc.expected, result) + }) + } +} diff --git a/internal/gatewayapi/testdata/clienttrafficpolicy-mtls-client-verification.in.yaml b/internal/gatewayapi/testdata/clienttrafficpolicy-mtls-client-verification.in.yaml index 9189d8e4db..6845f5eca7 100644 --- a/internal/gatewayapi/testdata/clienttrafficpolicy-mtls-client-verification.in.yaml +++ b/internal/gatewayapi/testdata/clienttrafficpolicy-mtls-client-verification.in.yaml @@ -32,6 +32,72 @@ clientTrafficPolicies: - kind: ConfigMap name: ca-configmap namespace: envoy-gateway +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: ClientTrafficPolicy + metadata: + namespace: envoy-gateway + name: target-gateway-3 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-3 + sectionName: http-1 + tls: + clientValidation: + optional: false + caCertificateRefs: + - kind: ConfigMap + name: ca-configmap + namespace: envoy-gateway + spkiHashes: + - NvqYIYSbgK2vCJpQhObf77vv+bQWtc5ek5RIOwPiC9A= + certificateHashes: + - df6ff72fe9116521268f6f2dd4966f51df479883fe7037b39f75916ac3049d1a + subjectAltNames: + dnsNames: + - {type: Exact, value: client1.example.com} + emailAddresses: + - {type: Suffix, value: "@example.com"} + ipAddresses: + - {type: Prefix, value: 192.168.} + uris: + - {value: spiffe://example.com/client1} + otherNames: + - {oid: 1.3.6.1.4.1.311.20.2.3, value: client1} +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: ClientTrafficPolicy + metadata: + namespace: envoy-gateway + name: target-gateway-4 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-3 + sectionName: tls-1 + tls: + clientValidation: + optional: false + caCertificateRefs: + - kind: ConfigMap + name: ca-configmap + namespace: envoy-gateway + spkiHashes: + - NvqYIYSbgK2vCJpQhObf77vv+bQWtc5ek5RIOwPiC9A= + certificateHashes: + - df6ff72fe9116521268f6f2dd4966f51df479883fe7037b39f75916ac3049d1a + subjectAltNames: + dnsNames: + - {type: Exact, value: client2.example.org} + emailAddresses: + - {type: Suffix, value: "@example.org"} + ipAddresses: + - {type: Prefix, value: "10."} + uris: + - {value: spiffe://example.com/client2} + otherNames: + - {oid: 1.3.6.1.4.1.311.20.2.3, value: client2} gateways: - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway @@ -77,6 +143,51 @@ gateways: certificateRefs: - name: tls-secret-1 namespace: envoy-gateway +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-3 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http-1 + protocol: HTTPS + port: 443 + allowedRoutes: + namespaces: + from: Same + tls: + mode: Terminate + certificateRefs: + - name: tls-secret-1 + namespace: envoy-gateway + - name: tls-1 + protocol: TLS + port: 6443 + allowedRoutes: + namespaces: + from: Same + tls: + mode: Terminate + certificateRefs: + - name: tls-secret-1 + namespace: envoy-gateway +tcpRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TCPRoute + metadata: + namespace: envoy-gateway + name: tcproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-3 + sectionName: tls-1 + rules: + - backendRefs: + - name: service-1 + port: 8080 configMaps: - apiVersion: v1 kind: ConfigMap diff --git a/internal/gatewayapi/testdata/clienttrafficpolicy-mtls-client-verification.out.yaml b/internal/gatewayapi/testdata/clienttrafficpolicy-mtls-client-verification.out.yaml index 7a11d2378c..d7fd002afc 100644 --- a/internal/gatewayapi/testdata/clienttrafficpolicy-mtls-client-verification.out.yaml +++ b/internal/gatewayapi/testdata/clienttrafficpolicy-mtls-client-verification.out.yaml @@ -1,4 +1,110 @@ clientTrafficPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: ClientTrafficPolicy + metadata: + creationTimestamp: null + name: target-gateway-3 + namespace: envoy-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-3 + sectionName: http-1 + tls: + clientValidation: + caCertificateRefs: + - group: null + kind: ConfigMap + name: ca-configmap + namespace: envoy-gateway + certificateHashes: + - df6ff72fe9116521268f6f2dd4966f51df479883fe7037b39f75916ac3049d1a + spkiHashes: + - NvqYIYSbgK2vCJpQhObf77vv+bQWtc5ek5RIOwPiC9A= + subjectAltNames: + dnsNames: + - type: Exact + value: client1.example.com + emailAddresses: + - type: Suffix + value: '@example.com' + ipAddresses: + - type: Prefix + value: 192.168. + otherNames: + - oid: 1.3.6.1.4.1.311.20.2.3 + value: client1 + uris: + - value: spiffe://example.com/client1 + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-3 + namespace: envoy-gateway + sectionName: http-1 + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: gateway.envoyproxy.io/gatewayclass-controller +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: ClientTrafficPolicy + metadata: + creationTimestamp: null + name: target-gateway-4 + namespace: envoy-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-3 + sectionName: tls-1 + tls: + clientValidation: + caCertificateRefs: + - group: null + kind: ConfigMap + name: ca-configmap + namespace: envoy-gateway + certificateHashes: + - df6ff72fe9116521268f6f2dd4966f51df479883fe7037b39f75916ac3049d1a + spkiHashes: + - NvqYIYSbgK2vCJpQhObf77vv+bQWtc5ek5RIOwPiC9A= + subjectAltNames: + dnsNames: + - type: Exact + value: client2.example.org + emailAddresses: + - type: Suffix + value: '@example.org' + ipAddresses: + - type: Prefix + value: "10." + otherNames: + - oid: 1.3.6.1.4.1.311.20.2.3 + value: client2 + uris: + - value: spiffe://example.com/client2 + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-3 + namespace: envoy-gateway + sectionName: tls-1 + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: gateway.envoyproxy.io/gatewayclass-controller - apiVersion: gateway.envoyproxy.io/v1alpha1 kind: ClientTrafficPolicy metadata: @@ -188,6 +294,87 @@ gateways: kind: HTTPRoute - group: gateway.networking.k8s.io kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-3 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http-1 + port: 443 + protocol: HTTPS + tls: + certificateRefs: + - group: null + kind: null + name: tls-secret-1 + namespace: envoy-gateway + mode: Terminate + - allowedRoutes: + namespaces: + from: Same + name: tls-1 + port: 6443 + protocol: TLS + tls: + certificateRefs: + - group: null + kind: null + name: tls-secret-1 + namespace: envoy-gateway + mode: Terminate + status: + listeners: + - attachedRoutes: 0 + 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-1 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - 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: tls-1 + supportedKinds: + - group: gateway.networking.k8s.io + kind: TCPRoute infraIR: envoy-gateway/gateway-1: proxy: @@ -234,6 +421,66 @@ infraIR: name: envoy-gateway-class name: envoy-gateway/gateway-2 namespace: envoy-gateway-system + envoy-gateway/gateway-3: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-3/http-1 + ports: + - containerPort: 10443 + name: https-443 + protocol: HTTPS + servicePort: 443 + - address: null + name: envoy-gateway/gateway-3/tls-1 + ports: + - containerPort: 6443 + name: tls-6443 + protocol: TLS + servicePort: 6443 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-3 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + ownerReference: + kind: GatewayClass + name: envoy-gateway-class + name: envoy-gateway/gateway-3 + namespace: envoy-gateway-system +tcpRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TCPRoute + metadata: + creationTimestamp: null + name: tcproute-1 + namespace: envoy-gateway + spec: + parentRefs: + - name: gateway-3 + namespace: envoy-gateway + sectionName: tls-1 + rules: + - backendRefs: + - name: service-1 + port: 8080 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: service envoy-gateway/service-1 not found + reason: BackendNotFound + status: "False" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-3 + namespace: envoy-gateway + sectionName: tls-1 xdsIR: envoy-gateway/gateway-1: accessLog: @@ -320,3 +567,136 @@ xdsIR: ipFamily: IPv4 path: /ready port: 19003 + envoy-gateway/gateway-3: + accessLog: + json: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + metadata: + kind: Gateway + name: gateway-3 + namespace: envoy-gateway + sectionName: http-1 + name: envoy-gateway/gateway-3/http-1 + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10443 + tls: + alpnProtocols: null + caCertificate: + certificate: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURRekNDQWl1Z0F3SUJBZ0lCQVRBTkJna3Foa2lHOXcwQkFRc0ZBREJDTVJNd0VRWURWUVFLRXdwRmJuWnYKZVZCeWIzaDVNUkF3RGdZRFZRUUxFd2RIWVhSbGQyRjVNUmt3RndZRFZRUURFeEJGYm5admVTQkhZWFJsZDJGNQpJRU5CTUNBWERUSTBNRE14TURFMU16SXhOMW9ZRHpJeE1qUXdNekV3TVRZek1qRTNXakJDTVJNd0VRWURWUVFLCkV3cEZiblp2ZVZCeWIzaDVNUkF3RGdZRFZRUUxFd2RIWVhSbGQyRjVNUmt3RndZRFZRUURFeEJGYm5admVTQkgKWVhSbGQyRjVJRU5CTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUE3WkZtR0I0ZQptMUtkR0VvaEFaQmZxeWRBRUdMREhKMVl5ZkhXZGQrdkJBZXZkVzY0Ylp4M3BnZ0pPdGdDbmVQdUZkMDJyRFFTCmRsc0psWC82bUZ0b1FpbG82d3Z4RFNKUmZhVERidGZUancrN2s4eWZkL0pzbWgwUldHK1VleUk3TmE5c1hBejcKYjU3bXB4c0NvTm93emVLNUVUaU9HR05XUGNqRU5Ka1NuQmFyejVtdU4wMHhJWldCVSt5TjVQTEpOeFp2eHBaSgpPbC9TU0k4c25vMGUwUHhBbXAzZmU3UWFYaVpqL1RBR0pQR3VUSmtVeHJIcXlaR0p0WVV4c1M4QTBkVDF6QmpqCml6QTVEcCtiNXl6WW8yM0hoN0JncGJaN1g0Z3NEVGhGdXdDRDZmSHllcHV2MnpIUHF2U3NkcWcyaEFoRHA5MVIKenJuN2E5R3hHMlZTSXdJREFRQUJvMEl3UURBT0JnTlZIUThCQWY4RUJBTUNBUVl3RHdZRFZSMFRBUUgvQkFVdwpBd0VCL3pBZEJnTlZIUTRFRmdRVVVwUDFhWjFNMktJdVBQV3JOUERWMmM1Q25nb3dEUVlKS29aSWh2Y05BUUVMCkJRQURnZ0VCQUdTRWtBVnorWjBxUzRGbUEwcTRTQ3BJSXE2NGJzZEVqaVV6ZXY3cEsxTEVLMC9ZMjhRQlBpeFYKY1VYZmF4MThWUFI5cGxzMUpnWHRvOXFZK0MwaG5SWmljNjYxMVFUSmxXSzFwNmRpblEvZURkWUNCQytudjV4eApzc0FTd21wbEl4TXZqM1MxcUY2ZHI3c01JMlpWRDVIRWxUV2RPMTlVQkx5aGlLS1pXMkt4RHNZais1TlJ3R0ZlCkcrSnVEZ3E3bmpVTThtZHlZazBOZWhlZmRCVUVVVUNRdG53VXRXOTUvNDI5WHdxUVJPdVJEdGVHVDlrakQrWTUKZWE1bVc0bWZxTGV1R0pYWnM5YmRXaktLZExRUHJuOUlzaFB5c1dxejJIejhkUTFmN045L2c4VVdWU2pkNGN5eApTNUVBb2x6VnYweUI3d0hDV0NnZkcvY2tkT1RVTm5FPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== + name: envoy-gateway/target-gateway-3/ca.crt + certificates: + - certificate: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUR6akNDQXJhZ0F3SUJBZ0lVT0dKOUx1VGtKWkU0NmNVaUpGYmJ2bm10elFvd0RRWUpLb1pJaHZjTkFRRUwKQlFBd2J6RUxNQWtHQTFVRUJoTUNWVk14Q3pBSkJnTlZCQWdNQWxaQk1SRXdEd1lEVlFRSERBaFRiMjFsUTJsMAplVEVUTUJFR0ExVUVDZ3dLUlc1MmIzbFFjbTk0ZVRFUU1BNEdBMVVFQ3d3SFIyRjBaWGRoZVRFWk1CY0dBMVVFCkF3d1FiWFJzY3k1bGVHRnRjR3hsTG1OdmJUQWdGdzB5TkRBM01UWXlNalV4TWpOYUdBOHlNVEkwTURZeU1qSXkKTlRFeU0xb3diekVMTUFrR0ExVUVCaE1DVlZNeEN6QUpCZ05WQkFnTUFsWkJNUkV3RHdZRFZRUUhEQWhUYjIxbApRMmwwZVRFVE1CRUdBMVVFQ2d3S1JXNTJiM2xRY205NGVURVFNQTRHQTFVRUN3d0hSMkYwWlhkaGVURVpNQmNHCkExVUVBd3dRYlhSc2N5NWxlR0Z0Y0d4bExtTnZiVENDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0MKQVFvQ2dnRUJBS3kwZnp5NWFaVnRNajAxVWJPRGtsU1IxbTI1Mkt0QTJ2L2tmT05vNTZkNEJQbGdqVXdXUVZNUgpTclUxZzd4T2tWdjZiL0czdG5tQVhwWWY2VlIxODIyak96cCsxQ0c4ZWlGSEpjT2ZxV2lZMjh1NnFSV2VKUFZlCnpYdUFsMmd4cWJpTzZLdDZwbnI0aXFoVGhIK3ZqV3NXTnBDSVhrbDFydVlYbnhWLzRCOENxY1JJeTZHaEp6bloKRjR3NHJBNkRlRlJmZHl0MWd1bWtkN25PVVhYKzRZMzJrd0xGRU8zR3NnUTh1aVpEQmN1UEs5RjRHRDUydDZYTgowT2tlNTU0eEl2VldvZ1M1Vzl4UFIvcU5kQlpIQ1pid05jZzRRTVllbWZva0pkUXo4elVJcnZ6VUltM3ZvOUs3CnE4MmI1eTVFSm4yWEU5OVMycDZUZnJlSG1sUHpKNHNDQXdFQUFhTmdNRjR3Q3dZRFZSMFBCQVFEQWdTd01CTUcKQTFVZEpRUU1NQW9HQ0NzR0FRVUZCd01CTUJzR0ExVWRFUVFVTUJLQ0VHMTBiSE11WlhoaGJYQnNaUzVqYjIwdwpIUVlEVlIwT0JCWUVGRm1FTjBqRVFpckpYeGlLRHFlK2tTMVV3Q2gyTUEwR0NTcUdTSWIzRFFFQkN3VUFBNElCCkFRQ0NTVlluRVJPbHJpWDM2M0VtRzd1b091Nm54ajU1eWVmOXRKbnRubFVMVFZsMjlqc205Z3d5VnFUVCtUcXMKdzFPYW01TExmMEpjSWNRdmFUM203b0RpMElDUUo5eTlRQkNwMTh1TDBUeElDaFdVRTVnRUIxM3MyTzEwWWNFMQp1K2ozSzM0MStQNStoaEJpQnJ1d0dncStkVVRGRTZTYVVMY0xMVlB1RjdTeG1KbTRHK0Q0NVlqM1NDVy9aMzU2CkFXZzB4L0prZGFKSVVLVFJaUDVJTEZKQ1lJTUM3QWI1RmdWeGRCVW5kNWxheUZGb2NVMzRQaDlwZUxiYW00alYKdGt0SGNTSFJ6OERNTm1PNHpHTEZYNzlQR0lsaTZzTDU3V0N6bkw5RFFtajRyajFIS2tyeEdnMVExbUcwbDhOTQo5cXQyWEZNNUttWkVOb2E1MmFWSklHYWoKLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo= + name: envoy-gateway/tls-secret-1 + privateKey: '[redacted]' + matchTypedSubjectAltNames: + - distinct: false + exact: client1.example.com + name: DNS + - distinct: false + name: EMAIL + suffix: '@example.com' + - distinct: false + name: IP_ADDRESS + prefix: 192.168. + - distinct: false + exact: spiffe://example.com/client1 + name: URI + - distinct: false + exact: client1 + name: 1.3.6.1.4.1.311.20.2.3 + maxVersion: "1.3" + minVersion: "1.2" + requireClientCertificate: true + verifyCertificateHash: + - df6ff72fe9116521268f6f2dd4966f51df479883fe7037b39f75916ac3049d1a + verifyCertificateSpki: + - NvqYIYSbgK2vCJpQhObf77vv+bQWtc5ek5RIOwPiC9A= + readyListener: + address: 0.0.0.0 + ipFamily: IPv4 + path: /ready + port: 19003 + tcp: + - address: 0.0.0.0 + name: envoy-gateway/gateway-3/tls-1 + port: 6443 + routes: + - destination: + metadata: + kind: TCPRoute + name: tcproute-1 + namespace: envoy-gateway + name: tcproute/envoy-gateway/tcproute-1/rule/-1 + name: tcproute/envoy-gateway/tcproute-1 + tls: + terminate: + alpnProtocols: [] + caCertificate: + certificate: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURRekNDQWl1Z0F3SUJBZ0lCQVRBTkJna3Foa2lHOXcwQkFRc0ZBREJDTVJNd0VRWURWUVFLRXdwRmJuWnYKZVZCeWIzaDVNUkF3RGdZRFZRUUxFd2RIWVhSbGQyRjVNUmt3RndZRFZRUURFeEJGYm5admVTQkhZWFJsZDJGNQpJRU5CTUNBWERUSTBNRE14TURFMU16SXhOMW9ZRHpJeE1qUXdNekV3TVRZek1qRTNXakJDTVJNd0VRWURWUVFLCkV3cEZiblp2ZVZCeWIzaDVNUkF3RGdZRFZRUUxFd2RIWVhSbGQyRjVNUmt3RndZRFZRUURFeEJGYm5admVTQkgKWVhSbGQyRjVJRU5CTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUE3WkZtR0I0ZQptMUtkR0VvaEFaQmZxeWRBRUdMREhKMVl5ZkhXZGQrdkJBZXZkVzY0Ylp4M3BnZ0pPdGdDbmVQdUZkMDJyRFFTCmRsc0psWC82bUZ0b1FpbG82d3Z4RFNKUmZhVERidGZUancrN2s4eWZkL0pzbWgwUldHK1VleUk3TmE5c1hBejcKYjU3bXB4c0NvTm93emVLNUVUaU9HR05XUGNqRU5Ka1NuQmFyejVtdU4wMHhJWldCVSt5TjVQTEpOeFp2eHBaSgpPbC9TU0k4c25vMGUwUHhBbXAzZmU3UWFYaVpqL1RBR0pQR3VUSmtVeHJIcXlaR0p0WVV4c1M4QTBkVDF6QmpqCml6QTVEcCtiNXl6WW8yM0hoN0JncGJaN1g0Z3NEVGhGdXdDRDZmSHllcHV2MnpIUHF2U3NkcWcyaEFoRHA5MVIKenJuN2E5R3hHMlZTSXdJREFRQUJvMEl3UURBT0JnTlZIUThCQWY4RUJBTUNBUVl3RHdZRFZSMFRBUUgvQkFVdwpBd0VCL3pBZEJnTlZIUTRFRmdRVVVwUDFhWjFNMktJdVBQV3JOUERWMmM1Q25nb3dEUVlKS29aSWh2Y05BUUVMCkJRQURnZ0VCQUdTRWtBVnorWjBxUzRGbUEwcTRTQ3BJSXE2NGJzZEVqaVV6ZXY3cEsxTEVLMC9ZMjhRQlBpeFYKY1VYZmF4MThWUFI5cGxzMUpnWHRvOXFZK0MwaG5SWmljNjYxMVFUSmxXSzFwNmRpblEvZURkWUNCQytudjV4eApzc0FTd21wbEl4TXZqM1MxcUY2ZHI3c01JMlpWRDVIRWxUV2RPMTlVQkx5aGlLS1pXMkt4RHNZais1TlJ3R0ZlCkcrSnVEZ3E3bmpVTThtZHlZazBOZWhlZmRCVUVVVUNRdG53VXRXOTUvNDI5WHdxUVJPdVJEdGVHVDlrakQrWTUKZWE1bVc0bWZxTGV1R0pYWnM5YmRXaktLZExRUHJuOUlzaFB5c1dxejJIejhkUTFmN045L2c4VVdWU2pkNGN5eApTNUVBb2x6VnYweUI3d0hDV0NnZkcvY2tkT1RVTm5FPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== + name: envoy-gateway/target-gateway-4/ca.crt + certificates: + - certificate: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUR6akNDQXJhZ0F3SUJBZ0lVT0dKOUx1VGtKWkU0NmNVaUpGYmJ2bm10elFvd0RRWUpLb1pJaHZjTkFRRUwKQlFBd2J6RUxNQWtHQTFVRUJoTUNWVk14Q3pBSkJnTlZCQWdNQWxaQk1SRXdEd1lEVlFRSERBaFRiMjFsUTJsMAplVEVUTUJFR0ExVUVDZ3dLUlc1MmIzbFFjbTk0ZVRFUU1BNEdBMVVFQ3d3SFIyRjBaWGRoZVRFWk1CY0dBMVVFCkF3d1FiWFJzY3k1bGVHRnRjR3hsTG1OdmJUQWdGdzB5TkRBM01UWXlNalV4TWpOYUdBOHlNVEkwTURZeU1qSXkKTlRFeU0xb3diekVMTUFrR0ExVUVCaE1DVlZNeEN6QUpCZ05WQkFnTUFsWkJNUkV3RHdZRFZRUUhEQWhUYjIxbApRMmwwZVRFVE1CRUdBMVVFQ2d3S1JXNTJiM2xRY205NGVURVFNQTRHQTFVRUN3d0hSMkYwWlhkaGVURVpNQmNHCkExVUVBd3dRYlhSc2N5NWxlR0Z0Y0d4bExtTnZiVENDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0MKQVFvQ2dnRUJBS3kwZnp5NWFaVnRNajAxVWJPRGtsU1IxbTI1Mkt0QTJ2L2tmT05vNTZkNEJQbGdqVXdXUVZNUgpTclUxZzd4T2tWdjZiL0czdG5tQVhwWWY2VlIxODIyak96cCsxQ0c4ZWlGSEpjT2ZxV2lZMjh1NnFSV2VKUFZlCnpYdUFsMmd4cWJpTzZLdDZwbnI0aXFoVGhIK3ZqV3NXTnBDSVhrbDFydVlYbnhWLzRCOENxY1JJeTZHaEp6bloKRjR3NHJBNkRlRlJmZHl0MWd1bWtkN25PVVhYKzRZMzJrd0xGRU8zR3NnUTh1aVpEQmN1UEs5RjRHRDUydDZYTgowT2tlNTU0eEl2VldvZ1M1Vzl4UFIvcU5kQlpIQ1pid05jZzRRTVllbWZva0pkUXo4elVJcnZ6VUltM3ZvOUs3CnE4MmI1eTVFSm4yWEU5OVMycDZUZnJlSG1sUHpKNHNDQXdFQUFhTmdNRjR3Q3dZRFZSMFBCQVFEQWdTd01CTUcKQTFVZEpRUU1NQW9HQ0NzR0FRVUZCd01CTUJzR0ExVWRFUVFVTUJLQ0VHMTBiSE11WlhoaGJYQnNaUzVqYjIwdwpIUVlEVlIwT0JCWUVGRm1FTjBqRVFpckpYeGlLRHFlK2tTMVV3Q2gyTUEwR0NTcUdTSWIzRFFFQkN3VUFBNElCCkFRQ0NTVlluRVJPbHJpWDM2M0VtRzd1b091Nm54ajU1eWVmOXRKbnRubFVMVFZsMjlqc205Z3d5VnFUVCtUcXMKdzFPYW01TExmMEpjSWNRdmFUM203b0RpMElDUUo5eTlRQkNwMTh1TDBUeElDaFdVRTVnRUIxM3MyTzEwWWNFMQp1K2ozSzM0MStQNStoaEJpQnJ1d0dncStkVVRGRTZTYVVMY0xMVlB1RjdTeG1KbTRHK0Q0NVlqM1NDVy9aMzU2CkFXZzB4L0prZGFKSVVLVFJaUDVJTEZKQ1lJTUM3QWI1RmdWeGRCVW5kNWxheUZGb2NVMzRQaDlwZUxiYW00alYKdGt0SGNTSFJ6OERNTm1PNHpHTEZYNzlQR0lsaTZzTDU3V0N6bkw5RFFtajRyajFIS2tyeEdnMVExbUcwbDhOTQo5cXQyWEZNNUttWkVOb2E1MmFWSklHYWoKLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo= + name: envoy-gateway/tls-secret-1 + privateKey: '[redacted]' + matchTypedSubjectAltNames: + - distinct: false + exact: client2.example.org + name: DNS + - distinct: false + name: EMAIL + suffix: '@example.org' + - distinct: false + name: IP_ADDRESS + prefix: "10." + - distinct: false + exact: spiffe://example.com/client2 + name: URI + - distinct: false + exact: client2 + name: 1.3.6.1.4.1.311.20.2.3 + maxVersion: "1.3" + minVersion: "1.2" + requireClientCertificate: true + verifyCertificateHash: + - df6ff72fe9116521268f6f2dd4966f51df479883fe7037b39f75916ac3049d1a + verifyCertificateSpki: + - NvqYIYSbgK2vCJpQhObf77vv+bQWtc5ek5RIOwPiC9A= + tls: + alpnProtocols: [] + caCertificate: + certificate: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURRekNDQWl1Z0F3SUJBZ0lCQVRBTkJna3Foa2lHOXcwQkFRc0ZBREJDTVJNd0VRWURWUVFLRXdwRmJuWnYKZVZCeWIzaDVNUkF3RGdZRFZRUUxFd2RIWVhSbGQyRjVNUmt3RndZRFZRUURFeEJGYm5admVTQkhZWFJsZDJGNQpJRU5CTUNBWERUSTBNRE14TURFMU16SXhOMW9ZRHpJeE1qUXdNekV3TVRZek1qRTNXakJDTVJNd0VRWURWUVFLCkV3cEZiblp2ZVZCeWIzaDVNUkF3RGdZRFZRUUxFd2RIWVhSbGQyRjVNUmt3RndZRFZRUURFeEJGYm5admVTQkgKWVhSbGQyRjVJRU5CTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUE3WkZtR0I0ZQptMUtkR0VvaEFaQmZxeWRBRUdMREhKMVl5ZkhXZGQrdkJBZXZkVzY0Ylp4M3BnZ0pPdGdDbmVQdUZkMDJyRFFTCmRsc0psWC82bUZ0b1FpbG82d3Z4RFNKUmZhVERidGZUancrN2s4eWZkL0pzbWgwUldHK1VleUk3TmE5c1hBejcKYjU3bXB4c0NvTm93emVLNUVUaU9HR05XUGNqRU5Ka1NuQmFyejVtdU4wMHhJWldCVSt5TjVQTEpOeFp2eHBaSgpPbC9TU0k4c25vMGUwUHhBbXAzZmU3UWFYaVpqL1RBR0pQR3VUSmtVeHJIcXlaR0p0WVV4c1M4QTBkVDF6QmpqCml6QTVEcCtiNXl6WW8yM0hoN0JncGJaN1g0Z3NEVGhGdXdDRDZmSHllcHV2MnpIUHF2U3NkcWcyaEFoRHA5MVIKenJuN2E5R3hHMlZTSXdJREFRQUJvMEl3UURBT0JnTlZIUThCQWY4RUJBTUNBUVl3RHdZRFZSMFRBUUgvQkFVdwpBd0VCL3pBZEJnTlZIUTRFRmdRVVVwUDFhWjFNMktJdVBQV3JOUERWMmM1Q25nb3dEUVlKS29aSWh2Y05BUUVMCkJRQURnZ0VCQUdTRWtBVnorWjBxUzRGbUEwcTRTQ3BJSXE2NGJzZEVqaVV6ZXY3cEsxTEVLMC9ZMjhRQlBpeFYKY1VYZmF4MThWUFI5cGxzMUpnWHRvOXFZK0MwaG5SWmljNjYxMVFUSmxXSzFwNmRpblEvZURkWUNCQytudjV4eApzc0FTd21wbEl4TXZqM1MxcUY2ZHI3c01JMlpWRDVIRWxUV2RPMTlVQkx5aGlLS1pXMkt4RHNZais1TlJ3R0ZlCkcrSnVEZ3E3bmpVTThtZHlZazBOZWhlZmRCVUVVVUNRdG53VXRXOTUvNDI5WHdxUVJPdVJEdGVHVDlrakQrWTUKZWE1bVc0bWZxTGV1R0pYWnM5YmRXaktLZExRUHJuOUlzaFB5c1dxejJIejhkUTFmN045L2c4VVdWU2pkNGN5eApTNUVBb2x6VnYweUI3d0hDV0NnZkcvY2tkT1RVTm5FPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== + name: envoy-gateway/target-gateway-4/ca.crt + certificates: + - certificate: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUR6akNDQXJhZ0F3SUJBZ0lVT0dKOUx1VGtKWkU0NmNVaUpGYmJ2bm10elFvd0RRWUpLb1pJaHZjTkFRRUwKQlFBd2J6RUxNQWtHQTFVRUJoTUNWVk14Q3pBSkJnTlZCQWdNQWxaQk1SRXdEd1lEVlFRSERBaFRiMjFsUTJsMAplVEVUTUJFR0ExVUVDZ3dLUlc1MmIzbFFjbTk0ZVRFUU1BNEdBMVVFQ3d3SFIyRjBaWGRoZVRFWk1CY0dBMVVFCkF3d1FiWFJzY3k1bGVHRnRjR3hsTG1OdmJUQWdGdzB5TkRBM01UWXlNalV4TWpOYUdBOHlNVEkwTURZeU1qSXkKTlRFeU0xb3diekVMTUFrR0ExVUVCaE1DVlZNeEN6QUpCZ05WQkFnTUFsWkJNUkV3RHdZRFZRUUhEQWhUYjIxbApRMmwwZVRFVE1CRUdBMVVFQ2d3S1JXNTJiM2xRY205NGVURVFNQTRHQTFVRUN3d0hSMkYwWlhkaGVURVpNQmNHCkExVUVBd3dRYlhSc2N5NWxlR0Z0Y0d4bExtTnZiVENDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0MKQVFvQ2dnRUJBS3kwZnp5NWFaVnRNajAxVWJPRGtsU1IxbTI1Mkt0QTJ2L2tmT05vNTZkNEJQbGdqVXdXUVZNUgpTclUxZzd4T2tWdjZiL0czdG5tQVhwWWY2VlIxODIyak96cCsxQ0c4ZWlGSEpjT2ZxV2lZMjh1NnFSV2VKUFZlCnpYdUFsMmd4cWJpTzZLdDZwbnI0aXFoVGhIK3ZqV3NXTnBDSVhrbDFydVlYbnhWLzRCOENxY1JJeTZHaEp6bloKRjR3NHJBNkRlRlJmZHl0MWd1bWtkN25PVVhYKzRZMzJrd0xGRU8zR3NnUTh1aVpEQmN1UEs5RjRHRDUydDZYTgowT2tlNTU0eEl2VldvZ1M1Vzl4UFIvcU5kQlpIQ1pid05jZzRRTVllbWZva0pkUXo4elVJcnZ6VUltM3ZvOUs3CnE4MmI1eTVFSm4yWEU5OVMycDZUZnJlSG1sUHpKNHNDQXdFQUFhTmdNRjR3Q3dZRFZSMFBCQVFEQWdTd01CTUcKQTFVZEpRUU1NQW9HQ0NzR0FRVUZCd01CTUJzR0ExVWRFUVFVTUJLQ0VHMTBiSE11WlhoaGJYQnNaUzVqYjIwdwpIUVlEVlIwT0JCWUVGRm1FTjBqRVFpckpYeGlLRHFlK2tTMVV3Q2gyTUEwR0NTcUdTSWIzRFFFQkN3VUFBNElCCkFRQ0NTVlluRVJPbHJpWDM2M0VtRzd1b091Nm54ajU1eWVmOXRKbnRubFVMVFZsMjlqc205Z3d5VnFUVCtUcXMKdzFPYW01TExmMEpjSWNRdmFUM203b0RpMElDUUo5eTlRQkNwMTh1TDBUeElDaFdVRTVnRUIxM3MyTzEwWWNFMQp1K2ozSzM0MStQNStoaEJpQnJ1d0dncStkVVRGRTZTYVVMY0xMVlB1RjdTeG1KbTRHK0Q0NVlqM1NDVy9aMzU2CkFXZzB4L0prZGFKSVVLVFJaUDVJTEZKQ1lJTUM3QWI1RmdWeGRCVW5kNWxheUZGb2NVMzRQaDlwZUxiYW00alYKdGt0SGNTSFJ6OERNTm1PNHpHTEZYNzlQR0lsaTZzTDU3V0N6bkw5RFFtajRyajFIS2tyeEdnMVExbUcwbDhOTQo5cXQyWEZNNUttWkVOb2E1MmFWSklHYWoKLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo= + name: envoy-gateway/tls-secret-1 + privateKey: '[redacted]' + matchTypedSubjectAltNames: + - distinct: false + exact: client2.example.org + name: DNS + - distinct: false + name: EMAIL + suffix: '@example.org' + - distinct: false + name: IP_ADDRESS + prefix: "10." + - distinct: false + exact: spiffe://example.com/client2 + name: URI + - distinct: false + exact: client2 + name: 1.3.6.1.4.1.311.20.2.3 + maxVersion: "1.3" + minVersion: "1.2" + requireClientCertificate: true + verifyCertificateHash: + - df6ff72fe9116521268f6f2dd4966f51df479883fe7037b39f75916ac3049d1a + verifyCertificateSpki: + - NvqYIYSbgK2vCJpQhObf77vv+bQWtc5ek5RIOwPiC9A= diff --git a/internal/ir/xds.go b/internal/ir/xds.go index cd46ebb1e9..6a51beee5d 100644 --- a/internal/ir/xds.go +++ b/internal/ir/xds.go @@ -411,6 +411,12 @@ type TLSConfig struct { CACertificate *TLSCACertificate `json:"caCertificate,omitempty" yaml:"caCertificate,omitempty"` // RequireClientCertificate to enforce client certificate RequireClientCertificate bool `json:"requireClientCertificate,omitempty" yaml:"requireClientCertificate,omitempty"` + // A list of allowed base64-encoded SHA-256 hashes of the DER-encoded Subject Public Key Information (SPKI) + VerifyCertificateSpki []string `json:"verifyCertificateSpki,omitempty" yaml:"verifyCertificateSpki,omitempty"` + // A list of allowed hex-encoded SHA-256 hashes of the DER-encoded certificate + VerifyCertificateHash []string `json:"verifyCertificateHash,omitempty" yaml:"verifyCertificateHash,omitempty"` + // A list of Subject Alternative name matchers + MatchTypedSubjectAltNames []*StringMatch `json:"matchTypedSubjectAltNames,omitempty" yaml:"matchTypedSubjectAltNames,omitempty"` // MinVersion defines the minimal version of the TLS protocol supported by this listener. MinVersion *TLSVersion `json:"minVersion,omitempty" yaml:"version,omitempty"` // MaxVersion defines the maximal version of the TLS protocol supported by this listener. diff --git a/internal/ir/zz_generated.deepcopy.go b/internal/ir/zz_generated.deepcopy.go index 238501b8d2..ab27afd5ac 100644 --- a/internal/ir/zz_generated.deepcopy.go +++ b/internal/ir/zz_generated.deepcopy.go @@ -3651,6 +3651,27 @@ func (in *TLSConfig) DeepCopyInto(out *TLSConfig) { *out = new(TLSCACertificate) (*in).DeepCopyInto(*out) } + if in.VerifyCertificateSpki != nil { + in, out := &in.VerifyCertificateSpki, &out.VerifyCertificateSpki + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.VerifyCertificateHash != nil { + in, out := &in.VerifyCertificateHash, &out.VerifyCertificateHash + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.MatchTypedSubjectAltNames != nil { + in, out := &in.MatchTypedSubjectAltNames, &out.MatchTypedSubjectAltNames + *out = make([]*StringMatch, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(StringMatch) + (*in).DeepCopyInto(*out) + } + } + } if in.MinVersion != nil { in, out := &in.MinVersion, &out.MinVersion *out = new(TLSVersion) diff --git a/internal/xds/translator/listener.go b/internal/xds/translator/listener.go index fa1a7620df..05a2a99e82 100644 --- a/internal/xds/translator/listener.go +++ b/internal/xds/translator/listener.go @@ -723,12 +723,7 @@ func buildDownstreamQUICTransportSocket(tlsConfig *ir.TLSConfig) (*corev3.Transp if tlsConfig.CACertificate != nil { tlsCtx.DownstreamTlsContext.RequireClientCertificate = &wrapperspb.BoolValue{Value: true} - tlsCtx.DownstreamTlsContext.CommonTlsContext.ValidationContextType = &tlsv3.CommonTlsContext_ValidationContextSdsSecretConfig{ - ValidationContextSdsSecretConfig: &tlsv3.SdsSecretConfig{ - Name: tlsConfig.CACertificate.Name, - SdsConfig: makeConfigSource(), - }, - } + setTLSValidationContext(tlsConfig, tlsCtx.DownstreamTlsContext.CommonTlsContext) } setDownstreamTLSSessionSettings(tlsConfig, tlsCtx.DownstreamTlsContext) @@ -765,12 +760,7 @@ func buildXdsDownstreamTLSSocket(tlsConfig *ir.TLSConfig) (*corev3.TransportSock if tlsConfig.CACertificate != nil { tlsCtx.RequireClientCertificate = &wrapperspb.BoolValue{Value: tlsConfig.RequireClientCertificate} - tlsCtx.CommonTlsContext.ValidationContextType = &tlsv3.CommonTlsContext_ValidationContextSdsSecretConfig{ - ValidationContextSdsSecretConfig: &tlsv3.SdsSecretConfig{ - Name: tlsConfig.CACertificate.Name, - SdsConfig: makeConfigSource(), - }, - } + setTLSValidationContext(tlsConfig, tlsCtx.CommonTlsContext) } setDownstreamTLSSessionSettings(tlsConfig, tlsCtx) @@ -788,6 +778,51 @@ func buildXdsDownstreamTLSSocket(tlsConfig *ir.TLSConfig) (*corev3.TransportSock }, nil } +func setTLSValidationContext(tlsConfig *ir.TLSConfig, tlsCtx *tlsv3.CommonTlsContext) { + sdsSecretConfig := &tlsv3.SdsSecretConfig{ + Name: tlsConfig.CACertificate.Name, + SdsConfig: makeConfigSource(), + } + if len(tlsConfig.VerifyCertificateSpki) > 0 || len(tlsConfig.VerifyCertificateHash) > 0 || len(tlsConfig.MatchTypedSubjectAltNames) > 0 { + validationContext := &tlsv3.CertificateValidationContext{} + validationContext.VerifyCertificateSpki = append(validationContext.VerifyCertificateSpki, tlsConfig.VerifyCertificateSpki...) + validationContext.VerifyCertificateHash = append(validationContext.VerifyCertificateHash, tlsConfig.VerifyCertificateHash...) + for _, match := range tlsConfig.MatchTypedSubjectAltNames { + sanType := tlsv3.SubjectAltNameMatcher_OTHER_NAME + oid := "" + switch match.Name { + case "": + sanType = tlsv3.SubjectAltNameMatcher_SAN_TYPE_UNSPECIFIED + case "EMAIL": + sanType = tlsv3.SubjectAltNameMatcher_EMAIL + case "DNS": + sanType = tlsv3.SubjectAltNameMatcher_DNS + case "URI": + sanType = tlsv3.SubjectAltNameMatcher_URI + case "IP_ADDRESS": + sanType = tlsv3.SubjectAltNameMatcher_IP_ADDRESS + default: + oid = match.Name + } + validationContext.MatchTypedSubjectAltNames = append(validationContext.MatchTypedSubjectAltNames, &tlsv3.SubjectAltNameMatcher{ + SanType: sanType, + Matcher: buildXdsStringMatcher(match), + Oid: oid, + }) + } + tlsCtx.ValidationContextType = &tlsv3.CommonTlsContext_CombinedValidationContext{ + CombinedValidationContext: &tlsv3.CommonTlsContext_CombinedCertificateValidationContext{ + DefaultValidationContext: validationContext, + ValidationContextSdsSecretConfig: sdsSecretConfig, + }, + } + } else { + tlsCtx.ValidationContextType = &tlsv3.CommonTlsContext_ValidationContextSdsSecretConfig{ + ValidationContextSdsSecretConfig: sdsSecretConfig, + } + } +} + func setDownstreamTLSSessionSettings(tlsConfig *ir.TLSConfig, tlsCtx *tlsv3.DownstreamTlsContext) { if !tlsConfig.StatefulSessionResumption { tlsCtx.DisableStatefulSessionResumption = true diff --git a/internal/xds/translator/testdata/in/xds-ir/mutual-tls-san.yaml b/internal/xds/translator/testdata/in/xds-ir/mutual-tls-san.yaml new file mode 100644 index 0000000000..81f750353f --- /dev/null +++ b/internal/xds/translator/testdata/in/xds-ir/mutual-tls-san.yaml @@ -0,0 +1,123 @@ +http: +- name: "first-listener" + address: "::" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + tls: + alpnProtocols: + - h2 + - http/1.1 + certificates: + - name: secret-1 + # byte slice representation of "key-data" + certificate: [99, 101, 114, 116, 45, 100, 97, 116, 97] + # byte slice representation of "key-data" + privateKey: [107, 101, 121, 45, 100, 97, 116, 97] + - name: secret-2 + certificate: [99, 101, 114, 116, 45, 100, 97, 116, 97] + privateKey: [107, 101, 121, 45, 100, 97, 116, 97] + caCertificate: + name: ca-cert + certificate: [99, 101, 114, 116, 45, 100, 97, 116, 97] + requireClientCertificate: true + matchTypedSubjectAltNames: + - distinct: false + exact: client1.example.com + name: DNS + - distinct: false + name: EMAIL + suffix: '@example.com' + - distinct: false + name: IP_ADDRESS + prefix: 192.168. + - distinct: false + exact: spiffe://example.com/client1 + name: URI + - distinct: false + exact: client1 + name: 1.3.6.1.4.1.311.20.2.3 + verifyCertificateHash: + - df6ff72fe9116521268f6f2dd4966f51df479883fe7037b39f75916ac3049d1a + verifyCertificateSpki: + - NvqYIYSbgK2vCJpQhObf77vv+bQWtc5ek5RIOwPiC9A= + routes: + - name: "first-route" + destination: + name: "first-route-dest" +tcp: +- name: "second-listener" + address: "::" + port: 10081 + tls: + alpnProtocols: [] + certificates: + - name: secret-3 + # byte slice representation of "key-data" + certificate: [99, 101, 114, 116, 45, 100, 97, 116, 97] + # byte slice representation of "key-data" + privateKey: [107, 101, 121, 45, 100, 97, 116, 97] + caCertificate: + name: ca-cert-2 + certificate: [99, 101, 114, 116, 45, 100, 97, 116, 97] + requireClientCertificate: true + matchTypedSubjectAltNames: + - distinct: false + exact: client2.example.org + name: DNS + - distinct: false + name: EMAIL + suffix: '@example.org' + - distinct: false + name: IP_ADDRESS + prefix: "10." + - distinct: false + exact: spiffe://example.com/client2 + name: URI + - distinct: false + exact: client2 + name: 1.3.6.1.4.1.311.20.2.3 + verifyCertificateHash: + - df6ff72fe9116521268f6f2dd4966f51df479883fe7037b39f75916ac3049d1a + verifyCertificateSpki: + - NvqYIYSbgK2vCJpQhObf77vv+bQWtc5ek5RIOwPiC9A= + routes: + - name: "tls-route-terminate" + tls: + terminate: + alpnProtocols: [] + certificates: + - name: secret-3 + # byte slice representation of "key-data" + certificate: [99, 101, 114, 116, 45, 100, 97, 116, 97] + # byte slice representation of "key-data" + privateKey: [107, 101, 121, 45, 100, 97, 116, 97] + caCertificate: + name: ca-cert-2 + certificate: [99, 101, 114, 116, 45, 100, 97, 116, 97] + requireClientCertificate: true + matchTypedSubjectAltNames: + - distinct: false + exact: client2.example.org + name: DNS + - distinct: false + name: EMAIL + suffix: '@example.org' + - distinct: false + name: IP_ADDRESS + prefix: "10." + - distinct: false + exact: spiffe://example.com/client2 + name: URI + - distinct: false + exact: client2 + name: 1.3.6.1.4.1.311.20.2.3 + verifyCertificateHash: + - df6ff72fe9116521268f6f2dd4966f51df479883fe7037b39f75916ac3049d1a + verifyCertificateSpki: + - NvqYIYSbgK2vCJpQhObf77vv+bQWtc5ek5RIOwPiC9A= + destination: + name: "tls-terminate-dest" diff --git a/internal/xds/translator/testdata/out/xds-ir/mutual-tls-san.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/mutual-tls-san.clusters.yaml new file mode 100644 index 0000000000..4521d562ef --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/mutual-tls-san.clusters.yaml @@ -0,0 +1,34 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_PREFERRED + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + ignoreHealthOnHostRemoval: true + lbPolicy: LEAST_REQUEST + name: first-route-dest + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_PREFERRED + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: tls-terminate-dest + ignoreHealthOnHostRemoval: true + lbPolicy: LEAST_REQUEST + name: tls-terminate-dest + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/mutual-tls-san.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/mutual-tls-san.endpoints.yaml new file mode 100644 index 0000000000..d4b8322ede --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/mutual-tls-san.endpoints.yaml @@ -0,0 +1,2 @@ +- clusterName: first-route-dest +- clusterName: tls-terminate-dest diff --git a/internal/xds/translator/testdata/out/xds-ir/mutual-tls-san.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/mutual-tls-san.listeners.yaml new file mode 100644 index 0000000000..93319d4945 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/mutual-tls-san.listeners.yaml @@ -0,0 +1,137 @@ +- address: + socketAddress: + address: '::' + portValue: 10080 + filterChains: + - filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: https-10080 + useRemoteAddress: true + name: first-listener + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext + commonTlsContext: + alpnProtocols: + - h2 + - http/1.1 + combinedValidationContext: + defaultValidationContext: + matchTypedSubjectAltNames: + - matcher: + exact: client1.example.com + sanType: DNS + - matcher: + suffix: '@example.com' + sanType: EMAIL + - matcher: + prefix: 192.168. + sanType: IP_ADDRESS + - matcher: + exact: spiffe://example.com/client1 + sanType: URI + - matcher: + exact: client1 + oid: 1.3.6.1.4.1.311.20.2.3 + sanType: OTHER_NAME + verifyCertificateHash: + - df6ff72fe9116521268f6f2dd4966f51df479883fe7037b39f75916ac3049d1a + verifyCertificateSpki: + - NvqYIYSbgK2vCJpQhObf77vv+bQWtc5ek5RIOwPiC9A= + validationContextSdsSecretConfig: + name: ca-cert + sdsConfig: + ads: {} + resourceApiVersion: V3 + tlsCertificateSdsSecretConfigs: + - name: secret-1 + sdsConfig: + ads: {} + resourceApiVersion: V3 + - name: secret-2 + sdsConfig: + ads: {} + resourceApiVersion: V3 + disableStatefulSessionResumption: true + disableStatelessSessionResumption: true + requireClientCertificate: true + name: first-listener + perConnectionBufferLimitBytes: 32768 +- address: + socketAddress: + address: '::' + portValue: 10081 + filterChains: + - filters: + - name: envoy.filters.network.tcp_proxy + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy + cluster: tls-terminate-dest + statPrefix: tls-terminate-10081 + name: tls-route-terminate + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext + commonTlsContext: + combinedValidationContext: + defaultValidationContext: + matchTypedSubjectAltNames: + - matcher: + exact: client2.example.org + sanType: DNS + - matcher: + suffix: '@example.org' + sanType: EMAIL + - matcher: + prefix: "10." + sanType: IP_ADDRESS + - matcher: + exact: spiffe://example.com/client2 + sanType: URI + - matcher: + exact: client2 + oid: 1.3.6.1.4.1.311.20.2.3 + sanType: OTHER_NAME + verifyCertificateHash: + - df6ff72fe9116521268f6f2dd4966f51df479883fe7037b39f75916ac3049d1a + verifyCertificateSpki: + - NvqYIYSbgK2vCJpQhObf77vv+bQWtc5ek5RIOwPiC9A= + validationContextSdsSecretConfig: + name: ca-cert-2 + sdsConfig: + ads: {} + resourceApiVersion: V3 + tlsCertificateSdsSecretConfigs: + - name: secret-3 + sdsConfig: + ads: {} + resourceApiVersion: V3 + disableStatefulSessionResumption: true + disableStatelessSessionResumption: true + requireClientCertificate: true + name: second-listener + perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/mutual-tls-san.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/mutual-tls-san.routes.yaml new file mode 100644 index 0000000000..9e64eb8111 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/mutual-tls-san.routes.yaml @@ -0,0 +1,14 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - "" + name: first-listener/ + routes: + - match: + prefix: / + name: first-route + route: + cluster: first-route-dest + upgradeConfigs: + - upgradeType: websocket diff --git a/internal/xds/translator/testdata/out/xds-ir/mutual-tls-san.secrets.yaml b/internal/xds/translator/testdata/out/xds-ir/mutual-tls-san.secrets.yaml new file mode 100644 index 0000000000..a9683e8d03 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/mutual-tls-san.secrets.yaml @@ -0,0 +1,26 @@ +- name: secret-1 + tlsCertificate: + certificateChain: + inlineBytes: Y2VydC1kYXRh + privateKey: + inlineBytes: a2V5LWRhdGE= +- name: secret-2 + tlsCertificate: + certificateChain: + inlineBytes: Y2VydC1kYXRh + privateKey: + inlineBytes: a2V5LWRhdGE= +- name: ca-cert + validationContext: + trustedCa: + inlineBytes: Y2VydC1kYXRh +- name: secret-3 + tlsCertificate: + certificateChain: + inlineBytes: Y2VydC1kYXRh + privateKey: + inlineBytes: a2V5LWRhdGE= +- name: ca-cert-2 + validationContext: + trustedCa: + inlineBytes: Y2VydC1kYXRh diff --git a/release-notes/current.yaml b/release-notes/current.yaml index fd913d8d84..3974f82430 100644 --- a/release-notes/current.yaml +++ b/release-notes/current.yaml @@ -27,8 +27,10 @@ new features: | Added support for configuring the cache sync period for K8s provider. Added support for fallback to first key when load ca certificate from Secret or ConfigMap. Added support for configuring user provided name to generated HorizontalPodAutoscaler and PodDisruptionBudget resources. + Added support for client certificate validation (SPKI, hash, SAN) in ClientTrafficPolicy. Added support for OIDC RP initialized logout. If the end session endpoint is explicitly specified or discovered from the issuer's well-known url, the end session endpoint will be invoked when the user logs out. + bug fixes: | Handle integer zone annotation values Fixed issue where WASM cache init failure caused routes with WASM-less EnvoyExtensionPolicies to have 500 direct responses. diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md index 0cd5ba11b0..4e1169bdef 100644 --- a/site/content/en/latest/api/extension_types.md +++ b/site/content/en/latest/api/extension_types.md @@ -793,6 +793,9 @@ _Appears in:_ | --- | --- | --- | --- | --- | | `optional` | _boolean_ | false | | Optional set to true accepts connections even when a client doesn't present a certificate.
Defaults to false, which rejects connections without a valid client certificate. | | `caCertificateRefs` | _[SecretObjectReference](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1.SecretObjectReference) array_ | false | | CACertificateRefs contains one or more references to
Kubernetes objects that contain TLS certificates of
the Certificate Authorities that can be used
as a trust anchor to validate the certificates presented by the client.
A single reference to a Kubernetes ConfigMap or a Kubernetes Secret,
with the CA certificate in a key named `ca.crt` is currently supported.
References to a resource in different namespace are invalid UNLESS there
is a ReferenceGrant in the target namespace that allows the certificate
to be attached. | +| `spkiHashes` | _string array_ | false | | An optional list of base64-encoded SHA-256 hashes. If specified, Envoy will
verify that the SHA-256 of the DER-encoded Subject Public Key Information
(SPKI) of the presented certificate matches one of the specified values. | +| `certificateHashes` | _string array_ | false | | An optional list of hex-encoded SHA-256 hashes. If specified, Envoy will
verify that the SHA-256 of the DER-encoded presented certificate matches
one of the specified values. | +| `subjectAltNames` | _[SubjectAltNames](#subjectaltnames)_ | false | | An optional list of Subject Alternative name matchers. If specified, Envoy
will verify that the Subject Alternative Name of the presented certificate
matches one of the specified matchers | #### ClusterSettings @@ -3360,6 +3363,22 @@ _Appears in:_ +#### OtherSANMatch + + + + + +_Appears in:_ +- [SubjectAltNames](#subjectaltnames) + +| Field | Type | Required | Default | Description | +| --- | --- | --- | --- | --- | +| `oid` | _string_ | true | | OID Value | +| `type` | _[StringMatchType](#stringmatchtype)_ | false | Exact | Type specifies how to match against a string. | +| `value` | _string_ | true | | Value specifies the string value that the match must have. | + + #### PassiveHealthCheck @@ -4610,7 +4629,9 @@ that need to match against a string. _Appears in:_ - [OIDCDenyRedirectHeader](#oidcdenyredirectheader) +- [OtherSANMatch](#othersanmatch) - [ProxyMetrics](#proxymetrics) +- [SubjectAltNames](#subjectaltnames) | Field | Type | Required | Default | Description | | --- | --- | --- | --- | --- | @@ -4627,6 +4648,7 @@ Valid MatchType values are "Exact", "Prefix", "Suffix", "RegularExpression". _Appears in:_ - [OIDCDenyRedirectHeader](#oidcdenyredirectheader) +- [OtherSANMatch](#othersanmatch) - [StringMatch](#stringmatch) | Value | Description | @@ -4637,6 +4659,24 @@ _Appears in:_ | `RegularExpression` | StringMatchRegularExpression :The input string must match the regular expression
specified in the match value.
The regex string must adhere to the syntax documented in
https://github.com/google/re2/wiki/Syntax.
| +#### SubjectAltNames + + + + + +_Appears in:_ +- [ClientValidationContext](#clientvalidationcontext) + +| Field | Type | Required | Default | Description | +| --- | --- | --- | --- | --- | +| `dnsNames` | _[StringMatch](#stringmatch) array_ | false | | DNS names matchers | +| `emailAddresses` | _[StringMatch](#stringmatch) array_ | false | | Email addresses matchers | +| `ipAddresses` | _[StringMatch](#stringmatch) array_ | false | | IP addresses matchers | +| `uris` | _[StringMatch](#stringmatch) array_ | false | | URIs matchers | +| `otherNames` | _[OtherSANMatch](#othersanmatch) array_ | false | | Other names matchers | + + #### TCPActiveHealthChecker diff --git a/test/helm/gateway-crds-helm/all.out.yaml b/test/helm/gateway-crds-helm/all.out.yaml index cab95036d5..cffd0e7583 100644 --- a/test/helm/gateway-crds-helm/all.out.yaml +++ b/test/helm/gateway-crds-helm/all.out.yaml @@ -20812,11 +20812,174 @@ spec: type: object maxItems: 8 type: array + certificateHashes: + description: |- + An optional list of hex-encoded SHA-256 hashes. If specified, Envoy will + verify that the SHA-256 of the DER-encoded presented certificate matches + one of the specified values. + items: + type: string + type: array optional: description: |- Optional set to true accepts connections even when a client doesn't present a certificate. Defaults to false, which rejects connections without a valid client certificate. type: boolean + spkiHashes: + description: |- + An optional list of base64-encoded SHA-256 hashes. If specified, Envoy will + verify that the SHA-256 of the DER-encoded Subject Public Key Information + (SPKI) of the presented certificate matches one of the specified values. + items: + type: string + type: array + subjectAltNames: + description: |- + An optional list of Subject Alternative name matchers. If specified, Envoy + will verify that the Subject Alternative Name of the presented certificate + matches one of the specified matchers + properties: + dnsNames: + description: DNS names matchers + items: + description: |- + StringMatch defines how to match any strings. + This is a general purpose match condition that can be used by other EG APIs + that need to match against a string. + properties: + type: + default: Exact + description: Type specifies how to match against + a string. + enum: + - Exact + - Prefix + - Suffix + - RegularExpression + type: string + value: + description: Value specifies the string value that + the match must have. + maxLength: 1024 + minLength: 1 + type: string + required: + - value + type: object + type: array + emailAddresses: + description: Email addresses matchers + items: + description: |- + StringMatch defines how to match any strings. + This is a general purpose match condition that can be used by other EG APIs + that need to match against a string. + properties: + type: + default: Exact + description: Type specifies how to match against + a string. + enum: + - Exact + - Prefix + - Suffix + - RegularExpression + type: string + value: + description: Value specifies the string value that + the match must have. + maxLength: 1024 + minLength: 1 + type: string + required: + - value + type: object + type: array + ipAddresses: + description: IP addresses matchers + items: + description: |- + StringMatch defines how to match any strings. + This is a general purpose match condition that can be used by other EG APIs + that need to match against a string. + properties: + type: + default: Exact + description: Type specifies how to match against + a string. + enum: + - Exact + - Prefix + - Suffix + - RegularExpression + type: string + value: + description: Value specifies the string value that + the match must have. + maxLength: 1024 + minLength: 1 + type: string + required: + - value + type: object + type: array + otherNames: + description: Other names matchers + items: + properties: + oid: + description: OID Value + type: string + type: + default: Exact + description: Type specifies how to match against + a string. + enum: + - Exact + - Prefix + - Suffix + - RegularExpression + type: string + value: + description: Value specifies the string value that + the match must have. + maxLength: 1024 + minLength: 1 + type: string + required: + - oid + - value + type: object + type: array + uris: + description: URIs matchers + items: + description: |- + StringMatch defines how to match any strings. + This is a general purpose match condition that can be used by other EG APIs + that need to match against a string. + properties: + type: + default: Exact + description: Type specifies how to match against + a string. + enum: + - Exact + - Prefix + - Suffix + - RegularExpression + type: string + value: + description: Value specifies the string value that + the match must have. + maxLength: 1024 + minLength: 1 + type: string + required: + - value + type: object + type: array + type: object type: object ecdhCurves: description: |- diff --git a/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml b/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml index 18a2009c50..db66e42c18 100644 --- a/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml +++ b/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml @@ -3500,11 +3500,174 @@ spec: type: object maxItems: 8 type: array + certificateHashes: + description: |- + An optional list of hex-encoded SHA-256 hashes. If specified, Envoy will + verify that the SHA-256 of the DER-encoded presented certificate matches + one of the specified values. + items: + type: string + type: array optional: description: |- Optional set to true accepts connections even when a client doesn't present a certificate. Defaults to false, which rejects connections without a valid client certificate. type: boolean + spkiHashes: + description: |- + An optional list of base64-encoded SHA-256 hashes. If specified, Envoy will + verify that the SHA-256 of the DER-encoded Subject Public Key Information + (SPKI) of the presented certificate matches one of the specified values. + items: + type: string + type: array + subjectAltNames: + description: |- + An optional list of Subject Alternative name matchers. If specified, Envoy + will verify that the Subject Alternative Name of the presented certificate + matches one of the specified matchers + properties: + dnsNames: + description: DNS names matchers + items: + description: |- + StringMatch defines how to match any strings. + This is a general purpose match condition that can be used by other EG APIs + that need to match against a string. + properties: + type: + default: Exact + description: Type specifies how to match against + a string. + enum: + - Exact + - Prefix + - Suffix + - RegularExpression + type: string + value: + description: Value specifies the string value that + the match must have. + maxLength: 1024 + minLength: 1 + type: string + required: + - value + type: object + type: array + emailAddresses: + description: Email addresses matchers + items: + description: |- + StringMatch defines how to match any strings. + This is a general purpose match condition that can be used by other EG APIs + that need to match against a string. + properties: + type: + default: Exact + description: Type specifies how to match against + a string. + enum: + - Exact + - Prefix + - Suffix + - RegularExpression + type: string + value: + description: Value specifies the string value that + the match must have. + maxLength: 1024 + minLength: 1 + type: string + required: + - value + type: object + type: array + ipAddresses: + description: IP addresses matchers + items: + description: |- + StringMatch defines how to match any strings. + This is a general purpose match condition that can be used by other EG APIs + that need to match against a string. + properties: + type: + default: Exact + description: Type specifies how to match against + a string. + enum: + - Exact + - Prefix + - Suffix + - RegularExpression + type: string + value: + description: Value specifies the string value that + the match must have. + maxLength: 1024 + minLength: 1 + type: string + required: + - value + type: object + type: array + otherNames: + description: Other names matchers + items: + properties: + oid: + description: OID Value + type: string + type: + default: Exact + description: Type specifies how to match against + a string. + enum: + - Exact + - Prefix + - Suffix + - RegularExpression + type: string + value: + description: Value specifies the string value that + the match must have. + maxLength: 1024 + minLength: 1 + type: string + required: + - oid + - value + type: object + type: array + uris: + description: URIs matchers + items: + description: |- + StringMatch defines how to match any strings. + This is a general purpose match condition that can be used by other EG APIs + that need to match against a string. + properties: + type: + default: Exact + description: Type specifies how to match against + a string. + enum: + - Exact + - Prefix + - Suffix + - RegularExpression + type: string + value: + description: Value specifies the string value that + the match must have. + maxLength: 1024 + minLength: 1 + type: string + required: + - value + type: object + type: array + type: object type: object ecdhCurves: description: |-