rack-dedos 0.5.1 → 0.7.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: 7345523b97820b5bae83175067c3f76b69a4c9f7f57d3bb5faa5175f5fbcdbe4
4
- data.tar.gz: 241fc35b7c5c8df31468186079dc1ba27039481437cb249ae8884cee76c94f68
3
+ metadata.gz: d666dc81c74730f8c69f0cbf84a171aa39b0f94f393b4538cf8c81c244dd8fe1
4
+ data.tar.gz: 6b22c751e94241988408cc38c63606204180c2f212c3416292c273b95dcdc2bc
5
5
  SHA512:
6
- metadata.gz: 7daa7932e86cea82bf48f6b26e14c70e3b8732b59d6b8495531795f60c79449fe7c25f40f213bfd22098dfe5565b20e34200e5b3a035840171286b93c569deb6
7
- data.tar.gz: 94f5e9db12c676fb325624679b3105bdcdf1c107e552725cc56fc8ff77d3c84b9e617c30ccc183866c5ce7b36942843b1385befc0ec29c229807a2fa15a5f973
6
+ metadata.gz: e211d5dcbb8a2d3422e24638317080db89e54367e8e542ecd720e87dec6a0fca95ae1ff332a2e15d72e1b2bf0f2fa72456aadc895078d1121de2cffd5661e24c
7
+ data.tar.gz: dc1dce126f9177c30174edcb3d232435c8ad5c40142af5bc708c391380f0313cd94c8dcea7cab5cb41d2f5e24574357112ce6be04f2771420cb0ce24ce3d183f
data/CHANGELOG.md CHANGED
@@ -2,6 +2,24 @@
2
2
 
3
3
  Nothing so far
4
4
 
5
+ ## 0.7.0
6
+
7
+ ### Breaking changes
8
+ * Change format of country code details in log
9
+
10
+ ### Fixes
11
+ * Drop keyword arguments for Rack compatibility
12
+ * Fix collision with Rack::Logger
13
+
14
+ ## 0.6.0 (yanked)
15
+
16
+ ### Breaking changes
17
+ * Log to STDOUT (instead of STDERR) by default
18
+
19
+ ### Additions
20
+ * Suppport custom loggers
21
+ * Optionally log request headers
22
+
5
23
  ## 0.5.1
6
24
 
7
25
  ### Fixes
data/README.md CHANGED
@@ -122,6 +122,33 @@ use Rack::Dedos,
122
122
  text: "Temporary Server Error"
123
123
  ```
124
124
 
125
+ ### Log
126
+
127
+ By default, blocked request are logged as info to `$stdout` such as:
128
+
129
+ > rack-dedos: request /foobar from 1.2.3.4 blocked by Rack::Dedos::Filters::Country: CN
130
+
131
+ The level of details may differ by filter, however, if you want to drill down and log additional request headers, you can configure which headers to include as follows:
132
+
133
+ ```ruby
134
+ use Rack::Dedos,
135
+ headers: ['HTTP_USER_AGENT', 'HTTP_REFERER']
136
+ ```
137
+
138
+ You can direct the log stream to any logger object you wish:
139
+
140
+ ```ruby
141
+ use Rack::Dedos,
142
+ logger: Logger.new('/var/log/rack-dedos.log')
143
+ ```
144
+
145
+ Or disable logging entirely:
146
+
147
+ ```ruby
148
+ use Rack::Dedos,
149
+ logger: Logger.new(nil)
150
+ ```
151
+
125
152
  ## Filters
126
153
 
127
154
  By default, all filters described below are applied. You can exclude certain filters:
@@ -199,12 +226,19 @@ cd rack-dedos
199
226
  git submodule update --init
200
227
  ```
201
228
 
202
- To install the development dependencies and then run the test suite:
229
+ To install only the development dependencies:
230
+
231
+ ```
232
+ bundle install
233
+ bundle exec rake # run the tests once
234
+ ```
235
+
236
+ To install the development dependencies and development tools (such as guard):
203
237
 
204
238
  ```
239
+ bundle config set --local with toolbox
205
240
  bundle install
206
- bundle exec rake # run tests once
207
- bundle exec guard # run tests whenever files are modified
241
+ bundle exec guard # run the tests whenever files are modified
208
242
  ```
209
243
 
210
244
  You're welcome to [submit issues](https://github.com/svoop/rack-dedos/issues) and contribute code by [forking the project and submitting pull requests](https://docs.github.com/en/get-started/quickstart/fork-a-repo).
@@ -1,27 +1,29 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'logger'
4
+
3
5
  module Rack
4
6
  module Dedos
5
7
  module Filters
6
8
  class Base
7
9
 
8
10
  DEFAULT_OPTIONS = {
11
+ logger: nil,
9
12
  only_paths: [],
10
13
  except_paths: [],
11
14
  status: 403,
12
- text: 'Forbidden (Temporarily Blocked by Rules)'
15
+ text: 'Forbidden (Temporarily Blocked by Rules)',
16
+ headers: []
13
17
  }.freeze
14
18
 
15
- attr_reader :app
16
- attr_reader :options
17
- attr_reader :details
19
+ attr_reader :app, :options, :details
18
20
 
19
21
  # @param app [#call]
20
22
  # @param options [Hash{Symbol => Object}]
21
- def initialize(app, options = {})
23
+ def initialize(app, options={})
22
24
  @app = app
23
25
  @options = DEFAULT_OPTIONS.merge(options)
24
- @details = nil
26
+ @details = {}
25
27
  end
26
28
 
27
29
  def call(env)
@@ -30,8 +32,10 @@ module Rack
30
32
  if !apply?(request) || allowed?(request, ip)
31
33
  app.call(env)
32
34
  else
33
- message = "rack-dedos: request #{request.path} from #{ip} blocked by #{name}"
34
- warn([message, details].compact.join(": "))
35
+ message = ["request #{request.path} from #{ip} blocked by #{name}"]
36
+ message += details_list
37
+ message += headers_list(request)
38
+ logger.info(message.join(' '))
35
39
  [options[:status], { 'Content-Type' => 'text/plain' }, [options[:text]]]
36
40
  end
37
41
  end
@@ -42,6 +46,10 @@ module Rack
42
46
  Rack::Dedos.config
43
47
  end
44
48
 
49
+ def logger
50
+ @logger ||= options[:logger] || ::Logger.new($stdout, progname: 'rack-dedos')
51
+ end
52
+
45
53
  def apply?(request)
46
54
  return false if @options[:except_paths].any? { request.path.match? _1 }
47
55
  return true if @options[:only_paths].none?
@@ -71,6 +79,17 @@ module Rack
71
79
  end
72
80
  end
73
81
 
82
+ def details_list
83
+ details.map do |key, value|
84
+ "#{key.upcase}=#{value.inspect}"
85
+ end
86
+ end
87
+
88
+ def headers_list(request)
89
+ options[:headers].map do |header|
90
+ "#{header.upcase}=#{request.get_header(header).inspect}"
91
+ end
92
+ end
74
93
  end
75
94
  end
76
95
  end
@@ -14,7 +14,7 @@ module Rack
14
14
  # @option options [String] :maxmind_db_file MaxMind database file
15
15
  # @option options [Symbol, Array<Symbol>] :allowed_countries ISO 3166-1 alpha 2
16
16
  # @option options [Symbol, Array<Symbol>] :denied_countries ISO 3166-1 alpha 2
17
- def initialize(*)
17
+ def initialize(...)
18
18
  super
19
19
  @maxmind_db_file = options[:maxmind_db_file] or fail "MaxMind database file not set"
20
20
  @allowed = case
@@ -26,14 +26,14 @@ module Rack
26
26
  end
27
27
 
28
28
  def allowed?(request, ip)
29
- if country = maxmind_db&.get(ip)
30
- country_code = @details = country.dig('country', 'iso_code').to_sym
31
- @countries.include?(country_code) ? @allowed : !@allowed
29
+ if db = maxmind_db&.get(ip)
30
+ details[:country_code] = db.dig('country', 'iso_code').to_sym
31
+ @countries.include?(details[:country_code]) ? @allowed : !@allowed
32
32
  else # not found in database
33
33
  true
34
34
  end
35
35
  rescue => error
36
- warn("rack-dedos: request from #{ip} allowed due to error: #{error.message}")
36
+ logger.error("request from #{ip} allowed due to error: #{error.message}")
37
37
  true
38
38
  end
39
39
 
@@ -12,7 +12,7 @@ module Rack
12
12
  # @option options [String] :cache_url URL of the cache backend
13
13
  # @option options [Integer] :cache_period how long to retain cached IP
14
14
  # addresses in seconds (default: 900)
15
- def initialize(*)
15
+ def initialize(...)
16
16
  super
17
17
  @cache_url = options[:cache_url] or fail "cache URL not set"
18
18
  @cache_period = options[:cache_period] || 900
@@ -34,7 +34,7 @@ module Rack
34
34
  false
35
35
  end
36
36
  rescue => error
37
- warn("rack-dedos: request from #{ip} allowed due to error: #{error.message}")
37
+ logger.error("request from #{ip} allowed due to error: #{error.message}")
38
38
  true
39
39
  end
40
40
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Rack
4
4
  module Dedos
5
- VERSION = "0.5.1"
5
+ VERSION = "0.7.0"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-dedos
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sven Schwyn
@@ -24,13 +24,13 @@ dependencies:
24
24
  - !ruby/object:Gem::Version
25
25
  version: 2.2.0
26
26
  - !ruby/object:Gem::Dependency
27
- name: redis
27
+ name: logger
28
28
  requirement: !ruby/object:Gem::Requirement
29
29
  requirements:
30
30
  - - ">="
31
31
  - !ruby/object:Gem::Version
32
32
  version: '0'
33
- type: :development
33
+ type: :runtime
34
34
  prerelease: false
35
35
  version_requirements: !ruby/object:Gem::Requirement
36
36
  requirements:
@@ -38,7 +38,7 @@ dependencies:
38
38
  - !ruby/object:Gem::Version
39
39
  version: '0'
40
40
  - !ruby/object:Gem::Dependency
41
- name: maxmind-db
41
+ name: rake
42
42
  requirement: !ruby/object:Gem::Requirement
43
43
  requirements:
44
44
  - - ">="
@@ -52,7 +52,7 @@ dependencies:
52
52
  - !ruby/object:Gem::Version
53
53
  version: '0'
54
54
  - !ruby/object:Gem::Dependency
55
- name: debug
55
+ name: redis
56
56
  requirement: !ruby/object:Gem::Requirement
57
57
  requirements:
58
58
  - - ">="
@@ -66,7 +66,7 @@ dependencies:
66
66
  - !ruby/object:Gem::Version
67
67
  version: '0'
68
68
  - !ruby/object:Gem::Dependency
69
- name: rake
69
+ name: maxmind-db
70
70
  requirement: !ruby/object:Gem::Requirement
71
71
  requirements:
72
72
  - - ">="
@@ -107,62 +107,6 @@ dependencies:
107
107
  - - ">="
108
108
  - !ruby/object:Gem::Version
109
109
  version: '0'
110
- - !ruby/object:Gem::Dependency
111
- name: minitest-flash
112
- requirement: !ruby/object:Gem::Requirement
113
- requirements:
114
- - - ">="
115
- - !ruby/object:Gem::Version
116
- version: '0'
117
- type: :development
118
- prerelease: false
119
- version_requirements: !ruby/object:Gem::Requirement
120
- requirements:
121
- - - ">="
122
- - !ruby/object:Gem::Version
123
- version: '0'
124
- - !ruby/object:Gem::Dependency
125
- name: guard
126
- requirement: !ruby/object:Gem::Requirement
127
- requirements:
128
- - - ">="
129
- - !ruby/object:Gem::Version
130
- version: '0'
131
- type: :development
132
- prerelease: false
133
- version_requirements: !ruby/object:Gem::Requirement
134
- requirements:
135
- - - ">="
136
- - !ruby/object:Gem::Version
137
- version: '0'
138
- - !ruby/object:Gem::Dependency
139
- name: guard-minitest
140
- requirement: !ruby/object:Gem::Requirement
141
- requirements:
142
- - - ">="
143
- - !ruby/object:Gem::Version
144
- version: '0'
145
- type: :development
146
- prerelease: false
147
- version_requirements: !ruby/object:Gem::Requirement
148
- requirements:
149
- - - ">="
150
- - !ruby/object:Gem::Version
151
- version: '0'
152
- - !ruby/object:Gem::Dependency
153
- name: yard
154
- requirement: !ruby/object:Gem::Requirement
155
- requirements:
156
- - - ">="
157
- - !ruby/object:Gem::Version
158
- version: '0'
159
- type: :development
160
- prerelease: false
161
- version_requirements: !ruby/object:Gem::Requirement
162
- requirements:
163
- - - ">="
164
- - !ruby/object:Gem::Version
165
- version: '0'
166
110
  description: |
167
111
  Somewhat more radical filters designed to decimate malicious requests during
168
112
  a denial-of-service (DoS) attack by chopping their connection well before