Allow non-SNI requests and fix 421 responses#4878
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #4878 +/- ##
==========================================
+ Coverage 85.89% 85.91% +0.02%
==========================================
Files 143 143
Lines 16744 16813 +69
Branches 35 35
==========================================
+ Hits 14382 14445 +63
- Misses 2111 2116 +5
- Partials 251 252 +1 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
This PR updates NGINX Gateway Fabric’s HTTPS handling to support TLS termination for requests without SNI (when an HTTPS Listener has no hostname) and adjusts “misdirected request” (421) behavior to align with the Gateway API’s connection coalescing expectations.
Changes:
- Populate the default SSL server with a cert/key derived from the empty-hostname HTTPS Listener, enabling non-SNI TLS handshakes.
- Introduce per-port NGINX
mapvariables to compare SNI-derived vs Host-derived listener selection for misdirected request detection. - Advertise
SupportGatewayHTTPSListenerDetectMisdirectedRequestsas a supported Gateway API feature and extend unit tests accordingly.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| internal/controller/status/gatewayclass.go | Adds the misdirected-requests feature to the supported feature set. |
| internal/controller/status/gatewayclass_test.go | Updates supported-feature tests to include the new feature. |
| internal/controller/state/dataplane/types.go | Adds SSLListenerHostnames to the dataplane configuration model. |
| internal/controller/state/dataplane/configuration.go | Builds SSLListenerHostnames and propagates default SSL cert config for the default server. |
| internal/controller/state/dataplane/configuration_test.go | Updates expected configurations to include default SSL and listener hostname mappings. |
| internal/controller/nginx/config/http/config.go | Adds MisdirectedRequestVars to the rendered server model. |
| internal/controller/nginx/config/servers.go | Threads misdirected-request vars into SSL servers; supports default SSL server cert/key material. |
| internal/controller/nginx/config/servers_template.go | Updates templates to optionally terminate TLS on the default server and to use the new misdirected-request vars. |
| internal/controller/nginx/config/servers_test.go | Updates server generation tests for the new misdirected-request variable names. |
| internal/controller/nginx/config/maps.go | Adds misdirected-request map generation driven by SSLListenerHostnames. |
| internal/controller/nginx/config/maps_test.go | Extends map execution tests and adds focused tests for the new misdirected-request maps. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Problem: Some frontend load balancers strip out SNI info before the client request reaches the NGINX Gateway. With the current behavior, the request will hit our default_server and the handshake is rejected (handshake rejected while SSL handshaking). It will never be forwarded to the proper server block to handle that request and proxy to the backend.
In addition, we always return a 421 when the SNI and Host headers don't match, but the Gateway API indicates that this isn't always the case.
* If another Listener has an exact match or more specific wildcard entry, the Gateway SHOULD return a 421.
* If the current Listener (selected by SNI matching during ClientHello) oes not match the Host:
* If another Listener does match the Host, the Gateway SHOULD return a 421.
* If no other Listener matches the Host, the Gateway MUST return a 404.
Solution: If an HTTPS Listener without a hostname is specified, use the cert/key on that listener in the default server block to allow for non-SNI requests to have TLS termination, before sent to the proper server block based on the host header. Adjust the logic for returning 421 to match the conditions in the Gateway API spec.
Problem: Some frontend load balancers strip out SNI info before the client request reaches the NGINX Gateway. With the current behavior, the request will hit our default_server and the handshake is rejected (handshake rejected while SSL handshaking). It will never be forwarded to the proper server block to handle that request and proxy to the backend.
In addition, we always return a 421 when the SNI and Host headers don't match, but the Gateway API indicates that this isn't always the case.
Solution: If an HTTPS Listener without a hostname is specified, use the cert/key on that listener in the default server block to allow for non-SNI requests to have TLS termination, before sent to the proper server block based on the host header. Adjust the logic for returning 421 to match the conditions in the Gateway API spec.
Testing: Manually verified request without SNI are routed properly.
Please focus on (optional): If you any specific areas where you would like reviewers to focus their attention or provide
specific feedback, add them here.
Closes #4836
Closes #4838
Checklist
Before creating a PR, run through this checklist and mark each as complete.
Release notes
If this PR introduces a change that affects users and needs to be mentioned in the release notes,
please add a brief note that summarizes the change.