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: |-