Remove explicit placeholder restrictions in interface binding to allow any globally registered placeholder

This commit is contained in:
Pavel Siomachkin 2025-10-12 14:13:12 +02:00
parent 7be7fe15d0
commit 47aa09b008
2 changed files with 14 additions and 29 deletions

View file

@ -891,27 +891,18 @@ func isValidInterfaceChar(r rune) bool {
} }
// resolveInterfacePlaceholder resolves Caddy placeholders in interface names. // resolveInterfacePlaceholder resolves Caddy placeholders in interface names.
// Only env.* and file.* placeholders are supported for interface binding. // Returns the resolved interface name and whether resolution was successful.
// Returns the resolved interface name and whether it contains only supported placeholders. // Any placeholder available in the global replacer context can be used.
func resolveInterfacePlaceholder(s string) (string, bool) { func resolveInterfacePlaceholder(s string) (string, bool) {
// If no placeholders, return as-is // If no placeholders, return as-is
if !strings.Contains(s, "{") { if !strings.Contains(s, "{") {
return s, true return s, true
} }
// Only allow env and file placeholders for interface names
if !strings.Contains(s, "{env.") && !strings.Contains(s, "{file.") {
return "", false
}
// Check for other placeholders that ReplaceKnown would process but we don't want
if strings.Contains(s, "{system.") || strings.Contains(s, "{time.") {
return "", false
}
repl := NewReplacer() repl := NewReplacer()
resolved := repl.ReplaceKnown(s, "") resolved := repl.ReplaceKnown(s, "")
// If no replacements were made or result is empty, reject it
if resolved == s || resolved == "" { if resolved == s || resolved == "" {
return "", false return "", false
} }

View file

@ -56,12 +56,10 @@ func TestIsInterfaceName(t *testing.T) {
{"eth\t0", false, "interface with tab"}, {"eth\t0", false, "interface with tab"},
{"eth\x00", false, "interface with null character"}, {"eth\x00", false, "interface with null character"},
// Invalid interface names (unsupported Caddy placeholders) // Invalid interface names (unregistered Caddy placeholders that won't be replaced)
{"{upstream}", false, "Caddy upstream placeholder"}, {"{upstream}", false, "Caddy upstream placeholder (not registered in global replacer)"},
{"{http.request.host}", false, "Caddy HTTP placeholder"}, {"{http.request.host}", false, "Caddy HTTP placeholder (not registered in global replacer)"},
{"{vars.interface}", false, "Caddy variable placeholder"}, {"{vars.interface}", false, "Caddy variable placeholder (not registered in global replacer)"},
{"{system.hostname}", false, "Caddy system placeholder"},
{"{time.now}", false, "Caddy time placeholder"},
} }
for _, test := range tests { for _, test := range tests {
@ -322,18 +320,14 @@ func TestIsInterfaceNameWithPlaceholders(t *testing.T) {
{"{env.TEST_INVALID_INTERFACE}", false, "env placeholder resolving to IP address"}, {"{env.TEST_INVALID_INTERFACE}", false, "env placeholder resolving to IP address"},
{"{file." + invalidTempFile.Name() + "}", false, "file placeholder resolving to hostname"}, {"{file." + invalidTempFile.Name() + "}", false, "file placeholder resolving to hostname"},
// Unsupported placeholders // Unregistered placeholders (not in global replacer, won't be replaced)
{"{http.request.host}", false, "unsupported HTTP placeholder"}, {"{http.request.host}", false, "HTTP placeholder (not in global replacer)"},
{"{vars.interface}", false, "unsupported variable placeholder"}, {"{vars.interface}", false, "vars placeholder (not in global replacer)"},
{"{system.hostname}", false, "unsupported system placeholder"}, {"{upstream}", false, "upstream placeholder (not in global replacer)"},
{"{time.now}", false, "unsupported time placeholder"},
{"{upstream}", false, "unsupported upstream placeholder"},
// Mixed placeholders (supported + unsupported) // Mixed with unregistered placeholders (partial replacement will fail)
{"eth{env.INTERFACE_NUM}-{http.request.host}", false, "mixed env and HTTP placeholders"}, {"eth{env.INTERFACE_NUM}-{http.request.host}", false, "mixed env and HTTP (HTTP not replaced, contains {)"},
{"{env.PREFIX}-{vars.suffix}", false, "mixed env and vars placeholders"}, {"{env.PREFIX}-{vars.suffix}", false, "mixed env and vars (vars not replaced, contains {)"},
{"eth{env.INTERFACE_NUM}{system.hostname}", false, "mixed env and system placeholders - system not allowed"},
{"{env.PREFIX}-{time.now}", false, "mixed env and time placeholders - time not allowed"},
// Invalid placeholder resolution // Invalid placeholder resolution
{"{env.NONEXISTENT}", false, "nonexistent environment variable"}, {"{env.NONEXISTENT}", false, "nonexistent environment variable"},