fix HTTPS frontend name mismatch breaking backend pathID matching#1433
fix HTTPS frontend name mismatch breaking backend pathID matching#1433jcmoraisjr merged 2 commits intojcmoraisjr:masterfrom
Conversation
|
I couldn't get the integration tests working on my laptop, so 🤞 that the automated CI ones will work (but I'm going to sleep and might not be able to check for a few days). |
When ssl-passthrough is enabled, the HTTPS frontend is rendered as _front_https__local in haproxy.tmpl, but createPathsMaps() used Frontend.Name (_front_https without the suffix). This caused the var(req.fe) string match to fail, silently skipping all path-conditional rules (rewrite-target, HSTS, ssl-redirect, cors, etc.). Add Frontend.RenderedName() that includes the __local suffix when appropriate, and use it in both createPathsMaps() and the template. Fixes jcmoraisjr#1432
8bd092d to
0a1eb32
Compare
There was a problem hiding this comment.
Pull request overview
Fixes a mismatch between the rendered HAProxy HTTPS frontend name when SSL passthrough is enabled (e.g. _front_https__local) and the name used to build backend path maps / frontend ACL matching, which previously caused var(req.fe) checks to miss and path-conditional rules to be skipped.
Changes:
- Introduce
Frontend.RenderedName()to centralize the “__localwhen SSL passthrough” naming logic. - Use
RenderedName()in bothhaproxy.tmpland backendcreatePathsMaps()so map filenames +req.fematching align with the actual frontend name. - Add a regression test covering mixed HTTP/HTTPS paths on the same backend plus an SSL-passthrough host forcing the
__localfrontend name.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
rootfs/etc/templates/haproxy/haproxy.tmpl |
Switch HTTPS frontend naming to RenderedName() so the config frontend name matches the model. |
pkg/haproxy/types/frontend.go |
Add RenderedName() helper for consistent frontend naming when SSL passthrough is active. |
pkg/haproxy/types/backend.go |
Use RenderedName() when grouping/creating per-frontend backend path maps. |
pkg/haproxy/instance_test.go |
Add regression test asserting correct req.fe matching with _front_https__local. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
|
Awesome, thanks for the report and PR! I think that the method could be a field instead, populated via However, my guess is that the @copilot do you see any caveat or hidden misbehavior if we fix #1432 by simply patching the -{{- $httpsFrontendName := printf "%s%s" $frontend.Name (iif $hasSSLPassthrough "__local" "") }}
-frontend {{ $httpsFrontendName }}
+frontend {{ $frontend.Name }} |
Thank you for all the work over the last many years!
That makes sense.
If I remember right from my limited understanding yesterday, in the scenario where a frontend is used for passthrough then we need two "frontends", the one listening to incoming connections and another for dispatching the actual HTTP requests that aren't passthrough (or vice versa?). __local might not be the best name though! |
<3
The one for the SNI inspection is already named differently: Define: haproxy-ingress/pkg/haproxy/config.go Lines 103 to 112 in 1ab99c2 Use: haproxy-ingress/rootfs/etc/templates/haproxy/haproxy.tmpl Lines 1174 to 1180 in 1ab99c2 IIRC the I prefer not having another indirection, but also don't want to break things in the metrics side. We are not in a hurry, this patch can wait the next week, so I'll invest some time revisiting the metrics dashboard and will let you know if we are good removing the |
|
I revisited metrics and in fact it depends on the frontends named the way they are now, otherwise some counters should overlap each other, providing non accurate values. So any changes on this land should have a metrics counterpart, and probably adding more backward incompatibility. That said, lets go to the render name approach which fixes the issue for now. However, I think we can use instead a field in the frontend type, updated via SyncConfig(), since all the calls happen after that point. This way we have a better performance in the hot path and make copilot happy. |
Replace the Frontend.RenderedName() method with a RenderedName string field, set via AcquireFrontend (default) and overridden in config.syncFrontend() when the frontend is HTTPS with SSL passthrough. Keeps the __local suffix (metrics counters depend on the current frontend naming) while avoiding the linear host scan on every createPathsMaps() iteration.
|
Thanks for digging into the metrics side. Went with the field approach as you suggested — 1b377a2 replaces the method with a |
There was a problem hiding this comment.
Pull request overview
Fixes a mismatch between the rendered HTTPS frontend name in haproxy.cfg (notably _front_https__local when ssl-passthrough is active) and the frontend name used when building backend path maps, which previously caused txn.pathID-dependent features (rewrite-target, HSTS, ssl-redirect, CORS, etc.) to be skipped.
Changes:
- Introduces
Frontend.RenderedNameand populates it duringconfig.SyncConfig()(adding__localfor HTTPS frontends when ssl-passthrough is enabled). - Uses
RenderedNamefor HTTPS frontend naming inhaproxy.tmpland when grouping backend path maps (createPathsMaps()), aligningvar(req.fe)matching with the actual frontend name. - Adds an instance-level regression test asserting correct
var(req.fe)matching for_front_https__local.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| rootfs/etc/templates/haproxy/haproxy.tmpl | Renders HTTPS frontend using Frontend.RenderedName to match actual HAProxy frontend name under ssl-passthrough. |
| pkg/haproxy/types/types.go | Adds RenderedName field to Frontend. |
| pkg/haproxy/types/frontend.go | Initializes RenderedName when acquiring a frontend. |
| pkg/haproxy/types/backend.go | Uses Frontend.RenderedName when building backend PathsMaps() groups/keys. |
| pkg/haproxy/config.go | Sets Frontend.RenderedName during SyncConfig() based on ssl-passthrough state. |
| pkg/haproxy/instance_test.go | Adds a regression test covering _front_https__local frontend ACL/pathID matching. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| func (b *Backend) createPathsMaps() []*BackendPathsMaps { | ||
| var pathsMaps []*BackendPathsMaps | ||
| for _, path := range b.Paths { | ||
| frontendName := path.Host.Frontend.Name | ||
| frontendName := path.Host.Frontend.RenderedName | ||
| i := slices.IndexFunc(pathsMaps, func(b *BackendPathsMaps) bool { return slices.Contains(b.Frontends, frontendName) }) | ||
| if i < 0 { |
There was a problem hiding this comment.
Backend.PathsMaps() caches the derived Frontends list (now based on Frontend.RenderedName). Because RenderedName is mutated during config.SyncConfig() (e.g., when any host enables/disables ssl-passthrough) but b.pathsMaps is never invalidated, a subsequent update can keep stale frontend names and keep generating var(req.fe) -m str _front_https / old map filenames for backends that were not rebuilt in a partial sync. This can reintroduce the pathID mismatch for unchanged backends when ssl-passthrough is toggled elsewhere.
Suggested fix: make PathsMaps()/PathConfigs() caches self-invalidating when any referenced path.Host.Frontend.RenderedName changes (e.g., track a hash/version), or explicitly clear these caches on backends whenever RenderedName changes during sync (and ensure map generation considers those backends dirty as needed). Adding a regression test that performs two consecutive updates (first without ssl-passthrough, then enabling it on a different host) would catch this.
There was a problem hiding this comment.
I understand Copilot's worry here, but this is not something that should impact the expected behavior. There are two premises used in haproxy model that makes our life easier: fields are never changed once populated during a single reconciliation, and caches are invalidated and rebuilt on every reconciliation. That said, it's safe the way it is, further safeguards would only make code more complex.
|
Looks good, thanks! |
When ssl-passthrough is enabled, the HTTPS frontend is rendered as _front_https__local in haproxy.tmpl, but createPathsMaps() used Frontend.Name (_front_https without the suffix). This caused the var(req.fe) string match to fail, silently skipping all path-conditional rules (rewrite-target, HSTS, ssl-redirect, cors, etc.).
Add Frontend.RenderedName() that includes the __local suffix when appropriate, and use it in both createPathsMaps() and the template.
Fixes #1432.