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.
// Only env.* and file.* placeholders are supported for interface binding.
// Returns the resolved interface name and whether it contains only supported placeholders.
// Returns the resolved interface name and whether resolution was successful.
// Any placeholder available in the global replacer context can be used.
func resolveInterfacePlaceholder(s string) (string, bool) {
// If no placeholders, return as-is
if !strings.Contains(s, "{") {
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()
resolved := repl.ReplaceKnown(s, "")
// If no replacements were made or result is empty, reject it
if resolved == s || resolved == "" {
return "", false
}

View file

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