dust-deploy 0.3.1 → 0.3.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/changelog.md +8 -0
- data/lib/dust/recipes/iptables.rb +220 -161
- data/lib/dust/version.rb +1 -1
- metadata +3 -3
data/changelog.md
CHANGED
@@ -1,6 +1,14 @@
|
|
1
1
|
Changelog
|
2
2
|
=============
|
3
3
|
|
4
|
+
0.3.2
|
5
|
+
------------
|
6
|
+
|
7
|
+
- huge refactoring was done to the iptables recipe, no config changes needed on your side though.
|
8
|
+
- iptables now supports table support for rpm machines
|
9
|
+
- reduced verbosity for iptables recipe
|
10
|
+
|
11
|
+
|
4
12
|
0.3.1
|
5
13
|
------------
|
6
14
|
|
@@ -1,188 +1,181 @@
|
|
1
1
|
require 'ipaddress'
|
2
2
|
|
3
3
|
class Iptables < Thor
|
4
|
+
|
4
5
|
desc 'iptables:deploy', 'configures iptables firewall'
|
5
6
|
def deploy node, rules, options
|
6
7
|
template_path = "./templates/#{ File.basename(__FILE__).chomp( File.extname(__FILE__) ) }"
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
8
|
+
@node = node
|
9
|
+
@rules = rules
|
10
|
+
@options = options
|
11
|
+
|
12
|
+
# list of all tables and chains
|
13
|
+
@tables = {}
|
14
|
+
@tables['ipv4'] = {}
|
15
|
+
@tables['ipv4']['filter'] = [ 'INPUT', 'OUTPUT', 'FORWARD' ]
|
16
|
+
@tables['ipv4']['nat'] = [ 'OUTPUT', 'PREROUTING', 'POSTROUTING' ]
|
17
|
+
@tables['ipv4']['mangle'] = [ 'INPUT', 'OUTPUT', 'FORWARD', 'PREROUTING', 'POSTROUTING' ]
|
18
|
+
@tables['ipv4']['raw'] = [ 'OUTPUT', 'PREROUTING' ]
|
19
|
+
|
20
|
+
@tables['ipv6'] = {}
|
21
|
+
@tables['ipv6']['filter'] = [ 'INPUT', 'OUTPUT', 'FORWARD' ]
|
22
|
+
@tables['ipv6']['mangle'] = [ 'INPUT', 'OUTPUT', 'FORWARD', 'PREROUTING', 'POSTROUTING' ]
|
23
|
+
@tables['ipv6']['raw'] = [ 'OUTPUT', 'PREROUTING' ]
|
24
|
+
|
25
|
+
|
26
|
+
return unless install
|
27
|
+
|
28
|
+
[4, 6].each do |v|
|
29
|
+
@script = ''
|
30
|
+
@ip_version = v
|
31
|
+
|
32
|
+
::Dust.print_msg "generating ipv#{@ip_version} rules\n"
|
33
|
+
|
34
|
+
clear_all_tables
|
35
|
+
populate_rule_defaults
|
36
|
+
generate_all_rules
|
37
|
+
|
38
|
+
deploy_script
|
39
|
+
apply_rules
|
40
|
+
|
41
|
+
puts
|
18
42
|
end
|
43
|
+
end
|
19
44
|
|
20
45
|
|
21
|
-
|
22
|
-
if iptables == 'iptables'
|
23
|
-
ipv = 4
|
24
|
-
ipv4 = true
|
25
|
-
ipv6 = false
|
26
|
-
elsif iptables == 'ip6tables'
|
27
|
-
ipv = 6
|
28
|
-
ipv4 = false
|
29
|
-
ipv6 = true
|
30
|
-
end
|
31
|
-
|
32
|
-
::Dust.print_msg "configuring and deploying ipv4 rules\n" if ipv4
|
33
|
-
::Dust.print_msg "configuring and deploying ipv6 rules\n" if ipv6
|
34
|
-
|
35
|
-
iptables_filter = ''
|
36
|
-
iptables_nat = '' if node.uses_rpm?
|
37
|
-
|
38
|
-
# default policy for chains
|
39
|
-
if node.uses_apt? or node.uses_emerge?
|
40
|
-
iptables_filter += rules['input'] ? "-P INPUT DROP\n" : "-P INPUT ACCEPT\n"
|
41
|
-
iptables_filter += rules['output'] ? "-P OUTPUT DROP\n" : "-P OUTPUT ACCEPT\n"
|
42
|
-
iptables_filter += rules['forward'] ? "-P FORWARD DROP\n" : "-P FORWARD ACCEPT\n"
|
43
|
-
|
44
|
-
iptables_filter += "-F\n"
|
45
|
-
iptables_filter += "-F -t nat\n" if ipv4
|
46
|
-
iptables_filter += "-X\n"
|
47
|
-
|
48
|
-
elsif node.uses_rpm?
|
49
|
-
iptables_filter += "*filter\n"
|
50
|
-
|
51
|
-
iptables_filter += rules['input'] ? ":INPUT DROP [0:0]\n" : ":INPUT ACCEPT [0:0]\n"
|
52
|
-
iptables_filter += rules['output'] ? ":OUTPUT DROP [0:0]\n" : ":OUTPUT ACCEPT [0:0]\n"
|
53
|
-
iptables_filter += rules['forward'] ? ":FORWARD DROP [0:0]\n" : ":FORWARD ACCEPT [0:0]\n"
|
54
|
-
|
55
|
-
# also create a *nat element, centos-like systems need that.
|
56
|
-
if ipv4
|
57
|
-
iptables_nat += "*nat\n"
|
58
|
-
iptables_nat += ":PREROUTING ACCEPT [0:0]\n"
|
59
|
-
iptables_nat += ":POSTROUTING ACCEPT [0:0]\n"
|
60
|
-
iptables_nat += ":OUTPUT ACCEPT [0:0]\n"
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
# map rules to iptables strings
|
65
|
-
rules.each do |chain, chain_rules|
|
66
|
-
::Dust.print_msg "#{::Dust.pink}#{chain.upcase}#{::Dust.none}\n", :indent => 2
|
67
|
-
chain_rules.sort.each do |name, rule|
|
68
|
-
# set default variables
|
69
|
-
rule['jump'] ||= ['ACCEPT']
|
70
|
-
|
71
|
-
# if we want to use ports, we're going to need a protocol. defaulting to tcp
|
72
|
-
rule['protocol'] ||= ['tcp'] if rule['dport'] or rule['sport']
|
73
|
-
|
74
|
-
# convert non-array variables to array, so we won't get hickups when using .each and .combine
|
75
|
-
rule.each { |k, v| rule[k] = [ rule[k] ] unless rule[k].is_a? Array }
|
76
|
-
|
77
|
-
next unless check_ipversion rule, ipv
|
78
|
-
|
79
|
-
# on centos-like machines, nat tables are handled differently
|
80
|
-
# remove --table argument and
|
81
|
-
is_nat = false
|
82
|
-
if node.uses_rpm? and rule['table']
|
83
|
-
rule.delete 'table'
|
84
|
-
is_nat = true
|
85
|
-
end
|
86
|
-
|
87
|
-
parse_rule(rule).each do |r|
|
88
|
-
# TODO: parse nicer output
|
89
|
-
::Dust.print_msg "#{name}:#{::Dust.grey 0} '#{r.join ' ' }'#{::Dust.none}\n", :indent => 3
|
90
|
-
|
91
|
-
if is_nat
|
92
|
-
# handle centos special case
|
93
|
-
iptables_nat += "-A #{chain.upcase} #{r.join ' '}\n"
|
94
|
-
else
|
95
|
-
iptables_filter += "-A #{chain.upcase} #{r.join ' '}\n"
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
# put commit statement for rpm machines
|
102
|
-
if node.uses_rpm?
|
103
|
-
iptables_filter += "COMMIT\n"
|
104
|
-
iptables_nat += "COMMIT\n" if ipv4
|
105
|
-
end
|
106
|
-
|
107
|
-
# prepend iptables command on non-centos-like machines
|
108
|
-
if node.uses_apt? or node.uses_emerge?
|
109
|
-
iptables_filter = iptables_filter.map { |s| "#{iptables} #{s}" }.to_s
|
110
|
-
end
|
46
|
+
private
|
111
47
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
48
|
+
# install iptables
|
49
|
+
def install
|
50
|
+
return false unless @node.install_package 'iptables'
|
51
|
+
return false unless @node.install_package 'iptables-ipv6' if @node.uses_rpm?
|
52
|
+
true
|
53
|
+
end
|
54
|
+
|
55
|
+
# deletes all rules/chains
|
56
|
+
def clear_all_tables
|
57
|
+
return if @node.uses_rpm?
|
58
|
+
|
59
|
+
# clear all rules
|
60
|
+
@tables['ipv' + @ip_version.to_s].keys.each { |table| @script.concat "--flush --table #{table}\n" }
|
61
|
+
|
62
|
+
# delete all custom chains
|
63
|
+
@script.concat "--delete-chain \n" unless @node.uses_rpm?
|
64
|
+
end
|
65
|
+
|
66
|
+
# inserts default values to chains, if not given
|
67
|
+
# table defaults to filter
|
68
|
+
# jump target to ACCEPT
|
69
|
+
# protocol to tcp (if port is given)
|
70
|
+
# and converts non-arrays to arrays, so .each and .combine won't cause hickups
|
71
|
+
def populate_rule_defaults
|
72
|
+
@rules.values.each do |chain_rules|
|
73
|
+
chain_rules.values.each do |rule|
|
74
|
+
rule['table'] ||= ['filter']
|
75
|
+
rule['jump'] ||= ['ACCEPT']
|
76
|
+
rule['protocol'] ||= ['tcp'] if rule['dport'] or rule['sport']
|
77
|
+
rule.each { |k, v| rule[k] = [ rule[k] ] unless rule[k].is_a? Array }
|
116
78
|
end
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
node.write target, iptables_filter, :quiet => true
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# generates all iptables rules
|
83
|
+
def generate_all_rules
|
84
|
+
@tables['ipv' + @ip_version.to_s].each do |table, chains|
|
85
|
+
@script.concat "*#{table}\n" if @node.uses_rpm?
|
86
|
+
set_chain_policies table
|
87
|
+
generate_rules_for_table table
|
88
|
+
end
|
89
|
+
end
|
129
90
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
91
|
+
# set the chain default policies to DROP/ACCEPT
|
92
|
+
# according to whether chain is specified in config file
|
93
|
+
def set_chain_policies table
|
94
|
+
#::Dust.print_msg "#{::Dust.pink}#{table}#{Dust.none} table\n", :indent => 2
|
95
|
+
#::Dust.print_msg "setting default policies\n", :indent => 3
|
96
|
+
|
97
|
+
@tables['ipv' + @ip_version.to_s][table].each do |chain|
|
98
|
+
policy = get_chain_policy table, chain
|
99
|
+
#::Dust.print_msg "#{table}/#{chain} -> #{policy}", :indent => 4
|
100
|
+
|
101
|
+
if @node.uses_rpm?
|
102
|
+
@script.concat ":#{chain.upcase} #{policy} [0:0]\n"
|
103
|
+
else
|
104
|
+
@script.concat "--table #{table} --policy #{chain.upcase} #{policy}\n"
|
134
105
|
end
|
106
|
+
|
107
|
+
#::Dust.print_ok
|
108
|
+
end
|
109
|
+
end
|
135
110
|
|
136
|
-
|
137
|
-
|
138
|
-
|
111
|
+
# returns DROP if chain and table is specified in config file
|
112
|
+
# ACCEPT if not
|
113
|
+
def get_chain_policy table, chain
|
114
|
+
return 'ACCEPT' unless @rules[chain.downcase]
|
139
115
|
|
140
|
-
|
141
|
-
|
116
|
+
@rules[chain.downcase].values.each do |rule|
|
117
|
+
return 'DROP' if rule['table'].include? table
|
118
|
+
end
|
142
119
|
|
143
|
-
|
144
|
-
|
145
|
-
::Dust.print_result( (ret[:exit_code] == 0 and ret[:stdout].empty? and ret[:stderr].empty?) )
|
146
|
-
end
|
147
|
-
end
|
120
|
+
return 'ACCEPT'
|
121
|
+
end
|
148
122
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
123
|
+
# generate iptables rules for table 'table'
|
124
|
+
def generate_rules_for_table table
|
125
|
+
@rules.each do |chain, chain_rules|
|
126
|
+
rules = get_rules_for_table chain_rules, table
|
127
|
+
next if rules.empty?
|
128
|
+
|
129
|
+
#::Dust.print_msg "#{::Dust.pink}#{chain}#{::Dust.none} rules\n", :indent => 3
|
130
|
+
rules.sort.each do |name, rule|
|
131
|
+
next unless rule['table'].include? table
|
132
|
+
next unless check_ip_version rule
|
133
|
+
|
134
|
+
::Dust.print_msg "adding rule: #{name}", :indent => 2
|
135
|
+
generate_iptables_string chain, rule
|
136
|
+
::Dust.print_ok
|
155
137
|
end
|
156
|
-
|
157
|
-
puts
|
158
138
|
end
|
139
|
+
@script.concat "COMMIT\n" if @node.uses_rpm?
|
159
140
|
end
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
141
|
+
|
142
|
+
def get_rules_for_table rules, table
|
143
|
+
rules.select { |name, rule| rule['table'].include? table }
|
144
|
+
end
|
145
|
+
|
164
146
|
# check if source and destination ip (if given)
|
165
147
|
# are valid ips for this ip version
|
166
|
-
def
|
148
|
+
def check_ip_version rule
|
167
149
|
['source', 'src', 'destination', 'dest', 'to-source'].each do |attr|
|
168
150
|
if rule[attr]
|
169
151
|
rule[attr].each do |addr|
|
170
|
-
return false unless IPAddress(addr).send "ipv#{
|
152
|
+
return false unless IPAddress(addr).send "ipv#{@ip_version}?"
|
171
153
|
end
|
172
154
|
end
|
173
155
|
# return false if this ip version was manually disabled
|
174
|
-
return false unless rule['ip-version'].include?
|
156
|
+
return false unless rule['ip-version'].include? @ip_version if rule['ip-version']
|
175
157
|
end
|
176
158
|
true
|
159
|
+
end
|
160
|
+
|
161
|
+
# generates the iptables string out of a rule
|
162
|
+
def generate_iptables_string chain, rule
|
163
|
+
parse_rule(rule).each do |r|
|
164
|
+
#::Dust.print_msg "#{::Dust.grey}#{r.join ' '}#{::Dust.none}\n", :indent => 5
|
165
|
+
@script.concat "--append #{chain.upcase} #{r.join ' '}\n"
|
166
|
+
end
|
177
167
|
end
|
178
168
|
|
179
169
|
# map iptables options
|
180
170
|
def parse_rule r
|
181
171
|
with_dashes = {}
|
182
172
|
result = []
|
183
|
-
|
173
|
+
|
174
|
+
# map r[key] = value to '--key value'
|
184
175
|
r.each do |k, v|
|
185
176
|
next if k == 'ip-version' # skip ip-version, since its not iptables option
|
177
|
+
next if k == 'table' if @node.uses_rpm? # rpm-firewall takes table argument with *table
|
178
|
+
|
186
179
|
with_dashes[k] = r[k].map do |v|
|
187
180
|
value = v.to_s
|
188
181
|
if value.start_with? '!', '! '
|
@@ -190,44 +183,110 @@ class Iptables < Thor
|
|
190
183
|
value.slice! '!'
|
191
184
|
value.lstrip!
|
192
185
|
"! --#{k} #{value}"
|
193
|
-
|
186
|
+
else
|
194
187
|
"--#{k} #{value}"
|
195
188
|
end
|
196
189
|
end
|
197
190
|
end
|
198
191
|
with_dashes.values.each { |a| result = result.combine a }
|
199
|
-
|
200
|
-
|
192
|
+
|
193
|
+
sort_rule_options result
|
194
|
+
end
|
195
|
+
|
196
|
+
# make sure the options are sorted in a way that works.
|
197
|
+
def sort_rule_options rule
|
201
198
|
sorted = []
|
202
|
-
|
199
|
+
rule.each do |r|
|
203
200
|
# sort rules so it makes sense
|
204
201
|
r = r.sort_by do |x|
|
205
202
|
if x.include? '--match'
|
206
203
|
-1
|
207
|
-
|
204
|
+
elsif x.include? '--protocol'
|
208
205
|
-2
|
209
|
-
|
206
|
+
elsif x.include? '--jump'
|
210
207
|
1
|
211
|
-
|
208
|
+
elsif x.include? '--to-port'
|
212
209
|
2
|
213
|
-
|
210
|
+
elsif x.include? '--to-destination'
|
214
211
|
3
|
215
|
-
|
212
|
+
elsif x.include? '--to-source'
|
216
213
|
4
|
217
|
-
|
214
|
+
elsif x.include? '--ttl-set'
|
218
215
|
5
|
219
|
-
|
216
|
+
elsif x.include? '--clamp-mss-to-pmtu'
|
220
217
|
6
|
221
|
-
|
218
|
+
elsif x.include? '--reject-with'
|
222
219
|
7
|
223
|
-
|
220
|
+
else
|
224
221
|
0
|
225
222
|
end
|
226
223
|
end
|
227
224
|
sorted.push r
|
228
225
|
end
|
226
|
+
|
227
|
+
sorted
|
228
|
+
end
|
229
|
+
|
230
|
+
def deploy_script
|
231
|
+
target = get_target
|
232
|
+
|
233
|
+
prepend_cmd
|
234
|
+
prepend_header
|
235
|
+
|
236
|
+
@node.write target, @script, :quiet => true
|
237
|
+
|
238
|
+
if @node.uses_rpm?
|
239
|
+
@node.chmod '600', target
|
240
|
+
else
|
241
|
+
@node.chmod '700', target
|
242
|
+
end
|
243
|
+
end
|
229
244
|
|
230
|
-
|
245
|
+
# put dust comment at the beginning of the file
|
246
|
+
def prepend_header
|
247
|
+
@script.insert 0, "#!/bin/sh\n" unless @node.uses_rpm?
|
248
|
+
@script.insert 0, "# automatically generated by dust\n\n"
|
249
|
+
end
|
250
|
+
|
251
|
+
# prepend iptables command on non-centos-like machines
|
252
|
+
def prepend_cmd
|
253
|
+
@script = @script.map { |s| "#{cmd} #{s}" }.to_s unless @node.uses_rpm?
|
231
254
|
end
|
255
|
+
|
256
|
+
# apply newly pushed rules
|
257
|
+
def apply_rules
|
258
|
+
if @options.restart?
|
259
|
+
::Dust.print_msg "applying ipv#{@ip_version} rules"
|
260
|
+
|
261
|
+
if @node.uses_rpm?
|
262
|
+
::Dust.print_result @node.exec("/etc/init.d/#{cmd} restart")[:exit_code]
|
263
|
+
|
264
|
+
else
|
265
|
+
ret = @node.exec get_target
|
266
|
+
::Dust.print_result( (ret[:exit_code] == 0 and ret[:stdout].empty? and ret[:stderr].empty?) )
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
# on gentoo, rules have to be saved using the init script,
|
271
|
+
# otherwise they won't get re-applied on next startup
|
272
|
+
if @node.uses_emerge?
|
273
|
+
::Dust.print_msg "saving ipv#{@ip_version} rules"
|
274
|
+
::Dust.print_result @node.exec("/etc/init.d/#{cmd} save")[:exit_code]
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
# set the target file depending on distribution
|
279
|
+
def get_target
|
280
|
+
target = "/etc/#{cmd}"
|
281
|
+
target = "/etc/network/if-pre-up.d/#{cmd}" if @node.uses_apt?
|
282
|
+
target = "/etc/sysconfig/#{cmd}" if @node.uses_rpm?
|
283
|
+
target
|
284
|
+
end
|
285
|
+
|
286
|
+
def cmd
|
287
|
+
return 'iptables' if @ip_version == 4
|
288
|
+
return 'ip6tables' if @ip_version == 6
|
289
|
+
end
|
290
|
+
|
232
291
|
end
|
233
292
|
|
data/lib/dust/version.rb
CHANGED
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 3
|
8
|
-
-
|
9
|
-
version: 0.3.
|
8
|
+
- 2
|
9
|
+
version: 0.3.2
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- kris kechagia
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2012-01-
|
17
|
+
date: 2012-01-14 00:00:00 +01:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|