Skip to content

Commit c5ac328

Browse files
authored
Add AAAA Support
* Remove logic making sites with AAAA DNS entries invalid * Add aaaa_present? * Add support to test against Ruby 3.0 in build pipeline.
1 parent d29e6fe commit c5ac328

File tree

9 files changed

+232
-56
lines changed

9 files changed

+232
-56
lines changed

.github/workflows/push-cibuild.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ jobs:
1010
- 2.5
1111
- 2.6
1212
- 2.7
13+
- 3.0
1314
steps:
1415
- uses: actions/checkout@master
1516
- name: script/cibuild-docker

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: 45 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,23 @@ class Domain < Checkable
7777
185.199.111.153
7878
).freeze
7979

80+
CURRENT_IPV6_ADDRESSES = %w(
81+
2606:50c0:8000::153
82+
2606:50c0:8001::153
83+
2606:50c0:8002::153
84+
2606:50c0:8003::153
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?
83-
mx_records_present? valid_domain? apex_domain? should_be_a_record?
84-
cname_to_github_user_domain? cname_to_pages_dot_github_dot_com?
85-
cname_to_fastly? pointed_to_github_pages_ip?
86-
non_github_pages_ip_present? pages_domain?
92+
fastly_ip? old_ip_address? a_record? aaaa_record? aaaa_record_present?
93+
cname_record? mx_records_present? valid_domain? apex_domain?
94+
should_be_a_record? cname_to_github_user_domain?
95+
cname_to_pages_dot_github_dot_com? cname_to_fastly?
96+
pointed_to_github_pages_ip? non_github_pages_ip_present? pages_domain?
8797
served_by_pages? valid? reason valid_domain? https?
8898
enforces_https? https_error https_eligible? caa_error dns_zone_soa? dns_zone_ns?
8999
].freeze
@@ -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?
@@ -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 address_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| Dnsruby::Types::A == a.type || Dnsruby::Types::AAAA == 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?
@@ -345,9 +355,18 @@ def old_ip_address?
345355

346356
# Is this domain's first response an A 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 = Dnsruby::Types::A == dns.first.type
362+
end
363+
364+
# Is this domain's first response an AAAA record?
365+
def aaaa_record?
366+
return @is_aaaa_record if defined?(@is_aaaa_record)
367+
return unless dns?
368+
369+
@is_aaaa_record = Dnsruby::Types::AAAA == dns.first.type
351370
end
352371

353372
def aaaa_record_present?
@@ -423,8 +442,6 @@ def enforces_https?
423442
def https_eligible?
424443
# Can't have any IP's which aren't GitHub's present.
425444
return false if non_github_pages_ip_present?
426-
# Can't have any AAAA records present
427-
return false if aaaa_record_present?
428445
# Must be a CNAME or point to our IPs.
429446

430447
# Only check the one domain if a CNAME. Don't check the parent domain.
@@ -443,6 +460,10 @@ def caa_error
443460

444461
private
445462

463+
def address_record?
464+
a_record? || aaaa_record?
465+
end
466+
446467
def caa
447468
@caa ||= GitHubPages::HealthCheck::CAA.new(
448469
:host => cname&.host || host,
@@ -517,10 +538,12 @@ def scheme
517538
def cdn_ip?(cdn)
518539
return unless dns?
519540

520-
a_records = dns.select { |answer| answer.type == Dnsruby::Types::A }
521-
return false if !a_records || a_records.empty?
541+
address_records = dns.select do |answer|
542+
Dnsruby::Types::A == answer.type || Dnsruby::Types::AAAA == answer.type
543+
end
544+
return false if !address_records || address_records.empty?
522545

523-
a_records.all? do |answer|
546+
address_records.all? do |answer|
524547
cdn.controls_ip?(answer.address)
525548
end
526549
end
@@ -530,7 +553,7 @@ def legacy_ip?(ip_addr)
530553
end
531554

532555
def github_pages_ip?(ip_addr)
533-
CURRENT_IP_ADDRESSES.include?(ip_addr)
556+
CURRENT_IP_ADDRESSES_ALL.include?(ip_addr&.to_s&.downcase)
534557
end
535558
end
536559
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)