fix: restrict supervisor drain to loopback

This commit is contained in:
Josh Creek
2026-08-31 21:49:22 +01:00
parent faede927fc
commit 6253b620a9
4 changed files with 42 additions and 2 deletions
+25
View File
@@ -7,7 +7,9 @@ import (
"context"
"encoding/json"
"fmt"
"net"
"net/http"
"net/url"
"os"
"os/exec"
"strconv"
@@ -63,9 +65,32 @@ func New(config Config) (*Supervisor, error) {
if config.HTTPClient == nil {
config.HTTPClient = http.DefaultClient
}
if (config.DrainURL == "") != (config.DrainToken == "") {
return nil, fmt.Errorf("drain URL and token must be configured together")
}
if config.DrainURL != "" {
if err := validateLocalDrainURL(config.DrainURL); err != nil {
return nil, err
}
}
return &Supervisor{config: config, client: config.HTTPClient}, nil
}
func validateLocalDrainURL(raw string) error {
parsed, err := url.Parse(raw)
if err != nil || (parsed.Scheme != "http" && parsed.Scheme != "https") || parsed.Host == "" || parsed.Path == "" || parsed.User != nil || parsed.RawQuery != "" || parsed.Fragment != "" {
return fmt.Errorf("drain URL must be a loopback HTTP endpoint")
}
host := parsed.Hostname()
if host != "localhost" {
ip := net.ParseIP(host)
if ip == nil || !ip.IsLoopback() {
return fmt.Errorf("drain URL must be a loopback HTTP endpoint")
}
}
return nil
}
// Start launches the process and marks Agones Ready only after the explicit
// readiness probe succeeds. No stdout/log scraping is used. With no SDK URL,
// this is direct/Compose mode and the command is simply started.
+13
View File
@@ -167,3 +167,16 @@ func TestAssignedEndpointRejectsMalformedAddressAndPort(t *testing.T) {
server.Close()
}
}
func TestSupervisorRejectsRemoteOrPartialDrainConfiguration(t *testing.T) {
for _, config := range []Config{
{Command: []string{"/bin/sh", "-c", "exit 0"}, DrainURL: "https://example.com/drain", DrainToken: "token-1234567890123456"},
{Command: []string{"/bin/sh", "-c", "exit 0"}, DrainURL: "http://127.0.0.1/drain"},
{Command: []string{"/bin/sh", "-c", "exit 0"}, DrainToken: "token-1234567890123456"},
{Command: []string{"/bin/sh", "-c", "exit 0"}, DrainURL: "http://127.0.0.1/drain?token=leaked", DrainToken: "token-1234567890123456"},
} {
if _, err := New(config); err == nil {
t.Fatalf("unsafe drain configuration accepted: %+v", config)
}
}
}