fix(netpolicy): normalize allowlist CIDR inputs
This commit is contained in:
parent
bf89c4101f
commit
a86705340f
3 changed files with 57 additions and 2 deletions
|
|
@ -62,7 +62,7 @@ When `gateway.host` is a loopback address (`127.0.0.1`, `::1`, or `localhost`) a
|
||||||
CIDR sources in fallback mode:
|
CIDR sources in fallback mode:
|
||||||
|
|
||||||
- If `gateway.allowed_cidrs` is configured, that list is used.
|
- If `gateway.allowed_cidrs` is configured, that list is used.
|
||||||
- If `gateway.allowed_cidrs` is empty, discovered private CIDRs are used (`10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`, `100.64.0.0/10`, `fc00::/7`).
|
- If `gateway.allowed_cidrs` is empty, PicoClaw discovers CIDR networks from local interfaces and uses only those that fall within private address ranges (for example, within `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`, `100.64.0.0/10`, `fc00::/7`).
|
||||||
- If no private non-loopback CIDR can be discovered, gateway startup fails. On public-only hosts, configure `gateway.allowed_cidrs` explicitly.
|
- If no private non-loopback CIDR can be discovered, gateway startup fails. On public-only hosts, configure `gateway.allowed_cidrs` explicitly.
|
||||||
|
|
||||||
Loopback clients are always allowed for local administration.
|
Loopback clients are always allowed for local administration.
|
||||||
|
|
|
||||||
|
|
@ -19,15 +19,31 @@ func NewIPAllowlist(allowedCIDRs []string) (*IPAllowlist, error) {
|
||||||
return &IPAllowlist{}, nil
|
return &IPAllowlist{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
seen := make(map[string]struct{}, len(allowedCIDRs))
|
||||||
nets := make([]*net.IPNet, 0, len(allowedCIDRs))
|
nets := make([]*net.IPNet, 0, len(allowedCIDRs))
|
||||||
for _, cidr := range allowedCIDRs {
|
for _, rawCIDR := range allowedCIDRs {
|
||||||
|
cidr := strings.TrimSpace(rawCIDR)
|
||||||
|
if cidr == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
_, ipNet, err := net.ParseCIDR(cidr)
|
_, ipNet, err := net.ParseCIDR(cidr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("invalid CIDR %q: %w", cidr, err)
|
return nil, fmt.Errorf("invalid CIDR %q: %w", cidr, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
canonical := ipNet.String()
|
||||||
|
if _, ok := seen[canonical]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[canonical] = struct{}{}
|
||||||
nets = append(nets, ipNet)
|
nets = append(nets, ipNet)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(nets) == 0 {
|
||||||
|
return &IPAllowlist{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
return &IPAllowlist{nets: nets}, nil
|
return &IPAllowlist{nets: nets}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -69,3 +69,42 @@ func TestIPAllowlistWithZoneAddressInCIDR(t *testing.T) {
|
||||||
t.Fatal("allowlist should accept IPv6 link-local with zone")
|
t.Fatal("allowlist should accept IPv6 link-local with zone")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNewIPAllowlistTrimsSkipsAndDedups(t *testing.T) {
|
||||||
|
allowlist, err := NewIPAllowlist([]string{
|
||||||
|
" 192.168.1.8/24 ",
|
||||||
|
"",
|
||||||
|
"192.168.1.0/24",
|
||||||
|
" ",
|
||||||
|
"10.0.0.0/8",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewIPAllowlist() error = %v", err)
|
||||||
|
}
|
||||||
|
if allowlist.IsOpen() {
|
||||||
|
t.Fatal("allowlist should not be open")
|
||||||
|
}
|
||||||
|
if len(allowlist.nets) != 2 {
|
||||||
|
t.Fatalf("len(allowlist.nets) = %d, want 2", len(allowlist.nets))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !allowlist.AllowsRemoteAddr("192.168.1.22:1234") {
|
||||||
|
t.Fatal("allowlist should allow deduplicated 192.168.1.0/24 CIDR")
|
||||||
|
}
|
||||||
|
if !allowlist.AllowsRemoteAddr("10.9.8.7:1234") {
|
||||||
|
t.Fatal("allowlist should allow 10.0.0.0/8 CIDR")
|
||||||
|
}
|
||||||
|
if allowlist.AllowsRemoteAddr("203.0.113.7:1234") {
|
||||||
|
t.Fatal("allowlist should reject outside CIDR")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewIPAllowlistAllEmptyEntries(t *testing.T) {
|
||||||
|
allowlist, err := NewIPAllowlist([]string{"", " ", "\t"})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewIPAllowlist() error = %v", err)
|
||||||
|
}
|
||||||
|
if !allowlist.IsOpen() {
|
||||||
|
t.Fatal("allowlist should be open when all entries are empty")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue