Skip to content

Add ability to disable SNI host validation #3659

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions apis/v1alpha2/nginxproxy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,15 @@ type NginxProxySpec struct {
//
// +optional
DisableHTTP2 *bool `json:"disableHTTP2,omitempty"`
// DisableSNIHostValidation disables the validation that ensures the SNI hostname
// matches the Host header in HTTPS requests. When disabled, HTTPS connections can
// be reused for requests to different hostnames covered by the same certificate.
// This resolves HTTP/2 connection coalescing issues with wildcard certificates but
// introduces security risks as described in Gateway API GEP-3567.
// If not specified, defaults to false (validation enabled).
//
// +optional
DisableSNIHostValidation *bool `json:"disableSNIHostValidation,omitempty"`
// Kubernetes contains the configuration for the NGINX Deployment and Service Kubernetes objects.
//
// +optional
Expand Down
5 changes: 5 additions & 0 deletions apis/v1alpha2/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions charts/nginx-gateway-fabric/values.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,11 @@
"required": [],
"type": "boolean"
},
"disableSNIHostValidation": {
"description": "DisableSNIHostValidation disables the validation that ensures the SNI hostname matches the Host header in HTTPS requests. This resolves HTTP/2 connection coalescing issues with wildcard certificates but introduces security risks as described in Gateway API GEP-3567.",
"required": [],
"type": "boolean"
},
"ipFamily": {
"description": "IPFamily specifies the IP family to be used by the NGINX.",
"enum": [
Expand Down
3 changes: 3 additions & 0 deletions charts/nginx-gateway-fabric/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,9 @@ nginx:
# disableHTTP2:
# description: DisableHTTP2 defines if http2 should be disabled for all servers.
# type: boolean
# disableSNIHostValidation:
# description: DisableSNIHostValidation disables the validation that ensures the SNI hostname matches the Host header in HTTPS requests. This resolves HTTP/2 connection coalescing issues with wildcard certificates but introduces security risks as described in Gateway API GEP-3567.
# type: boolean
# ipFamily:
# description: IPFamily specifies the IP family to be used by the NGINX.
# type: string
Expand Down
9 changes: 9 additions & 0 deletions config/crd/bases/gateway.nginx.org_nginxproxies.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,15 @@ spec:
DisableHTTP2 defines if http2 should be disabled for all servers.
If not specified, or set to false, http2 will be enabled for all servers.
type: boolean
disableSNIHostValidation:
description: |-
DisableSNIHostValidation disables the validation that ensures the SNI hostname
matches the Host header in HTTPS requests. When disabled, HTTPS connections can
be reused for requests to different hostnames covered by the same certificate.
This resolves HTTP/2 connection coalescing issues with wildcard certificates but
introduces security risks as described in Gateway API GEP-3567.
If not specified, defaults to false (validation enabled).
type: boolean
ipFamily:
default: dual
description: |-
Expand Down
9 changes: 9 additions & 0 deletions deploy/crds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,15 @@ spec:
DisableHTTP2 defines if http2 should be disabled for all servers.
If not specified, or set to false, http2 will be enabled for all servers.
type: boolean
disableSNIHostValidation:
description: |-
DisableSNIHostValidation disables the validation that ensures the SNI hostname
matches the Host header in HTTPS requests. When disabled, HTTPS connections can
be reused for requests to different hostnames covered by the same certificate.
This resolves HTTP/2 connection coalescing issues with wildcard certificates but
introduces security risks as described in Gateway API GEP-3567.
If not specified, defaults to false (validation enabled).
type: boolean
ipFamily:
default: dual
description: |-
Expand Down
9 changes: 5 additions & 4 deletions internal/controller/nginx/config/http/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,9 @@ type ProxySSLVerify struct {

// ServerConfig holds configuration for an HTTP server and IP family to be used by NGINX.
type ServerConfig struct {
Servers []Server
RewriteClientIP shared.RewriteClientIPSettings
IPFamily shared.IPFamily
Plus bool
Servers []Server
RewriteClientIP shared.RewriteClientIPSettings
IPFamily shared.IPFamily
Plus bool
DisableSNIHostValidation bool
}
9 changes: 5 additions & 4 deletions internal/controller/nginx/config/servers.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,11 @@ func (g GeneratorImpl) executeServers(
servers, httpMatchPairs := createServers(conf, generator, keepAliveCheck)

serverConfig := http.ServerConfig{
Servers: servers,
IPFamily: getIPFamily(conf.BaseHTTPConfig),
Plus: g.plus,
RewriteClientIP: getRewriteClientIPSettings(conf.BaseHTTPConfig.RewriteClientIPSettings),
Servers: servers,
IPFamily: getIPFamily(conf.BaseHTTPConfig),
Plus: g.plus,
RewriteClientIP: getRewriteClientIPSettings(conf.BaseHTTPConfig.RewriteClientIPSettings),
DisableSNIHostValidation: conf.BaseHTTPConfig.DisableSNIHostValidation,
}

serverResult := executeResult{
Expand Down
2 changes: 2 additions & 0 deletions internal/controller/nginx/config/servers_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,11 @@ server {
ssl_certificate {{ $s.SSL.Certificate }};
ssl_certificate_key {{ $s.SSL.CertificateKey }};

{{- if not $.DisableSNIHostValidation }}
if ($ssl_server_name != $host) {
return 421;
}
{{- end }}
{{- else }}
{{- if $.IPFamily.IPv4 }}
listen {{ $s.Listen }}{{ $.RewriteClientIP.ProxyProtocol }};
Expand Down
38 changes: 38 additions & 0 deletions internal/controller/nginx/config/servers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4024,3 +4024,41 @@ func TestGetIPFamily(t *testing.T) {
})
}
}

func TestExecuteServers_DisableSNIHostValidation(t *testing.T) {
t.Parallel()
g := NewWithT(t)

sslServer := dataplane.VirtualServer{
Hostname: "example.com",
SSL: &dataplane.SSL{
KeyPairID: "test-keypair",
},
Port: 8443,
}
gen := GeneratorImpl{}

// Case 1: DisableSNIHostValidation = false (default)
confWithValidation := dataplane.Configuration{
SSLServers: []dataplane.VirtualServer{sslServer},
BaseHTTPConfig: dataplane.BaseHTTPConfig{
DisableSNIHostValidation: false,
},
}
results := gen.executeServers(confWithValidation, &policiesfakes.FakeGenerator{}, alwaysFalseKeepAliveChecker)
serverConf := string(results[0].data)
g.Expect(serverConf).To(ContainSubstring("if ($ssl_server_name != $host)"),
"Expected SNI host validation block to be present when DisableSNIHostValidation is false")

// Case 2: DisableSNIHostValidation = true
confWithoutValidation := dataplane.Configuration{
SSLServers: []dataplane.VirtualServer{sslServer},
BaseHTTPConfig: dataplane.BaseHTTPConfig{
DisableSNIHostValidation: true,
},
}
results = gen.executeServers(confWithoutValidation, &policiesfakes.FakeGenerator{}, alwaysFalseKeepAliveChecker)
serverConf = string(results[0].data)
g.Expect(serverConf).NotTo(ContainSubstring("if ($ssl_server_name != $host)"),
"Expected SNI host validation block to be absent when DisableSNIHostValidation is true")
}
4 changes: 4 additions & 0 deletions internal/controller/state/dataplane/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -1010,6 +1010,10 @@ func buildBaseHTTPConfig(
baseConfig.HTTP2 = false
}

if np.DisableSNIHostValidation != nil && *np.DisableSNIHostValidation {
baseConfig.DisableSNIHostValidation = true
}

if np.IPFamily != nil {
switch *np.IPFamily {
case ngfAPIv1alpha2.IPv4:
Expand Down
12 changes: 7 additions & 5 deletions internal/controller/state/dataplane/configuration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -936,8 +936,9 @@ func TestBuildConfiguration(t *testing.T) {
},
ServiceName: helpers.GetPointer("my-svc"),
},
DisableHTTP2: helpers.GetPointer(true),
IPFamily: helpers.GetPointer(ngfAPIv1alpha2.Dual),
DisableHTTP2: helpers.GetPointer(true),
IPFamily: helpers.GetPointer(ngfAPIv1alpha2.Dual),
DisableSNIHostValidation: helpers.GetPointer(true),
}

nginxProxyIPv4 := &graph.EffectiveNginxProxy{
Expand Down Expand Up @@ -2236,9 +2237,10 @@ func TestBuildConfiguration(t *testing.T) {
SpanAttributes: []SpanAttribute{},
}
conf.BaseHTTPConfig = BaseHTTPConfig{
HTTP2: false,
IPFamily: Dual,
NginxReadinessProbePort: DefaultNginxReadinessProbePort,
HTTP2: false,
IPFamily: Dual,
NginxReadinessProbePort: DefaultNginxReadinessProbePort,
DisableSNIHostValidation: true,
}
return conf
}),
Expand Down
6 changes: 4 additions & 2 deletions internal/controller/state/dataplane/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -372,10 +372,12 @@ type BaseHTTPConfig struct {
Snippets []Snippet
// RewriteIPSettings defines configuration for rewriting the client IP to the original client's IP.
RewriteClientIPSettings RewriteClientIPSettings
// HTTP2 specifies whether http2 should be enabled for all servers.
HTTP2 bool
// NginxReadinessProbePort is the port on which the health check endpoint for NGINX is exposed.
NginxReadinessProbePort int32
// HTTP2 specifies whether http2 should be enabled for all servers.
HTTP2 bool
// DisableSNIHostValidation specifies if the SNI host validation should be disabled.
DisableSNIHostValidation bool
}

// Snippet is a snippet of configuration.
Expand Down
Loading