rack-cloudflare_middleware 1.0.0 → 1.2.0
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 +4 -4
- data/.github/workflows/ci.yml +6 -6
- data/.github/workflows/release.yml +2 -2
- data/.pre-commit-config.yaml +1 -1
- data/CHANGELOG.md +15 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +23 -35
- data/README.md +25 -0
- data/lib/rack/cloudflare_middleware/deny_others.rb +15 -4
- data/lib/rack/cloudflare_middleware/rewrite_remote_addr.rb +5 -3
- data/lib/rack/cloudflare_middleware/version.rb +1 -1
- data/lib/rack/cloudflare_middleware.rb +10 -1
- data/rack-cloudflare_middleware.gemspec +3 -1
- metadata +13 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1806fad87fa57e8317ff1937cd2cd45db0bcef1b89aabb7ba1c3d4aa71434d22
|
4
|
+
data.tar.gz: 47811778ab426303239fa18e3e29310e5f212cd54740efc71674061e30787c85
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '084ac8b92afac4bd6aff7435c56a546c3f4faafaf4124d19a3f542fcef07b43b31ffa1222bccc49d46693deb97db5aad3dfd31f1800cc4dcdd1c7475afe0f806'
|
7
|
+
data.tar.gz: a10319a8ec1dabed6022d7efe051b8af63752f134fbafa4c9fc35d3e563780ec3e3d6fb3de26b81ffc637f1ac0c37ae87a6dabae78cbbeadc2a8b0cbb4d37715
|
data/.github/workflows/ci.yml
CHANGED
@@ -19,9 +19,9 @@ jobs:
|
|
19
19
|
ruby: ["2.7", "3.0", "3.1", "3.2"]
|
20
20
|
steps:
|
21
21
|
- name: Checkout code
|
22
|
-
uses: actions/checkout@
|
22
|
+
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
|
23
23
|
- name: Install Ruby and gems
|
24
|
-
uses: ruby/setup-ruby@
|
24
|
+
uses: ruby/setup-ruby@8a45918450651f5e4784b6031db26f4b9f76b251
|
25
25
|
with:
|
26
26
|
bundler-cache: true
|
27
27
|
ruby-version: ${{ matrix.ruby }}
|
@@ -35,17 +35,17 @@ jobs:
|
|
35
35
|
contents: read
|
36
36
|
steps:
|
37
37
|
- name: Checkout code
|
38
|
-
uses: actions/checkout@
|
38
|
+
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
|
39
39
|
- name: Install Ruby and gems
|
40
|
-
uses: ruby/setup-ruby@
|
40
|
+
uses: ruby/setup-ruby@8a45918450651f5e4784b6031db26f4b9f76b251
|
41
41
|
with:
|
42
42
|
bundler-cache: true
|
43
43
|
ruby-version: "3.1"
|
44
44
|
- name: Bundle Audit Check
|
45
45
|
run: bundle exec bundle-audit update && bundle exec bundle-audit check
|
46
46
|
- name: Setup Python
|
47
|
-
uses: actions/setup-python@
|
47
|
+
uses: actions/setup-python@bd6b4b6205c4dbad673328db7b31b7fab9e241c0
|
48
48
|
with:
|
49
49
|
python-version: "3.10"
|
50
50
|
- name: Run pre-commit
|
51
|
-
uses: pre-commit/action@
|
51
|
+
uses: pre-commit/action@5f528da5c95691c4cf42ff76a4d10854b62cbb82
|
@@ -7,9 +7,9 @@ jobs:
|
|
7
7
|
runs-on: ubuntu-latest
|
8
8
|
steps:
|
9
9
|
- name: Checkout code
|
10
|
-
uses: actions/checkout@
|
10
|
+
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
|
11
11
|
- name: Install Ruby and gems
|
12
|
-
uses: ruby/setup-ruby@
|
12
|
+
uses: ruby/setup-ruby@8a45918450651f5e4784b6031db26f4b9f76b251
|
13
13
|
with:
|
14
14
|
bundler-cache: true
|
15
15
|
ruby-version: "3.2"
|
data/.pre-commit-config.yaml
CHANGED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
v1.2.0 - 2023-06-05
|
2
|
+
-------------------
|
3
|
+
- Set `required_ruby_version` in the gemspec
|
4
|
+
- Add `trusted_request_proc` kwarg to DenyOthers middleware
|
5
|
+
|
6
|
+
v1.1.0 - 2023-03-31
|
7
|
+
-------------------
|
8
|
+
- Expand requirements to allow using Rack 3.x
|
9
|
+
- Add `trust_xff_if_private` kwarg to both middlewares
|
10
|
+
- Add `on_fail_proc` to DenyOthers middleware
|
11
|
+
- Bump various build-time dependencies
|
12
|
+
|
13
|
+
v1.0.0 - 2023-03-31
|
14
|
+
-------------------
|
15
|
+
- Initial release
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
rack-cloudflare_middleware (1.
|
4
|
+
rack-cloudflare_middleware (1.2.0)
|
5
5
|
faraday (>= 1.0, < 3)
|
6
|
-
rack (
|
6
|
+
rack (>= 2, < 4)
|
7
7
|
|
8
8
|
GEM
|
9
9
|
remote: https://rubygems.org/
|
@@ -20,47 +20,28 @@ GEM
|
|
20
20
|
crack (0.4.5)
|
21
21
|
rexml
|
22
22
|
diff-lcs (1.5.0)
|
23
|
-
faraday (
|
24
|
-
faraday-
|
25
|
-
faraday-em_synchrony (~> 1.0)
|
26
|
-
faraday-excon (~> 1.1)
|
27
|
-
faraday-httpclient (~> 1.0)
|
28
|
-
faraday-multipart (~> 1.0)
|
29
|
-
faraday-net_http (~> 1.0)
|
30
|
-
faraday-net_http_persistent (~> 1.0)
|
31
|
-
faraday-patron (~> 1.0)
|
32
|
-
faraday-rack (~> 1.0)
|
33
|
-
faraday-retry (~> 1.0)
|
23
|
+
faraday (2.7.5)
|
24
|
+
faraday-net_http (>= 2.0, < 3.1)
|
34
25
|
ruby2_keywords (>= 0.0.4)
|
35
|
-
faraday-
|
36
|
-
faraday-em_synchrony (1.0.0)
|
37
|
-
faraday-excon (1.1.0)
|
38
|
-
faraday-httpclient (1.0.1)
|
39
|
-
faraday-multipart (1.0.4)
|
40
|
-
multipart-post (~> 2)
|
41
|
-
faraday-net_http (1.0.1)
|
42
|
-
faraday-net_http_persistent (1.2.0)
|
43
|
-
faraday-patron (1.0.0)
|
44
|
-
faraday-rack (1.0.0)
|
45
|
-
faraday-retry (1.0.3)
|
26
|
+
faraday-net_http (3.0.2)
|
46
27
|
hashdiff (1.0.1)
|
47
28
|
json (2.6.3)
|
48
29
|
language_server-protocol (3.17.0.3)
|
30
|
+
lint_roller (1.0.0)
|
49
31
|
method_source (1.0.0)
|
50
|
-
|
51
|
-
|
52
|
-
parser (3.2.1.1)
|
32
|
+
parallel (1.23.0)
|
33
|
+
parser (3.2.2.1)
|
53
34
|
ast (~> 2.4.1)
|
54
35
|
pry (0.14.2)
|
55
36
|
coderay (~> 1.1)
|
56
37
|
method_source (~> 1.0)
|
57
38
|
public_suffix (5.0.1)
|
58
|
-
rack (
|
39
|
+
rack (3.0.7)
|
59
40
|
rack-test (2.1.0)
|
60
41
|
rack (>= 1.3)
|
61
42
|
rainbow (3.1.1)
|
62
43
|
rake (13.0.6)
|
63
|
-
regexp_parser (2.
|
44
|
+
regexp_parser (2.8.0)
|
64
45
|
rexml (3.2.5)
|
65
46
|
rspec (3.12.0)
|
66
47
|
rspec-core (~> 3.12.0)
|
@@ -78,26 +59,33 @@ GEM
|
|
78
59
|
diff-lcs (>= 1.2.0, < 2.0)
|
79
60
|
rspec-support (~> 3.12.0)
|
80
61
|
rspec-support (3.12.0)
|
81
|
-
rubocop (1.
|
62
|
+
rubocop (1.50.2)
|
82
63
|
json (~> 2.3)
|
83
64
|
parallel (~> 1.10)
|
84
65
|
parser (>= 3.2.0.0)
|
85
66
|
rainbow (>= 2.2.2, < 4.0)
|
86
67
|
regexp_parser (>= 1.8, < 3.0)
|
87
68
|
rexml (>= 3.2.5, < 4.0)
|
88
|
-
rubocop-ast (>= 1.
|
69
|
+
rubocop-ast (>= 1.28.0, < 2.0)
|
89
70
|
ruby-progressbar (~> 1.7)
|
90
71
|
unicode-display_width (>= 2.4.0, < 3.0)
|
91
|
-
rubocop-ast (1.28.
|
72
|
+
rubocop-ast (1.28.1)
|
92
73
|
parser (>= 3.2.1.0)
|
93
74
|
rubocop-performance (1.16.0)
|
94
75
|
rubocop (>= 1.7.0, < 2.0)
|
95
76
|
rubocop-ast (>= 0.4.0)
|
96
77
|
ruby-progressbar (1.13.0)
|
97
78
|
ruby2_keywords (0.0.5)
|
98
|
-
standard (1.
|
79
|
+
standard (1.28.4)
|
99
80
|
language_server-protocol (~> 3.17.0.2)
|
100
|
-
|
81
|
+
lint_roller (~> 1.0)
|
82
|
+
rubocop (~> 1.50.2)
|
83
|
+
standard-custom (~> 1.0.0)
|
84
|
+
standard-performance (~> 1.0.1)
|
85
|
+
standard-custom (1.0.0)
|
86
|
+
lint_roller (~> 1.0)
|
87
|
+
standard-performance (1.0.1)
|
88
|
+
lint_roller (~> 1.0)
|
101
89
|
rubocop-performance (~> 1.16.0)
|
102
90
|
thor (1.2.1)
|
103
91
|
unicode-display_width (2.4.2)
|
@@ -113,7 +101,7 @@ PLATFORMS
|
|
113
101
|
DEPENDENCIES
|
114
102
|
bundle-audit (~> 0.1.0)
|
115
103
|
bundler (~> 2)
|
116
|
-
faraday (~>
|
104
|
+
faraday (~> 2.7)
|
117
105
|
pry
|
118
106
|
rack-cloudflare_middleware!
|
119
107
|
rack-test (~> 2)
|
data/README.md
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
This is a small Rack middleware for use with the [Cloudflare](https://www.cloudflare.com/) CDN.
|
2
2
|
|
3
|
+
[](https://github.com/instrumentl/rack-cloudflare_middleware/actions/workflows/ci.yml)
|
4
|
+
[](https://badge.fury.io/rb/rack-cloudflare_middleware)
|
5
|
+
|
3
6
|
We include two middlewares:
|
4
7
|
|
5
8
|
* `Rack::CloudflareMiddleware::RewriteRemoteAddr` swaps in `CF-Connecting-IP` for `REMOTE_ADDR` if and only if the "real" remote address is a trusted Cloudflare source IP address.
|
@@ -27,3 +30,25 @@ run Sinatra::Application
|
|
27
30
|
```
|
28
31
|
|
29
32
|
The `allow_private` kwarg to `DenyOthers` controls whether or not private and loopback addresses are allowed through. Whether or not you should set this depends on the exact specifics of your deployment environment; often it should be set in development, but not in production.
|
33
|
+
|
34
|
+
`DenyOthers` also takes an `on_fail_proc` kwarg which receives the request environment and should return an appropriate error response (as a standard Rack tuple of `(status, headers, body)`). Example usage:
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
require "rack/cloudflare_middleware"
|
38
|
+
|
39
|
+
use Rack::CloudflareMiddleware::DenyOthers, on_fail_proc: ->(env) do
|
40
|
+
MyLogger.warn "Bad request from #{env["REMOTE_ADDR"]}"
|
41
|
+
[403, {"Content-Type" => "text/plain"}, ["you did a bad thing"]]
|
42
|
+
end
|
43
|
+
```
|
44
|
+
|
45
|
+
`DenyOthers` also takes a `trusted_request_proc` which receives a `Rack::Request` object and should return a boolean of whether or not the request is to be allowed through regardless of Source IP. This is primarily intended for healthchecks. Example usage:
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
require "rack/cloudflare_middleware"
|
49
|
+
use Rack::CloudflareMiddleware::DenyOthers, trusted_request_proc: ->(request) do
|
50
|
+
request.path.start_with? "/health/check"
|
51
|
+
end
|
52
|
+
```
|
53
|
+
|
54
|
+
Both middlewares also include a convenience called `trust_xff_if_private` mode; this will change them to use the right-most contents of `X-Forwarded-For` as `REMOTE_ADDR` if and only if the actual `REMOTE_ADDR` is a private address. This is a moderately-unsafe option, but may be required if your application provider has made poor choices in routing technologies (and, for example, is required on Heroku). If you're in this state, you should tell your provider to use the PROXY protocol internally instead of `X-Forwarded-For`. There have been many security issues related to Heroku's poor parsing of `X-Forwarded-For` in their router/load-balancer layer, and may be more in the future.
|
@@ -3,20 +3,31 @@
|
|
3
3
|
module Rack
|
4
4
|
module CloudflareMiddleware
|
5
5
|
class DenyOthers
|
6
|
-
def initialize(app, allow_private: false)
|
6
|
+
def initialize(app, allow_private: false, trusted_request_proc: nil, on_fail_proc: nil, trust_xff_if_private: false)
|
7
7
|
@allow_private = allow_private
|
8
|
+
@trusted_request_proc = trusted_request_proc
|
9
|
+
@on_fail_proc = on_fail_proc
|
10
|
+
@trust_xff_if_private = trust_xff_if_private
|
8
11
|
@app = app
|
9
12
|
end
|
10
13
|
|
11
14
|
def call(env)
|
12
15
|
TrustedIps.instance.check_update
|
13
|
-
remote_addr =
|
14
|
-
if (@allow_private && (remote_addr.private? || remote_addr.loopback?)) || TrustedIps.instance.include?(remote_addr)
|
16
|
+
remote_addr = Rack::CloudflareMiddleware.get_remote_addr(env, @trust_xff_if_private)
|
17
|
+
if (@allow_private && (remote_addr.private? || remote_addr.loopback?)) || TrustedIps.instance.include?(remote_addr) || @trusted_request_proc&.call(Rack::Request.new(env))
|
15
18
|
@app.call(env)
|
19
|
+
elsif @on_fail_proc.nil?
|
20
|
+
default_on_fail(remote_addr)
|
16
21
|
else
|
17
|
-
|
22
|
+
@on_fail_proc.call(env)
|
18
23
|
end
|
19
24
|
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def default_on_fail(remote_addr)
|
29
|
+
["403", {"Content-Type" => "text/plain"}, ["Forbidden by policy statement (#{remote_addr})"]]
|
30
|
+
end
|
20
31
|
end
|
21
32
|
end
|
22
33
|
end
|
@@ -3,15 +3,17 @@
|
|
3
3
|
module Rack
|
4
4
|
module CloudflareMiddleware
|
5
5
|
class RewriteRemoteAddr
|
6
|
-
def initialize(app)
|
6
|
+
def initialize(app, trust_xff_if_private: false)
|
7
|
+
@trust_xff_if_private = trust_xff_if_private
|
7
8
|
@app = app
|
8
9
|
end
|
9
10
|
|
10
11
|
def call(env)
|
11
12
|
TrustedIps.instance.check_update
|
12
|
-
|
13
|
+
remote_addr = Rack::CloudflareMiddleware.get_remote_addr(env, @trust_xff_if_private)
|
14
|
+
if TrustedIps.instance.include? remote_addr
|
13
15
|
unless env["HTTP_CF_CONNECTING_IP"].nil?
|
14
|
-
env["HTTP_CF_ORIGINAL_REMOTE_ADDR"] =
|
16
|
+
env["HTTP_CF_ORIGINAL_REMOTE_ADDR"] = remote_addr
|
15
17
|
env["REMOTE_ADDR"] = env["HTTP_CF_CONNECTING_IP"]
|
16
18
|
end
|
17
19
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "ipaddr"
|
4
|
+
|
3
5
|
require_relative "cloudflare_middleware/version"
|
4
6
|
require_relative "cloudflare_middleware/trusted_ips"
|
5
7
|
require_relative "cloudflare_middleware/rewrite_remote_addr"
|
@@ -7,6 +9,13 @@ require_relative "cloudflare_middleware/deny_others"
|
|
7
9
|
|
8
10
|
module Rack
|
9
11
|
module CloudflareMiddleware
|
10
|
-
|
12
|
+
def self.get_remote_addr(env, trust_xff_if_private)
|
13
|
+
if trust_xff_if_private && IPAddr.new(env["REMOTE_ADDR"]).private? &&
|
14
|
+
!env["HTTP_X_FORWARDED_FOR"].nil? && env["HTTP_X_FORWARDED_FOR"] != ""
|
15
|
+
IPAddr.new(env["HTTP_X_FORWARDED_FOR"].split(",")&.last&.strip)
|
16
|
+
else
|
17
|
+
IPAddr.new(env["REMOTE_ADDR"])
|
18
|
+
end
|
19
|
+
end
|
11
20
|
end
|
12
21
|
end
|
@@ -21,8 +21,10 @@ Gem::Specification.new do |spec|
|
|
21
21
|
end
|
22
22
|
spec.require_paths = ["lib"]
|
23
23
|
|
24
|
+
spec.required_ruby_version = ">= 2.7"
|
25
|
+
|
24
26
|
spec.add_dependency "faraday", ">= 1.0", "< 3"
|
25
|
-
spec.add_dependency "rack", "
|
27
|
+
spec.add_dependency "rack", ">= 2", "< 4"
|
26
28
|
|
27
29
|
spec.add_development_dependency "bundler", "~> 2"
|
28
30
|
spec.add_development_dependency "rake", "~> 13.0"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-cloudflare_middleware
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- James Brown
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-06-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|
@@ -34,16 +34,22 @@ dependencies:
|
|
34
34
|
name: rack
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
36
36
|
requirements:
|
37
|
-
- - "
|
37
|
+
- - ">="
|
38
38
|
- !ruby/object:Gem::Version
|
39
39
|
version: '2'
|
40
|
+
- - "<"
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '4'
|
40
43
|
type: :runtime
|
41
44
|
prerelease: false
|
42
45
|
version_requirements: !ruby/object:Gem::Requirement
|
43
46
|
requirements:
|
44
|
-
- - "
|
47
|
+
- - ">="
|
45
48
|
- !ruby/object:Gem::Version
|
46
49
|
version: '2'
|
50
|
+
- - "<"
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '4'
|
47
53
|
- !ruby/object:Gem::Dependency
|
48
54
|
name: bundler
|
49
55
|
requirement: !ruby/object:Gem::Requirement
|
@@ -113,6 +119,7 @@ files:
|
|
113
119
|
- ".gitignore"
|
114
120
|
- ".pre-commit-config.yaml"
|
115
121
|
- ".rubocop.yml"
|
122
|
+
- CHANGELOG.md
|
116
123
|
- Gemfile
|
117
124
|
- Gemfile.lock
|
118
125
|
- LICENSE.txt
|
@@ -137,14 +144,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
137
144
|
requirements:
|
138
145
|
- - ">="
|
139
146
|
- !ruby/object:Gem::Version
|
140
|
-
version: '
|
147
|
+
version: '2.7'
|
141
148
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
142
149
|
requirements:
|
143
150
|
- - ">="
|
144
151
|
- !ruby/object:Gem::Version
|
145
152
|
version: '0'
|
146
153
|
requirements: []
|
147
|
-
rubygems_version: 3.4.
|
154
|
+
rubygems_version: 3.4.10
|
148
155
|
signing_key:
|
149
156
|
specification_version: 4
|
150
157
|
summary: Rack middleware for handling Cloudflare remote IP headers
|