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.
@@ -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
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /log/
11
+ .ruby-gemset
12
+ .ruby-version
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format p
2
+ --color
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.3
4
+ before_install: gem install bundler -v 1.10.6
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in cloudflare-rails.gemspec
4
+ gemspec
@@ -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.
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -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
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -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,11 @@
1
+ require "cloudflare/rails/version"
2
+ require "httparty"
3
+
4
+ module Cloudflare
5
+ module Rails
6
+
7
+
8
+ end
9
+ end
10
+
11
+ require "cloudflare/rails/railtie"
@@ -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
@@ -0,0 +1,5 @@
1
+ module Cloudflare
2
+ module Rails
3
+ VERSION = "0.1.0"
4
+ end
5
+ 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: []