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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e108186bec2a6dff38a2f6fa5edcfd196aa4e2199333a61d8c9a888edf65c3be
4
- data.tar.gz: a8e0131c2fd8270784ed1155f114892bc26e3f65bf1eda55a547450bdf272fdb
3
+ metadata.gz: 1d791a3a095bff874bd6a9f6b400a03d1d9a442417720af6dc504738048f5011
4
+ data.tar.gz: 75d1b89659db8315982e2ce22020611f4522c9d80b6e25253bf3132d3529babe
5
5
  SHA512:
6
- metadata.gz: 5762e62571ea9c5dce83039b0616a3074a0ffa0f165430c4cc5dbc5c70ef0d3d34553965abe3f85c3c8e6222f0247290e1acb8a4538538e30bd1b88e6f5bbe7a
7
- data.tar.gz: f04fa0b616f69d62f81568dc29c5e0428511a884e774685a6d869dfbef5765c9b0cac577fc42d1f08d747084369a1ff11baba8853cbbc36b71e8fee20e7773b2
6
+ metadata.gz: 0cd8c07c159038648cd922543f0ab50a764ee99075a02fd4a1a2b6b11c5d8f2f53351f71646790e864e0e3e559f7e28256240ee99512f1b99a710b6577eba1ab
7
+ data.tar.gz: 8fc4cbc087364618a42298f20bc23a6e46be124db6f509ece22ddb5971d1bda081019b2a479cb563bfb40a21e1d11178a686368cb249b19bc2d57c70cab7661c
@@ -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@ac593985615ec2ede58e132d2e21d2b1cbd6127c
22
+ uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3
23
23
  - name: Install Ruby and gems
24
- uses: ruby/setup-ruby@93287a1fa82c6ddbb6d8db978df4b0119cd8879f
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@ac593985615ec2ede58e132d2e21d2b1cbd6127c
38
+ uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3
39
39
  - name: Install Ruby and gems
40
- uses: ruby/setup-ruby@93287a1fa82c6ddbb6d8db978df4b0119cd8879f
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@efd3bcfec120bd343786e46318186153b7bc8c68
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@ac593985615ec2ede58e132d2e21d2b1cbd6127c
10
+ uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3
11
11
  - name: Install Ruby and gems
12
- uses: ruby/setup-ruby@93287a1fa82c6ddbb6d8db978df4b0119cd8879f
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
@@ -4,7 +4,7 @@ source "https://rubygems.org"
4
4
 
5
5
  gemspec
6
6
 
7
- gem "faraday", "~> 1.0"
7
+ gem "faraday", "~> 2.7"
8
8
  gem "rake", "~> 13.0"
9
9
 
10
10
  group :development, :test do
data/Gemfile.lock CHANGED
@@ -1,9 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rack-cloudflare_middleware (1.0.0)
4
+ rack-cloudflare_middleware (1.1.0)
5
5
  faraday (>= 1.0, < 3)
6
- rack (~> 2)
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 (1.10.3)
24
- faraday-em_http (~> 1.0)
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-em_http (1.0.0)
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 (2.2.6.4)
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 (~> 1.0)
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
+ [![CI](https://github.com/instrumentl/rack-cloudflare_middleware/actions/workflows/ci.yml/badge.svg)](https://github.com/instrumentl/rack-cloudflare_middleware/actions/workflows/ci.yml)
4
+ [![Gem Version](https://badge.fury.io/rb/rack-cloudflare_middleware.svg)](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 = IPAddr.new env["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
- ["403", {"Content-Type" => "text/plain"}, ["Forbidden by policy statement"]]
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
- if TrustedIps.instance.include? env["REMOTE_ADDR"]
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"] = env["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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Rack
4
4
  module CloudflareMiddleware
5
- VERSION = "1.0.0"
5
+ VERSION = "1.1.0"
6
6
  end
7
7
  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
- attr_accessor :logger
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", "~> 2"
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.0.0
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.1
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