spanx 0.1.0 → 0.1.1
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.
- data/.travis.yml +6 -0
- data/README.md +7 -3
- data/lib/spanx/actor/analyzer.rb +2 -9
- data/lib/spanx/actor/collector.rb +1 -1
- data/lib/spanx/actor/writer.rb +1 -1
- data/lib/spanx/config.rb +1 -1
- data/lib/spanx/ip_checker.rb +1 -1
- data/lib/spanx/version.rb +1 -1
- data/spanx.gemspec +1 -1
- data/spec/spanx/actor/analyzer_spec.rb +11 -12
- data/spec/spanx/actor/writer_spec.rb +1 -1
- data/spec/spanx/notifier/base_spec.rb +1 -1
- data/spec/spanx/notifier/email_spec.rb +1 -1
- metadata +4 -4
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -1,9 +1,13 @@
|
|
1
|
-
|
1
|
+
Spanx
|
2
|
+
=====
|
3
|
+
|
4
|
+
[](http://travis-ci.org/wanelo/spanx)
|
2
5
|
|
3
6
|
Spank down IP spam: IP-based rate limiting for web applications behind HTTP server such as nginx or Apache.
|
4
7
|
|
5
|
-
Spanx
|
6
|
-
|
8
|
+
Spanx integrates into any web application simply by monitoring one or more HTTP server access log file(s)
|
9
|
+
in real time (think Apache/nginx access.log). Spanx is built on top of the gem Pause, which is a simple Redis-based rate
|
10
|
+
limiter.
|
7
11
|
|
8
12
|
Basic flow is as follows:
|
9
13
|
|
data/lib/spanx/actor/analyzer.rb
CHANGED
@@ -38,13 +38,12 @@ module Spanx
|
|
38
38
|
def analyze_all_ips
|
39
39
|
return unless Spanx::IPChecker.enabled?
|
40
40
|
|
41
|
-
@previously_blocked_ips = Spanx::IPChecker.
|
42
|
-
|
41
|
+
@previously_blocked_ips = Spanx::IPChecker.rate_limited_identifiers
|
43
42
|
ips = Spanx::IPChecker.tracked_identifiers
|
44
43
|
|
45
44
|
Logger.logging "analyzed #{ips.size} IPs" do
|
46
45
|
ips.each do |ip|
|
47
|
-
blocked_ip =
|
46
|
+
blocked_ip = Spanx::IPChecker.new(ip).analyze
|
48
47
|
blocked_ips << blocked_ip if blocked_ip
|
49
48
|
end
|
50
49
|
end
|
@@ -54,12 +53,6 @@ module Spanx
|
|
54
53
|
blocked_ips.clear
|
55
54
|
end
|
56
55
|
|
57
|
-
# Analyze individual IP for all defined periods. As soon as one
|
58
|
-
# rule is triggered, exit the method
|
59
|
-
def analyze_ip(ip)
|
60
|
-
Spanx::IPChecker.new(ip).analyze
|
61
|
-
end
|
62
|
-
|
63
56
|
private
|
64
57
|
|
65
58
|
def initialize_notifiers(config)
|
@@ -39,7 +39,7 @@ module Spanx
|
|
39
39
|
semaphore.synchronize {
|
40
40
|
Logger.logging "flushing cache with [#{cache.keys.size}] keys" do
|
41
41
|
cache.each_pair do |key, count|
|
42
|
-
Spanx::IPChecker.new(key[0]).increment!(key[1]
|
42
|
+
Spanx::IPChecker.new(key[0]).increment!(count, key[1])
|
43
43
|
end
|
44
44
|
reset_cache
|
45
45
|
end
|
data/lib/spanx/actor/writer.rb
CHANGED
data/lib/spanx/config.rb
CHANGED
@@ -25,7 +25,7 @@ module Spanx
|
|
25
25
|
|
26
26
|
if self.has_key?(:analyzer) && self[:analyzer].has_key?(:period_checks)
|
27
27
|
self[:analyzer][:period_checks].each do |check|
|
28
|
-
Spanx::IPChecker.check
|
28
|
+
Spanx::IPChecker.check(check)
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
data/lib/spanx/ip_checker.rb
CHANGED
data/lib/spanx/version.rb
CHANGED
data/spanx.gemspec
CHANGED
@@ -15,7 +15,7 @@ Gem::Specification.new do |gem|
|
|
15
15
|
gem.require_paths = %w(lib)
|
16
16
|
gem.version = Spanx::VERSION
|
17
17
|
|
18
|
-
gem.add_dependency 'pause', '~> 0.0.
|
18
|
+
gem.add_dependency 'pause', '~> 0.0.4'
|
19
19
|
gem.add_dependency 'file-tail'
|
20
20
|
gem.add_dependency 'mixlib-cli'
|
21
21
|
gem.add_dependency 'daemons'
|
@@ -36,24 +36,24 @@ describe Spanx::Actor::Analyzer do
|
|
36
36
|
let(:ip1) { "127.0.0.1" }
|
37
37
|
let(:ip2) { "192.168.0.1" }
|
38
38
|
|
39
|
-
describe "#
|
39
|
+
describe "#RateLimitedEvent" do
|
40
40
|
|
41
41
|
let(:now) { period_marker(10, Time.now.to_i) + 1 }
|
42
42
|
|
43
43
|
context "IP blocking rules are not matched" do
|
44
44
|
it "returns nil" do
|
45
|
-
|
45
|
+
Spanx::IPChecker.new(ip1).analyze.should be_nil
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
49
|
context "IP blocking rules are matched" do
|
50
50
|
before do
|
51
|
-
Spanx::IPChecker.new(ip1).increment!(now - 5
|
52
|
-
Spanx::IPChecker.new(ip1).increment!(now - 15
|
51
|
+
Spanx::IPChecker.new(ip1).increment!(2, now - 5)
|
52
|
+
Spanx::IPChecker.new(ip1).increment!(1, now - 15)
|
53
53
|
end
|
54
54
|
|
55
|
-
it "returns a Pause::
|
56
|
-
|
55
|
+
it "returns a Pause::RateLimitedEvent" do
|
56
|
+
Spanx::IPChecker.new(ip1).analyze.should be_a(Pause::RateLimitedEvent)
|
57
57
|
end
|
58
58
|
end
|
59
59
|
end
|
@@ -61,7 +61,7 @@ describe Spanx::Actor::Analyzer do
|
|
61
61
|
describe "#analyze_all_ips" do
|
62
62
|
context "checker is disabled" do
|
63
63
|
before do
|
64
|
-
Spanx::IPChecker.stub(:
|
64
|
+
Spanx::IPChecker.stub(:rate_limited_identifiers).and_return([ip1, ip2])
|
65
65
|
Spanx::IPChecker.stub(:enabled?).and_return(false)
|
66
66
|
analyzer.should_not_receive(:analyze_ip)
|
67
67
|
end
|
@@ -73,12 +73,11 @@ describe Spanx::Actor::Analyzer do
|
|
73
73
|
|
74
74
|
context "adapter is enabled" do
|
75
75
|
let(:period_check) { double(period_seconds: 1, max_allowed: 1, block_ttl: nil) }
|
76
|
-
let(:blocked_ip) { Pause::BlockedAction.new(mock(identifier:ip2), period_check, 200, 1234566) }
|
77
76
|
|
78
77
|
before do
|
79
78
|
Spanx::IPChecker.should_receive(:tracked_identifiers).and_return([ip1, ip2])
|
80
|
-
|
81
|
-
|
79
|
+
Spanx::IPChecker.should_receive(:new).with(ip1).and_return(mock(analyze: nil))
|
80
|
+
Spanx::IPChecker.should_receive(:new).with(ip2).and_return(mock(analyze: nil))
|
82
81
|
end
|
83
82
|
|
84
83
|
it "analyzes each IP found" do
|
@@ -105,8 +104,8 @@ describe Spanx::Actor::Analyzer do
|
|
105
104
|
end
|
106
105
|
|
107
106
|
it "should publish to notifiers on blocking IP" do
|
108
|
-
fake_notifier.should_receive(:publish).with(an_instance_of(Pause::
|
109
|
-
Spanx::IPChecker.new(ip1).increment!(Time.now.to_i - 5
|
107
|
+
fake_notifier.should_receive(:publish).with(an_instance_of(Pause::RateLimitedEvent))
|
108
|
+
Spanx::IPChecker.new(ip1).increment!(50000, Time.now.to_i - 5)
|
110
109
|
analyzer.analyze_all_ips
|
111
110
|
end
|
112
111
|
end
|
@@ -19,7 +19,7 @@ describe Spanx::Actor::Writer do
|
|
19
19
|
end
|
20
20
|
|
21
21
|
before do
|
22
|
-
Spanx::IPChecker.stub(:
|
22
|
+
Spanx::IPChecker.stub(:rate_limited_identifiers).and_return(["1.2.3.4", "127.0.0.1"])
|
23
23
|
end
|
24
24
|
|
25
25
|
context "when adapter is enabled" do
|
@@ -6,7 +6,7 @@ describe Spanx::Notifier::Base do
|
|
6
6
|
let(:time) { Time.now }
|
7
7
|
let(:ip_check) { Spanx::IPChecker.new("1.2.3.4") }
|
8
8
|
let(:period_check) { Pause::PeriodCheck.new(60, 100, 80)}
|
9
|
-
let(:blocked_action) { Pause::
|
9
|
+
let(:blocked_action) { Pause::RateLimitedEvent.new(ip_check, period_check, 500, time.to_i)}
|
10
10
|
|
11
11
|
it "should set the correct message content" do
|
12
12
|
Spanx::Notifier::Base.new.send(:generate_block_ip_message, blocked_action).should ==
|
@@ -47,7 +47,7 @@ describe Spanx::Notifier::Email, "#publish" do
|
|
47
47
|
let(:time_blocked) { Time.now }
|
48
48
|
let(:period) { mock() }
|
49
49
|
let(:action) { Spanx::IPChecker.new("1.2.3.4") }
|
50
|
-
let(:blocked_ip) { Pause::
|
50
|
+
let(:blocked_ip) { Pause::RateLimitedEvent.new(action, period, 50, time_blocked) }
|
51
51
|
|
52
52
|
before { Spanx::Notifier::Email.any_instance.stub(:configure_email_gateway) }
|
53
53
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: spanx
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -19,7 +19,7 @@ dependencies:
|
|
19
19
|
requirements:
|
20
20
|
- - ~>
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: 0.0.
|
22
|
+
version: 0.0.4
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -27,7 +27,7 @@ dependencies:
|
|
27
27
|
requirements:
|
28
28
|
- - ~>
|
29
29
|
- !ruby/object:Gem::Version
|
30
|
-
version: 0.0.
|
30
|
+
version: 0.0.4
|
31
31
|
- !ruby/object:Gem::Dependency
|
32
32
|
name: file-tail
|
33
33
|
requirement: !ruby/object:Gem::Requirement
|
@@ -201,6 +201,7 @@ files:
|
|
201
201
|
- .pairs
|
202
202
|
- .rspec
|
203
203
|
- .rvmrc
|
204
|
+
- .travis.yml
|
204
205
|
- Gemfile
|
205
206
|
- Guardfile
|
206
207
|
- LICENSE
|
@@ -299,4 +300,3 @@ test_files:
|
|
299
300
|
- spec/spec_helper.rb
|
300
301
|
- spec/support/fakeredis.rb
|
301
302
|
- spec/support/mail.rb
|
302
|
-
has_rdoc:
|