Files
seaweedfs/weed/security/guard_whitelist_test.go
Chris Lu 43a8c4fdca Revert #9440 — volume admin fail-closed gate breaks multi-host clusters (#9472)
* Revert "volume: fail closed in admin gRPC gate when no whitelist is configured (#9440)"

This reverts commit 21054b6c18.

The fail-closed gate broke any multi-host cluster: in compose / k8s /
remote-host deployments the master's IP isn't loopback, so every
master->volume admin RPC (AllocateVolume, BatchDelete, EC reroute,
vacuum, scrub, ...) is rejected with PermissionDenied unless the
operator manually configures -whiteList. The e2e workflow has been
failing since 10cc06333 with `not authorized: 172.18.0.2` on
AllocateVolume; downstream symptom is fio fsync EIO because zero
volumes can be grown.

The gate's intent was to lock down destructive admin tooling, but the
same RPCs are the master's normal mechanism for growing and managing
volumes. Reverting to restore cluster-internal operation; a narrower
re-do should distinguish operator/admin callers from the master peer
(e.g. trust IPs resolved from -master) before going back in.

* security: skip invalid CIDR in UpdateWhiteList so IsWhiteListed can't panic

The revert in the previous commit also rolled back an unrelated bug fix
that lived inside #9440: UpdateWhiteList logged on net.ParseCIDR error
but did not continue, so the nil *net.IPNet was stored in whiteListCIDR
and IsWhiteListed would panic dereferencing cidrnet.Contains(remote) on
the next gRPC admin check.

Restore the continue. Orthogonal to the fail-closed semantics this PR
is reverting.
2026-05-12 16:00:44 -07:00

51 lines
1.7 KiB
Go

package security
import "testing"
// TestIsWhiteListedEmptyConfigAllowsEverything pins the contract that the
// gRPC admin auth helper relies on: when no whitelist and no signing key
// are configured, IsWhiteListed accepts any host (including the empty
// string and unparseable peer addresses like the gRPC "@" passthrough
// form). Otherwise insecure default deployments would lock themselves out
// of every destructive admin RPC.
func TestIsWhiteListedEmptyConfigAllowsEverything(t *testing.T) {
g := NewGuard(nil, "", 0, "", 0)
for _, host := range []string{"", "@", "127.0.0.1", "::1", "garbage:value"} {
if !g.IsWhiteListed(host) {
t.Errorf("empty config should accept host=%q, got false", host)
}
}
}
func TestIsWhiteListedWithListRejectsUnknown(t *testing.T) {
g := NewGuard([]string{"10.0.0.1", "192.168.1.0/24"}, "", 0, "", 0)
cases := []struct {
host string
want bool
}{
{"10.0.0.1", true},
{"192.168.1.42", true},
{"192.168.2.1", false},
{"127.0.0.1", false},
{"", false},
{"@", false},
}
for _, tc := range cases {
if got := g.IsWhiteListed(tc.host); got != tc.want {
t.Errorf("IsWhiteListed(%q) = %v want %v", tc.host, got, tc.want)
}
}
}
func TestIsWhiteListedWithSigningKeyButNoWhitelistAllowsAll(t *testing.T) {
// JWT-only mode: signing key set, whitelist empty. Without this branch
// the gRPC admin auth helper would falsely deny in-process callers when
// the operator wires up signing keys but hasn't enumerated IPs.
g := NewGuard(nil, "deadbeef", 0, "", 0)
for _, host := range []string{"", "@", "127.0.0.1"} {
if !g.IsWhiteListed(host) {
t.Errorf("signing-key + empty whitelist should accept host=%q", host)
}
}
}