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:
|
||||
|
||||
- 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.
|
||||
|
||||
Loopback clients are always allowed for local administration.
|
||||
|
|
|
|||
|
|
@ -19,15 +19,31 @@ func NewIPAllowlist(allowedCIDRs []string) (*IPAllowlist, error) {
|
|||
return &IPAllowlist{}, nil
|
||||
}
|
||||
|
||||
seen := make(map[string]struct{}, 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)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
if len(nets) == 0 {
|
||||
return &IPAllowlist{}, 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")
|
||||
}
|
||||
}
|
||||
|
||||
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