diff --git a/apis/projectcontour/v1alpha1/contourconfig.go b/apis/projectcontour/v1alpha1/contourconfig.go index 16845ac6b55..0c001e6b22b 100644 --- a/apis/projectcontour/v1alpha1/contourconfig.go +++ b/apis/projectcontour/v1alpha1/contourconfig.go @@ -508,6 +508,24 @@ type EnvoyTLS struct { // Note: This list is a superset of what is valid for stock Envoy builds and those using BoringSSL FIPS. // +optional CipherSuites []string `json:"cipherSuites,omitempty"` + + // EnableJA3Fingerprinting enables JA3 TLS fingerprinting in the + // TLS Inspector listener filter. When enabled, Envoy will extract JA3 + // fingerprints from TLS client hellos. + // Note: JA3 fingerprinting requires Envoy 1.21.0 or later. + // + // Contour's default is false. + // +optional + EnableJA3Fingerprinting *bool `json:"enableJA3Fingerprinting,omitempty"` + + // EnableJA4Fingerprinting enables JA4 TLS fingerprinting in the + // TLS Inspector listener filter. When enabled, Envoy will extract JA4 + // fingerprints from TLS client hellos. + // Note: JA4 fingerprinting requires Envoy 1.35.0 or later. + // + // Contour's default is false. + // +optional + EnableJA4Fingerprinting *bool `json:"enableJA4Fingerprinting,omitempty"` } // EnvoyListener defines parameters for an Envoy Listener. diff --git a/apis/projectcontour/v1alpha1/zz_generated.deepcopy.go b/apis/projectcontour/v1alpha1/zz_generated.deepcopy.go index b0565dc55d8..fa442c55e0f 100644 --- a/apis/projectcontour/v1alpha1/zz_generated.deepcopy.go +++ b/apis/projectcontour/v1alpha1/zz_generated.deepcopy.go @@ -759,6 +759,16 @@ func (in *EnvoyTLS) DeepCopyInto(out *EnvoyTLS) { *out = make([]string, len(*in)) copy(*out, *in) } + if in.EnableJA3Fingerprinting != nil { + in, out := &in.EnableJA3Fingerprinting, &out.EnableJA3Fingerprinting + *out = new(bool) + **out = **in + } + if in.EnableJA4Fingerprinting != nil { + in, out := &in.EnableJA4Fingerprinting, &out.EnableJA4Fingerprinting + *out = new(bool) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyTLS. diff --git a/cmd/contour/serve.go b/cmd/contour/serve.go index 16a00bdd283..b9f24ea4513 100644 --- a/cmd/contour/serve.go +++ b/cmd/contour/serve.go @@ -138,6 +138,9 @@ func registerServe(app *kingpin.Application) (*kingpin.CmdClause, *serveContext) serve.Flag("disable-feature", "Do not start an informer for the specified resources.").PlaceHolder("").EnumsVar(&ctx.disabledFeatures, "extensionservices", "tlsroutes", "grpcroutes", "tcproutes", "backendtlspolicies") serve.Flag("disable-leader-election", "Disable leader election mechanism.").BoolVar(&ctx.LeaderElection.Disable) + serve.Flag("enable-ja3-fingerprinting", "Enable JA3 fingerprinting in the TLS Inspector filter (requires Envoy 1.21.0+).").BoolVar(&ctx.enableJA3Fingerprinting) + serve.Flag("enable-ja4-fingerprinting", "Enable JA4 fingerprinting in the TLS Inspector filter (requires Envoy 1.35.0+).").BoolVar(&ctx.enableJA4Fingerprinting) + serve.Flag("envoy-http-access-log", "Envoy HTTP access log.").PlaceHolder("/path/to/file").StringVar(&ctx.httpAccessLog) serve.Flag("envoy-https-access-log", "Envoy HTTPS access log.").PlaceHolder("/path/to/file").StringVar(&ctx.httpsAccessLog) serve.Flag("envoy-service-http-address", "Kubernetes Service address for HTTP requests.").PlaceHolder("").StringVar(&ctx.httpAddr) @@ -444,6 +447,8 @@ func (s *Server) doServe() error { listenerConfig := xdscache_v3.ListenerConfig{ Compression: contourConfiguration.Envoy.Listener.Compression, UseProxyProto: *contourConfiguration.Envoy.Listener.UseProxyProto, + EnableJA3Fingerprinting: *contourConfiguration.Envoy.Listener.TLS.EnableJA3Fingerprinting, + EnableJA4Fingerprinting: *contourConfiguration.Envoy.Listener.TLS.EnableJA4Fingerprinting, HTTPAccessLog: contourConfiguration.Envoy.HTTPListener.AccessLog, HTTPSAccessLog: contourConfiguration.Envoy.HTTPSListener.AccessLog, AccessLogType: contourConfiguration.Envoy.Logging.AccessLogFormat, diff --git a/cmd/contour/servecontext.go b/cmd/contour/servecontext.go index 8c4ee27d73b..737b82a82af 100644 --- a/cmd/contour/servecontext.go +++ b/cmd/contour/servecontext.go @@ -75,7 +75,9 @@ type serveContext struct { statsPort int // envoy's listener parameters - useProxyProto bool + useProxyProto bool + enableJA3Fingerprinting bool + enableJA4Fingerprinting bool // envoy's http listener parameters httpAddr string @@ -558,9 +560,11 @@ func (ctx *serveContext) convertToContourConfigurationSpec() contour_v1alpha1.Co HTTP2MaxConcurrentStreams: ctx.Config.Listener.HTTP2MaxConcurrentStreams, MaxConnectionsPerListener: ctx.Config.Listener.MaxConnectionsPerListener, TLS: &contour_v1alpha1.EnvoyTLS{ - MinimumProtocolVersion: ctx.Config.TLS.MinimumProtocolVersion, - MaximumProtocolVersion: ctx.Config.TLS.MaximumProtocolVersion, - CipherSuites: cipherSuites, + MinimumProtocolVersion: ctx.Config.TLS.MinimumProtocolVersion, + MaximumProtocolVersion: ctx.Config.TLS.MaximumProtocolVersion, + CipherSuites: cipherSuites, + EnableJA3Fingerprinting: &ctx.enableJA3Fingerprinting, + EnableJA4Fingerprinting: &ctx.enableJA4Fingerprinting, }, SocketOptions: &contour_v1alpha1.SocketOptions{ TOS: ctx.Config.Listener.SocketOptions.TOS, diff --git a/cmd/contour/servecontext_test.go b/cmd/contour/servecontext_test.go index b3dfee1f816..458ffbebc60 100644 --- a/cmd/contour/servecontext_test.go +++ b/cmd/contour/servecontext_test.go @@ -411,8 +411,10 @@ func defaultContourConfiguration() contour_v1alpha1.ContourConfigurationSpec { DisableMergeSlashes: ptr.To(false), ServerHeaderTransformation: contour_v1alpha1.OverwriteServerHeader, TLS: &contour_v1alpha1.EnvoyTLS{ - MinimumProtocolVersion: "", - MaximumProtocolVersion: "", + MinimumProtocolVersion: "", + MaximumProtocolVersion: "", + EnableJA3Fingerprinting: ptr.To(false), + EnableJA4Fingerprinting: ptr.To(false), }, SocketOptions: &contour_v1alpha1.SocketOptions{ TOS: 0, diff --git a/examples/contour/01-crds.yaml b/examples/contour/01-crds.yaml index bd251103c8b..26bd99ba8e6 100644 --- a/examples/contour/01-crds.yaml +++ b/examples/contour/01-crds.yaml @@ -200,6 +200,22 @@ spec: items: type: string type: array + enableJA3Fingerprinting: + description: |- + EnableJA3Fingerprinting enables JA3 TLS fingerprinting in the + TLS Inspector listener filter. When enabled, Envoy will extract JA3 + fingerprints from TLS client hellos. + Note: JA3 fingerprinting requires Envoy 1.21.0 or later. + Contour's default is false. + type: boolean + enableJA4Fingerprinting: + description: |- + EnableJA4Fingerprinting enables JA4 TLS fingerprinting in the + TLS Inspector listener filter. When enabled, Envoy will extract JA4 + fingerprints from TLS client hellos. + Note: JA4 fingerprinting requires Envoy 1.35.0 or later. + Contour's default is false. + type: boolean maximumProtocolVersion: description: |- MaximumProtocolVersion is the maximum TLS version this vhost should @@ -438,6 +454,22 @@ spec: items: type: string type: array + enableJA3Fingerprinting: + description: |- + EnableJA3Fingerprinting enables JA3 TLS fingerprinting in the + TLS Inspector listener filter. When enabled, Envoy will extract JA3 + fingerprints from TLS client hellos. + Note: JA3 fingerprinting requires Envoy 1.21.0 or later. + Contour's default is false. + type: boolean + enableJA4Fingerprinting: + description: |- + EnableJA4Fingerprinting enables JA4 TLS fingerprinting in the + TLS Inspector listener filter. When enabled, Envoy will extract JA4 + fingerprints from TLS client hellos. + Note: JA4 fingerprinting requires Envoy 1.35.0 or later. + Contour's default is false. + type: boolean maximumProtocolVersion: description: |- MaximumProtocolVersion is the maximum TLS version this vhost should @@ -4147,6 +4179,22 @@ spec: items: type: string type: array + enableJA3Fingerprinting: + description: |- + EnableJA3Fingerprinting enables JA3 TLS fingerprinting in the + TLS Inspector listener filter. When enabled, Envoy will extract JA3 + fingerprints from TLS client hellos. + Note: JA3 fingerprinting requires Envoy 1.21.0 or later. + Contour's default is false. + type: boolean + enableJA4Fingerprinting: + description: |- + EnableJA4Fingerprinting enables JA4 TLS fingerprinting in the + TLS Inspector listener filter. When enabled, Envoy will extract JA4 + fingerprints from TLS client hellos. + Note: JA4 fingerprinting requires Envoy 1.35.0 or later. + Contour's default is false. + type: boolean maximumProtocolVersion: description: |- MaximumProtocolVersion is the maximum TLS version this vhost should @@ -4385,6 +4433,22 @@ spec: items: type: string type: array + enableJA3Fingerprinting: + description: |- + EnableJA3Fingerprinting enables JA3 TLS fingerprinting in the + TLS Inspector listener filter. When enabled, Envoy will extract JA3 + fingerprints from TLS client hellos. + Note: JA3 fingerprinting requires Envoy 1.21.0 or later. + Contour's default is false. + type: boolean + enableJA4Fingerprinting: + description: |- + EnableJA4Fingerprinting enables JA4 TLS fingerprinting in the + TLS Inspector listener filter. When enabled, Envoy will extract JA4 + fingerprints from TLS client hellos. + Note: JA4 fingerprinting requires Envoy 1.35.0 or later. + Contour's default is false. + type: boolean maximumProtocolVersion: description: |- MaximumProtocolVersion is the maximum TLS version this vhost should diff --git a/examples/render/contour-deployment.yaml b/examples/render/contour-deployment.yaml index 11e6a8925ba..1ba868f24b0 100644 --- a/examples/render/contour-deployment.yaml +++ b/examples/render/contour-deployment.yaml @@ -419,6 +419,22 @@ spec: items: type: string type: array + enableJA3Fingerprinting: + description: |- + EnableJA3Fingerprinting enables JA3 TLS fingerprinting in the + TLS Inspector listener filter. When enabled, Envoy will extract JA3 + fingerprints from TLS client hellos. + Note: JA3 fingerprinting requires Envoy 1.21.0 or later. + Contour's default is false. + type: boolean + enableJA4Fingerprinting: + description: |- + EnableJA4Fingerprinting enables JA4 TLS fingerprinting in the + TLS Inspector listener filter. When enabled, Envoy will extract JA4 + fingerprints from TLS client hellos. + Note: JA4 fingerprinting requires Envoy 1.35.0 or later. + Contour's default is false. + type: boolean maximumProtocolVersion: description: |- MaximumProtocolVersion is the maximum TLS version this vhost should @@ -657,6 +673,22 @@ spec: items: type: string type: array + enableJA3Fingerprinting: + description: |- + EnableJA3Fingerprinting enables JA3 TLS fingerprinting in the + TLS Inspector listener filter. When enabled, Envoy will extract JA3 + fingerprints from TLS client hellos. + Note: JA3 fingerprinting requires Envoy 1.21.0 or later. + Contour's default is false. + type: boolean + enableJA4Fingerprinting: + description: |- + EnableJA4Fingerprinting enables JA4 TLS fingerprinting in the + TLS Inspector listener filter. When enabled, Envoy will extract JA4 + fingerprints from TLS client hellos. + Note: JA4 fingerprinting requires Envoy 1.35.0 or later. + Contour's default is false. + type: boolean maximumProtocolVersion: description: |- MaximumProtocolVersion is the maximum TLS version this vhost should @@ -4366,6 +4398,22 @@ spec: items: type: string type: array + enableJA3Fingerprinting: + description: |- + EnableJA3Fingerprinting enables JA3 TLS fingerprinting in the + TLS Inspector listener filter. When enabled, Envoy will extract JA3 + fingerprints from TLS client hellos. + Note: JA3 fingerprinting requires Envoy 1.21.0 or later. + Contour's default is false. + type: boolean + enableJA4Fingerprinting: + description: |- + EnableJA4Fingerprinting enables JA4 TLS fingerprinting in the + TLS Inspector listener filter. When enabled, Envoy will extract JA4 + fingerprints from TLS client hellos. + Note: JA4 fingerprinting requires Envoy 1.35.0 or later. + Contour's default is false. + type: boolean maximumProtocolVersion: description: |- MaximumProtocolVersion is the maximum TLS version this vhost should @@ -4604,6 +4652,22 @@ spec: items: type: string type: array + enableJA3Fingerprinting: + description: |- + EnableJA3Fingerprinting enables JA3 TLS fingerprinting in the + TLS Inspector listener filter. When enabled, Envoy will extract JA3 + fingerprints from TLS client hellos. + Note: JA3 fingerprinting requires Envoy 1.21.0 or later. + Contour's default is false. + type: boolean + enableJA4Fingerprinting: + description: |- + EnableJA4Fingerprinting enables JA4 TLS fingerprinting in the + TLS Inspector listener filter. When enabled, Envoy will extract JA4 + fingerprints from TLS client hellos. + Note: JA4 fingerprinting requires Envoy 1.35.0 or later. + Contour's default is false. + type: boolean maximumProtocolVersion: description: |- MaximumProtocolVersion is the maximum TLS version this vhost should diff --git a/examples/render/contour-gateway-provisioner.yaml b/examples/render/contour-gateway-provisioner.yaml index 2496dac11ac..643709abb3d 100644 --- a/examples/render/contour-gateway-provisioner.yaml +++ b/examples/render/contour-gateway-provisioner.yaml @@ -211,6 +211,22 @@ spec: items: type: string type: array + enableJA3Fingerprinting: + description: |- + EnableJA3Fingerprinting enables JA3 TLS fingerprinting in the + TLS Inspector listener filter. When enabled, Envoy will extract JA3 + fingerprints from TLS client hellos. + Note: JA3 fingerprinting requires Envoy 1.21.0 or later. + Contour's default is false. + type: boolean + enableJA4Fingerprinting: + description: |- + EnableJA4Fingerprinting enables JA4 TLS fingerprinting in the + TLS Inspector listener filter. When enabled, Envoy will extract JA4 + fingerprints from TLS client hellos. + Note: JA4 fingerprinting requires Envoy 1.35.0 or later. + Contour's default is false. + type: boolean maximumProtocolVersion: description: |- MaximumProtocolVersion is the maximum TLS version this vhost should @@ -449,6 +465,22 @@ spec: items: type: string type: array + enableJA3Fingerprinting: + description: |- + EnableJA3Fingerprinting enables JA3 TLS fingerprinting in the + TLS Inspector listener filter. When enabled, Envoy will extract JA3 + fingerprints from TLS client hellos. + Note: JA3 fingerprinting requires Envoy 1.21.0 or later. + Contour's default is false. + type: boolean + enableJA4Fingerprinting: + description: |- + EnableJA4Fingerprinting enables JA4 TLS fingerprinting in the + TLS Inspector listener filter. When enabled, Envoy will extract JA4 + fingerprints from TLS client hellos. + Note: JA4 fingerprinting requires Envoy 1.35.0 or later. + Contour's default is false. + type: boolean maximumProtocolVersion: description: |- MaximumProtocolVersion is the maximum TLS version this vhost should @@ -4158,6 +4190,22 @@ spec: items: type: string type: array + enableJA3Fingerprinting: + description: |- + EnableJA3Fingerprinting enables JA3 TLS fingerprinting in the + TLS Inspector listener filter. When enabled, Envoy will extract JA3 + fingerprints from TLS client hellos. + Note: JA3 fingerprinting requires Envoy 1.21.0 or later. + Contour's default is false. + type: boolean + enableJA4Fingerprinting: + description: |- + EnableJA4Fingerprinting enables JA4 TLS fingerprinting in the + TLS Inspector listener filter. When enabled, Envoy will extract JA4 + fingerprints from TLS client hellos. + Note: JA4 fingerprinting requires Envoy 1.35.0 or later. + Contour's default is false. + type: boolean maximumProtocolVersion: description: |- MaximumProtocolVersion is the maximum TLS version this vhost should @@ -4396,6 +4444,22 @@ spec: items: type: string type: array + enableJA3Fingerprinting: + description: |- + EnableJA3Fingerprinting enables JA3 TLS fingerprinting in the + TLS Inspector listener filter. When enabled, Envoy will extract JA3 + fingerprints from TLS client hellos. + Note: JA3 fingerprinting requires Envoy 1.21.0 or later. + Contour's default is false. + type: boolean + enableJA4Fingerprinting: + description: |- + EnableJA4Fingerprinting enables JA4 TLS fingerprinting in the + TLS Inspector listener filter. When enabled, Envoy will extract JA4 + fingerprints from TLS client hellos. + Note: JA4 fingerprinting requires Envoy 1.35.0 or later. + Contour's default is false. + type: boolean maximumProtocolVersion: description: |- MaximumProtocolVersion is the maximum TLS version this vhost should diff --git a/examples/render/contour-gateway.yaml b/examples/render/contour-gateway.yaml index a211d8238d6..ed5c4439eda 100644 --- a/examples/render/contour-gateway.yaml +++ b/examples/render/contour-gateway.yaml @@ -236,6 +236,22 @@ spec: items: type: string type: array + enableJA3Fingerprinting: + description: |- + EnableJA3Fingerprinting enables JA3 TLS fingerprinting in the + TLS Inspector listener filter. When enabled, Envoy will extract JA3 + fingerprints from TLS client hellos. + Note: JA3 fingerprinting requires Envoy 1.21.0 or later. + Contour's default is false. + type: boolean + enableJA4Fingerprinting: + description: |- + EnableJA4Fingerprinting enables JA4 TLS fingerprinting in the + TLS Inspector listener filter. When enabled, Envoy will extract JA4 + fingerprints from TLS client hellos. + Note: JA4 fingerprinting requires Envoy 1.35.0 or later. + Contour's default is false. + type: boolean maximumProtocolVersion: description: |- MaximumProtocolVersion is the maximum TLS version this vhost should @@ -474,6 +490,22 @@ spec: items: type: string type: array + enableJA3Fingerprinting: + description: |- + EnableJA3Fingerprinting enables JA3 TLS fingerprinting in the + TLS Inspector listener filter. When enabled, Envoy will extract JA3 + fingerprints from TLS client hellos. + Note: JA3 fingerprinting requires Envoy 1.21.0 or later. + Contour's default is false. + type: boolean + enableJA4Fingerprinting: + description: |- + EnableJA4Fingerprinting enables JA4 TLS fingerprinting in the + TLS Inspector listener filter. When enabled, Envoy will extract JA4 + fingerprints from TLS client hellos. + Note: JA4 fingerprinting requires Envoy 1.35.0 or later. + Contour's default is false. + type: boolean maximumProtocolVersion: description: |- MaximumProtocolVersion is the maximum TLS version this vhost should @@ -4183,6 +4215,22 @@ spec: items: type: string type: array + enableJA3Fingerprinting: + description: |- + EnableJA3Fingerprinting enables JA3 TLS fingerprinting in the + TLS Inspector listener filter. When enabled, Envoy will extract JA3 + fingerprints from TLS client hellos. + Note: JA3 fingerprinting requires Envoy 1.21.0 or later. + Contour's default is false. + type: boolean + enableJA4Fingerprinting: + description: |- + EnableJA4Fingerprinting enables JA4 TLS fingerprinting in the + TLS Inspector listener filter. When enabled, Envoy will extract JA4 + fingerprints from TLS client hellos. + Note: JA4 fingerprinting requires Envoy 1.35.0 or later. + Contour's default is false. + type: boolean maximumProtocolVersion: description: |- MaximumProtocolVersion is the maximum TLS version this vhost should @@ -4421,6 +4469,22 @@ spec: items: type: string type: array + enableJA3Fingerprinting: + description: |- + EnableJA3Fingerprinting enables JA3 TLS fingerprinting in the + TLS Inspector listener filter. When enabled, Envoy will extract JA3 + fingerprints from TLS client hellos. + Note: JA3 fingerprinting requires Envoy 1.21.0 or later. + Contour's default is false. + type: boolean + enableJA4Fingerprinting: + description: |- + EnableJA4Fingerprinting enables JA4 TLS fingerprinting in the + TLS Inspector listener filter. When enabled, Envoy will extract JA4 + fingerprints from TLS client hellos. + Note: JA4 fingerprinting requires Envoy 1.35.0 or later. + Contour's default is false. + type: boolean maximumProtocolVersion: description: |- MaximumProtocolVersion is the maximum TLS version this vhost should diff --git a/examples/render/contour.yaml b/examples/render/contour.yaml index 414c87f9c95..02b2fe5c56e 100644 --- a/examples/render/contour.yaml +++ b/examples/render/contour.yaml @@ -419,6 +419,22 @@ spec: items: type: string type: array + enableJA3Fingerprinting: + description: |- + EnableJA3Fingerprinting enables JA3 TLS fingerprinting in the + TLS Inspector listener filter. When enabled, Envoy will extract JA3 + fingerprints from TLS client hellos. + Note: JA3 fingerprinting requires Envoy 1.21.0 or later. + Contour's default is false. + type: boolean + enableJA4Fingerprinting: + description: |- + EnableJA4Fingerprinting enables JA4 TLS fingerprinting in the + TLS Inspector listener filter. When enabled, Envoy will extract JA4 + fingerprints from TLS client hellos. + Note: JA4 fingerprinting requires Envoy 1.35.0 or later. + Contour's default is false. + type: boolean maximumProtocolVersion: description: |- MaximumProtocolVersion is the maximum TLS version this vhost should @@ -657,6 +673,22 @@ spec: items: type: string type: array + enableJA3Fingerprinting: + description: |- + EnableJA3Fingerprinting enables JA3 TLS fingerprinting in the + TLS Inspector listener filter. When enabled, Envoy will extract JA3 + fingerprints from TLS client hellos. + Note: JA3 fingerprinting requires Envoy 1.21.0 or later. + Contour's default is false. + type: boolean + enableJA4Fingerprinting: + description: |- + EnableJA4Fingerprinting enables JA4 TLS fingerprinting in the + TLS Inspector listener filter. When enabled, Envoy will extract JA4 + fingerprints from TLS client hellos. + Note: JA4 fingerprinting requires Envoy 1.35.0 or later. + Contour's default is false. + type: boolean maximumProtocolVersion: description: |- MaximumProtocolVersion is the maximum TLS version this vhost should @@ -4366,6 +4398,22 @@ spec: items: type: string type: array + enableJA3Fingerprinting: + description: |- + EnableJA3Fingerprinting enables JA3 TLS fingerprinting in the + TLS Inspector listener filter. When enabled, Envoy will extract JA3 + fingerprints from TLS client hellos. + Note: JA3 fingerprinting requires Envoy 1.21.0 or later. + Contour's default is false. + type: boolean + enableJA4Fingerprinting: + description: |- + EnableJA4Fingerprinting enables JA4 TLS fingerprinting in the + TLS Inspector listener filter. When enabled, Envoy will extract JA4 + fingerprints from TLS client hellos. + Note: JA4 fingerprinting requires Envoy 1.35.0 or later. + Contour's default is false. + type: boolean maximumProtocolVersion: description: |- MaximumProtocolVersion is the maximum TLS version this vhost should @@ -4604,6 +4652,22 @@ spec: items: type: string type: array + enableJA3Fingerprinting: + description: |- + EnableJA3Fingerprinting enables JA3 TLS fingerprinting in the + TLS Inspector listener filter. When enabled, Envoy will extract JA3 + fingerprints from TLS client hellos. + Note: JA3 fingerprinting requires Envoy 1.21.0 or later. + Contour's default is false. + type: boolean + enableJA4Fingerprinting: + description: |- + EnableJA4Fingerprinting enables JA4 TLS fingerprinting in the + TLS Inspector listener filter. When enabled, Envoy will extract JA4 + fingerprints from TLS client hellos. + Note: JA4 fingerprinting requires Envoy 1.35.0 or later. + Contour's default is false. + type: boolean maximumProtocolVersion: description: |- MaximumProtocolVersion is the maximum TLS version this vhost should diff --git a/internal/contourconfig/contourconfiguration.go b/internal/contourconfig/contourconfiguration.go index 256808d3e24..fccd0212d83 100644 --- a/internal/contourconfig/contourconfiguration.go +++ b/internal/contourconfig/contourconfiguration.go @@ -70,9 +70,11 @@ func Defaults() contour_v1alpha1.ContourConfigurationSpec { ServerHeaderTransformation: contour_v1alpha1.OverwriteServerHeader, ConnectionBalancer: "", TLS: &contour_v1alpha1.EnvoyTLS{ - MinimumProtocolVersion: "1.2", - MaximumProtocolVersion: "1.3", - CipherSuites: contour_v1alpha1.DefaultTLSCiphers, + MinimumProtocolVersion: "1.2", + MaximumProtocolVersion: "1.3", + CipherSuites: contour_v1alpha1.DefaultTLSCiphers, + EnableJA3Fingerprinting: ptr.To(false), + EnableJA4Fingerprinting: ptr.To(false), }, }, Service: &contour_v1alpha1.NamespacedName{ diff --git a/internal/contourconfig/contourconfiguration_test.go b/internal/contourconfig/contourconfiguration_test.go index f9787f7d678..0f9e11d77d0 100644 --- a/internal/contourconfig/contourconfiguration_test.go +++ b/internal/contourconfig/contourconfiguration_test.go @@ -70,6 +70,8 @@ func TestOverlayOnDefaults(t *testing.T) { "foo", "bar", }, + EnableJA3Fingerprinting: ptr.To(true), + EnableJA4Fingerprinting: ptr.To(true), }, }, Service: &contour_v1alpha1.NamespacedName{ diff --git a/internal/dag/httpproxy_processor_test.go b/internal/dag/httpproxy_processor_test.go index d1f30a17c82..0d69a6ecf03 100644 --- a/internal/dag/httpproxy_processor_test.go +++ b/internal/dag/httpproxy_processor_test.go @@ -1523,7 +1523,14 @@ func TestDetermineUpstreamTLS(t *testing.T) { for name, tc := range tests { t.Run(name, func(t *testing.T) { - got := (*UpstreamTLS)(tc.envoyTLS) + var got *UpstreamTLS + if tc.envoyTLS != nil { + got = &UpstreamTLS{ + MinimumProtocolVersion: tc.envoyTLS.MinimumProtocolVersion, + MaximumProtocolVersion: tc.envoyTLS.MaximumProtocolVersion, + CipherSuites: tc.envoyTLS.CipherSuites, + } + } assert.Equal(t, tc.want, got) }) } diff --git a/internal/envoy/v3/listener.go b/internal/envoy/v3/listener.go index 51a8a53bebe..0e4fea98ffc 100644 --- a/internal/envoy/v3/listener.go +++ b/internal/envoy/v3/listener.go @@ -114,11 +114,22 @@ func CodecForVersions(versions ...HTTPVersionType) HTTPVersionType { } // TLSInspector returns a new TLS inspector listener filter. -func TLSInspector() *envoy_config_listener_v3.ListenerFilter { +// If enableJA3 is true, JA3 fingerprinting will be enabled. +// If enableJA4 is true, JA4 fingerprinting will be enabled. +func TLSInspector(enableJA3, enableJA4 bool) *envoy_config_listener_v3.ListenerFilter { + tlsInspectorConfig := &envoy_filter_listener_tls_inspector_v3.TlsInspector{} + + if enableJA3 { + tlsInspectorConfig.EnableJa3Fingerprinting = wrapperspb.Bool(true) + } + if enableJA4 { + tlsInspectorConfig.EnableJa4Fingerprinting = wrapperspb.Bool(true) + } + return &envoy_config_listener_v3.ListenerFilter{ Name: wellknown.TlsInspector, ConfigType: &envoy_config_listener_v3.ListenerFilter_TypedConfig{ - TypedConfig: protobuf.MustMarshalAny(&envoy_filter_listener_tls_inspector_v3.TlsInspector{}), + TypedConfig: protobuf.MustMarshalAny(tlsInspectorConfig), }, } } diff --git a/internal/envoy/v3/listener_test.go b/internal/envoy/v3/listener_test.go index 2cc564d924d..6c073623f59 100644 --- a/internal/envoy/v3/listener_test.go +++ b/internal/envoy/v3/listener_test.go @@ -29,6 +29,7 @@ import ( envoy_filter_http_lua_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/lua/v3" envoy_filter_http_rbac_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/rbac/v3" envoy_filter_http_router_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/router/v3" + envoy_filter_listener_tls_inspector_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/tls_inspector/v3" envoy_filter_network_http_connection_manager_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" envoy_filter_network_tcp_proxy_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/tcp_proxy/v3" envoy_transport_socket_tls_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" @@ -77,6 +78,64 @@ func TestProtoNamesForVersions(t *testing.T) { assert.Equal(t, []string{"h2", "http/1.1"}, ProtoNamesForVersions(HTTPVersion1, HTTPVersion2)) } +func TestTLSInspector(t *testing.T) { + tests := map[string]struct { + enableJA3 bool + enableJA4 bool + wantJA3 bool + wantJA4 bool + }{ + "both disabled": { + enableJA3: false, + enableJA4: false, + wantJA3: false, + wantJA4: false, + }, + "JA3 enabled only": { + enableJA3: true, + enableJA4: false, + wantJA3: true, + wantJA4: false, + }, + "JA4 enabled only": { + enableJA3: false, + enableJA4: true, + wantJA3: false, + wantJA4: true, + }, + "both enabled": { + enableJA3: true, + enableJA4: true, + wantJA3: true, + wantJA4: true, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + got := TLSInspector(tc.enableJA3, tc.enableJA4) + + assert.Equal(t, wellknown.TlsInspector, got.Name) + + var tlsInspector envoy_filter_listener_tls_inspector_v3.TlsInspector + err := got.GetTypedConfig().UnmarshalTo(&tlsInspector) + require.NoError(t, err) + + if tc.wantJA3 { + assert.True(t, tlsInspector.GetEnableJa3Fingerprinting().GetValue()) + } else { + assert.Nil(t, tlsInspector.GetEnableJa3Fingerprinting()) + } + + if tc.wantJA4 { + assert.True(t, tlsInspector.GetEnableJa4Fingerprinting().GetValue()) + } else { + assert.Nil(t, tlsInspector.GetEnableJa4Fingerprinting()) + } + }) + } +} + func TestListener(t *testing.T) { envoygen := NewEnvoyGen(EnvoyGenOpt{ XDSClusterName: DefaultXDSClusterName, @@ -133,13 +192,13 @@ func TestListener(t *testing.T) { address: "0.0.0.0", port: 9000, lf: ListenerFilters( - TLSInspector(), + TLSInspector(false, false), ), want: &envoy_config_listener_v3.Listener{ Name: "https", Address: SocketAddress("0.0.0.0", 9000), ListenerFilters: ListenerFilters( - TLSInspector(), + TLSInspector(false, false), ), SocketOptions: NewSocketOptions().TCPKeepalive().Build(), }, @@ -150,14 +209,14 @@ func TestListener(t *testing.T) { port: 9000, lf: ListenerFilters( ProxyProtocol(), - TLSInspector(), + TLSInspector(false, false), ), want: &envoy_config_listener_v3.Listener{ Name: "https-proxy", Address: SocketAddress("0.0.0.0", 9000), ListenerFilters: ListenerFilters( ProxyProtocol(), - TLSInspector(), + TLSInspector(false, false), ), SocketOptions: NewSocketOptions().TCPKeepalive().Build(), }, @@ -186,14 +245,14 @@ func TestListener(t *testing.T) { port: 9000, perConnectionBufferLimitBytes: ptr.To(uint32(32768)), lf: ListenerFilters( - TLSInspector(), + TLSInspector(false, false), ), want: &envoy_config_listener_v3.Listener{ Name: "https", Address: SocketAddress("0.0.0.0", 9000), PerConnectionBufferLimitBytes: wrapperspb.UInt32(32768), ListenerFilters: ListenerFilters( - TLSInspector(), + TLSInspector(false, false), ), SocketOptions: NewSocketOptions().TCPKeepalive().Build(), }, diff --git a/internal/featuretests/v3/authorization_test.go b/internal/featuretests/v3/authorization_test.go index efbb9ae21e0..d8e63e1b0a3 100644 --- a/internal/featuretests/v3/authorization_test.go +++ b/internal/featuretests/v3/authorization_test.go @@ -84,7 +84,7 @@ func authzResponseTimeout(t *testing.T, rh ResourceEventHandlerWrapper, c *Conto Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: []*envoy_config_listener_v3.FilterChain{ filterchaintls(fqdn, featuretests.TLSSecret(t, "certificate", &featuretests.ServerCertificate), @@ -165,7 +165,7 @@ func authzFailOpen(t *testing.T, rh ResourceEventHandlerWrapper, c *Contour) { Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: []*envoy_config_listener_v3.FilterChain{ filterchaintls(fqdn, featuretests.TLSSecret(t, "certificate", &featuretests.ServerCertificate), @@ -473,7 +473,7 @@ func authzInvalidReference(t *testing.T, rh ResourceEventHandlerWrapper, c *Cont Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: []*envoy_config_listener_v3.FilterChain{ filterchaintls(fqdn, featuretests.TLSSecret(t, "certificate", &featuretests.ServerCertificate), @@ -532,7 +532,7 @@ func authzWithRequestBodyBufferSettings(t *testing.T, rh ResourceEventHandlerWra Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: []*envoy_config_listener_v3.FilterChain{ filterchaintls(fqdn, featuretests.TLSSecret(t, "certificate", &featuretests.ServerCertificate), diff --git a/internal/featuretests/v3/downstreamvalidation_test.go b/internal/featuretests/v3/downstreamvalidation_test.go index ef1d6028a26..b78e732b789 100644 --- a/internal/featuretests/v3/downstreamvalidation_test.go +++ b/internal/featuretests/v3/downstreamvalidation_test.go @@ -67,7 +67,7 @@ func TestDownstreamTLSCertificateValidation(t *testing.T) { Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: appendFilterChains( filterchaintls("example.com", serverTLSSecret, @@ -119,7 +119,7 @@ func TestDownstreamTLSCertificateValidation(t *testing.T) { Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: appendFilterChains( filterchaintls("example.com", serverTLSSecret, @@ -167,7 +167,7 @@ func TestDownstreamTLSCertificateValidation(t *testing.T) { Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: appendFilterChains( filterchaintls("example.com", serverTLSSecret, @@ -223,7 +223,7 @@ func TestDownstreamTLSCertificateValidation(t *testing.T) { Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: appendFilterChains( filterchaintls("example.com", serverTLSSecret, @@ -277,7 +277,7 @@ func TestDownstreamTLSCertificateValidation(t *testing.T) { Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: appendFilterChains( filterchaintls("example.com", serverTLSSecret, @@ -331,7 +331,7 @@ func TestDownstreamTLSCertificateValidation(t *testing.T) { Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: appendFilterChains( filterchaintls("example.com", serverTLSSecret, @@ -388,7 +388,7 @@ func TestDownstreamTLSCertificateValidation(t *testing.T) { Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: appendFilterChains( filterchaintls("example.com", serverTLSSecret, @@ -448,7 +448,7 @@ func TestDownstreamTLSCertificateValidation(t *testing.T) { Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: appendFilterChains( filterchaintls("example.com", serverTLSSecret, diff --git a/internal/featuretests/v3/fallbackcert_test.go b/internal/featuretests/v3/fallbackcert_test.go index 0f92dc731ed..f46f93754e2 100644 --- a/internal/featuretests/v3/fallbackcert_test.go +++ b/internal/featuretests/v3/fallbackcert_test.go @@ -87,7 +87,7 @@ func TestFallbackCertificate(t *testing.T) { Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: appendFilterChains( filterchaintls("fallback.example.com", sec1, @@ -149,7 +149,7 @@ func TestFallbackCertificate(t *testing.T) { Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: appendFilterChains( filterchaintls("fallback.example.com", sec1, @@ -191,7 +191,7 @@ func TestFallbackCertificate(t *testing.T) { Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: appendFilterChains( filterchaintls("fallback.example.com", sec1, @@ -260,7 +260,7 @@ func TestFallbackCertificate(t *testing.T) { Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: appendFilterChains( filterchaintls("anotherfallback.example.com", sec1, diff --git a/internal/featuretests/v3/global_authorization_test.go b/internal/featuretests/v3/global_authorization_test.go index c2b35f58cae..596a5261319 100644 --- a/internal/featuretests/v3/global_authorization_test.go +++ b/internal/featuretests/v3/global_authorization_test.go @@ -146,7 +146,7 @@ func globalExternalAuthorizationFilterExistsTLS(t *testing.T, rh ResourceEventHa Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: []*envoy_config_listener_v3.FilterChain{ filterchaintls("foo.com", @@ -216,7 +216,7 @@ func globalExternalAuthorizationWithTLSGlobalAuthDisabled(t *testing.T, rh Resou Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: []*envoy_config_listener_v3.FilterChain{ filterchaintls("foo.com", @@ -493,7 +493,7 @@ func globalExternalAuthorizationWithMergedAuthPolicyTLS(t *testing.T, rh Resourc Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: []*envoy_config_listener_v3.FilterChain{ filterchaintls("foo.com", @@ -613,7 +613,7 @@ func globalExternalAuthorizationWithTLSAuthOverride(t *testing.T, rh ResourceEve Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: []*envoy_config_listener_v3.FilterChain{ filterchaintls("foo.com", @@ -702,7 +702,7 @@ func globalExternalAuthorizationFilterTLSWithFallbackCertificate(t *testing.T, r Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: []*envoy_config_listener_v3.FilterChain{ filterchaintls("foo.com", diff --git a/internal/featuretests/v3/httproute_test.go b/internal/featuretests/v3/httproute_test.go index e87c4771588..849b2c4e6c9 100644 --- a/internal/featuretests/v3/httproute_test.go +++ b/internal/featuretests/v3/httproute_test.go @@ -167,7 +167,7 @@ func TestGateway_TLS(t *testing.T) { Name: "https-443", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: appendFilterChains( filterchaintls("test.projectcontour.io", sec1, diff --git a/internal/featuretests/v3/jwtverification_test.go b/internal/featuretests/v3/jwtverification_test.go index 6c91d08e62a..ea119b41ea0 100644 --- a/internal/featuretests/v3/jwtverification_test.go +++ b/internal/featuretests/v3/jwtverification_test.go @@ -76,7 +76,7 @@ func TestJWTVerification(t *testing.T) { Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: appendFilterChains( filterchaintls("jwt.example.com", sec1, httpsFilterFor("jwt.example.com"), @@ -128,7 +128,7 @@ func TestJWTVerification(t *testing.T) { Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: appendFilterChains( filterchaintls("jwt.example.com", sec1, @@ -281,7 +281,7 @@ func TestJWTVerification(t *testing.T) { Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: appendFilterChains( filterchaintls("jwt.example.com", sec1, @@ -389,7 +389,7 @@ func TestJWTVerification(t *testing.T) { Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: appendFilterChains( filterchaintls("jwt.example.com", sec1, @@ -499,7 +499,7 @@ func TestJWTVerification(t *testing.T) { Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: appendFilterChains( filterchaintls("jwt.example.com", sec1, @@ -613,7 +613,7 @@ func TestJWTVerification(t *testing.T) { Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: appendFilterChains( filterchaintls("jwt.example.com", sec1, @@ -761,7 +761,7 @@ func TestJWTVerification(t *testing.T) { Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: appendFilterChains( filterchaintls("jwt.example.com", sec1, @@ -925,7 +925,7 @@ func TestJWTVerification(t *testing.T) { Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: appendFilterChains( filterchaintls("jwt.example.com", sec1, @@ -1070,7 +1070,7 @@ func TestJWTVerification(t *testing.T) { Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: appendFilterChains( filterchaintls("jwt.example.com", sec1, @@ -1223,7 +1223,7 @@ func TestJWTVerification_Inclusion(t *testing.T) { Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: appendFilterChains( filterchaintls("jwt.example.com", sec1, httpsFilterFor("jwt.example.com"), @@ -1285,7 +1285,7 @@ func TestJWTVerification_Inclusion(t *testing.T) { Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: appendFilterChains( filterchaintls("jwt.example.com", sec1, @@ -1448,7 +1448,7 @@ func TestJWTVerification_Inclusion(t *testing.T) { Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: appendFilterChains( filterchaintls("jwt.example.com", sec1, @@ -1566,7 +1566,7 @@ func TestJWTVerification_Inclusion(t *testing.T) { Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: appendFilterChains( filterchaintls("jwt.example.com", sec1, @@ -1686,7 +1686,7 @@ func TestJWTVerification_Inclusion(t *testing.T) { Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: appendFilterChains( filterchaintls("jwt.example.com", sec1, diff --git a/internal/featuretests/v3/listeners_test.go b/internal/featuretests/v3/listeners_test.go index dc6c0a620f1..0e51d2f0666 100644 --- a/internal/featuretests/v3/listeners_test.go +++ b/internal/featuretests/v3/listeners_test.go @@ -205,7 +205,7 @@ func TestTLSListener(t *testing.T) { Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: []*envoy_config_listener_v3.FilterChain{ filterchaintls("kuard.example.com", s1, @@ -250,7 +250,7 @@ func TestTLSListener(t *testing.T) { Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: []*envoy_config_listener_v3.FilterChain{ filterchaintls("kuard.example.com", s1, @@ -355,7 +355,7 @@ func TestHTTPProxyTLSListener(t *testing.T) { Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: []*envoy_config_listener_v3.FilterChain{ filterchaintls("kuard.example.com", secret1, @@ -397,7 +397,7 @@ func TestHTTPProxyTLSListener(t *testing.T) { Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: []*envoy_config_listener_v3.FilterChain{ envoy_v3.FilterChainTLS( @@ -475,7 +475,7 @@ func TestTLSListenerCipherSuites(t *testing.T) { Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: []*envoy_config_listener_v3.FilterChain{ envoy_v3.FilterChainTLS( @@ -554,7 +554,7 @@ func TestLDSFilter(t *testing.T) { Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: []*envoy_config_listener_v3.FilterChain{ filterchaintls("kuard.example.com", s1, @@ -704,7 +704,7 @@ func TestLDSIngressHTTPSUseProxyProtocol(t *testing.T) { Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( envoy_v3.ProxyProtocol(), - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: []*envoy_config_listener_v3.FilterChain{ filterchaintls("kuard.example.com", s1, @@ -792,7 +792,7 @@ func TestLDSCustomAddressAndPort(t *testing.T) { Name: "ingress_https", Address: envoy_v3.SocketAddress("127.0.0.200", 9200), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: []*envoy_config_listener_v3.FilterChain{ filterchaintls("kuard.example.com", s1, @@ -875,7 +875,7 @@ func TestLDSCustomAccessLogPaths(t *testing.T) { Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: []*envoy_config_listener_v3.FilterChain{ filterchaintls("kuard.example.com", s1, @@ -959,7 +959,7 @@ func TestHTTPProxyHTTPS(t *testing.T) { Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: []*envoy_config_listener_v3.FilterChain{ filterchaintls("example.com", s1, @@ -1025,7 +1025,7 @@ func TestHTTPProxyTLSVersion(t *testing.T) { Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: []*envoy_config_listener_v3.FilterChain{ envoy_v3.FilterChainTLS( @@ -1083,7 +1083,7 @@ func TestHTTPProxyTLSVersion(t *testing.T) { Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: []*envoy_config_listener_v3.FilterChain{ envoy_v3.FilterChainTLS( @@ -1495,7 +1495,7 @@ func TestGatewayListenersSetAddress(t *testing.T) { Name: "https-443", Address: envoy_v3.SocketAddress("127.0.0.200", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: appendFilterChains( filterchaintls("test.projectcontour.io", tlssecret, @@ -1515,7 +1515,7 @@ func TestGatewayListenersSetAddress(t *testing.T) { Name: "https-8443", Address: envoy_v3.SocketAddress("127.0.0.200", 16443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: []*envoy_config_listener_v3.FilterChain{{ FilterChainMatch: &envoy_config_listener_v3.FilterChainMatch{ @@ -1622,7 +1622,7 @@ func TestSocketOptions(t *testing.T) { Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: []*envoy_config_listener_v3.FilterChain{ envoy_v3.FilterChainTLS( diff --git a/internal/featuretests/v3/routeweight_test.go b/internal/featuretests/v3/routeweight_test.go index 9725e34b15c..d9f19acb145 100644 --- a/internal/featuretests/v3/routeweight_test.go +++ b/internal/featuretests/v3/routeweight_test.go @@ -144,7 +144,7 @@ func TestHTTPProxy_TCPProxyWithAServiceWeight(t *testing.T) { }, }}, ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }, @@ -200,7 +200,7 @@ func TestHTTPProxy_TCPProxyWithAServiceWeight(t *testing.T) { }, }}, ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }, @@ -256,7 +256,7 @@ func TestHTTPProxy_TCPProxyWithAServiceWeight(t *testing.T) { }, }}, ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }, @@ -313,7 +313,7 @@ func TestHTTPProxy_TCPProxyWithAServiceWeight(t *testing.T) { }, }}, ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }, @@ -530,7 +530,7 @@ func TestTLSRoute_RouteWithAServiceWeight(t *testing.T) { }, }}, ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }, @@ -584,7 +584,7 @@ func TestTLSRoute_RouteWithAServiceWeight(t *testing.T) { }, }}, ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }, diff --git a/internal/featuretests/v3/tcpproxy_test.go b/internal/featuretests/v3/tcpproxy_test.go index cd556ee24be..699ccf1062b 100644 --- a/internal/featuretests/v3/tcpproxy_test.go +++ b/internal/featuretests/v3/tcpproxy_test.go @@ -89,7 +89,7 @@ func TestTCPProxy(t *testing.T) { filterchaintls("kuard-tcp.example.com", s1, tcpproxy("ingress_https", "default/correct-backend/80/da39a3ee5e"), nil), ), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }, @@ -173,7 +173,7 @@ func TestTCPProxyDelegation(t *testing.T) { filterchaintls("kuard-tcp.example.com", s1, tcpproxy("ingress_https", "app/backend/80/da39a3ee5e"), nil), ), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }, @@ -257,7 +257,7 @@ func TestTCPProxyTLSPassthrough(t *testing.T) { }, }}, ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }, @@ -330,7 +330,7 @@ func TestTCPProxyTLSBackend(t *testing.T) { tcpproxy("ingress_https", svc.Namespace+"/"+svc.Name+"/443/4929fca9d4"), nil), ), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }, @@ -415,7 +415,7 @@ func TestTCPProxyAndHTTPService(t *testing.T) { filterchaintls("kuard-tcp.example.com", s1, tcpproxy("ingress_https", "default/backend/80/da39a3ee5e"), nil), ), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }, @@ -498,7 +498,7 @@ func TestTCPProxyAndHTTPServicePermitInsecure(t *testing.T) { filterchaintls("kuard-tcp.example.com", s1, tcpproxy("ingress_https", "default/backend/80/da39a3ee5e"), nil), ), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }, @@ -586,7 +586,7 @@ func TestTCPProxyTLSPassthroughAndHTTPService(t *testing.T) { }, }}, ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }, @@ -675,7 +675,7 @@ func TestTCPProxyTLSPassthroughAndHTTPServicePermitInsecure(t *testing.T) { }, }}, ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }, diff --git a/internal/featuretests/v3/tcproute_test.go b/internal/featuretests/v3/tcproute_test.go index c864165a933..97396af04af 100644 --- a/internal/featuretests/v3/tcproute_test.go +++ b/internal/featuretests/v3/tcproute_test.go @@ -257,7 +257,7 @@ func TestTCPRoute_TLSTermination(t *testing.T) { Name: "https-5000", Address: envoy_v3.SocketAddress("0.0.0.0", 13000), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: appendFilterChains( filterchaintls("*", sec1, tcpproxy("https-5000", "default/backend-1/80/da39a3ee5e"), nil), diff --git a/internal/featuretests/v3/tlscertificatedelegation_test.go b/internal/featuretests/v3/tlscertificatedelegation_test.go index 6c9200eaa46..66dc200e729 100644 --- a/internal/featuretests/v3/tlscertificatedelegation_test.go +++ b/internal/featuretests/v3/tlscertificatedelegation_test.go @@ -95,7 +95,7 @@ func TestTLSCertificateDelegation(t *testing.T) { Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: appendFilterChains( filterchaintls("example.com", sec1, diff --git a/internal/featuretests/v3/tlsprotocolversion_test.go b/internal/featuretests/v3/tlsprotocolversion_test.go index 6f25fda7f84..9cfa7aa52ff 100644 --- a/internal/featuretests/v3/tlsprotocolversion_test.go +++ b/internal/featuretests/v3/tlsprotocolversion_test.go @@ -70,7 +70,7 @@ func TestTLSProtocolVersion(t *testing.T) { Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: appendFilterChains( filterchaintls("kuard.example.com", sec1, @@ -123,7 +123,7 @@ func TestTLSProtocolVersion(t *testing.T) { Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: []*envoy_config_listener_v3.FilterChain{ envoy_v3.FilterChainTLS( diff --git a/internal/featuretests/v3/tlsroute_test.go b/internal/featuretests/v3/tlsroute_test.go index f7422d4199d..cc6db1cee13 100644 --- a/internal/featuretests/v3/tlsroute_test.go +++ b/internal/featuretests/v3/tlsroute_test.go @@ -112,7 +112,7 @@ func TestTLSRoute_TLSPassthrough(t *testing.T) { }, }}, ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }, @@ -155,7 +155,7 @@ func TestTLSRoute_TLSPassthrough(t *testing.T) { }, }}, ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }, @@ -222,7 +222,7 @@ func TestTLSRoute_TLSPassthrough(t *testing.T) { }, }}, ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }, @@ -315,7 +315,7 @@ func TestTLSRoute_TLSTermination(t *testing.T) { Name: "https-5000", Address: envoy_v3.SocketAddress("0.0.0.0", 13000), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: appendFilterChains( filterchaintls("test1.projectcontour.io", sec1, tcpproxy("https-5000", "default/svc1/80/da39a3ee5e"), nil), @@ -352,7 +352,7 @@ func TestTLSRoute_TLSTermination(t *testing.T) { Name: "https-5000", Address: envoy_v3.SocketAddress("0.0.0.0", 13000), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: appendFilterChains( filterchaintls("test1.projectcontour.io", sec1, tcpproxy("https-5000", "default/svc1/80/da39a3ee5e"), nil), diff --git a/internal/featuretests/v3/wildcardhost_test.go b/internal/featuretests/v3/wildcardhost_test.go index a3bdd0c3f12..fd464720e41 100644 --- a/internal/featuretests/v3/wildcardhost_test.go +++ b/internal/featuretests/v3/wildcardhost_test.go @@ -223,7 +223,7 @@ func TestIngressWildcardHostHTTPSWildcardSecret(t *testing.T) { Name: "ingress_https", Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: appendFilterChains( filterchaintls("*.foo-tls.com", sec, diff --git a/internal/xdscache/v3/listener.go b/internal/xdscache/v3/listener.go index a0e62004d80..dc4495b29fd 100644 --- a/internal/xdscache/v3/listener.go +++ b/internal/xdscache/v3/listener.go @@ -65,6 +65,14 @@ type ListenerConfig struct { // If not set, defaults to false. UseProxyProto bool + // EnableJA3Fingerprinting enables JA3 fingerprinting in the TLS Inspector. + // If not set, defaults to false. + EnableJA3Fingerprinting bool + + // EnableJA4Fingerprinting enables JA4 fingerprinting in the TLS Inspector. + // If not set, defaults to false. + EnableJA4Fingerprinting bool + // Compression defines configuration related to compression in the default HTTP Listener filters. Compression *contour_v1alpha1.EnvoyCompression @@ -422,7 +430,7 @@ func (c *ListenerCache) OnChange(root *dag.DAG) { listener.Port, cfg.PerConnectionBufferLimitBytes, socketOptions, - secureProxyProtocol(cfg.UseProxyProto), + secureProxyProtocol(cfg.UseProxyProto, cfg.EnableJA3Fingerprinting, cfg.EnableJA4Fingerprinting), ) } @@ -667,6 +675,6 @@ func proxyProtocol(useProxy bool) []*envoy_config_listener_v3.ListenerFilter { return nil } -func secureProxyProtocol(useProxy bool) []*envoy_config_listener_v3.ListenerFilter { - return append(proxyProtocol(useProxy), envoy_v3.TLSInspector()) +func secureProxyProtocol(useProxy, enableJA3, enableJA4 bool) []*envoy_config_listener_v3.ListenerFilter { + return append(proxyProtocol(useProxy), envoy_v3.TLSInspector(enableJA3, enableJA4)) } diff --git a/internal/xdscache/v3/listener_test.go b/internal/xdscache/v3/listener_test.go index 299f996314c..28083307440 100644 --- a/internal/xdscache/v3/listener_test.go +++ b/internal/xdscache/v3/listener_test.go @@ -247,7 +247,7 @@ func TestListenerVisit(t *testing.T) { Name: ENVOY_HTTPS_LISTENER, Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: []*envoy_config_listener_v3.FilterChain{{ FilterChainMatch: &envoy_config_listener_v3.FilterChainMatch{ @@ -318,7 +318,7 @@ func TestListenerVisit(t *testing.T) { Name: ENVOY_HTTPS_LISTENER, Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: []*envoy_config_listener_v3.FilterChain{{ FilterChainMatch: &envoy_config_listener_v3.FilterChainMatch{ @@ -412,7 +412,7 @@ func TestListenerVisit(t *testing.T) { Filters: envoy_v3.Filters(httpsFilterFor("www.example.com")), }}, ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }), @@ -476,7 +476,7 @@ func TestListenerVisit(t *testing.T) { Filters: envoy_v3.Filters(httpsFilterFor("www.example.com")), }}, ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }), @@ -524,7 +524,171 @@ func TestListenerVisit(t *testing.T) { Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( envoy_v3.ProxyProtocol(), - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), + ), + FilterChains: []*envoy_config_listener_v3.FilterChain{{ + FilterChainMatch: &envoy_config_listener_v3.FilterChainMatch{ + ServerNames: []string{"whatever.example.com"}, + }, + TransportSocket: transportSocket(envoyGen, "secret", envoy_transport_socket_tls_v3.TlsParameters_TLSv1_2, envoy_transport_socket_tls_v3.TlsParameters_TLSv1_3, nil, "h2", "http/1.1"), + Filters: envoy_v3.Filters(httpsFilterFor("whatever.example.com")), + }}, + SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), + }), + }, + + "enable JA3 and JA4 TLS fingerprinting": { + ListenerConfig: ListenerConfig{ + EnableJA3Fingerprinting: true, + EnableJA4Fingerprinting: true, + }, + objs: []any{ + &networking_v1.Ingress{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "simple", + Namespace: "default", + }, + Spec: networking_v1.IngressSpec{ + TLS: []networking_v1.IngressTLS{{ + Hosts: []string{"whatever.example.com"}, + SecretName: "secret", + }}, + Rules: []networking_v1.IngressRule{{ + Host: "whatever.example.com", + IngressRuleValue: networking_v1.IngressRuleValue{ + HTTP: &networking_v1.HTTPIngressRuleValue{ + Paths: []networking_v1.HTTPIngressPath{{ + Backend: *backend("kuard", 8080), + }}, + }, + }, + }}, + }, + }, + secret, + service, + }, + want: listenermap(&envoy_config_listener_v3.Listener{ + Name: ENVOY_HTTP_LISTENER, + Address: envoy_v3.SocketAddress("0.0.0.0", 8080), + FilterChains: envoy_v3.FilterChains(envoyGen.HTTPConnectionManager(ENVOY_HTTP_LISTENER, envoy_v3.FileAccessLogEnvoy(DEFAULT_HTTP_ACCESS_LOG, "", nil, contour_v1alpha1.LogLevelInfo), 0)), + SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), + }, &envoy_config_listener_v3.Listener{ + Name: ENVOY_HTTPS_LISTENER, + Address: envoy_v3.SocketAddress("0.0.0.0", 8443), + ListenerFilters: envoy_v3.ListenerFilters( + envoy_v3.TLSInspector(true, true), + ), + FilterChains: []*envoy_config_listener_v3.FilterChain{{ + FilterChainMatch: &envoy_config_listener_v3.FilterChainMatch{ + ServerNames: []string{"whatever.example.com"}, + }, + TransportSocket: transportSocket(envoyGen, "secret", envoy_transport_socket_tls_v3.TlsParameters_TLSv1_2, envoy_transport_socket_tls_v3.TlsParameters_TLSv1_3, nil, "h2", "http/1.1"), + Filters: envoy_v3.Filters(httpsFilterFor("whatever.example.com")), + }}, + SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), + }), + }, + + "enable JA3 TLS fingerprinting only": { + ListenerConfig: ListenerConfig{ + EnableJA3Fingerprinting: true, + EnableJA4Fingerprinting: false, + }, + objs: []any{ + &networking_v1.Ingress{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "simple", + Namespace: "default", + }, + Spec: networking_v1.IngressSpec{ + TLS: []networking_v1.IngressTLS{{ + Hosts: []string{"whatever.example.com"}, + SecretName: "secret", + }}, + Rules: []networking_v1.IngressRule{{ + Host: "whatever.example.com", + IngressRuleValue: networking_v1.IngressRuleValue{ + HTTP: &networking_v1.HTTPIngressRuleValue{ + Paths: []networking_v1.HTTPIngressPath{{ + Backend: *backend("kuard", 8080), + }}, + }, + }, + }}, + }, + }, + secret, + service, + }, + want: listenermap(&envoy_config_listener_v3.Listener{ + Name: ENVOY_HTTP_LISTENER, + Address: envoy_v3.SocketAddress("0.0.0.0", 8080), + FilterChains: envoy_v3.FilterChains(envoyGen.HTTPConnectionManager(ENVOY_HTTP_LISTENER, envoy_v3.FileAccessLogEnvoy(DEFAULT_HTTP_ACCESS_LOG, "", nil, contour_v1alpha1.LogLevelInfo), 0)), + SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), + }, &envoy_config_listener_v3.Listener{ + Name: ENVOY_HTTPS_LISTENER, + Address: envoy_v3.SocketAddress("0.0.0.0", 8443), + ListenerFilters: envoy_v3.ListenerFilters( + envoy_v3.TLSInspector(true, false), + ), + FilterChains: []*envoy_config_listener_v3.FilterChain{{ + FilterChainMatch: &envoy_config_listener_v3.FilterChainMatch{ + ServerNames: []string{"whatever.example.com"}, + }, + TransportSocket: transportSocket(envoyGen, "secret", envoy_transport_socket_tls_v3.TlsParameters_TLSv1_2, envoy_transport_socket_tls_v3.TlsParameters_TLSv1_3, nil, "h2", "http/1.1"), + Filters: envoy_v3.Filters(httpsFilterFor("whatever.example.com")), + }}, + SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), + }), + }, + + "enable JA3 and JA4 TLS fingerprinting with proxy protocol": { + ListenerConfig: ListenerConfig{ + UseProxyProto: true, + EnableJA3Fingerprinting: true, + EnableJA4Fingerprinting: true, + }, + objs: []any{ + &networking_v1.Ingress{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "simple", + Namespace: "default", + }, + Spec: networking_v1.IngressSpec{ + TLS: []networking_v1.IngressTLS{{ + Hosts: []string{"whatever.example.com"}, + SecretName: "secret", + }}, + Rules: []networking_v1.IngressRule{{ + Host: "whatever.example.com", + IngressRuleValue: networking_v1.IngressRuleValue{ + HTTP: &networking_v1.HTTPIngressRuleValue{ + Paths: []networking_v1.HTTPIngressPath{{ + Backend: *backend("kuard", 8080), + }}, + }, + }, + }}, + }, + }, + secret, + service, + }, + want: listenermap(&envoy_config_listener_v3.Listener{ + Name: ENVOY_HTTP_LISTENER, + Address: envoy_v3.SocketAddress("0.0.0.0", 8080), + ListenerFilters: envoy_v3.ListenerFilters( + envoy_v3.ProxyProtocol(), + ), + FilterChains: envoy_v3.FilterChains(envoyGen.HTTPConnectionManager(ENVOY_HTTP_LISTENER, envoy_v3.FileAccessLogEnvoy(DEFAULT_HTTP_ACCESS_LOG, "", nil, contour_v1alpha1.LogLevelInfo), 0)), + SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), + }, &envoy_config_listener_v3.Listener{ + Name: ENVOY_HTTPS_LISTENER, + Address: envoy_v3.SocketAddress("0.0.0.0", 8443), + ListenerFilters: envoy_v3.ListenerFilters( + envoy_v3.ProxyProtocol(), + envoy_v3.TLSInspector(true, true), ), FilterChains: []*envoy_config_listener_v3.FilterChain{{ FilterChainMatch: &envoy_config_listener_v3.FilterChainMatch{ @@ -577,7 +741,7 @@ func TestListenerVisit(t *testing.T) { Name: ENVOY_HTTPS_LISTENER, Address: envoy_v3.SocketAddress("0.0.0.0", 8443), ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), FilterChains: []*envoy_config_listener_v3.FilterChain{{ FilterChainMatch: &envoy_config_listener_v3.FilterChainMatch{ @@ -643,7 +807,7 @@ func TestListenerVisit(t *testing.T) { Filters: envoy_v3.Filters(httpsFilterFor("whatever.example.com")), }}, ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }), @@ -700,7 +864,7 @@ func TestListenerVisit(t *testing.T) { Filters: envoy_v3.Filters(httpsFilterFor("whatever.example.com")), }}, ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }), @@ -753,7 +917,7 @@ func TestListenerVisit(t *testing.T) { Filters: envoy_v3.Filters(httpsFilterFor("www.example.com")), }}, ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }), @@ -805,7 +969,7 @@ func TestListenerVisit(t *testing.T) { Filters: envoy_v3.Filters(httpsFilterFor("www.example.com")), }}, ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }), @@ -857,7 +1021,7 @@ func TestListenerVisit(t *testing.T) { Filters: envoy_v3.Filters(httpsFilterFor("www.example.com")), }}, ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }), @@ -948,7 +1112,7 @@ func TestListenerVisit(t *testing.T) { Name: "fallback-certificate", }}, ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }), @@ -1039,7 +1203,7 @@ func TestListenerVisit(t *testing.T) { Name: "fallback-certificate", }}, ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }), @@ -1130,7 +1294,7 @@ func TestListenerVisit(t *testing.T) { Name: "fallback-certificate", }}, ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }), @@ -1221,7 +1385,7 @@ func TestListenerVisit(t *testing.T) { Name: "fallback-certificate", }}, ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }), @@ -1312,7 +1476,7 @@ func TestListenerVisit(t *testing.T) { Name: "fallback-certificate", }}, ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }), @@ -1403,7 +1567,7 @@ func TestListenerVisit(t *testing.T) { Name: "fallback-certificate", }}, ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }), @@ -1466,7 +1630,7 @@ func TestListenerVisit(t *testing.T) { Name: "fallback-certificate", }}, ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }), @@ -1564,7 +1728,7 @@ func TestListenerVisit(t *testing.T) { }, }, ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }), @@ -1656,7 +1820,7 @@ func TestListenerVisit(t *testing.T) { Filters: envoy_v3.Filters(httpsFilterFor("www.example.com")), }}, ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }), @@ -1942,7 +2106,7 @@ func TestListenerVisit(t *testing.T) { Get()), }}, ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }), @@ -2219,7 +2383,7 @@ func TestListenerVisit(t *testing.T) { Get()), }}, ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }), @@ -2285,7 +2449,7 @@ func TestListenerVisit(t *testing.T) { Get()), }}, ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }), @@ -2350,7 +2514,7 @@ func TestListenerVisit(t *testing.T) { Get()), }}, ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }), @@ -2415,7 +2579,7 @@ func TestListenerVisit(t *testing.T) { Get()), }}, ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }), @@ -2603,7 +2767,7 @@ func TestListenerVisit(t *testing.T) { Get()), }}, ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }), @@ -2766,7 +2930,7 @@ func TestListenerVisit(t *testing.T) { Name: "fallback-certificate", }}, ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }), @@ -2908,7 +3072,7 @@ func TestListenerVisit(t *testing.T) { Get()), }}, ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }), @@ -3013,7 +3177,7 @@ func TestListenerVisit(t *testing.T) { Get()), }}, ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }), @@ -3348,7 +3512,7 @@ func TestListenerVisit(t *testing.T) { Get()), }}, ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }), @@ -3443,7 +3607,7 @@ func TestListenerVisit(t *testing.T) { Get()), }}, ListenerFilters: envoy_v3.ListenerFilters( - envoy_v3.TLSInspector(), + envoy_v3.TLSInspector(false, false), ), SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }), diff --git a/site/content/docs/main/config/api-reference.html b/site/content/docs/main/config/api-reference.html index f985ae33037..938aaba1544 100644 --- a/site/content/docs/main/config/api-reference.html +++ b/site/content/docs/main/config/api-reference.html @@ -7603,6 +7603,40 @@

EnvoyTLS Note: This list is a superset of what is valid for stock Envoy builds and those using BoringSSL FIPS.

+ + +enableJA3Fingerprinting +
+ +bool + + + +(Optional) +

EnableJA3Fingerprinting enables JA3 TLS fingerprinting in the +TLS Inspector listener filter. When enabled, Envoy will extract JA3 +fingerprints from TLS client hellos. +Note: JA3 fingerprinting requires Envoy 1.21.0 or later.

+

Contour’s default is false.

+ + + + +enableJA4Fingerprinting +
+ +bool + + + +(Optional) +

EnableJA4Fingerprinting enables JA4 TLS fingerprinting in the +TLS Inspector listener filter. When enabled, Envoy will extract JA4 +fingerprints from TLS client hellos. +Note: JA4 fingerprinting requires Envoy 1.35.0 or later.

+

Contour’s default is false.

+ +

ExtensionProtocolVersion