rack-cloudflare 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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