Skip to content

Check for Fastly IP ownership #54

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Aug 9, 2016
Merged
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
14 changes: 14 additions & 0 deletions config/fastly-ips.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
23.235.32.0/20
43.249.72.0/22
103.244.50.0/24
103.245.222.0/23
103.245.224.0/24
104.156.80.0/20
151.101.0.0/16
157.52.64.0/18
172.111.64.0/18
185.31.16.0/22
199.27.72.0/21
199.232.0.0/16
202.21.128.0/24
203.57.145.0/24
4 changes: 3 additions & 1 deletion lib/github-pages-health-check.rb
Original file line number Diff line number Diff line change
@@ -19,7 +19,9 @@
module GitHubPages
module HealthCheck

autoload :CloudFlare, "github-pages-health-check/cloudflare"
autoload :CDN, "github-pages-health-check/cdn"
autoload :CloudFlare, "github-pages-health-check/cdns/cloudflare"
autoload :Fastly, "github-pages-health-check/cdns/fastly"
autoload :Error, "github-pages-health-check/error"
autoload :Errors, "github-pages-health-check/errors"
autoload :Checkable, "github-pages-health-check/checkable"
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
module GitHubPages
module HealthCheck
class CloudFlare
class CDN
include Singleton

# Internal: The path of the config file.
attr_reader :path
attr_reader :name, :path

# Public: Does cloudflare control this address?
def self.controls_ip?(address)
instance.controls_ip?(address)
end

# Internal: Create a new cloudflare info instance.
# Internal: Create a new CDN info instance.
def initialize(options = {})
@name = options.fetch(:name) { self.class.name.split("::").last.downcase }
@path = options.fetch(:path) { default_config_path }
end

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

def default_config_path
File.expand_path("../../config/cloudflare-ips.txt", File.dirname(__FILE__))
File.expand_path("../../config/#{name}-ips.txt", File.dirname(__FILE__))
end
end
end
8 changes: 8 additions & 0 deletions lib/github-pages-health-check/cdns/cloudflare.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module GitHubPages
module HealthCheck
# Instance of the CloudFlare CDN for checking IP ownership
# Specifically not namespaced to avoid a breaking change
class CloudFlare < CDN
end
end
end
8 changes: 8 additions & 0 deletions lib/github-pages-health-check/cdns/fastly.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module GitHubPages
module HealthCheck
# Instance of the Fastly CDN for checking IP ownership
# Specifically not namespaced to avoid a breaking change
class Fastly < CDN
end
end
end
22 changes: 16 additions & 6 deletions lib/github-pages-health-check/domain.rb
Original file line number Diff line number Diff line change
@@ -20,7 +20,7 @@ class Domain < Checkable
].freeze

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

# Does the domain resolve to a CloudFlare-owned IP
def cloudflare_ip?
return unless dns?
dns.all? do |answer|
answer.class == Net::DNS::RR::A && CloudFlare.controls_ip?(answer.address)
end
cdn_ip?(CloudFlare)
end

# Does the domain resolve to a Fastly-owned IP
def fastly_ip?
cdn_ip?(Fastly)
end

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

@@ -272,6 +274,14 @@ def absolute_domain
def scheme
@scheme ||= github_domain? ? "https" : "http"
end

# Does the domain resolve to a CDN-owned IP
def cdn_ip?(cdn)
return unless dns?
dns.all? do |answer|
answer.class == Net::DNS::RR::A && cdn.controls_ip?(answer.address)
end
end
end
end
end
20 changes: 20 additions & 0 deletions script/check-cdn-ips
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/bin/bash -e

script/update-cdn-ips >/dev/null 2>&1
files=( cloudflare fastly)

# `git diff --quiet` suppresses output and sets a return code
# 0 - no changes
# 1 - changes
for file in "${files[@]}"
do
if git diff -w --quiet --cached "config/$file-ips.txt"
then
echo "$file IP list is up-to-date."
else
echo git reset "config/$file-ips.txt"
git reset --quiet "config/$file-ips.txt"
echo "*** $file IP list is out of date! Run script/update-cdn-ips!"
exit 1
fi
done
17 changes: 0 additions & 17 deletions script/check-cloudflare-ips

This file was deleted.

2 changes: 1 addition & 1 deletion script/cibuild
Original file line number Diff line number Diff line change
@@ -5,5 +5,5 @@ set -ex
script/bootstrap

script/test
script/check-cloudflare-ips
script/check-cdn-ips
bundle exec gem build github-pages-health-check.gemspec
20 changes: 20 additions & 0 deletions script/update-cdn-ips
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env ruby
#/ Usage script/update-ips
#/ updates config/cloudflare-ips.txt and config/fastly-ips.txt

require 'open-uri'
require 'json'

SOURCES = {
cloudflare: "https://www.cloudflare.com/ips-v4",
fastly: "https://api.fastly.com/public-ip-list"
}

SOURCES.each do |source, url|
file = "config/#{source}-ips.txt"
puts "Fetching #{url}..."
data = open(url).read
data = JSON.parse(data)["addresses"].join("\n") if source == :fastly
File.write(file, data)
`git add --verbose #{file}`
end
14 changes: 0 additions & 14 deletions script/update-cloudflare-ips

This file was deleted.

Original file line number Diff line number Diff line change
@@ -3,18 +3,17 @@
require "tempfile"
require "ipaddr"

describe(GitHubPages::HealthCheck::CloudFlare) do

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

context "default" do
let(:instance) { GitHubPages::HealthCheck::CloudFlare.instance }
let(:instance) { described_class.instance }

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

it "works as a singleton" do
expect(GitHubPages::HealthCheck::CloudFlare.controls_ip?("108.162.196.20")).to be(true)
{ "Fastly" => "151.101.32.133", "CloudFlare" => "108.162.196.20" }.each do |service, ip|
context service do
it "works as s singleton" do
klass = Kernel.const_get("GitHubPages::HealthCheck::#{service}").send(:new)
expect(klass.controls_ip?(ip)).to be(true)

github_ip = GitHubPages::HealthCheck::Domain::CURRENT_IP_ADDRESSES.first
expect(klass.controls_ip?(github_ip)).to be(false)
end
end
end
end