Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# cloudranger

`cloudranger` is a Go library designed to identify cloud provider information from IP addresses, with current support for AWS and GCP.
`cloudranger` is a Go library designed to identify cloud provider information from IP addresses, with current support for AWS, Cloudflare, and GCP.

It functions without any external runtime dependencies, as IP range data is stored internally. Meant for high throughput, low-latency environments, `cloudranger` also focuses on rapid startup, loading in under 4ms. You can verify this on your system by running `make bench` and checking the `BenchmarkNew` results.

Expand Down Expand Up @@ -63,6 +63,7 @@ BenchmarkGetIP-16 6268292 194.8 ns/op 64 B/op
IP range data is sourced from:

- AWS: https://ip-ranges.amazonaws.com/ip-ranges.json
- Cloudflare: https://api.cloudflare.com/client/v4/ips?networks=jdcloud
- GCP: https://www.gstatic.com/ipranges/cloud.json

A GitHub Actions workflow is run weekly to update the IP range data if changed by the supported cloud providers. A new version is created and tagged if changes are detected. Use dependabot or renovate to automate updates to the latest version.
81 changes: 81 additions & 0 deletions cmd/gen/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,20 @@ type GCPPrefix struct {
Service string `json:"service"` // currently ignored, and it's always the same 'Google Cloud Platform' anyway
}

// CloudflareIPData represents the structure of the JSON data from Cloudflare.
type CloudflareIPData struct {
Result CloudflareIPResult `json:"result"`
Success bool `json:"success"` // currently ignored
}

// CloudflareIPResult contains the IP prefix lists.
type CloudflareIPResult struct {
IPv4CIDRs []string `json:"ipv4_cidrs"`
IPv6CIDRs []string `json:"ipv6_cidrs"`
JDCloudCIDRs []string `json:"jdcloud_cidrs"`
ETag string `json:"etag"` // currently ignored
}

func main() {
out, err := os.Create("zz_generated.go")
if err != nil {
Expand Down Expand Up @@ -164,6 +178,73 @@ func main() {
}
}

// Cloudflare
// {&net.IPNet{IP: []byte{173, 245, 48, 0}, Mask: []byte{255, 255, 240, 0}}, IPInfo{cloud: "Cloudflare", region: ""}},
{
var data CloudflareIPData

d, err := os.ReadFile("data/cloudflare-ips.json")
if err != nil {
log.Fatal(err)
}

if err := json.Unmarshal(d, &data); err != nil {
log.Fatal(err)
}

for _, prefix := range data.Result.IPv4CIDRs {
_, ipnet, err := net.ParseCIDR(prefix)
if err != nil {
log.Fatal(err)
}
fmt.Fprintf(out, "\t// Cloudflare: %s, region: \"\"\n", prefix) //nolint:errcheck
fmt.Fprintf(out, //nolint:errcheck
"\t{&net.IPNet{IP: []byte{%d, %d, %d, %d}, Mask: []byte{%d, %d, %d, %d}}, IPInfo{cloud: \"Cloudflare\", region: \"\"}},\n",
ipnet.IP[0], ipnet.IP[1], ipnet.IP[2], ipnet.IP[3],
ipnet.Mask[0], ipnet.Mask[1], ipnet.Mask[2], ipnet.Mask[3],
)
}

for _, prefix := range data.Result.IPv6CIDRs {
_, ipnet, err := net.ParseCIDR(prefix)
if err != nil {
log.Fatal(err)
}
i := ipnet.IP
m := ipnet.Mask
fmt.Fprintf(out, "\t// Cloudflare: %s, region: \"\"\n", prefix) //nolint:errcheck
fmt.Fprintf(out, //nolint:errcheck
"\t{&net.IPNet{IP: []byte{%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d}, Mask: []byte{%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d}}, IPInfo{cloud: \"Cloudflare\", region: \"\"}},\n",
i[0], i[1], i[2], i[3], i[4], i[5], i[6], i[7], i[8], i[9], i[10], i[11], i[12], i[13], i[14], i[15],
m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8], m[9], m[10], m[11], m[12], m[13], m[14], m[15],
)
}

for _, prefix := range data.Result.JDCloudCIDRs {
_, ipnet, err := net.ParseCIDR(prefix)
if err != nil {
log.Fatal(err)
}
if len(ipnet.IP) == net.IPv4len {
fmt.Fprintf(out, "\t// Cloudflare: %s, region: \"cn\"\n", prefix) //nolint:errcheck
fmt.Fprintf(out, //nolint:errcheck
"\t{&net.IPNet{IP: []byte{%d, %d, %d, %d}, Mask: []byte{%d, %d, %d, %d}}, IPInfo{cloud: \"Cloudflare\", region: \"cn\"}},\n",
ipnet.IP[0], ipnet.IP[1], ipnet.IP[2], ipnet.IP[3],
ipnet.Mask[0], ipnet.Mask[1], ipnet.Mask[2], ipnet.Mask[3],
)
} else {
i := ipnet.IP
m := ipnet.Mask
fmt.Fprintf(out, "\t// Cloudflare: %s, region: \"cn\"\n", prefix) //nolint:errcheck
fmt.Fprintf(out, //nolint:errcheck
"\t{&net.IPNet{IP: []byte{%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d}, Mask: []byte{%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d}}, IPInfo{cloud: \"Cloudflare\", region: \"cn\"}},\n",
i[0], i[1], i[2], i[3], i[4], i[5], i[6], i[7], i[8], i[9], i[10], i[11], i[12], i[13], i[14], i[15],
m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8], m[9], m[10], m[11], m[12], m[13], m[14], m[15],
)
}
}
}

_, err = io.WriteString(out, footer)
if err != nil {
log.Fatal(err)
Expand Down
1 change: 1 addition & 0 deletions data/cloudflare-ips.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"result":{"ipv4_cidrs":["173.245.48.0/20","103.21.244.0/22","103.22.200.0/22","103.31.4.0/22","141.101.64.0/18","108.162.192.0/18","190.93.240.0/20","188.114.96.0/20","197.234.240.0/22","198.41.128.0/17","162.158.0.0/15","104.16.0.0/13","104.24.0.0/14","172.64.0.0/13","131.0.72.0/22"],"ipv6_cidrs":["2400:cb00::/32","2606:4700::/32","2803:f800::/32","2405:b500::/32","2405:8100::/32","2a06:98c0::/29","2c0f:f248::/32"],"jdcloud_cidrs":["14.204.96.224/27","27.36.126.224/27","27.128.218.224/27","36.136.95.32/27","36.147.52.160/27","36.154.11.224/27","42.236.121.160/27","60.13.99.64/26","101.69.205.224/27","103.44.252.32/27","106.225.240.96/27","111.7.87.160/27","111.48.87.160/27","111.124.70.32/27","111.170.27.96/27","111.177.11.224/27","112.49.47.96/27","113.56.217.96/27","114.67.161.32/27","114.67.192.208/28","116.163.41.64/26","116.198.49.144/28","116.198.165.16/28","117.187.40.32/27","117.187.185.32/27","118.180.26.32/27","119.0.67.32/27","119.6.235.32/27","119.188.204.32/27","120.206.188.224/27","120.220.55.96/27","120.226.37.160/27","121.17.125.32/27","122.190.152.160/27","122.226.163.224/27","123.138.203.160/27","124.166.232.32/27","124.225.84.32/27","124.236.72.32/27","125.77.31.224/27","150.138.153.192/26","182.201.240.224/27","183.131.87.224/27","198.41.130.16/28","218.205.95.64/27","218.207.1.32/27","220.185.189.128/25","222.211.66.64/27","223.85.111.224/27","2400:cb00:164:0:1000::/68","2403:1ec0:1200:ff98::/64","2403:1ec0:1400:ff01::/64","2403:1ec0:1400:ff05::/64","2403:1ec0:1610:ff05::/64","2408:8719:64:50:1000::/68","2408:871a:8810:205:1000::/68","2408:8720:806:102:1000::/68","2408:8726:3000:fff4:1000::/68","2408:8740:71fc:406:1000::/68","2408:874f:8000:20:1000::/68","2408:874f:b000:4:1000::/68","2408:8752:600:6:1000::/68","2408:8756:4cff:d002:1000::/68","2408:8760:107:2:1000::/68","2408:8766:5:3:1000::/68","2408:876c:2c0:112:1000::/68","2408:8773:fff:ffe1:1000::/68","2409:8720:4001:2:1000::/68","2409:8760:1e81:52:1000::/68","2409:8c28:203:a:1000::/68","2409:8c34:d00:6:1000::/68","2409:8c38:c50:604:1000::/68","2409:8c3c:1400:5:1000::/68","2409:8c44:1b00:d06:1000::/68","2409:8c4d:5200:c05:1000::/68","2409:8c50:a00:2213:1000::/68","2409:8c5c:b00:206:1000::/68","2409:8c62:e10:a04:1000::/68","2409:8c6a:3a11:2:1000::/68","2409:8c6a:4c11:1101:1000::/68","2409:8c6c:561:8124:1000::/68","240e:b1:9801:20d:1000::/68","240e:cf:8800:1b:1000::/68","240e:f7:4d0f:601:1000::/68","240e:f7:7c00:821:1000::/68","240e:90d:1101:203:1000::/68","240e:914:4005:1:1000::/68","240e:935:a04:2b21:1000::/68","240e:938:a05:22:1000::/68","240e:940:e009:14e:1000::/68","240e:944:8:5:1000::/68","240e:95d:802:400:1000::/68","240e:95d:c02:7:1000::/68","240e:965:820:105:1000::/68","240e:974:e200:2305:1000::/68","240e:97c:4014:102:1000::/68"],"etag":"be8309b47ba8e9f8899716fdbad44de9"},"success":true,"errors":[],"messages":[]}
19 changes: 19 additions & 0 deletions ranger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,25 @@ func TestGetIP(t *testing.T) {
expectedRegion: "europe-west1",
found: true,
},
{
name: "valid IPv4 address in Cloudflare",
ip: "104.16.0.1",
expectedCloud: "Cloudflare",
found: true,
},
{
name: "valid IPv6 address in Cloudflare",
ip: "2606:4700::1",
expectedCloud: "Cloudflare",
found: true,
},
{
name: "valid IPv4 address in Cloudflare China Network",
ip: "14.204.96.225",
expectedCloud: "Cloudflare",
expectedRegion: "cn",
found: true,
},
{
name: "non-cloud IP address",
ip: "127.0.0.1",
Expand Down
3 changes: 2 additions & 1 deletion scripts/fetch-data.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
set -eou pipefail

curl -Lo 'data/aws-ip-ranges.json' 'https://ip-ranges.amazonaws.com/ip-ranges.json'
curl -Lo 'data/gcp-cloud.json' 'https://www.gstatic.com/ipranges/cloud.json'
curl -Lo 'data/gcp-cloud.json' 'https://www.gstatic.com/ipranges/cloud.json'
curl -Lo 'data/cloudflare-ips.json' 'https://api.cloudflare.com/client/v4/ips?networks=jdcloud'
Loading