Check Client IPs Against a CIDR Allowlist
Build an IP allowlist from CIDR prefixes and check IPv4 or IPv6 addresses without allocating or trusting raw strings.
Code
package main
import (
"fmt"
"net/netip"
)
type Allowlist struct {
prefixes []netip.Prefix
}
func NewAllowlist(cidrs []string) (*Allowlist, error) {
list := &Allowlist{}
for _, cidr := range cidrs {
prefix, err := netip.ParsePrefix(cidr)
if err != nil {
return nil, fmt.Errorf("parse %q: %w", cidr, err)
}
list.prefixes = append(list.prefixes, prefix.Masked())
}
return list, nil
}
func (a *Allowlist) Allows(raw string) bool {
address, err := netip.ParseAddr(raw)
if err != nil {
return false
}
for _, prefix := range a.prefixes {
if prefix.Contains(address) {
return true
}
}
return false
}
func main() {
list, err := NewAllowlist([]string{"10.0.0.0/8", "192.168.1.0/24", "2001:db8::/32"})
if err != nil {
panic(err)
}
cases := map[string]bool{
"10.2.3.4": true,
"192.168.1.200": true,
"192.168.2.1": false,
"2001:db8::1": true,
"203.0.113.9": false,
}
for address, want := range cases {
got := list.Allows(address)
fmt.Printf("%-15s allowed=%v\n", address, got)
if got != want {
panic("unexpected result for " + address)
}
}
if _, err := NewAllowlist([]string{"10.0.0.0/33"}); err == nil {
panic("invalid prefix should fail")
}
}
Report a problem with this exampleOpens a pre-filled issue on GitHub.