cloudflare-rails 0.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 +7 -0
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/README.md +39 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/cloudflare-rails.gemspec +35 -0
- data/lib/cloudflare/rails.rb +11 -0
- data/lib/cloudflare/rails/railtie.rb +174 -0
- data/lib/cloudflare/rails/version.rb +5 -0
- metadata +154 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: aa44d2f1afb581fda6300182a71286d900e69097
|
4
|
+
data.tar.gz: 3427435d477646191a2b05e289e52e7dc8a9f036
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f05254ee54a69a711e7e612f7e1ee029b25186efb04f693a199dc00bc1c991799e704419674827d15fd1cf49e047e0247595f839b290121332924c5f4ff79645
|
7
|
+
data.tar.gz: c2e048580350035589e53e0ababdf4bed4f5d693d383564adac879d2b08a10f8f66bdc3816d2acec5cd425a03fac273ac012b695b10130a9d7df6ac96cf6268f
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# Cloudflare::Rails
|
2
|
+
|
3
|
+
This gem correctly configures Rails for [CloudFlare](https://www.cloudflare.com) so that `request.remote_ip` / `request.ip` both work correctly.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's `Gemfile`:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
group :production
|
11
|
+
# or :staging or :beta or whatever environments you are using cloudflare in.
|
12
|
+
# you probably don't want this for :test or :development
|
13
|
+
gem 'cloudflare-rails'
|
14
|
+
end
|
15
|
+
```
|
16
|
+
|
17
|
+
And then execute:
|
18
|
+
|
19
|
+
$ bundle
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
This code will fetch CloudFlare's current [IPv4](https://www.cloudflare.com/ips-v4) and [IPv6](https://www.cloudflare.com/ips-v6) lists, store them in `Rails.cache`, and add them to `config.cloudflare.ips`.
|
24
|
+
|
25
|
+
You can configure the HTTP `timeout` and `expires_in` cache parameters inside of your rails config:
|
26
|
+
```
|
27
|
+
config.cloudflare.expires_in = 12.hours # default value
|
28
|
+
config.cloudfalre.timeout = 5.seconds # default value
|
29
|
+
```
|
30
|
+
|
31
|
+
## Development
|
32
|
+
|
33
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
34
|
+
|
35
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
36
|
+
|
37
|
+
## Contributing
|
38
|
+
|
39
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/cloudflare-rails.
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "cloudflare/rails"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'cloudflare/rails/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "cloudflare-rails"
|
8
|
+
spec.version = Cloudflare::Rails::VERSION
|
9
|
+
spec.authors = ["jonathan schatz"]
|
10
|
+
spec.email = ["modosc@users.noreply.github.com"]
|
11
|
+
|
12
|
+
spec.summary = "This gem configures Rails for CloudFlare so that request.ip and request.remote_ip and work correctly."
|
13
|
+
spec.description = ""
|
14
|
+
spec.homepage = "https://github.com/modosc/cloudflare-rails"
|
15
|
+
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
spec.bindir = "exe"
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.10"
|
23
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
24
|
+
spec.add_development_dependency "rspec"
|
25
|
+
spec.add_development_dependency "rspec-rails"
|
26
|
+
|
27
|
+
spec.add_development_dependency "webmock"
|
28
|
+
|
29
|
+
# TODO - no idea if this is 4.x specific, should test with older versions
|
30
|
+
spec.add_dependency "rails", "~> 4.0"
|
31
|
+
spec.add_dependency 'httparty'
|
32
|
+
|
33
|
+
# we need Module#prepend
|
34
|
+
spec.required_ruby_version = '>= 2.0'
|
35
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
require "httparty"
|
2
|
+
|
3
|
+
module Cloudflare
|
4
|
+
module Rails
|
5
|
+
class Railtie < ::Rails::Railtie
|
6
|
+
|
7
|
+
# patch rack::request to use our cloudflare ips - this way request.ip is
|
8
|
+
# correct inside of rack and rails
|
9
|
+
module CheckTrustedProxies
|
10
|
+
def trusted_proxy?(ip)
|
11
|
+
::Rails.application.config.cloudflare.ips.any?{ |proxy| proxy === ip } || super
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
Rack::Request.prepend CheckTrustedProxies
|
16
|
+
|
17
|
+
# patch ActionDispatch::RemoteIP to use our cloudflare ips - this way
|
18
|
+
# request.remote_ip is correct inside of rails
|
19
|
+
module RemoteIpProxies
|
20
|
+
def proxies
|
21
|
+
super + ::Rails.application.config.cloudflare.ips
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
ActionDispatch::RemoteIp.prepend RemoteIpProxies
|
26
|
+
|
27
|
+
class Importer
|
28
|
+
include HTTParty
|
29
|
+
base_uri 'https://www.cloudflare.com'
|
30
|
+
follow_redirects true
|
31
|
+
default_options.update(verify: true)
|
32
|
+
|
33
|
+
class ResponseError < HTTParty::ResponseError; end
|
34
|
+
|
35
|
+
IPS_V4_URL = '/ips-v4'
|
36
|
+
IPS_V6_URL = '/ips-v6'
|
37
|
+
|
38
|
+
class << self
|
39
|
+
def ips_v6
|
40
|
+
fetch IPS_V6_URL
|
41
|
+
end
|
42
|
+
|
43
|
+
def ips_v4
|
44
|
+
fetch IPS_V4_URL
|
45
|
+
end
|
46
|
+
|
47
|
+
def fetch(url)
|
48
|
+
resp = get url, timeout: ::Rails.application.config.cloudflare.timeout
|
49
|
+
if resp.success?
|
50
|
+
resp.body.split("\n").reject(&:blank?).map{|ip| IPAddr.new ip}
|
51
|
+
else
|
52
|
+
raise ResponseError.new(resp.response)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def fetch_with_cache(type)
|
57
|
+
::Rails.cache.fetch("cloudflare-rails:#{type}", expires_in: ::Rails.application.config.cloudflare.expires_in) do
|
58
|
+
self.send type
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# setup defaults before we configure our app.
|
67
|
+
DEFAULTS = {
|
68
|
+
expires_in: 12.hours,
|
69
|
+
timeout: 5.seconds,
|
70
|
+
ips: Array.new
|
71
|
+
}
|
72
|
+
|
73
|
+
config.before_configuration do |app|
|
74
|
+
app.config.cloudflare = ActiveSupport::OrderedOptions.new
|
75
|
+
app.config.cloudflare.reverse_merge! DEFAULTS
|
76
|
+
end
|
77
|
+
|
78
|
+
# we set config.cloudflare.ips after_initialize so that our cache will
|
79
|
+
# be correctly setup. we rescue and log errors so that failures won't prevent
|
80
|
+
# rails from booting
|
81
|
+
config.after_initialize do |app|
|
82
|
+
[:ips_v4, :ips_v6].each do |type|
|
83
|
+
begin
|
84
|
+
::Rails.application.config.cloudflare.ips += Importer.fetch_with_cache(type)
|
85
|
+
rescue Importer::ResponseError => e
|
86
|
+
::Rails.logger.error "Cloudflare::Rails: Couldn't import #{type} blocks from CloudFlare: #{e.response}"
|
87
|
+
rescue => e
|
88
|
+
::Rails.logger.error "Cloudflare::Rails: Got exception: #{e} for type:#{type}"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
# bail if ActionDispatch::RemoteIp isn't already loaded
|
97
|
+
# if app.config.middleware.middlewares.exclude? ActionDispatch::RemoteIp
|
98
|
+
# ::Rails.logger.error "Couldn't find ActionDispatch::RemoteIp middleware, skipping CloudFlare::Rails initialization"
|
99
|
+
# return false
|
100
|
+
# end
|
101
|
+
|
102
|
+
|
103
|
+
# change our default timeout if specified
|
104
|
+
# default_timeout app.config.cloudflare.timeout if app.config.cloudflare.timeout.present?
|
105
|
+
|
106
|
+
#cf_config = app.config.cloudflare.reverse_merge Importer::DEFAULT_CONFIG
|
107
|
+
|
108
|
+
# Importer.default_timeout Config.config
|
109
|
+
|
110
|
+
|
111
|
+
# caching is here so that we have app.config.cloudflare in scope - i
|
112
|
+
# suppose we could move this into fetch and take expires_in as a
|
113
|
+
# param?
|
114
|
+
|
115
|
+
# cloudflare_ips += ::Rails.cache.fetch("cloudflare-rails:ip_v4", expires_in: cf_config[:expires_in]) do
|
116
|
+
# ip_v4
|
117
|
+
# end.map{|ip| IPAddr.new ip }
|
118
|
+
#
|
119
|
+
# cloudflare_ips += ::Rails.cache.fetch("cloudflare-rails:ip_v6", expires_in: cf_config[:expires_in]) do
|
120
|
+
# ip_v6
|
121
|
+
# end.map{|ip| IPAddr.new "[#{ip}]" }
|
122
|
+
#
|
123
|
+
# [:ips_v4, :ips_v6].each do |type|
|
124
|
+
# begin
|
125
|
+
# ips = ::Rails.cache.fetch("cloudflare-rails:#{type}", expires_in: cf_config[:expires_in]) do
|
126
|
+
# Importer.send type
|
127
|
+
# end
|
128
|
+
# app.config.cloudflare.ips += ips if ips.present?
|
129
|
+
# rescue Importer::ResponseError => e
|
130
|
+
# ::Rails.logger.error "Cloudflare::Rails: Couldn't import #{type} blocks from CloudFlare: #{e.response}"
|
131
|
+
# rescue => e
|
132
|
+
# ::Rails.logger.error "Cloudflare::Rails: Got exception: #{e} for type:#{type}, cloudflare_ips: #{cloudflare_ips}"
|
133
|
+
# end
|
134
|
+
# end
|
135
|
+
#
|
136
|
+
# if app.config.cloudflare.ips.present?
|
137
|
+
# # i don't know what uses these beyond ActionDispatch::RemoteIp (which
|
138
|
+
# # we are patching below) but we should go ahead and keep this in sync
|
139
|
+
# # anyway
|
140
|
+
# if app.config.action_dispatch.trusted_proxies.blank?
|
141
|
+
# # this behavior is copied from:
|
142
|
+
# #
|
143
|
+
# # https://github.com/rails/rails/blob/a59a9b7f729870de6c9282bd8e2a7ed7f86fc868/actionpack/lib/action_dispatch/middleware/remote_ip.rb#L76
|
144
|
+
# #
|
145
|
+
# # we want to make the addition of cloudflare_ips as transparent as
|
146
|
+
# # possible but by adding our array in we change the behavior of
|
147
|
+
# # ActionDispatch::RemoteIp
|
148
|
+
# app.config.action_dispatch.trusted_proxies = ActionDispatch::RemoteIp::TRUSTED_PROXIES + cloudflare_ips
|
149
|
+
# elsif app.config.action_dispatch.trusted_proxies.respond_to?(:any)
|
150
|
+
# app.config.action_dispatch.trusted_proxies += app.config.cloudflare.ips
|
151
|
+
# else
|
152
|
+
# app.config.action_dispatch.trusted_proxies = Array(app.config.action_dispatch.trusted_proxies) + ActionDispatch::RemoteIp::TRUSTED_PROXIES + app.config.cloudflare.ips
|
153
|
+
# end
|
154
|
+
#
|
155
|
+
# # now we have to patch ActionDispatch::RemoteIp since by the time we
|
156
|
+
# # get here it's already been configured and initialized and we can't
|
157
|
+
# # easily mess around with the middleware stack.
|
158
|
+
# remote_ip_patch = Module.new
|
159
|
+
#
|
160
|
+
# remote_ip_patch.instance_eval do
|
161
|
+
# define_method :proxies do
|
162
|
+
# @proxies + app.config.cloudflare.ips
|
163
|
+
# end
|
164
|
+
# end
|
165
|
+
#
|
166
|
+
# # remote_ip_patch.const_set :CLOUDFLARE_IPS, cloudflare_ips
|
167
|
+
# # pp "remote_ip_patch.constants is #{remote_ip_patch.constants}"
|
168
|
+
# ActionDispatch::RemoteIp.prepend remote_ip_patch
|
169
|
+
# # pp ActionDispatch::RemoteIp::CLOUDFLARE_IPS
|
170
|
+
# end
|
171
|
+
# end
|
172
|
+
# end
|
173
|
+
# end
|
174
|
+
# end
|
metadata
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cloudflare-rails
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- jonathan schatz
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-01-28 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.10'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.10'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec-rails
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: webmock
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rails
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '4.0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '4.0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: httparty
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description: ''
|
112
|
+
email:
|
113
|
+
- modosc@users.noreply.github.com
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- ".gitignore"
|
119
|
+
- ".rspec"
|
120
|
+
- ".travis.yml"
|
121
|
+
- Gemfile
|
122
|
+
- README.md
|
123
|
+
- Rakefile
|
124
|
+
- bin/console
|
125
|
+
- bin/setup
|
126
|
+
- cloudflare-rails.gemspec
|
127
|
+
- lib/cloudflare/rails.rb
|
128
|
+
- lib/cloudflare/rails/railtie.rb
|
129
|
+
- lib/cloudflare/rails/version.rb
|
130
|
+
homepage: https://github.com/modosc/cloudflare-rails
|
131
|
+
licenses: []
|
132
|
+
metadata: {}
|
133
|
+
post_install_message:
|
134
|
+
rdoc_options: []
|
135
|
+
require_paths:
|
136
|
+
- lib
|
137
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
138
|
+
requirements:
|
139
|
+
- - ">="
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '2.0'
|
142
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
143
|
+
requirements:
|
144
|
+
- - ">="
|
145
|
+
- !ruby/object:Gem::Version
|
146
|
+
version: '0'
|
147
|
+
requirements: []
|
148
|
+
rubyforge_project:
|
149
|
+
rubygems_version: 2.4.8
|
150
|
+
signing_key:
|
151
|
+
specification_version: 4
|
152
|
+
summary: This gem configures Rails for CloudFlare so that request.ip and request.remote_ip
|
153
|
+
and work correctly.
|
154
|
+
test_files: []
|