Skip to content

Commit 975c7dc

Browse files
authored
Merge pull request #54 from github/check-fastly-ips
Check for Fastly IP ownership
2 parents 4bc33d1 + 0f7aa21 commit 975c7dc

File tree

12 files changed

+111
-52
lines changed

12 files changed

+111
-52
lines changed

config/fastly-ips.txt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
23.235.32.0/20
2+
43.249.72.0/22
3+
103.244.50.0/24
4+
103.245.222.0/23
5+
103.245.224.0/24
6+
104.156.80.0/20
7+
151.101.0.0/16
8+
157.52.64.0/18
9+
172.111.64.0/18
10+
185.31.16.0/22
11+
199.27.72.0/21
12+
199.232.0.0/16
13+
202.21.128.0/24
14+
203.57.145.0/24

lib/github-pages-health-check.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919
module GitHubPages
2020
module HealthCheck
2121

22-
autoload :CloudFlare, "github-pages-health-check/cloudflare"
22+
autoload :CDN, "github-pages-health-check/cdn"
23+
autoload :CloudFlare, "github-pages-health-check/cdns/cloudflare"
24+
autoload :Fastly, "github-pages-health-check/cdns/fastly"
2325
autoload :Error, "github-pages-health-check/error"
2426
autoload :Errors, "github-pages-health-check/errors"
2527
autoload :Checkable, "github-pages-health-check/checkable"

lib/github-pages-health-check/cloudflare.rb renamed to lib/github-pages-health-check/cdn.rb

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,23 @@
11
module GitHubPages
22
module HealthCheck
3-
class CloudFlare
3+
class CDN
44
include Singleton
55

66
# Internal: The path of the config file.
7-
attr_reader :path
7+
attr_reader :name, :path
88

99
# Public: Does cloudflare control this address?
1010
def self.controls_ip?(address)
1111
instance.controls_ip?(address)
1212
end
1313

14-
# Internal: Create a new cloudflare info instance.
14+
# Internal: Create a new CDN info instance.
1515
def initialize(options = {})
16+
@name = options.fetch(:name) { self.class.name.split("::").last.downcase }
1617
@path = options.fetch(:path) { default_config_path }
1718
end
1819

19-
# Internal: Does cloudflare control this address?
20+
# Internal: Does this CDN control this address?
2021
def controls_ip?(address)
2122
ranges.any? { |range| range.include?(address) }
2223
end
@@ -34,7 +35,7 @@ def load_ranges
3435
end
3536

3637
def default_config_path
37-
File.expand_path("../../config/cloudflare-ips.txt", File.dirname(__FILE__))
38+
File.expand_path("../../config/#{name}-ips.txt", File.dirname(__FILE__))
3839
end
3940
end
4041
end
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module GitHubPages
2+
module HealthCheck
3+
# Instance of the CloudFlare CDN for checking IP ownership
4+
# Specifically not namespaced to avoid a breaking change
5+
class CloudFlare < CDN
6+
end
7+
end
8+
end
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module GitHubPages
2+
module HealthCheck
3+
# Instance of the Fastly CDN for checking IP ownership
4+
# Specifically not namespaced to avoid a breaking change
5+
class Fastly < CDN
6+
end
7+
end
8+
end

lib/github-pages-health-check/domain.rb

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class Domain < Checkable
2020
].freeze
2121

2222
HASH_METHODS = [
23-
:host, :uri, :dns_resolves?, :proxied?, :cloudflare_ip?,
23+
:host, :uri, :dns_resolves?, :proxied?, :cloudflare_ip?, :fastly_ip?,
2424
:old_ip_address?, :a_record?, :cname_record?, :valid_domain?,
2525
:apex_domain?, :should_be_a_record?, :cname_to_github_user_domain?,
2626
:cname_to_pages_dot_github_dot_com?, :cname_to_fastly?,
@@ -142,10 +142,12 @@ def fastly?
142142

143143
# Does the domain resolve to a CloudFlare-owned IP
144144
def cloudflare_ip?
145-
return unless dns?
146-
dns.all? do |answer|
147-
answer.class == Net::DNS::RR::A && CloudFlare.controls_ip?(answer.address)
148-
end
145+
cdn_ip?(CloudFlare)
146+
end
147+
148+
# Does the domain resolve to a Fastly-owned IP
149+
def fastly_ip?
150+
cdn_ip?(Fastly)
149151
end
150152

151153
# Does this non-GitHub-pages domain proxy a GitHub Pages site?
@@ -158,7 +160,7 @@ def proxied?
158160
return unless dns?
159161
return true if cloudflare_ip?
160162
return false if pointed_to_github_pages_ip? || cname_to_github_user_domain?
161-
return false if cname_to_pages_dot_github_dot_com? || cname_to_fastly?
163+
return false if cname_to_pages_dot_github_dot_com? || cname_to_fastly? || fastly_ip?
162164
served_by_pages?
163165
end
164166

@@ -272,6 +274,14 @@ def absolute_domain
272274
def scheme
273275
@scheme ||= github_domain? ? "https" : "http"
274276
end
277+
278+
# Does the domain resolve to a CDN-owned IP
279+
def cdn_ip?(cdn)
280+
return unless dns?
281+
dns.all? do |answer|
282+
answer.class == Net::DNS::RR::A && cdn.controls_ip?(answer.address)
283+
end
284+
end
275285
end
276286
end
277287
end

script/check-cdn-ips

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#!/bin/bash -e
2+
3+
script/update-cdn-ips >/dev/null 2>&1
4+
files=( cloudflare fastly)
5+
6+
# `git diff --quiet` suppresses output and sets a return code
7+
# 0 - no changes
8+
# 1 - changes
9+
for file in "${files[@]}"
10+
do
11+
if git diff -w --quiet --cached "config/$file-ips.txt"
12+
then
13+
echo "$file IP list is up-to-date."
14+
else
15+
echo git reset "config/$file-ips.txt"
16+
git reset --quiet "config/$file-ips.txt"
17+
echo "*** $file IP list is out of date! Run script/update-cdn-ips!"
18+
exit 1
19+
fi
20+
done

script/check-cloudflare-ips

Lines changed: 0 additions & 17 deletions
This file was deleted.

script/cibuild

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ set -ex
55
script/bootstrap
66

77
script/test
8-
script/check-cloudflare-ips
8+
script/check-cdn-ips
99
bundle exec gem build github-pages-health-check.gemspec

script/update-cdn-ips

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#!/usr/bin/env ruby
2+
#/ Usage script/update-ips
3+
#/ updates config/cloudflare-ips.txt and config/fastly-ips.txt
4+
5+
require 'open-uri'
6+
require 'json'
7+
8+
SOURCES = {
9+
cloudflare: "https://www.cloudflare.com/ips-v4",
10+
fastly: "https://api.fastly.com/public-ip-list"
11+
}
12+
13+
SOURCES.each do |source, url|
14+
file = "config/#{source}-ips.txt"
15+
puts "Fetching #{url}..."
16+
data = open(url).read
17+
data = JSON.parse(data)["addresses"].join("\n") if source == :fastly
18+
File.write(file, data)
19+
`git add --verbose #{file}`
20+
end

script/update-cloudflare-ips

Lines changed: 0 additions & 14 deletions
This file was deleted.

spec/github_pages_health_check_cloudflare_spec.rb renamed to spec/github_pages_health_check_cdn_spec.rb

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,17 @@
33
require "tempfile"
44
require "ipaddr"
55

6-
describe(GitHubPages::HealthCheck::CloudFlare) do
7-
8-
let(:instance) { GitHubPages::HealthCheck::CloudFlare.send(:new, :path => ipaddr_path) }
9-
let(:tempfile) { Tempfile.new("pages-jekyll-alarmist-cloudflare-ips").tap { |f| f.sync = true } }
6+
describe(GitHubPages::HealthCheck::CDN) do
7+
let(:instance) { described_class.send(:new, :path => ipaddr_path) }
8+
let(:tempfile) { Tempfile.new("pages-cdn-ips").tap { |f| f.sync = true } }
109
let(:ipaddr_path) { tempfile.path }
1110

1211
context "default" do
13-
let(:instance) { GitHubPages::HealthCheck::CloudFlare.instance }
12+
let(:instance) { described_class.instance }
1413

1514
it "loads the default config" do
1615
path = File.expand_path(instance.path)
17-
expected = File.expand_path("../config/cloudflare-ips.txt", File.dirname(__FILE__))
16+
expected = File.expand_path("../config/cdn-ips.txt", File.dirname(__FILE__))
1817
expect(path).to eql(expected)
1918
end
2019
end
@@ -44,7 +43,15 @@
4443
it("controls? 200.27.128.55") { expect(instance.controls_ip?(IPAddr.new("200.27.128.55"))).to be_falsey }
4544
end
4645

47-
it "works as a singleton" do
48-
expect(GitHubPages::HealthCheck::CloudFlare.controls_ip?("108.162.196.20")).to be(true)
46+
{ "Fastly" => "151.101.32.133", "CloudFlare" => "108.162.196.20" }.each do |service, ip|
47+
context service do
48+
it "works as s singleton" do
49+
klass = Kernel.const_get("GitHubPages::HealthCheck::#{service}").send(:new)
50+
expect(klass.controls_ip?(ip)).to be(true)
51+
52+
github_ip = GitHubPages::HealthCheck::Domain::CURRENT_IP_ADDRESSES.first
53+
expect(klass.controls_ip?(github_ip)).to be(false)
54+
end
55+
end
4956
end
5057
end

0 commit comments

Comments
 (0)