Repository: envoyproxy/gateway

Issue:
Fail: TestE2E/EnvoyGatewayCustomSecurityContextUserid
```console
    http.go:251: 2025-06-22T13:53:59.313884692Z: Request passed
    httproute_with_custom_security_context_userid.go:92: 
        	Error Trace:	/home/runner/work/gateway/gateway/test/e2e/tests/httproute_with_custom_security_context_userid.go:92
        	            				/home/runner/work/gateway/gateway/test/e2e/tests/httproute_with_custom_security_context_userid.go:59
        	Error:      	Received unexpected error:
        	            	Operation cannot be fulfilled on deployments.apps "envoy-gateway": the object has been modified; please apply your changes to the latest version and try again
        	Test:       	TestE2E/EnvoyGatewayCustomSecurityContextUserid/route_with_custom_security_context_user_id
```

https://github.com/envoyproxy/gateway/actions/runs/15806490442/job/44553497748

Hints from issue comments:
@zirain: cc @zhaohuabing @zirain: https://github.com/envoyproxy/gateway/actions/runs/15892721341/job/44818696349

Solution patch:
diff --git a/.github/workflows/build_and_test.yaml b/.github/workflows/build_and_test.yaml
index f6256a5a5..6c9debaca 100644
--- a/.github/workflows/build_and_test.yaml
+++ b/.github/workflows/build_and_test.yaml
@@ -86,20 +86,20 @@ jobs:
       fail-fast: false
       matrix:
         target:
-        - version: v1.30.10
+        - version: v1.30.13
           ipFamily: ipv4
           profile: default
-        - version: v1.31.6
+        - version: v1.31.9
           ipFamily: ipv4
           profile: default
-        - version: v1.32.3
+        - version: v1.32.5
           ipFamily: ipv6  # only run ipv6 test on this version to save time
           profile: default
         # TODO: this's IPv4 first, need a way to test IPv6 first.
-        - version: v1.33.0
+        - version: v1.33.1
           ipFamily: dual  # only run dual test on latest version to save time
           profile: default
-        - version: v1.33.0
+        - version: v1.33.1
           ipFamily: dual  # only run dual test on latest version to save time
           profile: gateway-namespace-mode
     steps:
@@ -133,20 +133,20 @@ jobs:
       fail-fast: false
       matrix:
         target:
-        - version: v1.30.10
+        - version: v1.30.13
           ipFamily: ipv4
           profile: default
-        - version: v1.31.6
+        - version: v1.31.9
           ipFamily: ipv4
           profile: default
-        - version: v1.32.3
+        - version: v1.32.5
           ipFamily: ipv6  # only run ipv6 test on this version to save time
           profile: default
         # TODO: this's IPv4 first, need a way to test IPv6 first.
-        - version: v1.33.0
+        - version: v1.33.1
           ipFamily: dual  # only run dual test on latest version to save time
           profile: default
-        - version: v1.33.0
+        - version: v1.33.1
           ipFamily: dual  # only run dual test on latest version to save time
           profile: gateway-namespace-mode
     steps:
@@ -177,6 +177,8 @@ jobs:
         # This is not the limit of Envoy Gateway,
         # but the limit of running e2e tests in github CI.
         E2E_BACKEND_UPGRADE_QPS: "3000"
+        # Cluster trust bundle reach beta in v1.33, so we can enable it for v1.33 and later.
+        ENABLE_CLUSTER_TRUST_BUNDLE: ${{ startsWith(matrix.target.version, 'v1.33') }}
       run: make e2e
 
   benchmark-test:
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index 305120d74..fef0c7ae4 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -36,14 +36,14 @@ jobs:
     - uses: ./tools/github-actions/setup-deps
 
     - name: Initialize CodeQL
-      uses: github/codeql-action/init@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858  # v3.29.0
+      uses: github/codeql-action/init@39edc492dbe16b1465b0cafca41432d857bdb31a  # v3.29.1
       with:
         languages: ${{ matrix.language }}
 
     - name: Autobuild
-      uses: github/codeql-action/autobuild@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858  # v3.29.0
+      uses: github/codeql-action/autobuild@39edc492dbe16b1465b0cafca41432d857bdb31a  # v3.29.1
 
     - name: Perform CodeQL Analysis
-      uses: github/codeql-action/analyze@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858  # v3.29.0
+      uses: github/codeql-action/analyze@39edc492dbe16b1465b0cafca41432d857bdb31a  # v3.29.1
       with:
         category: "/language:${{matrix.language}}"
diff --git a/.github/workflows/experimental_conformance.yaml b/.github/workflows/experimental_conformance.yaml
index fbd1a5daf..fab5b1eb0 100644
--- a/.github/workflows/experimental_conformance.yaml
+++ b/.github/workflows/experimental_conformance.yaml
@@ -23,22 +23,22 @@ jobs:
     strategy:
       matrix:
         target:
-          - version: v1.30.10
+          - version: v1.30.13
             ipFamily: ipv4
             profile: default
-          - version: v1.31.6
+          - version: v1.31.9
             ipFamily: ipv4
             profile: default
-          - version: v1.32.3
+          - version: v1.32.5
             # only run ipv6 test on this version to save time
             ipFamily: ipv6
             profile: default
           # TODO: this's IPv4 first, need a way to test IPv6 first.
-          - version: v1.33.0
+          - version: v1.33.1
             # only run dual test on latest version to save time
             ipFamily: dual
             profile: default
-          - version: v1.33.0
+          - version: v1.33.1
             # only run dual test on latest version to save time
             ipFamily: dual
             profile: gateway-namespace-mode
diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml
index 602895107..f512e68a2 100644
--- a/.github/workflows/scorecard.yml
+++ b/.github/workflows/scorecard.yml
@@ -40,6 +40,6 @@ jobs:
         retention-days: 5
 
     - name: "Upload to code-scanning"
-      uses: github/codeql-action/upload-sarif@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858  # v3.29.0
+      uses: github/codeql-action/upload-sarif@39edc492dbe16b1465b0cafca41432d857bdb31a  # v3.29.1
       with:
         sarif_file: results.sarif
diff --git a/api/v1alpha1/tls_types.go b/api/v1alpha1/tls_types.go
index bf2a1f505..e5c19ad01 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 a15a3ed93..13f613f0b 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 3a48215f6..57ca85e44 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 fd37c74bd..b3d3218b6 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/go.mod b/go.mod
index 2b54f2626..d0ccaa9e7 100644
--- a/go.mod
+++ b/go.mod
@@ -236,7 +236,7 @@ require (
 	github.com/go-toolsmith/astp v1.1.0 // indirect
 	github.com/go-toolsmith/strparse v1.1.0 // indirect
 	github.com/go-toolsmith/typep v1.1.0 // indirect
-	github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
+	github.com/go-viper/mapstructure/v2 v2.3.0 // indirect
 	github.com/go-xmlfmt/xmlfmt v1.1.3 // indirect
 	github.com/gobuffalo/flect v1.0.3 // indirect
 	github.com/gobwas/glob v0.2.3 // indirect
@@ -262,7 +262,6 @@ require (
 	github.com/google/go-jsonnet v0.20.0 // indirect
 	github.com/google/gofuzz v1.2.0 // indirect
 	github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect
-	github.com/google/safetext v0.0.0-20220905092116-b49f7bc46da2 // indirect
 	github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
 	github.com/google/uuid v1.6.0 // indirect
 	github.com/gordonklaus/ineffassign v0.1.0 // indirect
@@ -510,7 +509,7 @@ require (
 	sigs.k8s.io/controller-runtime/tools/setup-envtest v0.0.0-20250217160221-5e8256e05002 // indirect
 	sigs.k8s.io/controller-tools v0.17.3 // indirect
 	sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
-	sigs.k8s.io/kind v0.27.0 // indirect
+	sigs.k8s.io/kind v0.29.0 // indirect
 	sigs.k8s.io/kustomize/api v0.19.0 // indirect
 	sigs.k8s.io/kustomize/kyaml v0.19.0 // indirect
 	sigs.k8s.io/randfill v1.0.0 // indirect
diff --git a/go.sum b/go.sum
index 4163cf659..0a633fd50 100644
--- a/go.sum
+++ b/go.sum
@@ -552,8 +552,8 @@ github.com/go-toolsmith/strparse v1.1.0 h1:GAioeZUK9TGxnLS+qfdqNbA4z0SSm5zVNtCQi
 github.com/go-toolsmith/strparse v1.1.0/go.mod h1:7ksGy58fsaQkGQlY8WVoBFNyEPMGuJin1rfoPS4lBSQ=
 github.com/go-toolsmith/typep v1.1.0 h1:fIRYDyF+JywLfqzyhdiHzRop/GQDxxNhLGQ6gFUNHus=
 github.com/go-toolsmith/typep v1.1.0/go.mod h1:fVIw+7zjdsMxDA3ITWnH1yOiw1rnTQKCsF/sk2H/qig=
-github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
-github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
+github.com/go-viper/mapstructure/v2 v2.3.0 h1:27XbWsHIqhbdR5TIC911OfYvgSaW93HM+dX7970Q7jk=
+github.com/go-viper/mapstructure/v2 v2.3.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
 github.com/go-xmlfmt/xmlfmt v1.1.3 h1:t8Ey3Uy7jDSEisW2K3somuMKIpzktkWptA0iFCnRUWY=
 github.com/go-xmlfmt/xmlfmt v1.1.3/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM=
 github.com/gobuffalo/flect v0.2.0/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80=
@@ -662,8 +662,6 @@ github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8I
 github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
 github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg=
 github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
-github.com/google/safetext v0.0.0-20220905092116-b49f7bc46da2 h1:SJ+NtwL6QaZ21U+IrK7d0gGgpjGGvd2kz+FzTHVzdqI=
-github.com/google/safetext v0.0.0-20220905092116-b49f7bc46da2/go.mod h1:Tv1PlzqC9t8wNnpPdctvtSUOPUUg4SHeE6vR1Ir2hmg=
 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
 github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -1828,8 +1826,8 @@ sigs.k8s.io/gateway-api v1.3.1-0.20250527223622-54df0a899c1c/go.mod h1:d8NV8nJba
 sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
 sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
 sigs.k8s.io/kind v0.8.1/go.mod h1:oNKTxUVPYkV9lWzY6CVMNluVq8cBsyq+UgPJdvA3uu4=
-sigs.k8s.io/kind v0.27.0 h1:PQ3f0iAWNIj66LYkZ1ivhEg/+Zb6UPMbO+qVei/INZA=
-sigs.k8s.io/kind v0.27.0/go.mod h1:RZVFmy6qcwlSWwp6xeIUv7kXCPF3i8MXsEXxW/J+gJY=
+sigs.k8s.io/kind v0.29.0 h1:3TpCsyh908IkXXpcSnsMjWdwdWjIl7o9IMZImZCWFnI=
+sigs.k8s.io/kind v0.29.0/go.mod h1:ldWQisw2NYyM6k64o/tkZng/1qQW7OlzcN5a8geJX3o=
 sigs.k8s.io/kubectl-validate v0.0.5-0.20241223122011-eb064d2f92d5 h1:hNBVJn2bLSAw6vfO2HATzBZlSPMuz5zm+uE+0N1hQx4=
 sigs.k8s.io/kubectl-validate v0.0.5-0.20241223122011-eb064d2f92d5/go.mod h1:ch1ZkZlHzATEduEoItW1Dro09kDMuUsbqFDCyfO0P6I=
 sigs.k8s.io/kustomize/api v0.19.0 h1:F+2HB2mU1MSiR9Hp1NEgoU2q9ItNOaBJl0I4Dlus5SQ=
diff --git a/internal/gatewayapi/clienttrafficpolicy.go b/internal/gatewayapi/clienttrafficpolicy.go
index 9db58a47f..46c41c026 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 8e8e4dae0..933a1fb94 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/listener.go b/internal/gatewayapi/listener.go
index a48cbcf6e..4d6fe2668 100644
--- a/internal/gatewayapi/listener.go
+++ b/internal/gatewayapi/listener.go
@@ -590,11 +590,6 @@ func (t *Translator) processAccessLog(envoyproxy *egv1a1.EnvoyProxy, resources *
 					}
 					irAccessLog.Text = append(irAccessLog.Text, al)
 				case egv1a1.ProxyAccessLogFormatTypeJSON:
-					if len(format.JSON) == 0 {
-						// TODO: use a default JSON format if not specified?
-						continue
-					}
-
 					al := &ir.JSONAccessLog{
 						JSON:       format.JSON,
 						Path:       sink.File.Path,
diff --git a/internal/ir/xds.go b/internal/ir/xds.go
index cd46ebb1e..6a51beee5 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 238501b8d..ab27afd5a 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 fa1a7620d..05a2a99e8 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/release-notes/current.yaml b/release-notes/current.yaml
index fd913d8d8..370871b3c 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.
@@ -40,6 +42,7 @@ bug fixes: |
   Fixed issue where EnvoyExtensionPolicy ExtProc body processing mode is set to FullDuplexStreamed, but trailers were not sent.
   Fixed validation issue where EnvoyExtensionPolicy ExtProc failOpen is true, and body processing mode FullDuplexStreamed is not rejected.
   Add ConfigMap indexers for EnvoyExtensionPolicies to reconcile Lua changes
+  Fixed issue that default accesslog format not working.
 
 
 # Enhancements that improve performance.
diff --git a/site/hugo.toml b/site/hugo.toml
index 4ffc5bb13..63e696c70 100644
--- a/site/hugo.toml
+++ b/site/hugo.toml
@@ -81,6 +81,11 @@ id = "G-DXJEH1ZRXX"
     style = "tango"
     # Uncomment if you want your chosen highlight style used for code blocks without a specified language
     # guessSyntax = "true"
+  [markup.tableOfContents]
+    # Need this for Gateway API Extensions page.
+    endLevel = 4
+    ordered = false
+    startLevel = 2
 
 # Everything below this are Site Params
 
diff --git a/tools/hack/create-cluster.sh b/tools/hack/create-cluster.sh
index 4644116a3..251898eb3 100755
--- a/tools/hack/create-cluster.sh
+++ b/tools/hack/create-cluster.sh
@@ -9,6 +9,7 @@ KIND_NODE_TAG=${KIND_NODE_TAG:-"v1.33.0"}
 NUM_WORKERS=${NUM_WORKERS:-""}
 IP_FAMILY=${IP_FAMILY:-"ipv4"}
 CUSTOM_CNI=${CUSTOM_CNI:-"false"}
+ENABLE_CLUSTER_TRUST_BUNDLE=${ENABLE_CLUSTER_TRUST_BUNDLE:-"false"}
 
 if [ "$CUSTOM_CNI" = "true" ]; then
   CNI_CONFIG="disableDefaultCNI: true"
@@ -20,10 +21,10 @@ KIND_CFG=$(cat <<-EOM
 kind: Cluster
 apiVersion: kind.x-k8s.io/v1alpha4
 runtimeConfig:
-  "certificates.k8s.io/v1alpha1/clustertrustbundles": "true"
+  certificates.k8s.io/v1beta1/clustertrustbundles: ${ENABLE_CLUSTER_TRUST_BUNDLE}
 featureGates:
-  "ClusterTrustBundle": true
-  "ClusterTrustBundleProjection": true
+  "ClusterTrustBundle": ${ENABLE_CLUSTER_TRUST_BUNDLE}
+  "ClusterTrustBundleProjection": ${ENABLE_CLUSTER_TRUST_BUNDLE}
 networking:
   ${CNI_CONFIG}
   ipFamily: ${IP_FAMILY}
@@ -50,6 +51,9 @@ fi
 if go tool kind get clusters | grep -q "${CLUSTER_NAME}"; then
   echo "Cluster ${CLUSTER_NAME} already exists."
 else
+  echo "Creating kind cluster ${CLUSTER_NAME} with the following configuration:"
+  echo "${KIND_CFG}"
+
 ## Create kind cluster.
 if [[ -z "${KIND_NODE_TAG}" ]]; then
   cat << EOF | go tool kind create cluster --name "${CLUSTER_NAME}" --config -



Test patch:
diff --git a/internal/gatewayapi/helpers_test.go b/internal/gatewayapi/helpers_test.go
index f39bacf83..72df6c66c 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 9189d8e4d..6845f5eca 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 7a11d2378..d7fd002af 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/gatewayapi/testdata/envoyproxy-accesslog-file-json-no-format.out.yaml b/internal/gatewayapi/testdata/envoyproxy-accesslog-file-json-no-format.out.yaml
index 32bbeab06..54d161e1b 100644
--- a/internal/gatewayapi/testdata/envoyproxy-accesslog-file-json-no-format.out.yaml
+++ b/internal/gatewayapi/testdata/envoyproxy-accesslog-file-json-no-format.out.yaml
@@ -128,7 +128,9 @@ infraIR:
       namespace: envoy-gateway-system
 xdsIR:
   envoy-gateway/gateway-1:
-    accessLog: {}
+    accessLog:
+      json:
+      - path: /dev/stdout
     http:
     - address: 0.0.0.0
       hostnames:
diff --git a/internal/gatewayapi/testdata/envoyproxy-accesslog-without-format.out.yaml b/internal/gatewayapi/testdata/envoyproxy-accesslog-without-format.out.yaml
index d8a3d583c..596392106 100644
--- a/internal/gatewayapi/testdata/envoyproxy-accesslog-without-format.out.yaml
+++ b/internal/gatewayapi/testdata/envoyproxy-accesslog-without-format.out.yaml
@@ -203,6 +203,8 @@ xdsIR:
             protocol: GRPC
         name: envoy-gateway-system/test
         type: TCP
+      json:
+      - path: /dev/stdout
       openTelemetry:
       - authority: otel-collector.monitoring.svc.cluster.local
         destination:
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 000000000..81f750353
--- /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 000000000..4521d562e
--- /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 000000000..d4b8322ed
--- /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 000000000..93319d494
--- /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 000000000..9e64eb811
--- /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 000000000..a9683e8d0
--- /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/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md
index 0cd5ba11b..4e1169bde 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.<br />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<br />Kubernetes objects that contain TLS certificates of<br />the Certificate Authorities that can be used<br />as a trust anchor to validate the certificates presented by the client.<br />A single reference to a Kubernetes ConfigMap or a Kubernetes Secret,<br />with the CA certificate in a key named `ca.crt` is currently supported.<br />References to a resource in different namespace are invalid UNLESS there<br />is a ReferenceGrant in the target namespace that allows the certificate<br />to be attached. |
+| `spkiHashes` | _string array_ |  false  |  | An optional list of base64-encoded SHA-256 hashes. If specified, Envoy will<br />verify that the SHA-256 of the DER-encoded Subject Public Key Information<br />(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<br />verify that the SHA-256 of the DER-encoded presented certificate matches<br />one of the specified values. |
+| `subjectAltNames` | _[SubjectAltNames](#subjectaltnames)_ |  false  |  | An optional list of Subject Alternative name matchers. If specified, Envoy<br />will verify that the Subject Alternative Name of the presented certificate<br />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<br />specified in the match value.<br />The regex string must adhere to the syntax documented in<br />https://github.com/google/re2/wiki/Syntax.<br /> | 
 
 
+#### 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/e2e/tests/httproute_with_custom_security_context_userid.go b/test/e2e/tests/httproute_with_custom_security_context_userid.go
index 2cd9c06ee..8abe27342 100644
--- a/test/e2e/tests/httproute_with_custom_security_context_userid.go
+++ b/test/e2e/tests/httproute_with_custom_security_context_userid.go
@@ -80,21 +80,31 @@ var EnvoyGatewayCustomSecurityContextUseridTest = suite.ConformanceTest{
 
 func setEGSecurityContextUserID(t *testing.T, suite *suite.ConformanceTestSuite, uid int64) {
 	// update envoy-gateway deployment with custom security context user id
-	egDeployment := &appsv1.Deployment{}
-	err := suite.Client.Get(
-		context.Background(),
-		types.NamespacedName{Name: "envoy-gateway", Namespace: "envoy-gateway-system"},
-		egDeployment)
-	require.NoError(t, err)
-	egDeployment.Spec.Template.Spec.Containers[0].SecurityContext.RunAsUser = ptr.To(uid)
-	egDeployment.Spec.Template.Spec.Containers[0].SecurityContext.RunAsGroup = ptr.To(uid)
-	err = suite.Client.Update(context.Background(), egDeployment)
+	retries := 5
+	var err error
+	for i := 0; i < retries; i++ { // retry a few times to avoid update conflicts
+		egDeployment := &appsv1.Deployment{}
+		err = suite.Client.Get(
+			context.Background(),
+			types.NamespacedName{Name: "envoy-gateway", Namespace: "envoy-gateway-system"},
+			egDeployment,
+		)
+		require.NoError(t, err)
+
+		egDeployment.Spec.Template.Spec.Containers[0].SecurityContext.RunAsUser = ptr.To(uid)
+		egDeployment.Spec.Template.Spec.Containers[0].SecurityContext.RunAsGroup = ptr.To(uid)
+
+		if err = suite.Client.Update(context.Background(), egDeployment); err == nil {
+			break
+		}
+	}
 	require.NoError(t, err)
+
 	// test that envoy-gateway pod is running with custom security context user id
 	WaitForPods(t, suite.Client, "envoy-gateway-system", map[string]string{"control-plane": "envoy-gateway"}, corev1.PodRunning, PodReady)
 
 	// test that envoy-gateway deployment is updated with custom security context user id
-	egDeployment = &appsv1.Deployment{}
+	egDeployment := &appsv1.Deployment{}
 	err = suite.Client.Get(
 		context.Background(),
 		types.NamespacedName{Name: "envoy-gateway", Namespace: "envoy-gateway-system"},
diff --git a/test/helm/gateway-crds-helm/all.out.yaml b/test/helm/gateway-crds-helm/all.out.yaml
index cab95036d..cffd0e758 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 18a2009c5..db66e42c1 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: |-