Skip to content

Commit b8d52f0

Browse files
authored
Add AAAA Support
1 parent e4d02db commit b8d52f0

File tree

8 files changed

+215
-52
lines changed

8 files changed

+215
-52
lines changed

Gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ group :development do
66
gem "dotenv", "~> 2.7"
77
gem "gem-release", "~> 2.1"
88
gem "pry", "~> 0.10"
9+
gem "pry-byebug"
910
gem "rspec", "~> 3.0"
1011
gem "rubocop", "~> 0.52"
1112
gem "webmock", "~> 3.8"

config/cloudflare-ips.txt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,11 @@
1212
104.16.0.0/13
1313
104.24.0.0/14
1414
172.64.0.0/13
15-
131.0.72.0/22
15+
131.0.72.0/22
16+
2400:cb00::/32
17+
2606:4700::/32
18+
2803:f800::/32
19+
2405:b500::/32
20+
2405:8100::/32
21+
2a06:98c0::/29
22+
2c0f:f248::/32

config/fastly-ips.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,6 @@
1414
172.111.64.0/18
1515
185.31.16.0/22
1616
199.27.72.0/21
17-
199.232.0.0/16
17+
199.232.0.0/16
18+
2a04:4e40::/32
19+
2a04:4e42::/32

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

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,19 @@ class Domain < Checkable
7777
185.199.111.153
7878
).freeze
7979

80+
CURRENT_IPV6_ADDRESSES = %w(
81+
2606:50c0:8000::1538
82+
2606:50c0:8001::1538
83+
2606:50c0:8002::1538
84+
2606:50c0:8003::1538
85+
).freeze
86+
87+
CURRENT_IP_ADDRESSES_ALL =
88+
(CURRENT_IP_ADDRESSES + CURRENT_IPV6_ADDRESSES).freeze
89+
8090
HASH_METHODS = %i[
8191
host uri nameservers dns_resolves? proxied? cloudflare_ip?
82-
fastly_ip? old_ip_address? a_record? cname_record?
92+
fastly_ip? old_ip_address? a_record? aaaa_record_present? cname_record?
8393
mx_records_present? valid_domain? apex_domain? should_be_a_record?
8494
cname_to_github_user_domain? cname_to_pages_dot_github_dot_com?
8595
cname_to_fastly? pointed_to_github_pages_ip?
@@ -128,8 +138,8 @@ def deprecated_ip?
128138
def invalid_aaaa_record?
129139
return @invalid_aaaa_record if defined? @invalid_aaaa_record
130140

131-
@invalid_aaaa_record = (valid_domain? && should_be_a_record? &&
132-
aaaa_record_present?)
141+
@invalid_aaaa_record =
142+
(valid_domain? && aaaa_record_present? && !should_be_a_record?)
133143
end
134144

135145
def invalid_a_record?
@@ -204,7 +214,7 @@ def dns_zone_ns?
204214
end
205215
end
206216

207-
# Should the domain use an A record?
217+
# Should the domain use an A or AAAA record?
208218
def should_be_a_record?
209219
!pages_io_domain? && (apex_domain? || mx_records_present?)
210220
end
@@ -213,20 +223,20 @@ def should_be_cname_record?
213223
!should_be_a_record?
214224
end
215225

216-
# Is the domain's first response an A record to a valid GitHub Pages IP?
226+
# Is the domain's first response an A or AAAA record to a valid GitHub Pages IP?
217227
def pointed_to_github_pages_ip?
218-
a_record? && CURRENT_IP_ADDRESSES.include?(dns.first.address.to_s)
228+
return false unless a_record?
229+
230+
CURRENT_IP_ADDRESSES_ALL.include?(dns.first.address.to_s.downcase)
219231
end
220232

221-
# Are any of the domain's A records pointing elsewhere?
233+
# Are any of the domain's A or AAAA records pointing elsewhere?
222234
def non_github_pages_ip_present?
223235
return unless dns?
224236

225-
a_records = dns.select { |answer| answer.type == Dnsruby::Types::A }
226-
227-
a_records.any? { |answer| !github_pages_ip?(answer.address.to_s) }
228-
229-
false
237+
dns
238+
.select { |a| A_RECORD_TYPES.include?(a.type) }
239+
.any? { |a| !github_pages_ip?(a.address.to_s) }
230240
end
231241

232242
# Is the domain's first response a CNAME to a pages domain?
@@ -343,11 +353,12 @@ def old_ip_address?
343353
end
344354
end
345355

346-
# Is this domain's first response an A record?
356+
# Is this domain's first response an A or AAAA record?
347357
def a_record?
358+
return @is_a_record if defined?(@is_a_record)
348359
return unless dns?
349360

350-
dns.first.type == Dnsruby::Types::A
361+
@is_a_record = A_RECORD_TYPES.include?(dns.first.type)
351362
end
352363

353364
def aaaa_record_present?
@@ -423,8 +434,6 @@ def enforces_https?
423434
def https_eligible?
424435
# Can't have any IP's which aren't GitHub's present.
425436
return false if non_github_pages_ip_present?
426-
# Can't have any AAAA records present
427-
return false if aaaa_record_present?
428437
# Must be a CNAME or point to our IPs.
429438

430439
# Only check the one domain if a CNAME. Don't check the parent domain.
@@ -443,6 +452,8 @@ def caa_error
443452

444453
private
445454

455+
A_RECORD_TYPES = [Dnsruby::Types::A, Dnsruby::Types::AAAA].freeze
456+
446457
def caa
447458
@caa ||= GitHubPages::HealthCheck::CAA.new(
448459
:host => cname&.host || host,
@@ -517,7 +528,7 @@ def scheme
517528
def cdn_ip?(cdn)
518529
return unless dns?
519530

520-
a_records = dns.select { |answer| answer.type == Dnsruby::Types::A }
531+
a_records = dns.select { |answer| A_RECORD_TYPES.include?(answer.type) }
521532
return false if !a_records || a_records.empty?
522533

523534
a_records.all? do |answer|
@@ -530,7 +541,7 @@ def legacy_ip?(ip_addr)
530541
end
531542

532543
def github_pages_ip?(ip_addr)
533-
CURRENT_IP_ADDRESSES.include?(ip_addr)
544+
CURRENT_IP_ADDRESSES_ALL.include?(ip_addr&.to_s&.downcase)
534545
end
535546
end
536547
end

script/update-cdn-ips

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,43 @@ require "open-uri"
88
require "json"
99

1010
SOURCES = {
11-
:cloudflare => "https://www.cloudflare.com/ips-v4",
12-
:fastly => "https://api.fastly.com/public-ip-list"
11+
:cloudflare => ["https://www.cloudflare.com/ips-v4", "https://www.cloudflare.com/ips-v6"],
12+
:fastly => ["https://api.fastly.com/public-ip-list"]
1313
}.freeze
1414

15-
SOURCES.each do |source, url|
15+
def parse_fastly(data)
16+
json_data = JSON.parse(data)
17+
(json_data["addresses"] + json_data["ipv6_addresses"]).join("\n")
18+
end
19+
20+
def parse_cloudflare(data)
21+
data
22+
end
23+
24+
def fetch_ips_from_cdn(urls)
25+
urls.map do |url|
26+
puts "Fetching #{url}..."
27+
URI.parse(url).open.read
28+
end.join("\n")
29+
end
30+
31+
def update_cdn_file(source, data)
1632
file = "config/#{source}-ips.txt"
17-
puts "Fetching #{url}..."
18-
data = open(url).read
19-
data = JSON.parse(data)["addresses"].join("\n") if source == :fastly
2033
File.write(file, data)
34+
puts "Writing contents to #{file} and staging changes."
2135
`git add --verbose #{file}`
2236
end
37+
38+
def parse_cdn_response(source, ips)
39+
send("parse_#{source}", ips)
40+
end
41+
42+
def update_cdn_ips(source, urls)
43+
ips = fetch_ips_from_cdn(urls)
44+
data = parse_cdn_response(source, ips)
45+
update_cdn_file(source, data)
46+
end
47+
48+
SOURCES.each do |source, urls|
49+
update_cdn_ips(source, urls)
50+
end

spec/github_pages_health_check/cdn_spec.rb

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,16 @@
3030
end
3131

3232
context "parses config" do
33-
before { tempfile.write("199.27.128.0/21\n173.245.48.0/20") }
33+
before { tempfile.write("199.27.128.0/21\n173.245.48.0/20\n2400:cb00::/32") }
3434

35-
it "has two IPs" do
36-
expect(subject.send(:ranges).size).to eql(2)
35+
it "has three IPs" do
36+
expect(subject.send(:ranges).size).to eql(3)
3737
end
3838

3939
it "loads the IP addresses" do
4040
expect(subject.send(:ranges)).to include(IPAddr.new("199.27.128.0/21"))
4141
expect(subject.send(:ranges)).to include(IPAddr.new("173.245.48.0/20"))
42+
expect(subject.send(:ranges)).to include(IPAddr.new("2400:cb00::/32"))
4243
end
4344

4445
it("controls? 199.27.128.55") do
@@ -49,20 +50,41 @@
4950
expect(subject.controls_ip?(IPAddr.new("173.245.48.55"))).to be_truthy
5051
end
5152

53+
it("controls? 2400:cb00:1000:2000:3000:4000:5000:6000") do
54+
expect(
55+
subject.controls_ip?(
56+
IPAddr.new("2400:cb00:1000:2000:3000:4000:5000:6000")
57+
)
58+
).to be_truthy
59+
end
60+
5261
it("controls? 200.27.128.55") do
5362
expect(subject.controls_ip?(IPAddr.new("200.27.128.55"))).to be_falsey
5463
end
5564
end
5665

5766
{
58-
"Fastly" => "151.101.32.133",
59-
"CloudFlare" => "108.162.196.20"
60-
}.each do |service, ip|
67+
"Fastly" => {
68+
:valid_ips => ["151.101.32.133", "2a04:4e40:1000:2000:3000:4000:5000:6000"],
69+
:invalid_ips => ["108.162.196.20", "2400:cb00:7000:8000:9000:A000:B000:C000"]
70+
},
71+
"CloudFlare" => {
72+
:valid_ips => ["108.162.196.20", "2400:cb00:7000:8000:9000:A000:B000:C000"],
73+
:invalid_ips => ["151.101.32.133", "2a04:4e40:1000:2000:3000:4000:5000:6000"]
74+
}
75+
}.each do |service, ips|
6176
context service do
62-
it "works as s singleton" do
77+
it "works as a singleton" do
6378
const = "GitHubPages::HealthCheck::#{service}"
6479
klass = Kernel.const_get(const).send(:new)
65-
expect(klass.controls_ip?(ip)).to be(true)
80+
81+
ips[:valid_ips].each do |ip|
82+
expect(klass.controls_ip?(ip)).to eq(true), ip
83+
end
84+
85+
ips[:invalid_ips].each do |ip|
86+
expect(klass.controls_ip?(ip)).to eq(false), ip
87+
end
6688

6789
github_ips = GitHubPages::HealthCheck::Domain::CURRENT_IP_ADDRESSES
6890
expect(klass.controls_ip?(github_ips.first)).to be(false)

0 commit comments

Comments
 (0)