firewool 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE.txt +20 -0
- data/README.rdoc +27 -21
- data/lib/firewool.rb +13 -1
- data/lib/firewool/hook.rb +2 -2
- data/lib/firewool/instance_methods.rb +1 -1
- data/lib/firewool/railtie.rb +1 -0
- data/lib/firewool/version.rb +1 -1
- data/test/dummy/Gemfile +1 -0
- data/test/dummy/app/controllers/dummy_controller.rb +4 -0
- data/test/dummy/app/controllers/foo_controller.rb +11 -0
- data/test/dummy/app/helpers/foo_helper.rb +2 -0
- data/test/dummy/app/views/foo/index.html.erb +2 -0
- data/test/dummy/config/routes.rb +4 -0
- data/test/dummy/test/functional/foo_controller_test.rb +9 -0
- data/test/dummy/test/unit/helpers/foo_helper_test.rb +4 -0
- data/test/firewool_test.rb +33 -7
- metadata +13 -2
data/MIT-LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Chris Dillon, http://squarism.com/
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
CHANGED
@@ -2,20 +2,23 @@
|
|
2
2
|
Firewool is an IP firewall for rails. You set what IPs to block and what IPs to allow. Specifics below.
|
3
3
|
|
4
4
|
== Why would I need this?
|
5
|
-
|
6
|
-
-
|
5
|
+
Using authentication to protect your app is great but sometimes you just want to do some simple IP filtering. Firewool can help you in the following use cases:
|
6
|
+
- You have a report job that needs to hit /users/report and you want to restrict access without authentication, ie: you don't want to create a "report" user which might get reported on or otherwise makes you a sad panda.
|
7
|
+
- A simple firewall with IP/ports (layer 3) can't protect rails URLs.
|
8
|
+
- A layer 7 firewall which can protect URLs is too expensive / hard to set up.
|
7
9
|
- Belt and suspenders style double security check.
|
10
|
+
- You killed your network guy and no one knows.
|
8
11
|
|
9
12
|
== Install
|
10
13
|
gem install firewool
|
11
14
|
|
12
15
|
- Tested on rails 3.0.4.
|
16
|
+
- Tested on ruby 1.9.2 / 1.8.7.
|
13
17
|
- Untested on rails 2.x. Probably won't work because no engines.
|
14
18
|
|
15
19
|
== Configuration
|
16
|
-
Add firewool
|
20
|
+
Add firewool dependency to Gemfile:
|
17
21
|
gem 'firewool'
|
18
|
-
gem 'ipaddress'
|
19
22
|
|
20
23
|
Create a configuration file in config/firewool.yml
|
21
24
|
|
@@ -24,7 +27,7 @@ Create a configuration file in config/firewool.yml
|
|
24
27
|
|
25
28
|
development:
|
26
29
|
ip_restriction: true
|
27
|
-
|
30
|
+
allow: [ 127.0.0.1 ]
|
28
31
|
|
29
32
|
test:
|
30
33
|
ip_restriction: false
|
@@ -63,33 +66,36 @@ IPs can be spoofed so in the case of strong security, you'll want to use this wi
|
|
63
66
|
== Quick Network Primer
|
64
67
|
So how do I write the rules when I'm not a network guy? No problem, let's go through some examples.
|
65
68
|
|
66
|
-
First, the IP is four numbers separated by periods. Each number is called an ocet. The slash number (like /16 up above) is how many octets match. So to match every usable IP from 10.0.0.1 to 10.0.0.254, we can just say:
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
69
|
+
First, the IP is four numbers separated by periods. Each number is called an ocet. The slash number (like /16 up above) is how many octets match. So to match every usable IP from 10.0.0.1 to 10.0.0.254, we can just say: 10.0.0.0/24 instead of naming all 253 ips one at a time.
|
70
|
+
|
71
|
+
10.0.0.0/24 matches 10.0.0.* so the following happens:
|
72
|
+
10.0.0.1 (match)
|
73
|
+
10.0.0.204 (match)
|
74
|
+
10.0.1.1 (no match)
|
75
|
+
7.8.9.10 (no match)
|
72
76
|
|
73
77
|
If we just want to match one IP we can use the /32 or just specify the IP by itself.
|
74
78
|
192.168.0.1/32 (matches only 192.168.0.1)
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
79
|
+
|
80
|
+
Some more examples:
|
81
|
+
192.168.0.1 (matches only 192.168.0.1, same meaning as /32)
|
82
|
+
5.0.0.0/8 (matches 5.*.*.*)
|
83
|
+
5.6.0.0/16 (matches 5.6.*.*)
|
84
|
+
5.6.0.0/24 (matches 5.6.0.*)
|
85
|
+
5.6.7.0/24 (matches 5.6.7.*)
|
80
86
|
|
81
|
-
These are the simplest examples of this notation (called CIDR if you want to read more) but it's enough to build a few use cases. Let's say we want to allow
|
87
|
+
These are the simplest examples of this notation (called CIDR if you want to read more) but it's enough to build a few use cases. Let's say we want to allow our customers in but block anyone coming from Evil Hackers' Inc. Our customer's external network is 5.6.7.* (ie: what they see when they go to whatismyip.com) and let's say that Evil Hackers' proxy is 58.14.0.0. This would be our firewool.yml config:
|
82
88
|
production:
|
83
89
|
ip_restriction: true
|
84
90
|
allow: [ 5.6.7.0/24 ]
|
85
91
|
deny: [ 58.14.0.0/16 ]
|
86
92
|
|
87
|
-
Now we'd want to be careful that 5.6.7.* was really where our users are coming from. If people that we want to keep out are coming from 5.6.7.200 then we'd want to tighten up our rule a little bit and not allow all of the 5.6.7
|
93
|
+
Now we'd want to be careful that 5.6.7.* was really where our users are coming from. If another group of people that we want to keep out are coming from 5.6.7.200 then we'd want to tighten up our rule a little bit and not allow all of the 5.6.7.* network in because .200 is in 5.6.7.*. So we would research what our customer's IP block really is, or add only the IPs we know about as individual IPs.
|
88
94
|
|
89
|
-
As a special case, 0.0.0.0 means *.*.*.*, or all IPs.
|
95
|
+
As a special case, 0.0.0.0 means *.*.*.*, or all IPs. Also a special case, 127.0.0.1 means localhost which is good to leave in your development allow section so you can develop your app with firewool on.
|
90
96
|
|
91
|
-
== Pretty
|
97
|
+
== Pretty Up
|
92
98
|
If 403.html doesn't exist in your public directory, then a blocked user will simply see "Public Access Denied." which isn't that great. Create a 403.html file in public, you can use this {403.html template as an example}[https://github.com/squarism/firewool/blob/master/test/dummy/public/403.html].
|
93
99
|
|
94
100
|
== Thanks to
|
95
|
-
Bluemonk for his awesome ipaddress gem.
|
101
|
+
{Bluemonk}[https://github.com/bluemonk] for his awesome ipaddress gem. And {sinisterchipmunk}[https://github.com/sinisterchipmunk] for his help in understanding how to test rails 3 gems quickly.
|
data/lib/firewool.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'active_support/core_ext'
|
2
2
|
require 'ipaddress'
|
3
|
+
require 'singleton'
|
3
4
|
|
4
5
|
# rails engine setup
|
5
6
|
require File.join(File.dirname(__FILE__), "firewool/railtie.rb")
|
@@ -11,10 +12,21 @@ module Firewool
|
|
11
12
|
end
|
12
13
|
|
13
14
|
class Config
|
15
|
+
include Singleton
|
14
16
|
attr_reader :yaml_config
|
17
|
+
attr_accessor :filesystem_hits # track how many times we read the yml
|
18
|
+
|
19
|
+
def load_config
|
20
|
+
@filesystem_hits += 1
|
21
|
+
YAML.load_file("#{Rails.root.to_s}/config/firewool.yml")
|
22
|
+
end
|
23
|
+
|
24
|
+
def yaml_config
|
25
|
+
@@yaml_config ||= self.load_config
|
26
|
+
end
|
15
27
|
|
16
28
|
def initialize
|
17
|
-
@
|
29
|
+
@filesystem_hits = 0
|
18
30
|
end
|
19
31
|
end
|
20
32
|
|
data/lib/firewool/hook.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
module Firewool
|
2
2
|
module Hook
|
3
3
|
def acts_as_firewalled
|
4
|
-
@firewool_config = Firewool::Config.
|
4
|
+
@firewool_config = Firewool::Config.instance
|
5
5
|
include Firewool::InstanceMethods
|
6
6
|
end
|
7
7
|
|
8
8
|
def firewool_config
|
9
|
-
@firewool_config
|
9
|
+
@firewool_config
|
10
10
|
end
|
11
11
|
end
|
12
12
|
end
|
data/lib/firewool/railtie.rb
CHANGED
data/lib/firewool/version.rb
CHANGED
data/test/dummy/Gemfile
CHANGED
@@ -0,0 +1,11 @@
|
|
1
|
+
# intentionally not including Firewool, test including from ApplicationController instead
|
2
|
+
|
3
|
+
class FooController < ApplicationController
|
4
|
+
include Firewool
|
5
|
+
acts_as_firewalled
|
6
|
+
before_filter :ip_filter
|
7
|
+
|
8
|
+
def index
|
9
|
+
render :text => "This is the index method on FooController"
|
10
|
+
end
|
11
|
+
end
|
data/test/dummy/config/routes.rb
CHANGED
@@ -1,10 +1,14 @@
|
|
1
1
|
Dummy::Application.routes.draw do
|
2
|
+
get "foo/index"
|
3
|
+
|
2
4
|
# The priority is based upon order of creation:
|
3
5
|
# first created -> highest priority.
|
4
6
|
|
5
7
|
# Sample of regular route:
|
6
8
|
# match 'products/:id' => 'catalog#view'
|
7
9
|
# Keep in mind you can assign values other than :controller and :action
|
10
|
+
match 'dummy' => 'dummy#index'
|
11
|
+
match 'foo' => 'foo#index'
|
8
12
|
|
9
13
|
# Sample of named route:
|
10
14
|
# match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase
|
data/test/firewool_test.rb
CHANGED
@@ -10,7 +10,7 @@ class FirewoolTest < ActionController::TestCase
|
|
10
10
|
dc = DummyController.new
|
11
11
|
|
12
12
|
# get our configuration, which we'll change later for certain tests
|
13
|
-
conf_file = dc.class.firewool_config[Rails.env]
|
13
|
+
conf_file = dc.class.firewool_config.yaml_config[Rails.env]
|
14
14
|
|
15
15
|
# test basic module includes/extends
|
16
16
|
context "The controller" do
|
@@ -30,9 +30,9 @@ class FirewoolTest < ActionController::TestCase
|
|
30
30
|
# test policy enforcement
|
31
31
|
context "The Firewool" do
|
32
32
|
should "allow valid IPs while blocking invalid IPs" do
|
33
|
-
#
|
34
|
-
#
|
35
|
-
|
33
|
+
# this is weird that I have to reset the configuration,
|
34
|
+
# I thought this would go in order
|
35
|
+
conf_file["allow"] = ["192.168.0.0/16"]
|
36
36
|
assert_equal false, dc.ip_allow?("172.168.0.1")
|
37
37
|
assert_equal false, dc.ip_allow?("12.168.0.1")
|
38
38
|
assert_equal false, dc.ip_allow?("0.0.0.0")
|
@@ -42,18 +42,44 @@ class FirewoolTest < ActionController::TestCase
|
|
42
42
|
|
43
43
|
context "The Firewool" do
|
44
44
|
should "allow valid IPs when using a default allow" do
|
45
|
-
|
45
|
+
conf_file["allow"] = ["0.0.0.0"]
|
46
46
|
assert_equal true, dc.ip_allow?("12.168.0.1")
|
47
47
|
end
|
48
48
|
end
|
49
49
|
|
50
50
|
context "The Firewool" do
|
51
51
|
should "allow and disallow correctly with a default allow" do
|
52
|
-
|
53
|
-
# puts dc.class.firewool_config[Rails.env]
|
52
|
+
conf_file["allow"] = ["0.0.0.0"]
|
54
53
|
assert_equal true, dc.ip_allow?("12.168.0.1")
|
55
54
|
assert_equal true, dc.ip_allow?("192.168.0.1")
|
56
55
|
assert_equal false, dc.ip_allow?("172.16.0.1")
|
57
56
|
end
|
58
57
|
end
|
59
58
|
end
|
59
|
+
|
60
|
+
|
61
|
+
class FooTest < ActionController::TestCase
|
62
|
+
tests FooController
|
63
|
+
fc = FooController.new
|
64
|
+
|
65
|
+
# conf_file = $firewool_config[Rails.env]
|
66
|
+
conf_file = fc.class.firewool_config.yaml_config[Rails.env]
|
67
|
+
|
68
|
+
# test reading firewool.yml config file
|
69
|
+
context "The Firewool" do
|
70
|
+
should "have the configuration loaded" do
|
71
|
+
assert conf_file.key?("ip_restriction"), "Should have the ip_restriction in firewool conf file"
|
72
|
+
assert conf_file.key?("allow"), "Should have the ip_ranges_allowed in firewool conf file"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context "The Firewool" do
|
77
|
+
should "not hit the filesystem more than once when loading the conf file" do
|
78
|
+
assert_equal 1, fc.class.firewool_config.filesystem_hits
|
79
|
+
fc = FooController.new
|
80
|
+
fc = FooController.new
|
81
|
+
assert_equal 1, fc.class.firewool_config.filesystem_hits
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: firewool
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.1.
|
5
|
+
version: 0.1.2
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Chris Dillon
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-02-
|
13
|
+
date: 2011-02-21 00:00:00 -05:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
@@ -47,6 +47,7 @@ extra_rdoc_files: []
|
|
47
47
|
files:
|
48
48
|
- .gitignore
|
49
49
|
- Gemfile
|
50
|
+
- MIT-LICENSE.txt
|
50
51
|
- README.rdoc
|
51
52
|
- Rakefile
|
52
53
|
- firewool.gemspec
|
@@ -61,8 +62,11 @@ files:
|
|
61
62
|
- test/dummy/Rakefile
|
62
63
|
- test/dummy/app/controllers/application_controller.rb
|
63
64
|
- test/dummy/app/controllers/dummy_controller.rb
|
65
|
+
- test/dummy/app/controllers/foo_controller.rb
|
64
66
|
- test/dummy/app/helpers/application_helper.rb
|
65
67
|
- test/dummy/app/helpers/dummy_helper.rb
|
68
|
+
- test/dummy/app/helpers/foo_helper.rb
|
69
|
+
- test/dummy/app/views/foo/index.html.erb
|
66
70
|
- test/dummy/app/views/layouts/application.html.erb
|
67
71
|
- test/dummy/config.ru
|
68
72
|
- test/dummy/config/application.rb
|
@@ -100,9 +104,11 @@ files:
|
|
100
104
|
- test/dummy/public/stylesheets/.gitkeep
|
101
105
|
- test/dummy/script/rails
|
102
106
|
- test/dummy/test/functional/dummy_controller_test.rb
|
107
|
+
- test/dummy/test/functional/foo_controller_test.rb
|
103
108
|
- test/dummy/test/performance/browsing_test.rb
|
104
109
|
- test/dummy/test/test_helper.rb
|
105
110
|
- test/dummy/test/unit/helpers/dummy_helper_test.rb
|
111
|
+
- test/dummy/test/unit/helpers/foo_helper_test.rb
|
106
112
|
- test/dummy/vendor/plugins/.gitkeep
|
107
113
|
- test/firewool_test.rb
|
108
114
|
- test/test_helper.rb
|
@@ -142,8 +148,11 @@ test_files:
|
|
142
148
|
- test/dummy/Rakefile
|
143
149
|
- test/dummy/app/controllers/application_controller.rb
|
144
150
|
- test/dummy/app/controllers/dummy_controller.rb
|
151
|
+
- test/dummy/app/controllers/foo_controller.rb
|
145
152
|
- test/dummy/app/helpers/application_helper.rb
|
146
153
|
- test/dummy/app/helpers/dummy_helper.rb
|
154
|
+
- test/dummy/app/helpers/foo_helper.rb
|
155
|
+
- test/dummy/app/views/foo/index.html.erb
|
147
156
|
- test/dummy/app/views/layouts/application.html.erb
|
148
157
|
- test/dummy/config.ru
|
149
158
|
- test/dummy/config/application.rb
|
@@ -181,9 +190,11 @@ test_files:
|
|
181
190
|
- test/dummy/public/stylesheets/.gitkeep
|
182
191
|
- test/dummy/script/rails
|
183
192
|
- test/dummy/test/functional/dummy_controller_test.rb
|
193
|
+
- test/dummy/test/functional/foo_controller_test.rb
|
184
194
|
- test/dummy/test/performance/browsing_test.rb
|
185
195
|
- test/dummy/test/test_helper.rb
|
186
196
|
- test/dummy/test/unit/helpers/dummy_helper_test.rb
|
197
|
+
- test/dummy/test/unit/helpers/foo_helper_test.rb
|
187
198
|
- test/dummy/vendor/plugins/.gitkeep
|
188
199
|
- test/firewool_test.rb
|
189
200
|
- test/test_helper.rb
|