diff --git a/pkg/mattermost/database_external.go b/pkg/mattermost/database_external.go index 6487faa6..7ebb644b 100644 --- a/pkg/mattermost/database_external.go +++ b/pkg/mattermost/database_external.go @@ -1,13 +1,10 @@ package mattermost import ( - "context" "errors" "fmt" - "net" "net/url" "strings" - "time" mmv1beta "github.com/mattermost/mattermost-operator/apis/mattermost/v1beta1" "github.com/mattermost/mattermost-operator/pkg/database" @@ -159,23 +156,8 @@ func isAllowedDBCheckScheme(dbType, scheme string) bool { return schemes[scheme] } -// metadataIPBlocks contains IP ranges commonly used for cloud metadata services. -var metadataIPBlocks []*net.IPNet - -func init() { - for _, cidr := range []string{ - "169.254.169.254/32", // AWS, GCP, Azure metadata - "100.100.100.200/32", // Alibaba metadata - "fd00:ec2::254/128", // AWS IPv6 metadata - } { - _, block, _ := net.ParseCIDR(cidr) - metadataIPBlocks = append(metadataIPBlocks, block) - } -} - // validateDBCheckURL validates that a DB connection check URL uses an allowed -// scheme for the given database type and does not target cloud metadata endpoints. -// For hostnames, it resolves DNS and blocks any IP in metadata ranges. +// scheme for the given database type. func validateDBCheckURL(rawURL, dbType string) error { rawURL = strings.TrimSpace(rawURL) if rawURL == "" { @@ -192,46 +174,9 @@ func validateDBCheckURL(rawURL, dbType string) error { return fmt.Errorf("scheme %q is not allowed for database type %q", scheme, dbType) } - hostname := parsed.Hostname() - if hostname == "" { + if parsed.Hostname() == "" { return errors.New("URL must contain a hostname") } - ips, err := hostnameResolver(hostname) - if err != nil { - return fmt.Errorf("failed to resolve hostname %q: %w", hostname, err) - } - for _, ip := range ips { - for _, block := range metadataIPBlocks { - if block.Contains(ip) { - return fmt.Errorf("URL targets a blocked metadata IP range: %s", hostname) - } - } - } - return nil } - -// hostnameResolver is the function used to resolve hostnames to IPs. -// It defaults to defaultResolveHostnameIPs but can be replaced in tests -// to avoid real DNS lookups. -var hostnameResolver = defaultResolveHostnameIPs - -// defaultResolveHostnameIPs returns IPs for a hostname. If hostname is a literal IP, -// returns it; otherwise performs DNS lookup with timeout. -func defaultResolveHostnameIPs(hostname string) ([]net.IP, error) { - if ip := net.ParseIP(hostname); ip != nil { - return []net.IP{ip}, nil - } - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - addrs, err := net.DefaultResolver.LookupIPAddr(ctx, hostname) - if err != nil { - return nil, err - } - ips := make([]net.IP, 0, len(addrs)) - for _, a := range addrs { - ips = append(ips, a.IP) - } - return ips, nil -} diff --git a/pkg/mattermost/database_external_test.go b/pkg/mattermost/database_external_test.go index d2ffc936..72fd5504 100644 --- a/pkg/mattermost/database_external_test.go +++ b/pkg/mattermost/database_external_test.go @@ -1,8 +1,6 @@ package mattermost import ( - "net" - "strings" "testing" mmv1beta "github.com/mattermost/mattermost-operator/apis/mattermost/v1beta1" @@ -13,26 +11,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func stubResolver(t *testing.T) { - t.Helper() - - original := hostnameResolver - hostnameResolver = func(hostname string) ([]net.IP, error) { - if ip := net.ParseIP(hostname); ip != nil { - return []net.IP{ip}, nil - } - - return []net.IP{net.ParseIP("10.0.0.99")}, nil - } - - t.Cleanup(func() { - hostnameResolver = original - }) -} - func TestNewExternalDBInfo(t *testing.T) { - stubResolver(t) - mattermost := &mmv1beta.Mattermost{ ObjectMeta: metav1.ObjectMeta{Name: "mm-test"}, Spec: mmv1beta.MattermostSpec{ @@ -106,8 +85,6 @@ func TestNewExternalDBInfo(t *testing.T) { } func TestExternalDBConfig_SeparateDatasourceKey(t *testing.T) { - stubResolver(t) - mattermost := &mmv1beta.Mattermost{ ObjectMeta: metav1.ObjectMeta{Name: "mm-test"}, Spec: mmv1beta.MattermostSpec{ @@ -212,8 +189,6 @@ func TestExternalDBConfig_SeparateDatasourceKey(t *testing.T) { } func TestValidateDBCheckURL(t *testing.T) { - stubResolver(t) - t.Run("valid URLs for MySQL", func(t *testing.T) { validURLs := []string{ "http://my-db:3306", @@ -258,34 +233,6 @@ func TestValidateDBCheckURL(t *testing.T) { } }) - t.Run("blocked metadata IPs", func(t *testing.T) { - metadataURLs := []string{ - "http://169.254.169.254/latest/meta-data", - "http://100.100.100.200/metadata", - } - for _, u := range metadataURLs { - err := validateDBCheckURL(u, database.MySQLDatabase) - assert.Error(t, err, "expected blocked: %s", u) - assert.Contains(t, err.Error(), "blocked metadata") - } - }) - - t.Run("blocked hostname resolving to metadata IP", func(t *testing.T) { - original := hostnameResolver - hostnameResolver = defaultResolveHostnameIPs - t.Cleanup(func() { - hostnameResolver = original - }) - - // 169.254.169.254.nip.io resolves to AWS metadata IP (requires network) - err := validateDBCheckURL("http://169.254.169.254.nip.io/metadata", database.MySQLDatabase) - require.Error(t, err) - if strings.Contains(err.Error(), "failed to resolve") { - t.Skip("DNS unavailable in test environment; skipping hostname resolution test") - } - assert.Contains(t, err.Error(), "blocked metadata") - }) - t.Run("empty and invalid", func(t *testing.T) { assert.Error(t, validateDBCheckURL("", database.MySQLDatabase)) assert.Error(t, validateDBCheckURL("://no-scheme", database.MySQLDatabase)) @@ -293,8 +240,6 @@ func TestValidateDBCheckURL(t *testing.T) { } func TestNewExternalDBConfig_InvalidCheckURL(t *testing.T) { - stubResolver(t) - mattermost := &mmv1beta.Mattermost{ ObjectMeta: metav1.ObjectMeta{Name: "mm-test"}, Spec: mmv1beta.MattermostSpec{ @@ -304,19 +249,6 @@ func TestNewExternalDBConfig_InvalidCheckURL(t *testing.T) { }, } - t.Run("rejects metadata URL", func(t *testing.T) { - secret := corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{Name: "secret"}, - Data: map[string][]byte{ - "DB_CONNECTION_STRING": []byte("postgres://my-postgres"), - "DB_CONNECTION_CHECK_URL": []byte("http://169.254.169.254/latest/meta-data"), - }, - } - _, err := NewExternalDBConfig(mattermost, secret) - require.Error(t, err) - assert.Contains(t, err.Error(), "invalid DB_CONNECTION_CHECK_URL") - }) - t.Run("rejects disallowed scheme", func(t *testing.T) { secret := corev1.Secret{ ObjectMeta: metav1.ObjectMeta{Name: "secret"},