diff options
author | pg9182 <96569817+pg9182@users.noreply.github.com> | 2022-10-21 02:25:01 -0400 |
---|---|---|
committer | pg9182 <96569817+pg9182@users.noreply.github.com> | 2022-10-21 02:25:01 -0400 |
commit | 950e311c82c90a00a3b5201b44c0faaaafb19967 (patch) | |
tree | 0d1b46d317799ff6b683ae8c6f0cd58a6f3f8b73 /pkg/cloudflare | |
parent | 2c983b4561d0d66b3613f32e1e0b6121779b094c (diff) | |
download | Atlas-950e311c82c90a00a3b5201b44c0faaaafb19967.tar.gz Atlas-950e311c82c90a00a3b5201b44c0faaaafb19967.zip |
pkg/cloudflare: Implement Cloudflare IP list checks
Diffstat (limited to 'pkg/cloudflare')
-rw-r--r-- | pkg/cloudflare/iplist.go | 88 | ||||
-rw-r--r-- | pkg/cloudflare/iplist_generate.go | 52 | ||||
-rw-r--r-- | pkg/cloudflare/iplist_generated.go | 34 |
3 files changed, 174 insertions, 0 deletions
diff --git a/pkg/cloudflare/iplist.go b/pkg/cloudflare/iplist.go new file mode 100644 index 0000000..d19367e --- /dev/null +++ b/pkg/cloudflare/iplist.go @@ -0,0 +1,88 @@ +// Package cloudflare contains Cloudflare-related stuff. +package cloudflare + +import ( + "bufio" + "context" + "fmt" + "net/http" + "net/netip" + "os" + "strconv" + "strings" + "sync" +) + +var iplistMu sync.Mutex +var iplist []netip.Prefix + +//go:generate go run iplist_generate.go + +// HasIP checks if ip is in a Cloudflare prefix. +func HasIP(ip netip.Addr) bool { + for _, p := range iplist { + if p.Contains(ip) { + return true + } + } + return false +} + +// UpdateIPs updates the Cloudflare IP list. +func UpdateIPs(ctx context.Context) error { + var ps []netip.Prefix + for _, v := range []int{4, 6} { + if v, err := fetchIPs(ctx, "https://www.cloudflare.com/ips-v"+strconv.Itoa(v)); err == nil { + ps = append(ps, v...) + } else { + fmt.Fprintf(os.Stderr, "error: fetch ipv%d list: %v\n", v, err) + os.Exit(1) + } + } + iplistMu.Lock() + iplist = ps + iplistMu.Unlock() + return nil +} + +func fetchIPs(ctx context.Context, u string) ([]netip.Prefix, error) { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil) + if err != nil { + return nil, err + } + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("response status %d (%s)", resp.StatusCode, resp.Status) + } + + var r []netip.Prefix + s := bufio.NewScanner(resp.Body) + for s.Scan() { + if t := strings.TrimSpace(s.Text()); t != "" { + if strings.ContainsRune(t, '/') { + if x, err := netip.ParsePrefix(t); err == nil { + r = append(r, x) + } else { + return nil, fmt.Errorf("invalid prefix %q: %w", t, err) + } + } else { + if x, err := netip.ParseAddr(t); err == nil { + if p, err := x.Prefix(x.BitLen()); err == nil { + r = append(r, p) + } else { + panic(err) + } + } else { + return nil, fmt.Errorf("invalid ip %q: %w", t, err) + } + } + } + } + return r, s.Err() +} diff --git a/pkg/cloudflare/iplist_generate.go b/pkg/cloudflare/iplist_generate.go new file mode 100644 index 0000000..7e169c4 --- /dev/null +++ b/pkg/cloudflare/iplist_generate.go @@ -0,0 +1,52 @@ +//go:build generate +// +build generate + +package main + +import ( + "bytes" + "context" + "fmt" + "go/format" + "net/netip" + "os" + "time" + _ "unsafe" + + "github.com/pg9182/atlas/pkg/cloudflare" +) + +//go:linkname iplist github.com/pg9182/atlas/pkg/cloudflare.iplist +var iplist []netip.Prefix + +func main() { + if err := cloudflare.UpdateIPs(context.Background()); err != nil { + fmt.Fprintf(os.Stderr, "error: fetch ip list: %v\n", err) + os.Exit(1) + } + + var b bytes.Buffer + fmt.Fprintf(&b, "// Code generated by iplist_generate on %s; DO NOT EDIT.\n", time.Now().UTC()) + fmt.Fprintf(&b, "\n\n") + fmt.Fprintf(&b, "package cloudflare\n") + fmt.Fprintf(&b, "\n\n") + fmt.Fprintf(&b, "import %#v\n", "net/netip") + fmt.Fprintf(&b, "\n\n") + fmt.Fprintf(&b, "//go:generate go run iplist_generate.go\n") + fmt.Fprintf(&b, "\n\n") + fmt.Fprintf(&b, "func init() {\n") + fmt.Fprintf(&b, "iplist = append(iplist,\n") + for _, p := range iplist { + fmt.Fprintf(&b, "netip.MustParsePrefix(%#v),\n", p.String()) + } + fmt.Fprintf(&b, ")\n") + fmt.Fprintf(&b, "}\n") + + if src, err := format.Source(b.Bytes()); err != nil { + fmt.Fprintf(os.Stderr, "error: format generated code: %v\n", err) + os.Exit(1) + } else if err = os.WriteFile("iplist_generated.go", src, 0666); err != nil { + fmt.Fprintf(os.Stderr, "error: write generated code: %v\n", err) + os.Exit(1) + } +} diff --git a/pkg/cloudflare/iplist_generated.go b/pkg/cloudflare/iplist_generated.go new file mode 100644 index 0000000..5bf8ed6 --- /dev/null +++ b/pkg/cloudflare/iplist_generated.go @@ -0,0 +1,34 @@ +// Code generated by iplist_generate on 2022-10-21 06:19:36.019416787 +0000 UTC; DO NOT EDIT. + +package cloudflare + +import "net/netip" + +//go:generate go run iplist_generate.go + +func init() { + iplist = append(iplist, + netip.MustParsePrefix("173.245.48.0/20"), + netip.MustParsePrefix("103.21.244.0/22"), + netip.MustParsePrefix("103.22.200.0/22"), + netip.MustParsePrefix("103.31.4.0/22"), + netip.MustParsePrefix("141.101.64.0/18"), + netip.MustParsePrefix("108.162.192.0/18"), + netip.MustParsePrefix("190.93.240.0/20"), + netip.MustParsePrefix("188.114.96.0/20"), + netip.MustParsePrefix("197.234.240.0/22"), + netip.MustParsePrefix("198.41.128.0/17"), + netip.MustParsePrefix("162.158.0.0/15"), + netip.MustParsePrefix("104.16.0.0/13"), + netip.MustParsePrefix("104.24.0.0/14"), + netip.MustParsePrefix("172.64.0.0/13"), + netip.MustParsePrefix("131.0.72.0/22"), + netip.MustParsePrefix("2400:cb00::/32"), + netip.MustParsePrefix("2606:4700::/32"), + netip.MustParsePrefix("2803:f800::/32"), + netip.MustParsePrefix("2405:b500::/32"), + netip.MustParsePrefix("2405:8100::/32"), + netip.MustParsePrefix("2a06:98c0::/29"), + netip.MustParsePrefix("2c0f:f248::/32"), + ) +} |