shutter 0.0.1 → 0.0.2
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/lib/shutter.rb +8 -1
- data/lib/shutter/command_line.rb +96 -62
- data/lib/shutter/content.rb +3 -3
- data/lib/shutter/iptables.rb +4 -3
- data/lib/shutter/iptables/base.rb +42 -38
- data/lib/shutter/iptables/eyepee.rb +28 -28
- data/lib/shutter/iptables/iface.rb +25 -25
- data/lib/shutter/iptables/jail.rb +21 -21
- data/lib/shutter/iptables/port.rb +29 -29
- data/lib/shutter/os.rb +41 -0
- data/lib/shutter/version.rb +1 -1
- metadata +2 -1
data/lib/shutter.rb
CHANGED
data/lib/shutter/command_line.rb
CHANGED
@@ -1,71 +1,105 @@
|
|
1
1
|
require 'optparse'
|
2
2
|
require 'shutter/iptables'
|
3
|
+
require 'shutter/os'
|
3
4
|
|
4
5
|
module Shutter
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
ports.private
|
15
|
-
ports.public
|
16
|
-
]
|
17
|
-
files.each do |name|
|
18
|
-
file = "#{@config_path}/#{name}"
|
19
|
-
unless File.exists?(file)
|
20
|
-
# puts "Creating: #{file}"
|
21
|
-
File.open(file, 'w') do |f|
|
22
|
-
f.write(Shutter.const_get(name.upcase.gsub(/\./, "_")))
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
6
|
+
class CommandLine
|
7
|
+
def initialize( path = "/etc/shutter.d")
|
8
|
+
# Currently only available to RedHat variants
|
9
|
+
@os = Shutter::OS.new
|
10
|
+
unless @os.redhat?
|
11
|
+
puts "Shutter is currently only compatible with RedHat and its variants."
|
12
|
+
puts "Help make it compatible with others (github.com/rlyon/shutter)"
|
13
|
+
exit
|
14
|
+
end
|
27
15
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
options[:command] = :save
|
33
|
-
opts.on( '-s', '--save', 'Output the firewall to stdout.') do
|
34
|
-
options[:command] = :save
|
35
|
-
end
|
36
|
-
opts.on( '-r', '--restore', 'Load the firewall through iptables-restore.') do
|
37
|
-
options[:command] = :restore
|
38
|
-
end
|
39
|
-
options[:debug] = false
|
40
|
-
opts.on( '-d', '--debug', 'Be a bit more chatty') do
|
41
|
-
options[:debug] = true
|
42
|
-
end
|
43
|
-
opts.on_tail( '-h', '--help', 'Display this screen' ) do
|
44
|
-
puts opts
|
45
|
-
exit
|
46
|
-
end
|
47
|
-
opts.on_tail( '--version', "Show the version") do
|
48
|
-
puts Shutter::VERSION
|
49
|
-
exit
|
50
|
-
end
|
51
|
-
end
|
52
|
-
optparse.parse!
|
53
|
-
puts "* Using config path: #{@config_path}" if options[:debug]
|
54
|
-
puts "* Running command: #{options[:command].to_s}" if options[:debug]
|
55
|
-
send(options[:command])
|
56
|
-
end
|
16
|
+
@config_path = path
|
17
|
+
@iptables = Shutter::IPTables::Base.new(@config_path)
|
18
|
+
|
19
|
+
end
|
57
20
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
21
|
+
def execute
|
22
|
+
options = {}
|
23
|
+
optparse = OptionParser.new do |opts|
|
24
|
+
opts.banner = "Usage: shutter [options]"
|
25
|
+
options[:command] = :save
|
26
|
+
opts.on( '--init', 'Create the initial configuration files' ) do
|
27
|
+
options[:command] = :init
|
28
|
+
end
|
29
|
+
opts.on( '--reinit', 'Rereate the initial configuration files' ) do
|
30
|
+
options[:command] = :reinit
|
31
|
+
end
|
32
|
+
opts.on( '-s', '--save', 'Output the firewall to stdout. (DEFAULT)') do
|
33
|
+
options[:command] = :save
|
34
|
+
end
|
35
|
+
opts.on( '-r', '--restore', 'Load the firewall through iptables-restore.') do
|
36
|
+
options[:command] = :restore
|
37
|
+
end
|
38
|
+
@persist = false
|
39
|
+
opts.on( '-p', '--persist', 'Make the changes persistant. (with --restore)') do
|
40
|
+
@persist = true
|
41
|
+
end
|
42
|
+
options[:debug] = false
|
43
|
+
opts.on( '-d', '--debug', 'Be a bit more chatty') do
|
44
|
+
options[:debug] = true
|
45
|
+
end
|
46
|
+
opts.on_tail( '-h', '--help', 'Display this screen' ) do
|
47
|
+
puts opts
|
48
|
+
exit
|
49
|
+
end
|
50
|
+
opts.on_tail( '--version', "Show the version") do
|
51
|
+
puts Shutter::VERSION
|
52
|
+
exit
|
53
|
+
end
|
54
|
+
end
|
55
|
+
optparse.parse!
|
56
|
+
puts "* Using config path: #{@config_path}" if @debug
|
57
|
+
puts "* Running command: #{options[:command].to_s}" if @debug
|
58
|
+
send(options[:command])
|
59
|
+
end
|
62
60
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
61
|
+
def init
|
62
|
+
Shutter::CONFIG_FILES.each do |name|
|
63
|
+
file = "#{@config_path}/#{name}"
|
64
|
+
unless File.exists?(file)
|
65
|
+
# puts "Creating: #{file}"
|
66
|
+
File.open(file, 'w') do |f|
|
67
|
+
f.write(Shutter.const_get(name.upcase.gsub(/\./, "_")))
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
69
72
|
|
70
|
-
|
73
|
+
def reinit
|
74
|
+
Shutter::CONFIG_FILES.each do |name|
|
75
|
+
file = "#{@config_path}/#{name}"
|
76
|
+
File.open(file, 'w') do |f|
|
77
|
+
f.write(Shutter.const_get(name.upcase.gsub(/\./, "_")))
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def save
|
83
|
+
init
|
84
|
+
@ipt = @iptables.generate
|
85
|
+
puts @ipt
|
86
|
+
end
|
87
|
+
|
88
|
+
def restore
|
89
|
+
init
|
90
|
+
@ipt = @iptables.generate
|
91
|
+
IO.popen("#{Shutter::IPTables::IPTABLES_RESTORE}", "r+") do |iptr|
|
92
|
+
iptr.puts @ipt ; iptr.close_write
|
93
|
+
end
|
94
|
+
persist if @persist
|
95
|
+
end
|
96
|
+
|
97
|
+
def persist
|
98
|
+
pfile = ENV['SHUTTER_PERSIST_FILE'] ? ENV['SHUTTER_PERSIST_FILE'] : @iptables.persist_file(@os)
|
99
|
+
File.open(pfile, "w") do |f|
|
100
|
+
f.write(@ipt)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
71
105
|
end
|
data/lib/shutter/content.rb
CHANGED
data/lib/shutter/iptables.rb
CHANGED
@@ -3,9 +3,10 @@ require 'shutter/iptables/eyepee'
|
|
3
3
|
require 'shutter/iptables/iface'
|
4
4
|
require 'shutter/iptables/jail'
|
5
5
|
require 'shutter/iptables/port'
|
6
|
+
require 'shutter/os'
|
6
7
|
|
7
8
|
module Shutter
|
8
|
-
|
9
|
-
|
10
|
-
|
9
|
+
module IPTables
|
10
|
+
IPTABLES_RESTORE="/sbin/iptables-restore"
|
11
|
+
end
|
11
12
|
end
|
@@ -1,45 +1,49 @@
|
|
1
1
|
module Shutter
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
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
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
def persist_file(os)
|
11
|
+
"/etc/sysconfig/iptables"
|
12
|
+
end
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
end
|
14
|
+
def to_s
|
15
|
+
@content
|
16
|
+
end
|
18
17
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
@content = @content.gsub(/#\ \[RULES:BASTARDS\]/, @bastards)
|
24
|
-
@public = Port.new("#{@path}", :public).to_ipt
|
25
|
-
@content = @content.gsub(/#\ \[RULES:PUBLIC\]/, @public)
|
26
|
-
@allow = EyePee.new("#{@path}", :allow).to_ipt
|
27
|
-
@content = @content.gsub(/#\ \[RULES:ALLOWIP\]/, @allow)
|
28
|
-
@private = Port.new("#{@path}", :private).to_ipt
|
29
|
-
@content = @content.gsub(/#\ \[RULES:PRIVATE\]/, @private)
|
18
|
+
def generate
|
19
|
+
#generate_nat
|
20
|
+
generate_filter
|
21
|
+
end
|
30
22
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
23
|
+
def generate_filter
|
24
|
+
@dmz = Iface.new("#{@path}", :dmz).to_ipt
|
25
|
+
@content = @content.gsub(/#\ \[RULES:DMZ\]/, @dmz)
|
26
|
+
@bastards = EyePee.new("#{@path}", :deny).to_ipt
|
27
|
+
@content = @content.gsub(/#\ \[RULES:BASTARDS\]/, @bastards)
|
28
|
+
@public = Port.new("#{@path}", :public).to_ipt
|
29
|
+
@content = @content.gsub(/#\ \[RULES:PUBLIC\]/, @public)
|
30
|
+
@allow = EyePee.new("#{@path}", :allow).to_ipt
|
31
|
+
@content = @content.gsub(/#\ \[RULES:ALLOWIP\]/, @allow)
|
32
|
+
@private = Port.new("#{@path}", :private).to_ipt
|
33
|
+
@content = @content.gsub(/#\ \[RULES:PRIVATE\]/, @private)
|
38
34
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
35
|
+
# Make sure we are restoring what fail2ban has added
|
36
|
+
@f2b_chains = Jail.new.fail2ban_chains
|
37
|
+
@content = @content.gsub(/#\ \[CHAIN:FAIL2BAN\]/, @f2b_chains)
|
38
|
+
@f2b_rules = Jail.new.fail2ban_rules
|
39
|
+
@content = @content.gsub(/#\ \[RULES:FAIL2BAN\]/, @f2b_rules)
|
40
|
+
@jail = Jail.new.jail_rules
|
41
|
+
@content = @content.gsub(/#\ \[RULES:JAIL\]/, @jail)
|
42
|
+
|
43
|
+
# Remove the rest of the comments and extra lines
|
44
|
+
@content = @content.gsub(/^#.*$/, "")
|
45
|
+
@content = @content.gsub(/^$\n/, "")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
45
49
|
end
|
@@ -1,34 +1,34 @@
|
|
1
1
|
module Shutter
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
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
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
def to_s
|
11
|
+
@content
|
12
|
+
end
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
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
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
25
|
+
def allow_ipt(ip)
|
26
|
+
"-A AllowIP -m state --state NEW -s #{ip} -j Allowed\n"
|
27
|
+
end
|
28
28
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
29
|
+
def deny_ipt(ip)
|
30
|
+
"-A Bastards -s #{ip} -j DropBastards\n"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
34
|
end
|
@@ -1,30 +1,30 @@
|
|
1
1
|
module Shutter
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
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
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
def to_s
|
11
|
+
@content
|
12
|
+
end
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
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
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
25
|
+
def dmz_ipt( iface )
|
26
|
+
"-A Dmz -i #{iface} -j ACCEPT\n"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
30
|
end
|
@@ -1,26 +1,26 @@
|
|
1
1
|
module Shutter
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
2
|
+
module IPTables
|
3
|
+
class Jail
|
4
|
+
def initialize( iptables = "/sbin/iptables")
|
5
|
+
@iptables = iptables
|
6
|
+
end
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
def fail2ban_chains
|
9
|
+
`/sbin/iptables-save | grep "^:fail2ban"`
|
10
|
+
end
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
def fail2ban_rules
|
13
|
+
`/sbin/iptables-save | grep "^-A fail2ban"`
|
14
|
+
end
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
16
|
+
def jail_rules
|
17
|
+
jail = `/sbin/iptables-save | grep "^-A Jail"`
|
18
|
+
lines = jail.split('\n')
|
19
|
+
unless lines != [] && lines[-1] == "-A Jail -j RETURN\n"
|
20
|
+
jail += "-A Jail -j RETURN\n"
|
21
|
+
end
|
22
|
+
jail
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
26
|
end
|
@@ -1,35 +1,35 @@
|
|
1
1
|
module Shutter
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
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
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
def to_s
|
11
|
+
@content
|
12
|
+
end
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
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
25
|
|
26
|
-
|
27
|
-
|
28
|
-
|
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
29
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
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
35
|
end
|
data/lib/shutter/os.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
module Shutter
|
2
|
+
class OS
|
3
|
+
def initialize
|
4
|
+
unless File.exist?('/proc/version')
|
5
|
+
@version = "Unknown"
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def family
|
10
|
+
@family ||= ENV['OS'] ? ENV['OS'] : RUBY_PLATFORM.split('-').last
|
11
|
+
end
|
12
|
+
|
13
|
+
def version
|
14
|
+
@version ||= IO.read('/proc/version')
|
15
|
+
end
|
16
|
+
|
17
|
+
def linux?
|
18
|
+
return family == "linux"
|
19
|
+
end
|
20
|
+
|
21
|
+
def dist
|
22
|
+
case version
|
23
|
+
when /Red Hat/
|
24
|
+
"RedHat"
|
25
|
+
when /Debian/
|
26
|
+
"Debian"
|
27
|
+
when /Ubuntu/
|
28
|
+
"Ubuntu"
|
29
|
+
else
|
30
|
+
"Unknown"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def redhat?
|
35
|
+
dist == "RedHat"
|
36
|
+
end
|
37
|
+
|
38
|
+
alias :centos? :redhat?
|
39
|
+
alias :fedora? :redhat?
|
40
|
+
end
|
41
|
+
end
|
data/lib/shutter/version.rb
CHANGED
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.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -34,6 +34,7 @@ files:
|
|
34
34
|
- lib/shutter/iptables/iface.rb
|
35
35
|
- lib/shutter/iptables/jail.rb
|
36
36
|
- lib/shutter/iptables/port.rb
|
37
|
+
- lib/shutter/os.rb
|
37
38
|
- lib/shutter/version.rb
|
38
39
|
- shutter.gemspec
|
39
40
|
homepage: ''
|