rack-cloudflare 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 226fb49f7f04f287c5ad80d37f92547073049bc1d222054a082a09f2eb85d7e0
4
- data.tar.gz: 2b196672e8bca43dfcd22f920ec69fc7250b71fced112aee3d01729c1b40976c
3
+ metadata.gz: 011cc369434439d481e71bd8010799cd88e87d982210c67981ac7105760aeff5
4
+ data.tar.gz: fe54456b3085e508bffffb3cab92a198bd0daba4ee91d9fd4166645bb6b2c8c3
5
5
  SHA512:
6
- metadata.gz: a9c0b7967fa66302c86268e9f80d5e1eece2401b95e4ab0b6b1b91ab41070e4fed3ee554385fe6f5f34cdea8a5a809fa731ebaa6c0ce0ad114c8f2a020bdc285
7
- data.tar.gz: 220c954ca1c7779bc811831168cfa15e44c72268917b3b8576eecc6b2794297c03a722a135fdb6710ae3e80d87a5f39d48fdbf46e1a729e4cf74a8a06ed3d212
6
+ metadata.gz: 63b836789620e7ea49f7f278077918177f51df466e6a7ebddf4eb9e2d8f11c9fc2f03cc923cdf2ddbb7d291d77e7a1a10d7885699b1fe450986a9f1349ca9c4f
7
+ data.tar.gz: ef8a50c433d5d64fe98ab645a7d3163615c5986ca00f17cb8a54517d3c5dffe6f0415c5fc8b4a6dbeb13b1374f0a8e601a975ae5442335f3b66c590924156bd8
data/README.md CHANGED
@@ -27,11 +27,11 @@ You can block access to non-Cloudflare networks using `Rack::Cloudflare::Middlew
27
27
  ```ruby
28
28
  require 'rack/cloudflare'
29
29
 
30
- # In config.ru
30
+ # In config.ru (recommended for Rails as well to preempt application loading)
31
31
  use Rack::Cloudflare::Middleware::AccessControl
32
32
 
33
- # In Rails config/application.rb
34
- config.middleware.use Rack::Cloudflare::Middleware::AccessControl
33
+ # In Rails middleware: config/application.rb
34
+ config.middleware.unshift Rack::Cloudflare::Middleware::AccessControl
35
35
 
36
36
  # Configure custom blocked message (defaults to "Forbidden")
37
37
  Rack::Cloudflare::Middleware::AccessControl.blocked_message = "You don't belong here..."
@@ -144,7 +144,7 @@ The list can be updated to Cloudflare's latest published IP lists in-memory:
144
144
 
145
145
  ```ruby
146
146
  # Fetches Rack::Cloudflare::IPs::V4_URL and Rack::Cloudflare::IPs::V6_URL
147
- Rack::Cloudflare::IPs.refresh!
147
+ Rack::Cloudflare::IPs.update!
148
148
 
149
149
  # Updates cached list in-memory
150
150
  Rack::Cloudflare::IPs.list
@@ -0,0 +1,3 @@
1
+ 10.0.0.0/8
2
+ 172.16.0.0/12
3
+ 192.168.0.0/16
@@ -32,10 +32,6 @@ module Rack
32
32
  end
33
33
  end
34
34
 
35
- self.backup = true
36
- self.original_remote_addr = 'ORIGINAL_REMOTE_ADDR'
37
- self.original_forwarded_for = 'ORIGINAL_FORWARDED_FOR'
38
-
39
35
  def initialize(headers)
40
36
  @headers = headers
41
37
  end
@@ -68,17 +64,15 @@ module Rack
68
64
 
69
65
  # "Cf-Visitor: { \"scheme\":\"https\"}"
70
66
  def visitor
71
- return unless has?(HTTP_CF_VISITOR)
72
- ::JSON.parse @headers[HTTP_CF_VISITOR]
67
+ @visitor ||= ::JSON.parse @headers[HTTP_CF_VISITOR] if has?(HTTP_CF_VISITOR)
73
68
  end
74
69
 
75
70
  def remote_addr
76
71
  @remote_addr ||= IPs.parse(@headers[REMOTE_ADDR]).first
77
72
  end
78
73
 
79
- # Indicates if the headers passed through Cloudflare
80
- def trusted?
81
- IPs.list.any? { |range| range.include? remote_addr }
74
+ def cloudflare_ip
75
+ @cloudflare_ip ||= IPs.private?(remote_addr) ? forwarded_for.last : remote_addr
82
76
  end
83
77
 
84
78
  def backup_headers
@@ -90,6 +84,12 @@ module Rack
90
84
  end
91
85
  end
92
86
 
87
+ # Headers that relate to Cloudflare
88
+ # See: https://support.cloudflare.com/hc/en-us/articles/200170986-How-does-Cloudflare-handle-HTTP-Request-headers-
89
+ def target_headers
90
+ @headers.select { |k, _| ALL.include? k }
91
+ end
92
+
93
93
  def rewritten_headers
94
94
  # Only rewrites headers if it's a Cloudflare request
95
95
  return {} unless trusted?
@@ -105,16 +105,10 @@ module Rack
105
105
  # Cloudflare will already have modified the header if
106
106
  # it was present in the original request.
107
107
  # See: https://support.cloudflare.com/hc/en-us/articles/200170986-How-does-Cloudflare-handle-HTTP-Request-headers-
108
- headers[HTTP_X_FORWARDED_FOR] = "#{connecting_ip}, #{remote_addr}" if forwarded_for.none?
108
+ headers[HTTP_X_FORWARDED_FOR] = "#{connecting_ip}, #{cloudflare_ip}" if forwarded_for.none?
109
109
  end
110
110
  end
111
111
 
112
- # Headers that relate to Cloudflare
113
- # See: https://support.cloudflare.com/hc/en-us/articles/200170986-How-does-Cloudflare-handle-HTTP-Request-headers-
114
- def target_headers
115
- @headers.select { |k, _| ALL.include? k }
116
- end
117
-
118
112
  def rewritten_target_headers
119
113
  target_headers.merge(rewritten_headers)
120
114
  end
@@ -123,9 +117,20 @@ module Rack
123
117
  @headers.merge(rewritten_headers)
124
118
  end
125
119
 
120
+ # Indicates if the headers passed through Cloudflare
121
+ def trusted?
122
+ @trusted ||= IPs.list.any? { |range| range.include? cloudflare_ip }
123
+ end
124
+
126
125
  def has?(header)
127
126
  @headers.key?(header)
128
127
  end
128
+
129
+ ### Configure
130
+
131
+ self.backup = true
132
+ self.original_remote_addr = 'ORIGINAL_REMOTE_ADDR'
133
+ self.original_forwarded_for = 'ORIGINAL_FORWARDED_FOR'
129
134
  end
130
135
  end
131
136
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'ipaddr'
4
- require 'net/http'
4
+ require 'open-uri'
5
5
 
6
6
  module Rack
7
7
  class Cloudflare
@@ -14,13 +14,20 @@ module Rack
14
14
  # List of IPs to reference
15
15
  attr_accessor :list
16
16
 
17
- # Refresh list of IPs in case local copy is outdated
18
- def refresh!
17
+ def private?(ip)
18
+ PRIVATE.any? { |range| range.include? ip }
19
+ end
20
+
21
+ # Update list of IPs in-memory in case local copy is outdated
22
+ def update!
19
23
  self.list = fetch(V4_URL) + fetch(V6_URL)
20
24
  end
21
25
 
22
26
  def fetch(url)
23
- parse ::Net::HTTP.get(URI(url))
27
+ parse URI(url).read
28
+ rescue OpenURI::HTTPError => ex
29
+ Cloudflare.error "[#{name}] #{ex.class.name} fetching #{url.inspect}: #{ex.message}"
30
+ []
24
31
  end
25
32
 
26
33
  def read(filename)
@@ -29,13 +36,13 @@ module Rack
29
36
 
30
37
  def parse(string)
31
38
  return [] if string.to_s.strip.empty?
32
- string.split(/[,\s]+/).map { |ip| ::IPAddr.new(ip.strip) }
39
+ string.strip.split(/[,\s]+/).map { |ip| ::IPAddr.new(ip.strip) }
33
40
  end
34
41
  end
35
42
 
36
- V4 = read("#{__dir__}/../../../data/ips_v4.txt")
37
- V6 = read("#{__dir__}/../../../data/ips_v6.txt")
38
-
43
+ V4 = read("#{__dir__}/../../../data/ips_v4.txt")
44
+ V6 = read("#{__dir__}/../../../data/ips_v6.txt")
45
+ PRIVATE = parse('10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16')
39
46
  DEFAULTS = V4 + V6
40
47
 
41
48
  ### Configure
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Rack
4
4
  class Cloudflare
5
- VERSION = '1.0.1'
5
+ VERSION = '1.0.2'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-cloudflare
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joel Van Horn
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-09-14 00:00:00.000000000 Z
11
+ date: 2018-09-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -96,6 +96,7 @@ files:
96
96
  - Rakefile
97
97
  - bin/console
98
98
  - bin/setup
99
+ - data/ips_private.txt
99
100
  - data/ips_v4.txt
100
101
  - data/ips_v6.txt
101
102
  - lib/rack/cloudflare.rb