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/README.md
CHANGED
@@ -3,10 +3,8 @@
|
|
3
3
|
Shutter is a tool that gives system administrators the ability to manage
|
4
4
|
iptables firewall settings through simple lists instead of complex iptables commands, making it
|
5
5
|
easier to define host and service firewall setting with configuration management tools. Please note:
|
6
|
-
This application currently only
|
7
|
-
|
8
|
-
|
9
|
-
**Note: Shutter server is not yet complete**
|
6
|
+
This application is currently only tested with Red Hat based distributions. Ubuntu and Debian should
|
7
|
+
work but are not supported.
|
10
8
|
|
11
9
|
## Installation
|
12
10
|
|
@@ -14,9 +12,11 @@ Instalation is through the gem package management program.
|
|
14
12
|
|
15
13
|
$ gem install shutter
|
16
14
|
|
17
|
-
## Upgrading from 0.0.
|
15
|
+
## Upgrading from <= 0.0.7 to 0.1.0
|
18
16
|
|
19
|
-
Version 0.0.7
|
17
|
+
Version 0.0.7 added forwarding capabilities to shutter so the base.ipt changed and needs to be upgraded. Version 0.1.0 was a complete rewrite which fixed multiple
|
18
|
+
bugs as well as the problem with the maximum prefix length for iptables logging in base.ipt. Support for ubuntu and debian was added but not tested well and
|
19
|
+
requires the iptables-persistant package. To upgrade the base template and add the new configuration files, use the following command:
|
20
20
|
|
21
21
|
$ shutter --upgrade
|
22
22
|
|
@@ -26,7 +26,8 @@ Version 0.0.7 adds forwarding capabilities to shutter. To upgrade the base temp
|
|
26
26
|
|
27
27
|
$ gem install shutter
|
28
28
|
|
29
|
-
#### Create the initial configuration files.
|
29
|
+
#### OPTIONAL: Create the initial configuration files.
|
30
|
+
Shutter automatically creates any missing configuration files anytime it is run, but you can create them prior to
|
30
31
|
|
31
32
|
$ shutter --init
|
32
33
|
|
@@ -39,6 +40,7 @@ just make sure you include the appropriate placeholder directives to allow
|
|
39
40
|
shutter to dynamically fill in the rules. It is possible to leave out any unwanted
|
40
41
|
placeholders. By default the files are will be found in the */etc/shutter.d* directory
|
41
42
|
* **iface.dmz:** Enter any private interfaces that will be unprotected by the firewall. One per line.
|
43
|
+
* **iface.forward:** Enter any source and destination interfaces that forwarding will occur.
|
42
44
|
* **ip.allow:** A list of IP addresses and ranges that are allowed to access the 'private' ports
|
43
45
|
* **ip.deny:** A list of IP addresses and ranges that are denied access to both public and private ports.
|
44
46
|
* **ports.private:** A list of ports and protocols that are available to traffic that passes through the AllowIP chain
|
@@ -50,65 +52,33 @@ access 'on-the-fly'. To work correctly, you configure fail2ban to use the Jail
|
|
50
52
|
INPUT. The dynamic rules that fail2ban has created in the jail chain remain persistant when
|
51
53
|
shutter is 'restored' or reloaded.
|
52
54
|
|
53
|
-
Shutter can also run as a server to recieve requests from clients to populate the ip.allow and ip.deny files from a central location. To use this feature, you will need to generate an encryption key on the system you plan on using as the server by running the command:
|
54
|
-
|
55
|
-
server $ shutter --keygen
|
56
|
-
|
57
|
-
This will create the file validation.pem in the /etc/shutter.d (or the user defined) folder. The validation key can then be distributed to the shutter clients to pull in lists. On the shutter server, you will need to define the available lists in the server.json configuration file. It could look like this:
|
58
|
-
|
59
|
-
{
|
60
|
-
'allow_lists': [
|
61
|
-
'default.allow',
|
62
|
-
'private.allow',
|
63
|
-
'public.allow'
|
64
|
-
],
|
65
|
-
'deny_lists': [
|
66
|
-
'default.deny',
|
67
|
-
'bastards.deny'
|
68
|
-
]
|
69
|
-
}
|
70
|
-
|
71
|
-
To start the server run:
|
72
|
-
|
73
|
-
server $ shutter --server start
|
74
|
-
|
75
|
-
To stop the server run:
|
76
|
-
|
77
|
-
server $ shutter --server stop
|
78
|
-
|
79
|
-
To restart the server run:
|
80
|
-
|
81
|
-
server $ shutter --server restart
|
82
|
-
|
83
|
-
The first time you run shutter-server, empty files will be created in /etc/shutter.d/lists. Edit the files just like you would the ip.allow and ip.deny files. Make sure you copy the validation.pem file to your client and then on the client run:
|
84
|
-
|
85
|
-
client $ shutter --allow private --remote shutter.example.com
|
86
|
-
|
87
|
-
If the '--allow' is not specified it will grab default.allow file and if the file does not exist on the server it will return an error. In this case, shutter will grab the private.allow file from the remote site and replace ip.allow with the contents if the contents have changed.
|
88
|
-
|
89
|
-
Under the hood: A request is sent out to retrieve the MD5 sum of the file that lives on the server, if the md5sum of the file on the remote server is different than the one that is on the local server, the file is retrieved and updated on the client.
|
90
|
-
|
91
|
-
|
92
55
|
#### To check your firewall you can run:
|
93
56
|
|
94
|
-
|
57
|
+
$ shutter --save
|
95
58
|
|
96
59
|
This command mimics the 'iptables-save' command which prints the rules out to the screen.
|
97
60
|
This does not modify the firewall settings.
|
98
61
|
|
99
62
|
#### To implement the changes, use:
|
100
63
|
|
101
|
-
|
64
|
+
$ shutter --restore
|
102
65
|
|
103
66
|
This command uses 'iptables-restore' under the hood to update the firewall. You can use the '--persist' option
|
104
|
-
to make the changes permanent and survive reboots.
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
67
|
+
to make the changes permanent and survive reboots. Persist can optionally take an argument which defines the location of the
|
68
|
+
persist file if it is in a non-standard location.
|
69
|
+
|
70
|
+
#### Command line options
|
71
|
+
Usage: shutter [options]
|
72
|
+
--init Create the initial configuration files.
|
73
|
+
--reinit Rereate the initial configuration files.
|
74
|
+
--upgrade Upgrade the configuration files that have changes with a new version.
|
75
|
+
-s, --save Output the firewall to stdout. This is the default behavior.
|
76
|
+
-r, --restore Restore the firewall through iptables-restore.
|
77
|
+
-p, --persist [FILE] Write the firewall to the persistance file. If an argument is given, it will be used as the persistance file
|
78
|
+
-d, --dir DIR Set the directory for configuration files. Default is /etc/shutter.d.
|
79
|
+
--debug Turn on debugging for extra output.
|
80
|
+
-h, --help Display help and exit.
|
81
|
+
--version Display version and exit.
|
112
82
|
|
113
83
|
More documentation to come...
|
114
84
|
|
data/bin/shutter
CHANGED
@@ -4,8 +4,14 @@ begin
|
|
4
4
|
require 'rubygems'
|
5
5
|
rescue LoadError
|
6
6
|
end
|
7
|
-
|
8
7
|
require 'shutter'
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
|
9
|
+
os = Shutter::OS.new
|
10
|
+
os.validate!
|
11
|
+
SHUTTER_PERSIST_FILE = os.persist_file
|
12
|
+
|
13
|
+
#TODO: Should make these not constant
|
14
|
+
#IPTABLES_RESTORE="/sbin/iptables-restore"
|
15
|
+
#IPTABLES_SAVE="/sbin/iptables-save"
|
16
|
+
|
17
|
+
Shutter::CommandLine.new.execute(ARGV)
|
data/lib/shutter.rb
CHANGED
@@ -1,15 +1,9 @@
|
|
1
1
|
require "shutter/version"
|
2
2
|
require "shutter/content"
|
3
3
|
require "shutter/command_line"
|
4
|
+
require "shutter/iptables"
|
5
|
+
require "shutter/os"
|
6
|
+
require "shutter/files"
|
4
7
|
|
5
8
|
module Shutter
|
6
|
-
CONFIG_FILES = %w[
|
7
|
-
base.ipt
|
8
|
-
iface.dmz
|
9
|
-
iface.forward
|
10
|
-
ip.allow
|
11
|
-
ip.deny
|
12
|
-
ports.private
|
13
|
-
ports.public
|
14
|
-
]
|
15
9
|
end
|
data/lib/shutter/command_line.rb
CHANGED
@@ -1,135 +1,117 @@
|
|
1
1
|
require 'optparse'
|
2
|
-
require 'shutter/iptables'
|
3
|
-
require 'shutter/os'
|
4
2
|
|
5
3
|
module Shutter
|
6
4
|
class CommandLine
|
5
|
+
DISPLAY_OPTS_INIT = %q{Create the initial configuration files.}
|
6
|
+
DISPLAY_OPTS_REINIT = %q{Rereate the initial configuration files.}
|
7
|
+
DISPLAY_OPTS_UPGRADE = %q{Upgrade the configuration files that have changes with a new version.}
|
8
|
+
DISPLAY_OPTS_DIR = %q{Set the directory for configuration files. Default is /etc/shutter.d.}
|
9
|
+
DISPLAY_OPTS_SAVE = %q{Output the firewall to stdout. This is the default behavior.}
|
10
|
+
DISPLAY_OPTS_RESTORE = %q{Restore the firewall through iptables-restore.}
|
11
|
+
DISPLAY_OPTS_PERSIST = %q{Write the firewall to the persistance file. If an argument is given, it will be used as the persistance file}
|
12
|
+
DISPLAY_OPTS_DEBUG = %q{Turn on debugging for extra output.}
|
13
|
+
DISPLAY_OPTS_HELP = %q{Display help and exit.}
|
14
|
+
DISPLAY_OPTS_VERSION = %q{Display version and exit.}
|
15
|
+
|
7
16
|
def initialize( path = "/etc/shutter.d")
|
8
|
-
# Currently only available to RedHat variants uless testing
|
9
|
-
unless ENV['SHUTTER_MODE'] == "testing"
|
10
|
-
@os = Shutter::OS.new
|
11
|
-
unless @os.redhat?
|
12
|
-
puts "Shutter is currently only compatible with RedHat and its variants."
|
13
|
-
puts "Help make it compatible with others (github.com/rlyon/shutter)"
|
14
|
-
exit
|
15
|
-
end
|
16
|
-
end
|
17
17
|
@config_path = path
|
18
|
+
@os = Shutter::OS.new
|
19
|
+
end
|
20
|
+
|
21
|
+
def persist
|
22
|
+
@persist ||= false
|
23
|
+
end
|
24
|
+
|
25
|
+
def persist_file
|
26
|
+
@persist_file ||= @os.persist_file
|
27
|
+
end
|
28
|
+
|
29
|
+
def command
|
30
|
+
@command ||= :save
|
18
31
|
end
|
19
32
|
|
20
|
-
def
|
21
|
-
@
|
33
|
+
def debug
|
34
|
+
@debug ||= false
|
22
35
|
end
|
23
36
|
|
24
|
-
def
|
37
|
+
def config_path
|
38
|
+
@config_path ||= "/etc/shutter.d"
|
39
|
+
end
|
40
|
+
|
41
|
+
def firewall
|
42
|
+
@firewall ||= Shutter::Firewall::IPTables.new(@config_path)
|
43
|
+
end
|
44
|
+
|
45
|
+
def execute(args, noop=false)
|
25
46
|
options = {}
|
26
47
|
optparse = OptionParser.new do |opts|
|
27
48
|
opts.banner = "Usage: shutter [options]"
|
28
|
-
|
29
|
-
opts.on( '--init',
|
30
|
-
|
49
|
+
# Initialize the configuration files
|
50
|
+
opts.on( '--init', DISPLAY_OPTS_INIT ) do
|
51
|
+
@command = :init
|
31
52
|
end
|
32
|
-
|
33
|
-
|
53
|
+
# Recreate the configuration files. Overwrites all changes
|
54
|
+
opts.on( '--reinit', DISPLAY_OPTS_REINIT ) do
|
55
|
+
@command = :reinit
|
34
56
|
end
|
35
|
-
|
36
|
-
|
57
|
+
# Upgrade the configuration files that have changes with a new version
|
58
|
+
opts.on( '--upgrade', DISPLAY_OPTS_UPGRADE ) do
|
59
|
+
@command = :upgrade
|
37
60
|
end
|
38
|
-
|
39
|
-
|
61
|
+
# Output the firewall to stdout
|
62
|
+
opts.on( '-s', '--save', DISPLAY_OPTS_SAVE) do
|
63
|
+
@command = :save
|
40
64
|
end
|
41
|
-
|
42
|
-
|
65
|
+
# Restore the firewall through iptables-restore
|
66
|
+
opts.on( '-r', '--restore', DISPLAY_OPTS_RESTORE) do
|
67
|
+
@command = :restore
|
43
68
|
end
|
44
|
-
|
45
|
-
opts.on( '-p',
|
69
|
+
# Write the firewall to the persistance file
|
70
|
+
opts.on( '-p', "--persist [FILE]", DISPLAY_OPTS_PERSIST) do |file|
|
46
71
|
@persist = true
|
72
|
+
@persist_file = file || persist_file
|
47
73
|
end
|
48
|
-
|
49
|
-
opts.on( '-d', '--
|
50
|
-
|
51
|
-
end
|
52
|
-
opts.on_tail( '-h', '--help', 'Display this screen' ) do
|
53
|
-
puts opts
|
54
|
-
exit
|
74
|
+
# Sets the directory for configuration files
|
75
|
+
opts.on( '-d', '--dir DIR', DISPLAY_OPTS_DIR) do |dir|
|
76
|
+
@config_path = dir
|
55
77
|
end
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
end
|
60
|
-
end
|
61
|
-
optparse.parse!
|
62
|
-
puts "* Using config path: #{@config_path}" if @debug
|
63
|
-
puts "* Running command: #{options[:command].to_s}" if @debug
|
64
|
-
send(options[:command])
|
65
|
-
end
|
66
|
-
|
67
|
-
def init
|
68
|
-
create_config_dir
|
69
|
-
Shutter::CONFIG_FILES.each do |name|
|
70
|
-
file = "#{@config_path}/#{name}"
|
71
|
-
unless File.exists?(file)
|
72
|
-
# puts "Creating: #{file}"
|
73
|
-
File.open(file, 'w') do |f|
|
74
|
-
f.write(Shutter.const_get(name.upcase.gsub(/\./, "_")))
|
75
|
-
end
|
78
|
+
# Turn on debugging
|
79
|
+
opts.on_tail( '--debug', DISPLAY_OPTS_DEBUG) do
|
80
|
+
@debug = true
|
76
81
|
end
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
def reinit
|
81
|
-
create_config_dir
|
82
|
-
Shutter::CONFIG_FILES.each do |name|
|
83
|
-
file = "#{@config_path}/#{name}"
|
84
|
-
File.open(file, 'w') do |f|
|
85
|
-
f.write(Shutter.const_get(name.upcase.gsub(/\./, "_")))
|
82
|
+
# Display help and exit
|
83
|
+
opts.on_tail( '-h', '--help', DISPLAY_OPTS_HELP ) do
|
84
|
+
puts opts ; exit
|
86
85
|
end
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
def upgrade
|
91
|
-
create_config_dir
|
92
|
-
["base.ipt", "iface.forward"].each do |name|
|
93
|
-
file = "#{@config_path}/#{name}"
|
94
|
-
File.open(file, 'w') do |f|
|
95
|
-
f.write(Shutter.const_get(name.upcase.gsub(/\./, "_")))
|
86
|
+
# Display version and exit
|
87
|
+
opts.on_tail( '--version', DISPLAY_OPTS_VERSION) do
|
88
|
+
puts Shutter::VERSION ; exit
|
96
89
|
end
|
97
90
|
end
|
91
|
+
optparse.parse!(args)
|
92
|
+
puts "* Using config path: #{@config_path}" if @debug
|
93
|
+
puts "* Running command: #{@command}" if @debug
|
94
|
+
puts "* Using persistance file: #{persist_file}" if @debug && persist
|
95
|
+
Shutter::Files.create_config_dir(config_path) unless noop
|
96
|
+
Shutter::Files.create(config_path)
|
97
|
+
run unless noop
|
98
98
|
end
|
99
99
|
|
100
|
-
def
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
def persist
|
116
|
-
pfile = ENV['SHUTTER_PERSIST_FILE'] ? ENV['SHUTTER_PERSIST_FILE'] : iptables.persist_file(@os)
|
117
|
-
File.open(pfile, "w") do |f|
|
118
|
-
f.write(@ipt)
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
private
|
123
|
-
def create_config_dir
|
124
|
-
# Check to see if the path to the config files exist
|
125
|
-
unless File.directory?(@config_path)
|
126
|
-
begin
|
127
|
-
Dir.mkdir(@config_path)
|
128
|
-
rescue Errno::ENOENT
|
129
|
-
raise "Could not create the configuration directory. Check to see if the parent directory exists."
|
130
|
-
end
|
100
|
+
def run
|
101
|
+
case @command
|
102
|
+
when :init
|
103
|
+
Shutter::Files.create(config_path)
|
104
|
+
when :reinit
|
105
|
+
Shutter::Files.create(config_path,true)
|
106
|
+
when :upgrade
|
107
|
+
Shutter::Files.create(config_path,false,["base.ipt", "iface.forward"])
|
108
|
+
when :save
|
109
|
+
firewall.save
|
110
|
+
when :restore
|
111
|
+
firewall.restore
|
112
|
+
puts "Writing to #{persist_file}" if persist
|
113
|
+
firewall.persist(persist_file) if persist
|
131
114
|
end
|
132
115
|
end
|
133
|
-
|
134
116
|
end
|
135
117
|
end
|
data/lib/shutter/content.rb
CHANGED
@@ -1,4 +1,14 @@
|
|
1
1
|
module Shutter
|
2
|
+
module Content
|
3
|
+
CONFIG_FILES = %w[
|
4
|
+
base.ipt
|
5
|
+
iface.dmz
|
6
|
+
iface.forward
|
7
|
+
ip.allow
|
8
|
+
ip.deny
|
9
|
+
ports.private
|
10
|
+
ports.public
|
11
|
+
]
|
2
12
|
|
3
13
|
BASE_IPT = %q{# Generated by Shutter
|
4
14
|
*filter
|
@@ -143,7 +153,7 @@ BASE_IPT = %q{# Generated by Shutter
|
|
143
153
|
# NATing
|
144
154
|
##################################################################
|
145
155
|
# [RULES:FORWARD]
|
146
|
-
-A FORWARD ! -d 0.0.0.255/0.0.0.255 -m limit --limit 3/min -j LOG --log-prefix "iptables:
|
156
|
+
-A FORWARD ! -d 0.0.0.255/0.0.0.255 -m limit --limit 3/min -j LOG --log-prefix "iptables: Bad NAT:"
|
147
157
|
-A FORWARD -j DROP
|
148
158
|
|
149
159
|
##################################################################
|
@@ -163,7 +173,7 @@ COMMIT
|
|
163
173
|
}
|
164
174
|
|
165
175
|
IFACE_DMZ = %q{# Generated by Shutter
|
166
|
-
#
|
176
|
+
# device
|
167
177
|
# eth0
|
168
178
|
# eth1
|
169
179
|
}
|
@@ -195,4 +205,41 @@ IFACE_FORWARD = %q{
|
|
195
205
|
# src iface | dst iface
|
196
206
|
# eth0 eth1
|
197
207
|
}
|
208
|
+
|
209
|
+
IFACE_DMZ_MOCK = %q{# Generated by Shutter
|
210
|
+
# device
|
211
|
+
eth0
|
212
|
+
eth1
|
213
|
+
}
|
214
|
+
|
215
|
+
IP_ALLOW_MOCK = %q{# Generated by Shutter
|
216
|
+
# ipaddr
|
217
|
+
# ipaddr/subnet
|
218
|
+
192.168.0.0/16
|
219
|
+
10.0.0.1
|
220
|
+
}
|
221
|
+
|
222
|
+
IP_DENY_MOCK = %q{# Generated by Shutter
|
223
|
+
# ipaddr
|
224
|
+
# ipaddr/subnet
|
225
|
+
172.31.0.0/24
|
226
|
+
8.9.9.9
|
227
|
+
}
|
228
|
+
|
229
|
+
PORTS_PUBLIC_MOCK = %q{
|
230
|
+
# proto port
|
231
|
+
80 tcp
|
232
|
+
443 tcp
|
233
|
+
}
|
234
|
+
|
235
|
+
PORTS_PRIVATE_MOCK = %q{
|
236
|
+
# proto port
|
237
|
+
22 tcp
|
238
|
+
}
|
239
|
+
|
240
|
+
IFACE_FORWARD_MOCK = %q{
|
241
|
+
# src iface | dst iface
|
242
|
+
eth0 eth1
|
243
|
+
}
|
244
|
+
end
|
198
245
|
end
|