diff --git a/.changelog/12878.txt b/.changelog/12878.txt new file mode 100644 index 0000000000..8d06d34401 --- /dev/null +++ b/.changelog/12878.txt @@ -0,0 +1,3 @@ +```release-note:improvement +xds: Envoy now inserts x-forwarded-client-cert for incoming proxy connections +``` diff --git a/agent/structs/config_entry_mesh.go b/agent/structs/config_entry_mesh.go index 2d983eb82e..868c07a9f5 100644 --- a/agent/structs/config_entry_mesh.go +++ b/agent/structs/config_entry_mesh.go @@ -15,6 +15,8 @@ type MeshConfigEntry struct { TLS *MeshTLSConfig `json:",omitempty"` + HTTP *MeshHTTPConfig `json:",omitempty"` + Meta map[string]string `json:",omitempty"` acl.EnterpriseMeta `hcl:",squash" mapstructure:",squash"` RaftIndex @@ -42,6 +44,10 @@ type MeshDirectionalTLSConfig struct { CipherSuites []types.TLSCipherSuite `json:",omitempty" alias:"cipher_suites"` } +type MeshHTTPConfig struct { + SanitizeXForwardedClientCert bool `alias:"sanitize_x_forwarded_client_cert"` +} + func (e *MeshConfigEntry) GetKind() string { return MeshConfig } diff --git a/agent/structs/config_entry_test.go b/agent/structs/config_entry_test.go index 661a87ce90..3814dd4c53 100644 --- a/agent/structs/config_entry_test.go +++ b/agent/structs/config_entry_test.go @@ -1694,6 +1694,9 @@ func TestDecodeConfigEntry(t *testing.T) { ] } } + http { + sanitize_x_forwarded_client_cert = true + } `, camel: ` Kind = "mesh" @@ -1722,6 +1725,9 @@ func TestDecodeConfigEntry(t *testing.T) { ] } } + HTTP { + SanitizeXForwardedClientCert = true + } `, expect: &MeshConfigEntry{ Meta: map[string]string{ @@ -1749,6 +1755,9 @@ func TestDecodeConfigEntry(t *testing.T) { }, }, }, + HTTP: &MeshHTTPConfig{ + SanitizeXForwardedClientCert: true, + }, }, }, { diff --git a/agent/xds/listeners.go b/agent/xds/listeners.go index 5fcc83a912..672d667ad9 100644 --- a/agent/xds/listeners.go +++ b/agent/xds/listeners.go @@ -859,6 +859,10 @@ func (s *ResourceGenerator) makeInboundListener(cfgSnap *proxycfg.ConfigSnapshot if err != nil { return nil, err } + if meshConfig := cfgSnap.MeshConfig(); meshConfig == nil || meshConfig.HTTP == nil || !meshConfig.HTTP.SanitizeXForwardedClientCert { + filterOpts.forwardClientDetails = true + filterOpts.forwardClientPolicy = envoy_http_v3.HttpConnectionManager_APPEND_FORWARD + } } filter, err := makeListenerFilter(filterOpts) if err != nil { @@ -1146,6 +1150,12 @@ func (s *ResourceGenerator) makeFilterChainTerminatingGateway( opts.cluster = "" opts.useRDS = true + + if meshConfig := cfgSnap.MeshConfig(); meshConfig == nil || meshConfig.HTTP == nil || !meshConfig.HTTP.SanitizeXForwardedClientCert { + opts.forwardClientDetails = true + // This assumes that we have a client cert (mTLS) (implied by the context of this function) + opts.forwardClientPolicy = envoy_http_v3.HttpConnectionManager_APPEND_FORWARD + } } filter, err := makeListenerFilter(opts) @@ -1366,16 +1376,18 @@ func (s *ResourceGenerator) getAndModifyUpstreamConfigForListener( } type listenerFilterOpts struct { - useRDS bool - protocol string - filterName string - routeName string - cluster string - statPrefix string - routePath string - requestTimeoutMs *int - ingressGateway bool - httpAuthzFilter *envoy_http_v3.HttpFilter + useRDS bool + protocol string + filterName string + routeName string + cluster string + statPrefix string + routePath string + requestTimeoutMs *int + ingressGateway bool + httpAuthzFilter *envoy_http_v3.HttpFilter + forwardClientDetails bool + forwardClientPolicy envoy_http_v3.HttpConnectionManager_ForwardClientCertDetails } func makeListenerFilter(opts listenerFilterOpts) (*envoy_listener_v3.Filter, error) { @@ -1513,6 +1525,18 @@ func makeHTTPFilter(opts listenerFilterOpts) (*envoy_listener_v3.Filter, error) cfg.Http2ProtocolOptions = &envoy_core_v3.Http2ProtocolOptions{} } + // Note the default leads to setting HttpConnectionManager_SANITIZE + if opts.forwardClientDetails { + cfg.ForwardClientCertDetails = opts.forwardClientPolicy + cfg.SetCurrentClientCertDetails = &envoy_http_v3.HttpConnectionManager_SetCurrentClientCertDetails{ + Subject: &wrappers.BoolValue{Value: true}, + Cert: true, + Chain: true, + Dns: true, + Uri: true, + } + } + // Like injectConnectFilters for L4, here we ensure that the first filter // (other than the "envoy.grpc_http1_bridge" filter) in the http filter // chain of a public listener is the authz filter to prevent unauthorized diff --git a/agent/xds/listeners_test.go b/agent/xds/listeners_test.go index d80cde7b18..dcdb375527 100644 --- a/agent/xds/listeners_test.go +++ b/agent/xds/listeners_test.go @@ -166,6 +166,27 @@ func TestListenersFromSnapshot(t *testing.T) { }, nil) }, }, + { + name: "http-public-listener-no-xfcc", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshot(t, + func(ns *structs.NodeService) { + ns.Proxy.Config["protocol"] = "http" + }, + []cache.UpdateEvent{ + { + CorrelationID: "mesh", + Result: &structs.ConfigEntryResponse{ + Entry: &structs.MeshConfigEntry{ + HTTP: &structs.MeshHTTPConfig{ + SanitizeXForwardedClientCert: true, + }, + }, + }, + }, + }) + }, + }, { name: "http-listener-with-timeouts", create: func(t testinf.T) *proxycfg.ConfigSnapshot { diff --git a/agent/xds/testdata/listeners/http-listener-with-timeouts.latest.golden b/agent/xds/testdata/listeners/http-listener-with-timeouts.latest.golden index 0cd9b242b7..679b07105d 100644 --- a/agent/xds/testdata/listeners/http-listener-with-timeouts.latest.golden +++ b/agent/xds/testdata/listeners/http-listener-with-timeouts.latest.golden @@ -67,6 +67,14 @@ "name": "envoy.filters.network.http_connection_manager", "typedConfig": { "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", + "forwardClientCertDetails": "APPEND_FORWARD", + "setCurrentClientCertDetails": { + "cert": true, + "chain": true, + "dns": true, + "subject": true, + "uri": true + }, "statPrefix": "public_listener", "routeConfig": { "name": "public_listener", diff --git a/agent/xds/testdata/listeners/http-public-listener-no-xfcc.latest.golden b/agent/xds/testdata/listeners/http-public-listener-no-xfcc.latest.golden new file mode 100644 index 0000000000..d0a676eff2 --- /dev/null +++ b/agent/xds/testdata/listeners/http-public-listener-no-xfcc.latest.golden @@ -0,0 +1,151 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", + "name": "db:127.0.0.1:9191", + "address": { + "socketAddress": { + "address": "127.0.0.1", + "portValue": 9191 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.filters.network.tcp_proxy", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy", + "statPrefix": "upstream.db.default.default.dc1", + "cluster": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + } + ] + } + ], + "trafficDirection": "OUTBOUND" + }, + { + "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", + "name": "prepared_query:geo-cache:127.10.10.10:8181", + "address": { + "socketAddress": { + "address": "127.10.10.10", + "portValue": 8181 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.filters.network.tcp_proxy", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy", + "statPrefix": "upstream.prepared_query_geo-cache", + "cluster": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul" + } + } + ] + } + ], + "trafficDirection": "OUTBOUND" + }, + { + "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", + "name": "public_listener:0.0.0.0:9999", + "address": { + "socketAddress": { + "address": "0.0.0.0", + "portValue": 9999 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.filters.network.http_connection_manager", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", + "statPrefix": "public_listener", + "routeConfig": { + "name": "public_listener", + "virtualHosts": [ + { + "name": "public_listener", + "domains": [ + "*" + ], + "routes": [ + { + "match": { + "prefix": "/" + }, + "route": { + "cluster": "local_app" + } + } + ] + } + ] + }, + "httpFilters": [ + { + "name": "envoy.filters.http.rbac", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC", + "rules": { + + } + } + }, + { + "name": "envoy.filters.http.router", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" + } + } + ], + "tracing": { + "randomSampling": { + + } + } + } + } + ], + "transportSocket": { + "name": "tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext", + "commonTlsContext": { + "tlsParams": { + + }, + "tlsCertificates": [ + { + "certificateChain": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n" + }, + "privateKey": { + "inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n" + } + } + ], + "validationContext": { + "trustedCa": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n" + } + } + }, + "requireClientCertificate": true + } + } + } + ], + "trafficDirection": "INBOUND" + } + ], + "typeUrl": "type.googleapis.com/envoy.config.listener.v3.Listener", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/listeners/http-public-listener.latest.golden b/agent/xds/testdata/listeners/http-public-listener.latest.golden index d0a676eff2..66db47bb2a 100644 --- a/agent/xds/testdata/listeners/http-public-listener.latest.golden +++ b/agent/xds/testdata/listeners/http-public-listener.latest.golden @@ -67,6 +67,14 @@ "name": "envoy.filters.network.http_connection_manager", "typedConfig": { "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", + "forwardClientCertDetails": "APPEND_FORWARD", + "setCurrentClientCertDetails": { + "cert": true, + "chain": true, + "dns": true, + "subject": true, + "uri": true + }, "statPrefix": "public_listener", "routeConfig": { "name": "public_listener", diff --git a/agent/xds/testdata/listeners/terminating-gateway-service-subsets.latest.golden b/agent/xds/testdata/listeners/terminating-gateway-service-subsets.latest.golden index c2ce2223ba..fea8b47757 100644 --- a/agent/xds/testdata/listeners/terminating-gateway-service-subsets.latest.golden +++ b/agent/xds/testdata/listeners/terminating-gateway-service-subsets.latest.golden @@ -184,6 +184,14 @@ "name": "envoy.filters.network.http_connection_manager", "typedConfig": { "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", + "forwardClientCertDetails": "APPEND_FORWARD", + "setCurrentClientCertDetails": { + "cert": true, + "chain": true, + "dns": true, + "subject": true, + "uri": true + }, "statPrefix": "upstream.web.default.default.dc1", "rds": { "configSource": { @@ -258,6 +266,14 @@ "name": "envoy.filters.network.http_connection_manager", "typedConfig": { "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", + "forwardClientCertDetails": "APPEND_FORWARD", + "setCurrentClientCertDetails": { + "cert": true, + "chain": true, + "dns": true, + "subject": true, + "uri": true + }, "statPrefix": "upstream.web.default.default.dc1", "rds": { "configSource": { @@ -332,6 +348,14 @@ "name": "envoy.filters.network.http_connection_manager", "typedConfig": { "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", + "forwardClientCertDetails": "APPEND_FORWARD", + "setCurrentClientCertDetails": { + "cert": true, + "chain": true, + "dns": true, + "subject": true, + "uri": true + }, "statPrefix": "upstream.web.default.default.dc1", "rds": { "configSource": { diff --git a/agent/xds/testdata/serverless_plugin/listeners/lambda-terminating-gateway-with-service-resolvers.latest.golden b/agent/xds/testdata/serverless_plugin/listeners/lambda-terminating-gateway-with-service-resolvers.latest.golden index 32cfda1206..158ea619c4 100644 --- a/agent/xds/testdata/serverless_plugin/listeners/lambda-terminating-gateway-with-service-resolvers.latest.golden +++ b/agent/xds/testdata/serverless_plugin/listeners/lambda-terminating-gateway-with-service-resolvers.latest.golden @@ -130,6 +130,14 @@ "name": "envoy.filters.network.http_connection_manager", "typedConfig": { "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", + "forwardClientCertDetails": "APPEND_FORWARD", + "setCurrentClientCertDetails": { + "cert": true, + "chain": true, + "dns": true, + "subject": true, + "uri": true + }, "statPrefix": "upstream.web.default.default.dc1", "rds": { "configSource": { @@ -212,6 +220,14 @@ "name": "envoy.filters.network.http_connection_manager", "typedConfig": { "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", + "forwardClientCertDetails": "APPEND_FORWARD", + "setCurrentClientCertDetails": { + "cert": true, + "chain": true, + "dns": true, + "subject": true, + "uri": true + }, "statPrefix": "upstream.web.default.default.dc1", "rds": { "configSource": { @@ -348,6 +364,14 @@ "name": "envoy.filters.network.http_connection_manager", "typedConfig": { "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", + "forwardClientCertDetails": "APPEND_FORWARD", + "setCurrentClientCertDetails": { + "cert": true, + "chain": true, + "dns": true, + "subject": true, + "uri": true + }, "statPrefix": "upstream.web.default.default.dc1", "rds": { "configSource": { diff --git a/agent/xds/testdata/serverless_plugin/listeners/lambda-terminating-gateway.latest.golden b/agent/xds/testdata/serverless_plugin/listeners/lambda-terminating-gateway.latest.golden index f415e40ab2..f30e7e8758 100644 --- a/agent/xds/testdata/serverless_plugin/listeners/lambda-terminating-gateway.latest.golden +++ b/agent/xds/testdata/serverless_plugin/listeners/lambda-terminating-gateway.latest.golden @@ -184,6 +184,14 @@ "name": "envoy.filters.network.http_connection_manager", "typedConfig": { "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", + "forwardClientCertDetails": "APPEND_FORWARD", + "setCurrentClientCertDetails": { + "cert": true, + "chain": true, + "dns": true, + "subject": true, + "uri": true + }, "statPrefix": "upstream.web.default.default.dc1", "rds": { "configSource": { diff --git a/api/config_entry_mesh.go b/api/config_entry_mesh.go index 30fab166c5..406e87dfc8 100644 --- a/api/config_entry_mesh.go +++ b/api/config_entry_mesh.go @@ -1,6 +1,8 @@ package api -import "encoding/json" +import ( + "encoding/json" +) // MeshConfigEntry manages the global configuration for all service mesh // proxies. @@ -19,6 +21,8 @@ type MeshConfigEntry struct { TLS *MeshTLSConfig `json:",omitempty"` + HTTP *MeshHTTPConfig `json:",omitempty"` + Meta map[string]string `json:",omitempty"` // CreateIndex is the Raft index this entry was created at. This is a @@ -46,6 +50,10 @@ type MeshDirectionalTLSConfig struct { CipherSuites []string `json:",omitempty" alias:"cipher_suites"` } +type MeshHTTPConfig struct { + SanitizeXForwardedClientCert bool `alias:"sanitize_x_forwarded_client_cert"` +} + func (e *MeshConfigEntry) GetKind() string { return MeshConfig } func (e *MeshConfigEntry) GetName() string { return MeshConfigMesh } func (e *MeshConfigEntry) GetPartition() string { return e.Partition } diff --git a/api/config_entry_test.go b/api/config_entry_test.go index 0f38f62cdf..2f28dcd754 100644 --- a/api/config_entry_test.go +++ b/api/config_entry_test.go @@ -1278,6 +1278,9 @@ func TestDecodeConfigEntry(t *testing.T) { "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" ] } + }, + "HTTP": { + "SanitizeXForwardedClientCert": true } } `, @@ -1307,6 +1310,9 @@ func TestDecodeConfigEntry(t *testing.T) { }, }, }, + HTTP: &MeshHTTPConfig{ + SanitizeXForwardedClientCert: true, + }, }, }, } { diff --git a/command/config/write/config_write_test.go b/command/config/write/config_write_test.go index 679a3b77ec..fc297aa094 100644 --- a/command/config/write/config_write_test.go +++ b/command/config/write/config_write_test.go @@ -126,6 +126,9 @@ meta { transparent_proxy { mesh_destinations_only = true } +http { + sanitize_x_forwarded_client_cert = true +} `) ui := cli.NewMockUi() @@ -143,6 +146,9 @@ transparent_proxy { proxy, ok := entry.(*api.MeshConfigEntry) require.True(t, ok) require.Equal(t, map[string]string{"foo": "bar", "gir": "zim"}, proxy.Meta) + require.True(t, proxy.TransparentProxy.MeshDestinationsOnly) + + require.True(t, proxy.HTTP.SanitizeXForwardedClientCert) }) } diff --git a/website/content/docs/connect/config-entries/mesh.mdx b/website/content/docs/connect/config-entries/mesh.mdx index a72a54aa06..a9da7a1adc 100644 --- a/website/content/docs/connect/config-entries/mesh.mdx +++ b/website/content/docs/connect/config-entries/mesh.mdx @@ -273,7 +273,7 @@ Note that the Kubernetes example does not include a `partition` field. Configura name: 'Incoming', yaml: false, type: 'TLSDirectionConfig: <optional>', - description: `TLS configuration for inbound mTLS connections targeting + description: `TLS configuration for inbound mTLS connections targeting the public listener on \`connect-proxy\` and \`terminating-gateway\` proxy kinds.`, children: [ @@ -359,6 +359,21 @@ Note that the Kubernetes example does not include a `partition` field. Configura }, ], }, + { + name: 'HTTP', + type: 'HTTPConfig: <optional>', + description: 'HTTP configuration for the service mesh.', + children: [ + { + name: 'SanitizeXForwardedClientCert', + yaml: false, + type: 'bool: <optional>', + description: `Set the envoy \`forward_client_cert_details\` option to \`SANITIZE\` for all proxies. This + configures Envoy to not send the \`x-forwarded-client-cert\` header to the next hop. If + unspecified or \`false\`, the XFCC header is propagated to upstream applications.`, + }, + ], + }, ]} />