rack-cloudflare_middleware 1.0.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 +7 -0
- data/.github/dependabot.yml +25 -0
- data/.github/workflows/ci.yml +51 -0
- data/.github/workflows/release.yml +25 -0
- data/.gitignore +2 -0
- data/.pre-commit-config.yaml +17 -0
- data/.rubocop.yml +10 -0
- data/Gemfile +18 -0
- data/Gemfile.lock +127 -0
- data/LICENSE.txt +13 -0
- data/README.md +29 -0
- data/data/ips-v4 +15 -0
- data/data/ips-v6 +7 -0
- data/lib/rack/cloudflare_middleware/deny_others.rb +22 -0
- data/lib/rack/cloudflare_middleware/rewrite_remote_addr.rb +23 -0
- data/lib/rack/cloudflare_middleware/trusted_ips.rb +90 -0
- data/lib/rack/cloudflare_middleware/version.rb +7 -0
- data/lib/rack/cloudflare_middleware.rb +12 -0
- data/rack-cloudflare_middleware.gemspec +31 -0
- metadata +151 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e108186bec2a6dff38a2f6fa5edcfd196aa4e2199333a61d8c9a888edf65c3be
|
4
|
+
data.tar.gz: a8e0131c2fd8270784ed1155f114892bc26e3f65bf1eda55a547450bdf272fdb
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5762e62571ea9c5dce83039b0616a3074a0ffa0f165430c4cc5dbc5c70ef0d3d34553965abe3f85c3c8e6222f0247290e1acb8a4538538e30bd1b88e6f5bbe7a
|
7
|
+
data.tar.gz: f04fa0b616f69d62f81568dc29c5e0428511a884e774685a6d869dfbef5765c9b0cac577fc42d1f08d747084369a1ff11baba8853cbbc36b71e8fee20e7773b2
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# To get started with Dependabot version updates, you'll need to specify which
|
2
|
+
# package ecosystems to update and where the package manifests are located.
|
3
|
+
# Please see the documentation for all configuration options:
|
4
|
+
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
5
|
+
|
6
|
+
version: 2
|
7
|
+
updates:
|
8
|
+
- package-ecosystem: "github-actions"
|
9
|
+
directory: "/"
|
10
|
+
schedule:
|
11
|
+
interval: "weekly"
|
12
|
+
day: "wednesday"
|
13
|
+
labels:
|
14
|
+
- ":robot: dependabot"
|
15
|
+
- ":octocat: github-actions"
|
16
|
+
- ":heavy_plus_sign: dependencies"
|
17
|
+
- package-ecosystem: "bundler"
|
18
|
+
directory: "/"
|
19
|
+
schedule:
|
20
|
+
interval: "weekly"
|
21
|
+
day: "wednesday"
|
22
|
+
labels:
|
23
|
+
- ":robot: dependabot"
|
24
|
+
- ":gem: ruby"
|
25
|
+
- ":heavy_plus_sign: dependencies"
|
@@ -0,0 +1,51 @@
|
|
1
|
+
name: "CI"
|
2
|
+
on:
|
3
|
+
pull_request:
|
4
|
+
push:
|
5
|
+
branches: [ main ]
|
6
|
+
|
7
|
+
jobs:
|
8
|
+
test:
|
9
|
+
runs-on: ubuntu-latest
|
10
|
+
timeout-minutes: 30
|
11
|
+
permissions:
|
12
|
+
contents: read
|
13
|
+
checks: write
|
14
|
+
env:
|
15
|
+
RAILS_ENV: test
|
16
|
+
strategy:
|
17
|
+
fail-fast: true
|
18
|
+
matrix:
|
19
|
+
ruby: ["2.7", "3.0", "3.1", "3.2"]
|
20
|
+
steps:
|
21
|
+
- name: Checkout code
|
22
|
+
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c
|
23
|
+
- name: Install Ruby and gems
|
24
|
+
uses: ruby/setup-ruby@93287a1fa82c6ddbb6d8db978df4b0119cd8879f
|
25
|
+
with:
|
26
|
+
bundler-cache: true
|
27
|
+
ruby-version: ${{ matrix.ruby }}
|
28
|
+
- name: Run RSpec Tests
|
29
|
+
timeout-minutes: 20
|
30
|
+
run: bundle exec rspec -f doc
|
31
|
+
lint:
|
32
|
+
runs-on: ubuntu-latest
|
33
|
+
timeout-minutes: 20
|
34
|
+
permissions:
|
35
|
+
contents: read
|
36
|
+
steps:
|
37
|
+
- name: Checkout code
|
38
|
+
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c
|
39
|
+
- name: Install Ruby and gems
|
40
|
+
uses: ruby/setup-ruby@93287a1fa82c6ddbb6d8db978df4b0119cd8879f
|
41
|
+
with:
|
42
|
+
bundler-cache: true
|
43
|
+
ruby-version: "3.1"
|
44
|
+
- name: Bundle Audit Check
|
45
|
+
run: bundle exec bundle-audit update && bundle exec bundle-audit check
|
46
|
+
- name: Setup Python
|
47
|
+
uses: actions/setup-python@d27e3f3d7c64b4bbf8e4abfb9b63b83e846e0435
|
48
|
+
with:
|
49
|
+
python-version: "3.10"
|
50
|
+
- name: Run pre-commit
|
51
|
+
uses: pre-commit/action@efd3bcfec120bd343786e46318186153b7bc8c68
|
@@ -0,0 +1,25 @@
|
|
1
|
+
name: Release to RubyGems
|
2
|
+
on:
|
3
|
+
release:
|
4
|
+
types: [published]
|
5
|
+
jobs:
|
6
|
+
release:
|
7
|
+
runs-on: ubuntu-latest
|
8
|
+
steps:
|
9
|
+
- name: Checkout code
|
10
|
+
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c
|
11
|
+
- name: Install Ruby and gems
|
12
|
+
uses: ruby/setup-ruby@93287a1fa82c6ddbb6d8db978df4b0119cd8879f
|
13
|
+
with:
|
14
|
+
bundler-cache: true
|
15
|
+
ruby-version: "3.2"
|
16
|
+
- name: Publish gem
|
17
|
+
run: |
|
18
|
+
umask 077
|
19
|
+
mkdir -p "$HOME/.gem"
|
20
|
+
printf -- "---\n:rubygems_api_key: ${RUBYGEMS_API_KEY}\n" > $HOME/.gem/credentials
|
21
|
+
gem build *.gemspec
|
22
|
+
gem push *.gem
|
23
|
+
rm -f "$HOME/.gem/credentials"
|
24
|
+
env:
|
25
|
+
RUBYGEMS_API_KEY: "${{secrets.RUBYGEMS_PUSH_API_KEY}}"
|
data/.gitignore
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
repos:
|
2
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
3
|
+
rev: v3.2.0
|
4
|
+
hooks:
|
5
|
+
- id: trailing-whitespace
|
6
|
+
- id: end-of-file-fixer
|
7
|
+
exclude: '^(config/secrets.yml.enc$)|(spec/data/)|(db/.*\.sql$)'
|
8
|
+
- id: check-yaml
|
9
|
+
exclude: '.rubocop.yml'
|
10
|
+
- id: check-added-large-files
|
11
|
+
- id: check-byte-order-marker
|
12
|
+
exclude: '^spec/data/'
|
13
|
+
- id: check-merge-conflict
|
14
|
+
- repo: https://github.com/instrumentl/pre-commit-standardrb.git
|
15
|
+
rev: '1ae56c7524a2d48cd2b7ca1f74bdb0cdd454477e'
|
16
|
+
hooks:
|
17
|
+
- id: standardrb
|
data/.rubocop.yml
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gemspec
|
6
|
+
|
7
|
+
gem "faraday", "~> 1.0"
|
8
|
+
gem "rake", "~> 13.0"
|
9
|
+
|
10
|
+
group :development, :test do
|
11
|
+
gem "rspec", "~> 3.0"
|
12
|
+
gem "rspec-its", "~> 1.3"
|
13
|
+
gem "rack-test", "~> 2"
|
14
|
+
gem "standard", "~> 1"
|
15
|
+
gem "pry"
|
16
|
+
gem "webmock", "~> 3.18"
|
17
|
+
gem "bundle-audit", "~> 0.1.0"
|
18
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
rack-cloudflare_middleware (1.0.0)
|
5
|
+
faraday (>= 1.0, < 3)
|
6
|
+
rack (~> 2)
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
addressable (2.8.1)
|
12
|
+
public_suffix (>= 2.0.2, < 6.0)
|
13
|
+
ast (2.4.2)
|
14
|
+
bundle-audit (0.1.0)
|
15
|
+
bundler-audit
|
16
|
+
bundler-audit (0.9.1)
|
17
|
+
bundler (>= 1.2.0, < 3)
|
18
|
+
thor (~> 1.0)
|
19
|
+
coderay (1.1.3)
|
20
|
+
crack (0.4.5)
|
21
|
+
rexml
|
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)
|
34
|
+
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)
|
46
|
+
hashdiff (1.0.1)
|
47
|
+
json (2.6.3)
|
48
|
+
language_server-protocol (3.17.0.3)
|
49
|
+
method_source (1.0.0)
|
50
|
+
multipart-post (2.3.0)
|
51
|
+
parallel (1.22.1)
|
52
|
+
parser (3.2.1.1)
|
53
|
+
ast (~> 2.4.1)
|
54
|
+
pry (0.14.2)
|
55
|
+
coderay (~> 1.1)
|
56
|
+
method_source (~> 1.0)
|
57
|
+
public_suffix (5.0.1)
|
58
|
+
rack (2.2.6.4)
|
59
|
+
rack-test (2.1.0)
|
60
|
+
rack (>= 1.3)
|
61
|
+
rainbow (3.1.1)
|
62
|
+
rake (13.0.6)
|
63
|
+
regexp_parser (2.7.0)
|
64
|
+
rexml (3.2.5)
|
65
|
+
rspec (3.12.0)
|
66
|
+
rspec-core (~> 3.12.0)
|
67
|
+
rspec-expectations (~> 3.12.0)
|
68
|
+
rspec-mocks (~> 3.12.0)
|
69
|
+
rspec-core (3.12.1)
|
70
|
+
rspec-support (~> 3.12.0)
|
71
|
+
rspec-expectations (3.12.2)
|
72
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
73
|
+
rspec-support (~> 3.12.0)
|
74
|
+
rspec-its (1.3.0)
|
75
|
+
rspec-core (>= 3.0.0)
|
76
|
+
rspec-expectations (>= 3.0.0)
|
77
|
+
rspec-mocks (3.12.4)
|
78
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
79
|
+
rspec-support (~> 3.12.0)
|
80
|
+
rspec-support (3.12.0)
|
81
|
+
rubocop (1.48.1)
|
82
|
+
json (~> 2.3)
|
83
|
+
parallel (~> 1.10)
|
84
|
+
parser (>= 3.2.0.0)
|
85
|
+
rainbow (>= 2.2.2, < 4.0)
|
86
|
+
regexp_parser (>= 1.8, < 3.0)
|
87
|
+
rexml (>= 3.2.5, < 4.0)
|
88
|
+
rubocop-ast (>= 1.26.0, < 2.0)
|
89
|
+
ruby-progressbar (~> 1.7)
|
90
|
+
unicode-display_width (>= 2.4.0, < 3.0)
|
91
|
+
rubocop-ast (1.28.0)
|
92
|
+
parser (>= 3.2.1.0)
|
93
|
+
rubocop-performance (1.16.0)
|
94
|
+
rubocop (>= 1.7.0, < 2.0)
|
95
|
+
rubocop-ast (>= 0.4.0)
|
96
|
+
ruby-progressbar (1.13.0)
|
97
|
+
ruby2_keywords (0.0.5)
|
98
|
+
standard (1.25.3)
|
99
|
+
language_server-protocol (~> 3.17.0.2)
|
100
|
+
rubocop (~> 1.48.1)
|
101
|
+
rubocop-performance (~> 1.16.0)
|
102
|
+
thor (1.2.1)
|
103
|
+
unicode-display_width (2.4.2)
|
104
|
+
webmock (3.18.1)
|
105
|
+
addressable (>= 2.8.0)
|
106
|
+
crack (>= 0.3.2)
|
107
|
+
hashdiff (>= 0.4.0, < 2.0.0)
|
108
|
+
|
109
|
+
PLATFORMS
|
110
|
+
arm64-darwin-22
|
111
|
+
x86_64-linux
|
112
|
+
|
113
|
+
DEPENDENCIES
|
114
|
+
bundle-audit (~> 0.1.0)
|
115
|
+
bundler (~> 2)
|
116
|
+
faraday (~> 1.0)
|
117
|
+
pry
|
118
|
+
rack-cloudflare_middleware!
|
119
|
+
rack-test (~> 2)
|
120
|
+
rake (~> 13.0)
|
121
|
+
rspec (~> 3.0)
|
122
|
+
rspec-its (~> 1.3)
|
123
|
+
standard (~> 1)
|
124
|
+
webmock (~> 3.18)
|
125
|
+
|
126
|
+
BUNDLED WITH
|
127
|
+
2.4.7
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Copyright (c) 2023 Instrumentl, Inc.
|
2
|
+
|
3
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
4
|
+
purpose with or without fee is hereby granted, provided that the above
|
5
|
+
copyright notice and this permission notice appear in all copies.
|
6
|
+
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
8
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
9
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
10
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
11
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
12
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
13
|
+
PERFORMANCE OF THIS SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
This is a small Rack middleware for use with the [Cloudflare](https://www.cloudflare.com/) CDN.
|
2
|
+
|
3
|
+
We include two middlewares:
|
4
|
+
|
5
|
+
* `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.
|
6
|
+
* `Rack::CloudflareMiddleware::DenyOthers` returns a 401 for all requests where `REMOTE_ADDR` is not Cloudflare
|
7
|
+
|
8
|
+
If you're using Rails, you may want to use [cloudflare-rails](https://github.com/modosc/cloudflare-rails) instead.
|
9
|
+
|
10
|
+
This library uses Faraday to dynamically update the list of trusted remote IPs every few hours, and includes a checked-in snapshot which will be used if the dynamic updater fails.
|
11
|
+
|
12
|
+
This library is licensed under the ISC license, a copy of which can be found at [LICENSE.txt](LICENSE.txt). "Cloudflare" is a registered trademark of Cloudflare, Inc and is used as per the [Cloudflare Trademark Guidelines](https://www.cloudflare.com/trademark/)
|
13
|
+
|
14
|
+
### Usage
|
15
|
+
|
16
|
+
Add this library to your Gemfile (or otherwise include it with your application), and add it as a middleware in your `config.ru`; for example, if you're using [Sinatra](), this might look like:
|
17
|
+
|
18
|
+
```ruby
|
19
|
+
require "rack/cloudflare_middleware"
|
20
|
+
require "sinatra"
|
21
|
+
require "./app"
|
22
|
+
|
23
|
+
use Rack::CloudflareMiddleware::DenyOthers, allow_private: true
|
24
|
+
use Rack::CloudflareMiddleware::RewriteRemoteAddr
|
25
|
+
|
26
|
+
run Sinatra::Application
|
27
|
+
```
|
28
|
+
|
29
|
+
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.
|
data/data/ips-v4
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
103.21.244.0/22
|
2
|
+
103.22.200.0/22
|
3
|
+
103.31.4.0/22
|
4
|
+
104.16.0.0/13
|
5
|
+
104.24.0.0/14
|
6
|
+
108.162.192.0/18
|
7
|
+
131.0.72.0/22
|
8
|
+
141.101.64.0/18
|
9
|
+
162.158.0.0/15
|
10
|
+
172.64.0.0/13
|
11
|
+
173.245.48.0/20
|
12
|
+
188.114.96.0/20
|
13
|
+
190.93.240.0/20
|
14
|
+
197.234.240.0/22
|
15
|
+
198.41.128.0/17
|
data/data/ips-v6
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
module CloudflareMiddleware
|
5
|
+
class DenyOthers
|
6
|
+
def initialize(app, allow_private: false)
|
7
|
+
@allow_private = allow_private
|
8
|
+
@app = app
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(env)
|
12
|
+
TrustedIps.instance.check_update
|
13
|
+
remote_addr = IPAddr.new env["REMOTE_ADDR"]
|
14
|
+
if (@allow_private && (remote_addr.private? || remote_addr.loopback?)) || TrustedIps.instance.include?(remote_addr)
|
15
|
+
@app.call(env)
|
16
|
+
else
|
17
|
+
["403", {"Content-Type" => "text/plain"}, ["Forbidden by policy statement"]]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
module CloudflareMiddleware
|
5
|
+
class RewriteRemoteAddr
|
6
|
+
def initialize(app)
|
7
|
+
@app = app
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(env)
|
11
|
+
TrustedIps.instance.check_update
|
12
|
+
if TrustedIps.instance.include? env["REMOTE_ADDR"]
|
13
|
+
unless env["HTTP_CF_CONNECTING_IP"].nil?
|
14
|
+
env["HTTP_CF_ORIGINAL_REMOTE_ADDR"] = env["REMOTE_ADDR"]
|
15
|
+
env["REMOTE_ADDR"] = env["HTTP_CF_CONNECTING_IP"]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
@app.call(env)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "ipaddr"
|
4
|
+
require "singleton"
|
5
|
+
|
6
|
+
require "faraday"
|
7
|
+
|
8
|
+
module Rack
|
9
|
+
module CloudflareMiddleware
|
10
|
+
class TrustedIps
|
11
|
+
TIMEOUT = 7.0
|
12
|
+
OPEN_TIMEOUT = 4.0
|
13
|
+
|
14
|
+
UPDATE_THRESHOLD = 21600
|
15
|
+
|
16
|
+
attr_reader :ranges
|
17
|
+
attr_reader :mtimes
|
18
|
+
|
19
|
+
include Singleton
|
20
|
+
|
21
|
+
def initialize
|
22
|
+
reset!
|
23
|
+
end
|
24
|
+
|
25
|
+
def reset!
|
26
|
+
@ranges = {4 => Set.new, 6 => Set.new}
|
27
|
+
@mtimes = {4 => Time.new(1970, 1, 1), 6 => Time.new(1970, 1, 1)}
|
28
|
+
load_from_files
|
29
|
+
end
|
30
|
+
|
31
|
+
def include?(ip)
|
32
|
+
unless ip.is_a? IPAddr
|
33
|
+
ip = IPAddr.new(ip)
|
34
|
+
end
|
35
|
+
if ip.ipv4?
|
36
|
+
@ranges[4].any? { _1.include? ip }
|
37
|
+
else
|
38
|
+
@ranges[6].any? { _1.include? ip }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def update!
|
43
|
+
read_network "https://www.cloudflare.com/ips-v4", 4
|
44
|
+
read_network "https://www.cloudflare.com/ips-v6", 6
|
45
|
+
end
|
46
|
+
|
47
|
+
def check_update
|
48
|
+
if [4, 6].any? { (Time.now - @mtimes[_1]) > UPDATE_THRESHOLD }
|
49
|
+
update!
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def load_from_files
|
56
|
+
read_file "#{__dir__}/../../../data/ips-v4", 4
|
57
|
+
read_file "#{__dir__}/../../../data/ips-v6", 6
|
58
|
+
end
|
59
|
+
|
60
|
+
def process_body(body)
|
61
|
+
body.lines.map(&:strip).filter { !_1.empty? }.map(&IPAddr.method(:new)).to_set
|
62
|
+
end
|
63
|
+
|
64
|
+
def read_file(path, version)
|
65
|
+
::File.open(path) do |f|
|
66
|
+
addresses = process_body(f.read)
|
67
|
+
@ranges[version] = addresses
|
68
|
+
@mtimes[version] = f.mtime
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def read_network(url, version)
|
73
|
+
start_request = Time.now
|
74
|
+
response = faraday_connection.get(url)
|
75
|
+
@ranges[version] = process_body(response.body)
|
76
|
+
@mtimes[version] = start_request
|
77
|
+
rescue Faraday::Error => e
|
78
|
+
warn "Unable to fetch Cloudflare remote IPs: #{e}"
|
79
|
+
end
|
80
|
+
|
81
|
+
def faraday_connection
|
82
|
+
@faraday_connection ||= Faraday.new do |builder|
|
83
|
+
builder.options.open_timeout = OPEN_TIMEOUT
|
84
|
+
builder.options.timeout = TIMEOUT
|
85
|
+
builder.response :raise_error
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "cloudflare_middleware/version"
|
4
|
+
require_relative "cloudflare_middleware/trusted_ips"
|
5
|
+
require_relative "cloudflare_middleware/rewrite_remote_addr"
|
6
|
+
require_relative "cloudflare_middleware/deny_others"
|
7
|
+
|
8
|
+
module Rack
|
9
|
+
module CloudflareMiddleware
|
10
|
+
attr_accessor :logger
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path("lib", __dir__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require "rack/cloudflare_middleware/version"
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = "rack-cloudflare_middleware"
|
9
|
+
spec.version = Rack::CloudflareMiddleware::VERSION
|
10
|
+
spec.authors = ["James Brown"]
|
11
|
+
spec.email = ["james@instrumentl.com"]
|
12
|
+
spec.license = "ISC"
|
13
|
+
|
14
|
+
spec.summary = "Rack middleware for handling Cloudflare remote IP headers"
|
15
|
+
spec.homepage = "https://github.com/instrumentl/rack-cloudflare_middleware"
|
16
|
+
|
17
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
18
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
19
|
+
f.match(%r{^(test|spec|features)/})
|
20
|
+
end
|
21
|
+
end
|
22
|
+
spec.require_paths = ["lib"]
|
23
|
+
|
24
|
+
spec.add_dependency "faraday", ">= 1.0", "< 3"
|
25
|
+
spec.add_dependency "rack", "~> 2"
|
26
|
+
|
27
|
+
spec.add_development_dependency "bundler", "~> 2"
|
28
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
29
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
30
|
+
spec.add_development_dependency "standard", "~> 1"
|
31
|
+
end
|
metadata
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rack-cloudflare_middleware
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- James Brown
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-03-31 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: faraday
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.0'
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '3'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.0'
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '3'
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: rack
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '2'
|
40
|
+
type: :runtime
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '2'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: bundler
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '2'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '2'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: rake
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '13.0'
|
68
|
+
type: :development
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '13.0'
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: rspec
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '3.0'
|
82
|
+
type: :development
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - "~>"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '3.0'
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: standard
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - "~>"
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '1'
|
96
|
+
type: :development
|
97
|
+
prerelease: false
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - "~>"
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '1'
|
103
|
+
description:
|
104
|
+
email:
|
105
|
+
- james@instrumentl.com
|
106
|
+
executables: []
|
107
|
+
extensions: []
|
108
|
+
extra_rdoc_files: []
|
109
|
+
files:
|
110
|
+
- ".github/dependabot.yml"
|
111
|
+
- ".github/workflows/ci.yml"
|
112
|
+
- ".github/workflows/release.yml"
|
113
|
+
- ".gitignore"
|
114
|
+
- ".pre-commit-config.yaml"
|
115
|
+
- ".rubocop.yml"
|
116
|
+
- Gemfile
|
117
|
+
- Gemfile.lock
|
118
|
+
- LICENSE.txt
|
119
|
+
- README.md
|
120
|
+
- data/ips-v4
|
121
|
+
- data/ips-v6
|
122
|
+
- lib/rack/cloudflare_middleware.rb
|
123
|
+
- lib/rack/cloudflare_middleware/deny_others.rb
|
124
|
+
- lib/rack/cloudflare_middleware/rewrite_remote_addr.rb
|
125
|
+
- lib/rack/cloudflare_middleware/trusted_ips.rb
|
126
|
+
- lib/rack/cloudflare_middleware/version.rb
|
127
|
+
- rack-cloudflare_middleware.gemspec
|
128
|
+
homepage: https://github.com/instrumentl/rack-cloudflare_middleware
|
129
|
+
licenses:
|
130
|
+
- ISC
|
131
|
+
metadata: {}
|
132
|
+
post_install_message:
|
133
|
+
rdoc_options: []
|
134
|
+
require_paths:
|
135
|
+
- lib
|
136
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
137
|
+
requirements:
|
138
|
+
- - ">="
|
139
|
+
- !ruby/object:Gem::Version
|
140
|
+
version: '0'
|
141
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
requirements: []
|
147
|
+
rubygems_version: 3.4.1
|
148
|
+
signing_key:
|
149
|
+
specification_version: 4
|
150
|
+
summary: Rack middleware for handling Cloudflare remote IP headers
|
151
|
+
test_files: []
|