rack-cloudflare_middleware 1.0.0 → 1.1.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 +5 -5
- data/.github/workflows/release.yml +2 -2
- data/CHANGELOG.md +10 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +7 -27
- data/README.md +16 -0
- data/lib/rack/cloudflare_middleware/deny_others.rb +13 -3
- 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 +1 -1
- metadata +11 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1d791a3a095bff874bd6a9f6b400a03d1d9a442417720af6dc504738048f5011
|
4
|
+
data.tar.gz: 75d1b89659db8315982e2ce22020611f4522c9d80b6e25253bf3132d3529babe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0cd8c07c159038648cd922543f0ab50a764ee99075a02fd4a1a2b6b11c5d8f2f53351f71646790e864e0e3e559f7e28256240ee99512f1b99a710b6577eba1ab
|
7
|
+
data.tar.gz: 8fc4cbc087364618a42298f20bc23a6e46be124db6f509ece22ddb5971d1bda081019b2a479cb563bfb40a21e1d11178a686368cb249b19bc2d57c70cab7661c
|
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@8f4b7f84864484a7bf31766abe9204da3cbe65b3
|
23
23
|
- name: Install Ruby and gems
|
24
|
-
uses: ruby/setup-ruby@
|
24
|
+
uses: ruby/setup-ruby@904f3fef85a9c80a3750cbe7d5159268fd5caa9f
|
25
25
|
with:
|
26
26
|
bundler-cache: true
|
27
27
|
ruby-version: ${{ matrix.ruby }}
|
@@ -35,9 +35,9 @@ jobs:
|
|
35
35
|
contents: read
|
36
36
|
steps:
|
37
37
|
- name: Checkout code
|
38
|
-
uses: actions/checkout@
|
38
|
+
uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3
|
39
39
|
- name: Install Ruby and gems
|
40
|
-
uses: ruby/setup-ruby@
|
40
|
+
uses: ruby/setup-ruby@904f3fef85a9c80a3750cbe7d5159268fd5caa9f
|
41
41
|
with:
|
42
42
|
bundler-cache: true
|
43
43
|
ruby-version: "3.1"
|
@@ -48,4 +48,4 @@ jobs:
|
|
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@8f4b7f84864484a7bf31766abe9204da3cbe65b3
|
11
11
|
- name: Install Ruby and gems
|
12
|
-
uses: ruby/setup-ruby@
|
12
|
+
uses: ruby/setup-ruby@904f3fef85a9c80a3750cbe7d5159268fd5caa9f
|
13
13
|
with:
|
14
14
|
bundler-cache: true
|
15
15
|
ruby-version: "3.2"
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
v1.1.0 - 2023-03-31
|
2
|
+
-------------------
|
3
|
+
- Expand requirements to allow using Rack 3.x
|
4
|
+
- Add `trust_xff_if_private` kwarg to both middlewares
|
5
|
+
- Add `on_fail_proc` to DenyOthers middleware
|
6
|
+
- Bump various build-time dependencies
|
7
|
+
|
8
|
+
v1.0.0 - 2023-03-31
|
9
|
+
-------------------
|
10
|
+
- 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.1.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,34 +20,14 @@ 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.4)
|
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)
|
49
30
|
method_source (1.0.0)
|
50
|
-
multipart-post (2.3.0)
|
51
31
|
parallel (1.22.1)
|
52
32
|
parser (3.2.1.1)
|
53
33
|
ast (~> 2.4.1)
|
@@ -55,7 +35,7 @@ GEM
|
|
55
35
|
coderay (~> 1.1)
|
56
36
|
method_source (~> 1.0)
|
57
37
|
public_suffix (5.0.1)
|
58
|
-
rack (
|
38
|
+
rack (3.0.7)
|
59
39
|
rack-test (2.1.0)
|
60
40
|
rack (>= 1.3)
|
61
41
|
rainbow (3.1.1)
|
@@ -113,7 +93,7 @@ PLATFORMS
|
|
113
93
|
DEPENDENCIES
|
114
94
|
bundle-audit (~> 0.1.0)
|
115
95
|
bundler (~> 2)
|
116
|
-
faraday (~>
|
96
|
+
faraday (~> 2.7)
|
117
97
|
pry
|
118
98
|
rack-cloudflare_middleware!
|
119
99
|
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,16 @@ 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
|
+
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,30 @@
|
|
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, on_fail_proc: nil, trust_xff_if_private: false)
|
7
7
|
@allow_private = allow_private
|
8
|
+
@on_fail_proc = on_fail_proc
|
9
|
+
@trust_xff_if_private = trust_xff_if_private
|
8
10
|
@app = app
|
9
11
|
end
|
10
12
|
|
11
13
|
def call(env)
|
12
14
|
TrustedIps.instance.check_update
|
13
|
-
remote_addr =
|
15
|
+
remote_addr = Rack::CloudflareMiddleware.get_remote_addr(env, @trust_xff_if_private)
|
14
16
|
if (@allow_private && (remote_addr.private? || remote_addr.loopback?)) || TrustedIps.instance.include?(remote_addr)
|
15
17
|
@app.call(env)
|
18
|
+
elsif @on_fail_proc.nil?
|
19
|
+
default_on_fail(remote_addr)
|
16
20
|
else
|
17
|
-
|
21
|
+
@on_fail_proc.call(env)
|
18
22
|
end
|
19
23
|
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def default_on_fail(remote_addr)
|
28
|
+
["403", {"Content-Type" => "text/plain"}, ["Forbidden by policy statement (#{remote_addr})"]]
|
29
|
+
end
|
20
30
|
end
|
21
31
|
end
|
22
32
|
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
|
@@ -22,7 +22,7 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.require_paths = ["lib"]
|
23
23
|
|
24
24
|
spec.add_dependency "faraday", ">= 1.0", "< 3"
|
25
|
-
spec.add_dependency "rack", "
|
25
|
+
spec.add_dependency "rack", ">= 2", "< 4"
|
26
26
|
|
27
27
|
spec.add_development_dependency "bundler", "~> 2"
|
28
28
|
spec.add_development_dependency "rake", "~> 13.0"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
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.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- James Brown
|
@@ -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
|
@@ -144,7 +151,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
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
|