shutter 0.0.7 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|