shutter 0.0.7 → 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.
- data/README.md +26 -56
- data/bin/shutter +10 -4
- data/lib/shutter.rb +3 -9
- data/lib/shutter/command_line.rb +86 -104
- data/lib/shutter/content.rb +49 -2
- data/lib/shutter/files.rb +31 -0
- data/lib/shutter/iptables.rb +215 -10
- data/lib/shutter/os.rb +40 -1
- data/lib/shutter/version.rb +1 -1
- data/shutter.gemspec +4 -3
- data/spec/command_line_spec.rb +75 -9
- data/spec/content_spec.rb +2 -2
- data/spec/files/base.ipt +160 -0
- data/spec/files/iface.dmz +4 -0
- data/spec/files/iface.forward +3 -0
- data/spec/files/ip.allow +5 -0
- data/spec/files/ip.deny +5 -0
- data/spec/files/iptables_save.out +86 -0
- data/spec/files/ports.private +2 -0
- data/spec/files/ports.public +3 -0
- data/spec/files_spec.rb +76 -0
- data/spec/iptables_spec.rb +157 -0
- data/spec/os_spec.rb +54 -0
- data/spec/spec_helper.rb +10 -4
- metadata +45 -14
- data/lib/shutter/iptables/base.rb +0 -59
- data/lib/shutter/iptables/eyepee.rb +0 -34
- data/lib/shutter/iptables/forward.rb +0 -47
- data/lib/shutter/iptables/iface.rb +0 -30
- data/lib/shutter/iptables/jail.rb +0 -26
- data/lib/shutter/iptables/port.rb +0 -35
- data/spec/env_spec.rb +0 -17
data/spec/os_spec.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe "Shutter::OS" do
|
4
|
+
before(:each) do
|
5
|
+
@os = Shutter::OS.new
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should have the correct data for redhat systems" do
|
9
|
+
@os.stubs(:version).returns("Red Hat")
|
10
|
+
@os.persist_file.should == "/etc/sysconfig/iptables"
|
11
|
+
@os.dist.should == "RedHat"
|
12
|
+
@os.redhat?.should == true
|
13
|
+
@os.centos?.should == true
|
14
|
+
@os.fedora?.should == true
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should have the correct data for ubuntu systems" do
|
18
|
+
@os.stubs(:version).returns("Ubuntu")
|
19
|
+
@os.persist_file.should == "/etc/iptables/rules"
|
20
|
+
@os.dist.should == "Ubuntu"
|
21
|
+
@os.redhat?.should == false
|
22
|
+
@os.centos?.should == false
|
23
|
+
@os.fedora?.should == false
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should have the correct data for debian systems" do
|
27
|
+
@os.stubs(:version).returns("Debian")
|
28
|
+
@os.persist_file.should == "/etc/iptables/rules"
|
29
|
+
@os.dist.should == "Debian"
|
30
|
+
@os.redhat?.should == false
|
31
|
+
@os.centos?.should == false
|
32
|
+
@os.fedora?.should == false
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should have the correct data for debian systems" do
|
36
|
+
@os.stubs(:version).returns("Unknown")
|
37
|
+
@os.persist_file.should == "/tmp/iptables.rules"
|
38
|
+
@os.dist.should == "Unknown"
|
39
|
+
@os.redhat?.should == false
|
40
|
+
@os.centos?.should == false
|
41
|
+
@os.fedora?.should == false
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should not validate any os except redhat" do
|
45
|
+
@os.stubs(:version).returns("Unknown")
|
46
|
+
expect { @os.validate! }.to raise_error
|
47
|
+
@os.stubs(:version).returns("Ubuntu")
|
48
|
+
expect { @os.validate! }.to_not raise_error
|
49
|
+
@os.stubs(:version).returns("Debian")
|
50
|
+
expect { @os.validate! }.to_not raise_error
|
51
|
+
@os.stubs(:version).returns("Red Hat")
|
52
|
+
expect { @os.validate! }.to_not raise_error
|
53
|
+
end
|
54
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,11 +1,17 @@
|
|
1
|
+
if ENV['COVERAGE'] == "true"
|
2
|
+
require 'simplecov'
|
3
|
+
FILTER_DIRS = ['spec']
|
4
|
+
|
5
|
+
SimpleCov.start do
|
6
|
+
FILTER_DIRS.each{ |f| add_filter f }
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
1
10
|
require 'rubygems'
|
2
11
|
require 'bundler/setup'
|
12
|
+
require 'mocha_standalone'
|
3
13
|
require 'shutter'
|
4
14
|
|
5
15
|
RSpec.configure do |config|
|
6
16
|
config.mock_with :mocha
|
7
17
|
end
|
8
|
-
|
9
|
-
ENV['SHUTTER_CONFIG'] = "./tmp"
|
10
|
-
ENV['SHUTTER_PERSIST_FILE'] = "./tmp/iptables"
|
11
|
-
ENV['SHUTTER_MODE'] = "testing"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shutter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-10-01 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
@@ -43,12 +43,28 @@ dependencies:
|
|
43
43
|
- - ! '>='
|
44
44
|
- !ruby/object:Gem::Version
|
45
45
|
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: simplecov
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
46
62
|
description: ! "Shutter is a tool that gives system administrators the ability \n
|
47
63
|
\ to manage iptables firewall settings through simple lists
|
48
|
-
instead \n of complex iptables rules. Please note:
|
49
|
-
application currently \n only
|
50
|
-
|
51
|
-
|
64
|
+
instead \n of complex iptables rules. Please note: This
|
65
|
+
application is currently \n only tested with Red Hat based
|
66
|
+
distributions. Ubuntu and Debian should \n work but are
|
67
|
+
not supported..\n "
|
52
68
|
email:
|
53
69
|
- nosignsoflifehere@gmail.com
|
54
70
|
executables:
|
@@ -66,19 +82,24 @@ files:
|
|
66
82
|
- lib/shutter.rb
|
67
83
|
- lib/shutter/command_line.rb
|
68
84
|
- lib/shutter/content.rb
|
85
|
+
- lib/shutter/files.rb
|
69
86
|
- lib/shutter/iptables.rb
|
70
|
-
- lib/shutter/iptables/base.rb
|
71
|
-
- lib/shutter/iptables/eyepee.rb
|
72
|
-
- lib/shutter/iptables/forward.rb
|
73
|
-
- lib/shutter/iptables/iface.rb
|
74
|
-
- lib/shutter/iptables/jail.rb
|
75
|
-
- lib/shutter/iptables/port.rb
|
76
87
|
- lib/shutter/os.rb
|
77
88
|
- lib/shutter/version.rb
|
78
89
|
- shutter.gemspec
|
79
90
|
- spec/command_line_spec.rb
|
80
91
|
- spec/content_spec.rb
|
81
|
-
- spec/
|
92
|
+
- spec/files/base.ipt
|
93
|
+
- spec/files/iface.dmz
|
94
|
+
- spec/files/iface.forward
|
95
|
+
- spec/files/ip.allow
|
96
|
+
- spec/files/ip.deny
|
97
|
+
- spec/files/iptables_save.out
|
98
|
+
- spec/files/ports.private
|
99
|
+
- spec/files/ports.public
|
100
|
+
- spec/files_spec.rb
|
101
|
+
- spec/iptables_spec.rb
|
102
|
+
- spec/os_spec.rb
|
82
103
|
- spec/spec_helper.rb
|
83
104
|
homepage: ''
|
84
105
|
licenses: []
|
@@ -107,5 +128,15 @@ summary: Shutter helps manage iptables firewalls
|
|
107
128
|
test_files:
|
108
129
|
- spec/command_line_spec.rb
|
109
130
|
- spec/content_spec.rb
|
110
|
-
- spec/
|
131
|
+
- spec/files/base.ipt
|
132
|
+
- spec/files/iface.dmz
|
133
|
+
- spec/files/iface.forward
|
134
|
+
- spec/files/ip.allow
|
135
|
+
- spec/files/ip.deny
|
136
|
+
- spec/files/iptables_save.out
|
137
|
+
- spec/files/ports.private
|
138
|
+
- spec/files/ports.public
|
139
|
+
- spec/files_spec.rb
|
140
|
+
- spec/iptables_spec.rb
|
141
|
+
- spec/os_spec.rb
|
111
142
|
- spec/spec_helper.rb
|
@@ -1,59 +0,0 @@
|
|
1
|
-
module Shutter
|
2
|
-
module IPTables
|
3
|
-
class Base
|
4
|
-
def initialize( path )
|
5
|
-
@path = path
|
6
|
-
file = File.open("#{path}/base.ipt", "r")
|
7
|
-
@content = file.read
|
8
|
-
end
|
9
|
-
|
10
|
-
def persist_file(os)
|
11
|
-
"/etc/sysconfig/iptables"
|
12
|
-
end
|
13
|
-
|
14
|
-
def to_s
|
15
|
-
@content
|
16
|
-
end
|
17
|
-
|
18
|
-
def generate
|
19
|
-
#generate_nat
|
20
|
-
generate_filter
|
21
|
-
end
|
22
|
-
|
23
|
-
def generate_filter
|
24
|
-
@dmz = Iface.new("#{@path}", :dmz).to_ipt
|
25
|
-
@content = @content.gsub(/#\ \[RULES:DMZ\]/, @dmz)
|
26
|
-
|
27
|
-
@forward = Forward.new("#{@path}")
|
28
|
-
@content = @content.gsub(/#\ \[RULES:FORWARD\]/, @forward.to_forward_ipt)
|
29
|
-
@content = @content.gsub(/#\ \[RULES:POSTROUTING\]/, @forward.to_masq_ipt)
|
30
|
-
|
31
|
-
@bastards = EyePee.new("#{@path}", :deny).to_ipt
|
32
|
-
@content = @content.gsub(/#\ \[RULES:BASTARDS\]/, @bastards)
|
33
|
-
|
34
|
-
@public = Port.new("#{@path}", :public).to_ipt
|
35
|
-
@content = @content.gsub(/#\ \[RULES:PUBLIC\]/, @public)
|
36
|
-
|
37
|
-
@allow = EyePee.new("#{@path}", :allow).to_ipt
|
38
|
-
@content = @content.gsub(/#\ \[RULES:ALLOWIP\]/, @allow)
|
39
|
-
|
40
|
-
@private = Port.new("#{@path}", :private).to_ipt
|
41
|
-
@content = @content.gsub(/#\ \[RULES:PRIVATE\]/, @private)
|
42
|
-
|
43
|
-
# Make sure we are restoring what fail2ban has added
|
44
|
-
@f2b_chains = Jail.new.fail2ban_chains
|
45
|
-
@content = @content.gsub(/#\ \[CHAIN:FAIL2BAN\]/, @f2b_chains)
|
46
|
-
|
47
|
-
@f2b_rules = Jail.new.fail2ban_rules
|
48
|
-
@content = @content.gsub(/#\ \[RULES:FAIL2BAN\]/, @f2b_rules)
|
49
|
-
|
50
|
-
@jail = Jail.new.jail_rules
|
51
|
-
@content = @content.gsub(/#\ \[RULES:JAIL\]/, @jail)
|
52
|
-
|
53
|
-
# Remove the rest of the comments and extra lines
|
54
|
-
@content = @content.gsub(/^#.*$/, "")
|
55
|
-
@content = @content.gsub(/^$\n/, "")
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
@@ -1,34 +0,0 @@
|
|
1
|
-
module Shutter
|
2
|
-
module IPTables
|
3
|
-
class EyePee
|
4
|
-
def initialize( path, state )
|
5
|
-
@state = state
|
6
|
-
file = File.open("#{path}/ip.#{state.to_s}", "r")
|
7
|
-
@content = file.read
|
8
|
-
end
|
9
|
-
|
10
|
-
def to_s
|
11
|
-
@content
|
12
|
-
end
|
13
|
-
|
14
|
-
def to_ipt
|
15
|
-
@rules = ""
|
16
|
-
@content.each_line do |ip|
|
17
|
-
ip_clean = ip.strip
|
18
|
-
if ip_clean =~ /^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}(\/[0-9]{0,2})*$/
|
19
|
-
@rules += send(:"#{@state.to_s}_ipt", ip_clean)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
@rules
|
23
|
-
end
|
24
|
-
|
25
|
-
def allow_ipt(ip)
|
26
|
-
"-A AllowIP -m state --state NEW -s #{ip} -j Allowed\n"
|
27
|
-
end
|
28
|
-
|
29
|
-
def deny_ipt(ip)
|
30
|
-
"-A Bastards -s #{ip} -j DropBastards\n"
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
@@ -1,47 +0,0 @@
|
|
1
|
-
module Shutter
|
2
|
-
module IPTables
|
3
|
-
class Forward
|
4
|
-
def initialize( path )
|
5
|
-
file = File.open("#{path}/iface.forward", "r")
|
6
|
-
@content = file.read
|
7
|
-
@forward = ""
|
8
|
-
@masq = ""
|
9
|
-
@masq_ifaces = []
|
10
|
-
@content.each_line do |line|
|
11
|
-
line = line.strip
|
12
|
-
if line =~ /^[a-z].+$/
|
13
|
-
src, dst = line.split(' ')
|
14
|
-
@forward += forward_ipt(src,dst)
|
15
|
-
@masq_ifaces << dst unless @masq_ifaces.include?(dst)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
@masq_ifaces.each do |iface|
|
19
|
-
@masq += masq_ipt(iface)
|
20
|
-
end
|
21
|
-
|
22
|
-
end
|
23
|
-
|
24
|
-
def to_s
|
25
|
-
@content
|
26
|
-
end
|
27
|
-
|
28
|
-
def to_forward_ipt
|
29
|
-
@forward
|
30
|
-
end
|
31
|
-
|
32
|
-
def to_masq_ipt
|
33
|
-
@masq
|
34
|
-
end
|
35
|
-
|
36
|
-
def forward_ipt( src, dst )
|
37
|
-
rule = "-A FORWARD -i #{src} -o #{dst} -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT\n"
|
38
|
-
rule += "-A FORWARD -i #{dst} -o #{src} -m state --state RELATED,ESTABLISHED -j ACCEPT\n"
|
39
|
-
rule
|
40
|
-
end
|
41
|
-
|
42
|
-
def masq_ipt( iface )
|
43
|
-
"-A POSTROUTING -o #{iface} -j MASQUERADE\n"
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
@@ -1,30 +0,0 @@
|
|
1
|
-
module Shutter
|
2
|
-
module IPTables
|
3
|
-
class Iface
|
4
|
-
def initialize( path, type )
|
5
|
-
@type = type
|
6
|
-
file = File.open("#{path}/iface.#{type.to_s}", "r")
|
7
|
-
@content = file.read
|
8
|
-
end
|
9
|
-
|
10
|
-
def to_s
|
11
|
-
@content
|
12
|
-
end
|
13
|
-
|
14
|
-
def to_ipt
|
15
|
-
@rules = ""
|
16
|
-
@content.each_line do |line|
|
17
|
-
line = line.strip
|
18
|
-
if line =~ /^[a-z].+$/
|
19
|
-
@rules += send(:"#{@type.to_s}_ipt", line)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
@rules
|
23
|
-
end
|
24
|
-
|
25
|
-
def dmz_ipt( iface )
|
26
|
-
"-A Dmz -i #{iface} -j ACCEPT\n"
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
@@ -1,26 +0,0 @@
|
|
1
|
-
module Shutter
|
2
|
-
module IPTables
|
3
|
-
class Jail
|
4
|
-
def initialize( iptables = "/sbin/iptables")
|
5
|
-
@iptables = iptables
|
6
|
-
end
|
7
|
-
|
8
|
-
def fail2ban_chains
|
9
|
-
`/sbin/iptables-save | grep "^:fail2ban"`
|
10
|
-
end
|
11
|
-
|
12
|
-
def fail2ban_rules
|
13
|
-
`/sbin/iptables-save | grep "^-A fail2ban"`
|
14
|
-
end
|
15
|
-
|
16
|
-
def jail_rules
|
17
|
-
jail = `/sbin/iptables-save | grep "^-A Jail"`
|
18
|
-
lines = jail.split('\n')
|
19
|
-
unless lines.last =~ /-A Jail -j RETURN/
|
20
|
-
jail += "-A Jail -j RETURN\n"
|
21
|
-
end
|
22
|
-
jail
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
@@ -1,35 +0,0 @@
|
|
1
|
-
module Shutter
|
2
|
-
module IPTables
|
3
|
-
class Port
|
4
|
-
def initialize( path, type )
|
5
|
-
@type = type
|
6
|
-
file = File.open("#{path}/ports.#{type.to_s}", "r")
|
7
|
-
@content = file.read
|
8
|
-
end
|
9
|
-
|
10
|
-
def to_s
|
11
|
-
@content
|
12
|
-
end
|
13
|
-
|
14
|
-
def to_ipt
|
15
|
-
@rules = ""
|
16
|
-
@content.each_line do |line|
|
17
|
-
line = line.strip
|
18
|
-
if line =~ /^[1-9].+$/
|
19
|
-
port,proto = line.split
|
20
|
-
@rules += send(:"#{@type.to_s}_ipt", port, proto)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
@rules
|
24
|
-
end
|
25
|
-
|
26
|
-
def private_ipt( port, proto )
|
27
|
-
"-A Private -m state --state NEW -p #{proto} -m #{proto} --dport #{port} -j RETURN\n"
|
28
|
-
end
|
29
|
-
|
30
|
-
def public_ipt( port, proto )
|
31
|
-
"-A Public -m state --state NEW -p #{proto} -m #{proto} --dport #{port} -j ACCEPT\n"
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
data/spec/env_spec.rb
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
-
|
3
|
-
describe "Environment Sanity Check" do
|
4
|
-
it "should have the SHUTTER_CONFIG variable set to ./tmp" do
|
5
|
-
ENV['SHUTTER_CONFIG'].should == "./tmp"
|
6
|
-
end
|
7
|
-
|
8
|
-
it "should have the SHUTTER_PERSIST_FILE variable set to ./tmp/iptables" do
|
9
|
-
ENV['SHUTTER_PERSIST_FILE'].should == "./tmp/iptables"
|
10
|
-
end
|
11
|
-
|
12
|
-
it "should be able to write to ./tmp" do
|
13
|
-
File.open("./tmp/test", "w") { |f| f.write("Foo") }
|
14
|
-
IO.read("./tmp/test").should == "Foo"
|
15
|
-
File.unlink("./tmp/test")
|
16
|
-
end
|
17
|
-
end
|