dust-deploy 0.1.8 → 0.2.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/changelog.md +21 -1
- data/dust.gemspec +1 -0
- data/lib/dust/recipes/iptables.rb +97 -166
- data/lib/dust/version.rb +1 -1
- metadata +15 -3
data/changelog.md
CHANGED
@@ -1,7 +1,27 @@
|
|
1
1
|
Changelog
|
2
2
|
=============
|
3
3
|
|
4
|
-
|
4
|
+
0.2.0
|
5
|
+
------------
|
6
|
+
|
7
|
+
heavily refactors iptables recipe. you HAVE to adapt your iptables settings. new usage:
|
8
|
+
|
9
|
+
recipe:
|
10
|
+
iptables:
|
11
|
+
input:
|
12
|
+
- input:
|
13
|
+
ssh: { dport: 22, match: state, state: NEW }
|
14
|
+
- output:
|
15
|
+
drop: { jump: DROP }
|
16
|
+
|
17
|
+
every iptables long option is allowed, it tries to automatically detect whether to use iptables, ip6tables or both.
|
18
|
+
known issues: --to-destination is not checked for ipv4/ipv6, because it might include port numbers.
|
19
|
+
default jump target ist ACCEPT
|
20
|
+
|
21
|
+
basic rules are added automatically, see iptables.rb for more information.
|
22
|
+
|
23
|
+
|
24
|
+
0.1.8
|
5
25
|
------------
|
6
26
|
|
7
27
|
adds recipe for making sure packages are _uninstalled_
|
data/dust.gemspec
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'ipaddress'
|
2
|
+
|
1
3
|
class Iptables < Thor
|
2
4
|
desc 'iptables:deploy', 'configures iptables firewall'
|
3
5
|
def deploy node, rules, options
|
@@ -15,226 +17,114 @@ class Iptables < Thor
|
|
15
17
|
return
|
16
18
|
end
|
17
19
|
|
18
|
-
# configure attributes (using empty arrays as default)
|
19
|
-
rules['ports'] ||= []
|
20
|
-
rules['ipv4-custom-input-rules'] ||= []
|
21
|
-
rules['ipv6-custom-input-rules'] ||= []
|
22
|
-
rules['ipv4-custom-output-rules'] ||= []
|
23
|
-
rules['ipv6-custom-output-rules'] ||= []
|
24
|
-
rules['ipv4-custom-forward-rules'] ||= []
|
25
|
-
rules['ipv6-custom-forward-rules'] ||= []
|
26
|
-
rules['ipv4-custom-prerouting-rules'] ||= []
|
27
|
-
rules['ipv6-custom-prerouting-rules'] ||= []
|
28
|
-
rules['ipv4-custom-postrouting-rules'] ||= []
|
29
|
-
rules['ipv6-custom-postrouting-rules'] ||= []
|
30
|
-
|
31
|
-
# convert ports: int to array if its just a single int so .each won't get hickups
|
32
|
-
rules['ports'] = [ rules['ports'] ] if rules['ports'].class == Fixnum
|
33
20
|
|
34
21
|
[ 'iptables', 'ip6tables' ].each do |iptables|
|
35
|
-
|
36
|
-
|
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
|
37
31
|
|
38
32
|
::Dust.print_msg "configuring and deploying ipv4 rules\n" if ipv4
|
39
33
|
::Dust.print_msg "configuring and deploying ipv6 rules\n" if ipv6
|
40
34
|
|
41
|
-
|
35
|
+
iptables_script = ''
|
42
36
|
|
43
37
|
# default policy for chains
|
44
38
|
if node.uses_apt? true or node.uses_emerge? true
|
45
|
-
|
39
|
+
iptables_script += "-P INPUT DROP\n" +
|
46
40
|
"-P OUTPUT DROP\n" +
|
47
41
|
"-P FORWARD DROP\n" +
|
48
42
|
"-F\n"
|
49
|
-
|
50
|
-
|
43
|
+
iptables_script += "-F -t nat\n" if ipv4
|
44
|
+
iptables_script += "-X\n"
|
51
45
|
|
52
46
|
elsif node.uses_rpm? true
|
53
|
-
|
47
|
+
iptables_script += "*filter\n" +
|
54
48
|
":INPUT DROP [0:0]\n" +
|
55
49
|
":FORWARD DROP [0:0]\n" +
|
56
50
|
":OUTPUT DROP [0:0]\n"
|
57
51
|
end
|
58
52
|
|
59
53
|
# allow localhost
|
60
|
-
|
54
|
+
iptables_script += "-A INPUT -i lo -j ACCEPT\n"
|
61
55
|
|
62
56
|
# drop invalid packets
|
63
|
-
|
57
|
+
iptables_script += "-A INPUT -m state --state INVALID -j DROP\n"
|
64
58
|
|
65
59
|
# allow related packets
|
66
|
-
|
60
|
+
iptables_script += "-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT\n"
|
67
61
|
|
68
62
|
# drop tcp packets with the syn bit set if the tcp connection is already established
|
69
|
-
|
63
|
+
iptables_script += "-A INPUT -p tcp --tcp-flags SYN SYN -m state --state ESTABLISHED -j DROP\n" # if ipv4
|
70
64
|
|
71
65
|
# drop icmp timestamps
|
72
|
-
|
73
|
-
|
66
|
+
iptables_script += "-A INPUT -p icmp --icmp-type timestamp-request -j DROP\n" if ipv4
|
67
|
+
iptables_script += "-A INPUT -p icmp --icmp-type timestamp-reply -j DROP\n" if ipv4
|
74
68
|
|
75
69
|
# allow other icmp packets
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
# open ports
|
80
|
-
rules['ports'].each do |rule|
|
81
|
-
# if config is something like
|
82
|
-
# ports: 22
|
83
|
-
# or
|
84
|
-
# ports: [ 22, 443, 1000:2000 ]
|
85
|
-
# generate a new hash, and set rule['port']
|
86
|
-
unless rule.class == Hash
|
87
|
-
port = rule
|
88
|
-
rule = {}
|
89
|
-
rule['port'] = port
|
90
|
-
end
|
70
|
+
iptables_script += "-A INPUT -p icmpv6 -j ACCEPT\n" if ipv6
|
71
|
+
iptables_script += "-A INPUT -p icmp -j ACCEPT\n"
|
91
72
|
|
92
|
-
# skip rule for other ipversion than specified
|
93
|
-
rule['ip-version'] ||= 0 # default to 0, means both protocols
|
94
|
-
next if rule['ip-version'].to_i == 4 and ipv6
|
95
|
-
next if rule['ip-version'].to_i == 6 and ipv4
|
96
73
|
|
97
|
-
|
98
|
-
|
74
|
+
# drop invalid outgoing packets
|
75
|
+
iptables_script += "-A OUTPUT -m state --state INVALID -j DROP\n"
|
99
76
|
|
100
|
-
|
101
|
-
|
102
|
-
::Dust.print_failed "no port specified: #{rule.inspect}", 2
|
103
|
-
next
|
104
|
-
end
|
77
|
+
# allow related outgoing packets
|
78
|
+
iptables_script += "-A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT\n"
|
105
79
|
|
106
|
-
|
107
|
-
|
80
|
+
# map rules to iptables strings
|
81
|
+
rules.each do |chain, chain_rules|
|
82
|
+
::Dust.print_msg "#{::Dust.pink}#{chain.upcase}#{::Dust.none}\n", 2
|
83
|
+
chain_rules.each do |name, rule|
|
84
|
+
# set default variables
|
85
|
+
rule['jump'] ||= ['ACCEPT']
|
108
86
|
|
109
|
-
|
110
|
-
|
111
|
-
::Dust.print_msg "allowing port #{port}:#{rule['protocol']}", 2
|
112
|
-
rule_file += "-A INPUT -p #{rule['protocol']} --dport #{port} "
|
113
|
-
if rule['interface']
|
114
|
-
print " [dev: #{rule['interface']}]"
|
115
|
-
rule_file += "-i #{rule['interface']} "
|
116
|
-
end
|
117
|
-
if rule['source']
|
118
|
-
print " [source: #{rule['source']}]"
|
119
|
-
rule_file += "--source #{rule['source']} "
|
120
|
-
end
|
121
|
-
rule_file += "-m state --state NEW "
|
122
|
-
rule_file += "-j ACCEPT\n"
|
123
|
-
::Dust.print_ok
|
124
|
-
end
|
125
|
-
end
|
87
|
+
# if we want to use ports, we're going to need a protocol. defaulting to tcp
|
88
|
+
rule['protocol'] ||= ['tcp'] if rule['dport'] or rule['sport']
|
126
89
|
|
127
|
-
|
128
|
-
|
129
|
-
::Dust.print_msg "adding custom ipv4 input rule: #{rule}", 2
|
130
|
-
rule_file += "-A INPUT #{rule}\n"
|
131
|
-
::Dust.print_ok
|
132
|
-
end if ipv4
|
133
|
-
|
134
|
-
# add custom ipv6 iput rules
|
135
|
-
rules['ipv6-custom-input-rules'].each do |rule|
|
136
|
-
::Dust.print_msg "adding custom ipv6 input rule: #{rule}", 2
|
137
|
-
rule_file += "-A INPUT #{rule}\n"
|
138
|
-
::Dust.print_ok
|
139
|
-
end if ipv6
|
140
|
-
|
141
|
-
# deny the rest
|
142
|
-
rule_file += "-A INPUT -p tcp -j REJECT --reject-with tcp-reset\n"
|
143
|
-
rule_file += "-A INPUT -j REJECT --reject-with icmp-port-unreachable\n" if ipv4
|
144
|
-
|
145
|
-
# add custom ipv4 prerouting rules
|
146
|
-
rules['ipv4-custom-prerouting-rules'].each do |rule|
|
147
|
-
::Dust.print_msg "adding custom ipv4 prerouting rule: #{rule}", 2
|
148
|
-
rule_file += "-A PREROUTING #{rule}\n"
|
149
|
-
::Dust.print_ok
|
150
|
-
end if ipv4
|
151
|
-
|
152
|
-
# add custom ipv6 prerouting rules
|
153
|
-
rules['ipv6-custom-prerouting-rules'].each do |rule|
|
154
|
-
::Dust.print_msg "adding custom ipv6 prerouting rule: #{rule}", 2
|
155
|
-
rule_file += "-A PREROUTING #{rule}\n"
|
156
|
-
::Dust.print_ok
|
157
|
-
end if ipv6
|
158
|
-
|
159
|
-
# add custom ipv4 postrouting rules
|
160
|
-
rules['ipv4-custom-postrouting-rules'].each do |rule|
|
161
|
-
::Dust.print_msg "adding custom ipv4 postrouting rule: #{rule}", 2
|
162
|
-
rule_file += "-A POSTROUTING #{rule}\n"
|
163
|
-
::Dust.print_ok
|
164
|
-
end if ipv4
|
165
|
-
|
166
|
-
# add custom ipv6 postrouting rules
|
167
|
-
rules['ipv6-custom-postrouting-rules'].each do |rule|
|
168
|
-
::Dust.print_msg "adding custom ipv6 postrouting rule: #{rule}", 2
|
169
|
-
rule_file += "-A POSTROUTING #{rule}\n"
|
170
|
-
::Dust.print_ok
|
171
|
-
end if ipv6
|
90
|
+
# convert non-array variables to array, so we won't get hickups when using .each and .combine
|
91
|
+
rule.each { |k, v| rule[k] = [ rule[k] ] if rule[k].class != Array }
|
172
92
|
|
93
|
+
next unless check_ipversion rule, ipv
|
173
94
|
|
174
|
-
|
175
|
-
|
95
|
+
parse_rule(rule).each do |r|
|
96
|
+
# TODO: parse nicer output
|
97
|
+
::Dust.print_msg "#{name}:#{::Dust.grey 0} '#{r.join ' ' }'#{::Dust.none}\n", 3
|
98
|
+
iptables_script += "-A #{chain.upcase} #{r.join ' '}\n"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
176
102
|
|
177
|
-
#
|
178
|
-
|
179
|
-
|
180
|
-
# add custom ipv4 output rules
|
181
|
-
rules['ipv4-custom-output-rules'].each do |rule|
|
182
|
-
::Dust.print_msg "adding custom ipv4 outgoing rule: #{rule}", 2
|
183
|
-
rule_file += "-A OUTPUT #{rule}\n"
|
184
|
-
::Dust.print_ok
|
185
|
-
end if ipv4
|
186
|
-
|
187
|
-
# add custom ipv6 output rules
|
188
|
-
rules['ipv6-custom-output-rules'].each do |rule|
|
189
|
-
::Dust.print_msg "adding custom ipv6 outgoing rule: #{rule}", 2
|
190
|
-
rule_file += "-A OUTPUT #{rule}\n"
|
191
|
-
::Dust.print_ok
|
192
|
-
end if ipv6
|
103
|
+
# deny the rest incoming
|
104
|
+
iptables_script += "-A INPUT -p tcp -j REJECT --reject-with tcp-reset\n"
|
105
|
+
iptables_script += "-A INPUT -j REJECT --reject-with icmp-port-unreachable\n" if ipv4
|
193
106
|
|
194
107
|
# allow everything out
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
if rules['forward']
|
200
|
-
::Dust.print_msg 'enabling ipv4 forwarding', 2
|
201
|
-
rule_file += "-A FORWARD -m state --state INVALID -j DROP\n"
|
202
|
-
rule_file += "-A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT\n"
|
203
|
-
rule_file += "-A FORWARD -j ACCEPT\n"
|
204
|
-
::Dust.print_ok
|
205
|
-
end if ipv4
|
206
|
-
|
207
|
-
# add custom ipv4 forward rules
|
208
|
-
rules['ipv4-custom-forward-rules'].each do |rule|
|
209
|
-
::Dust.print_msg "adding custom ipv4 forward rule: #{rule}", 2
|
210
|
-
rule_file += "-A FORWARD #{rule}\n"
|
211
|
-
::Dust.print_ok
|
212
|
-
end if ipv4
|
213
|
-
|
214
|
-
# add custom ipv6 forward rules
|
215
|
-
rules['ipv6-custom-forward-rules'].each do |rule|
|
216
|
-
::Dust.print_msg "adding custom ipv6 forward rule: #{rule}", 2
|
217
|
-
rule_file += "-A FORWARD #{rule}\n"
|
218
|
-
::Dust.print_ok
|
219
|
-
end if ipv6
|
220
|
-
|
221
|
-
rule_file += "COMMIT\n" if node.uses_rpm? true
|
108
|
+
iptables_script += "-A OUTPUT -j ACCEPT\n"
|
109
|
+
|
110
|
+
# put commit statement for rpm machines
|
111
|
+
iptables_script += "COMMIT\n" if node.uses_rpm? true
|
222
112
|
|
223
113
|
# prepend iptables command on non-centos-like machines
|
224
|
-
|
114
|
+
iptables_script = iptables_script.map { |s| "#{iptables} #{s}" }.to_s if node.uses_apt? true or node.uses_emerge? true
|
225
115
|
|
226
116
|
# set header
|
227
117
|
header = ''
|
228
118
|
header = "#!/bin/sh\n" if node.uses_apt? true or node.uses_emerge? true
|
229
119
|
header += "# automatically generated by dust\n\n"
|
230
|
-
|
120
|
+
iptables_script = header + iptables_script
|
231
121
|
|
232
122
|
# set the target file depending on distribution
|
233
123
|
target = "/etc/network/if-pre-up.d/#{iptables}" if node.uses_apt? true
|
234
124
|
target = "/etc/#{iptables}" if node.uses_emerge? true
|
235
125
|
target = "/etc/sysconfig/#{iptables}" if node.uses_rpm? true
|
236
126
|
|
237
|
-
node.write target,
|
127
|
+
node.write target, iptables_script, true
|
238
128
|
|
239
129
|
node.chmod '700', target if node.uses_apt? true or node.uses_emerge? true
|
240
130
|
node.chmod '600', target if node.uses_rpm? true
|
@@ -263,5 +153,46 @@ class Iptables < Thor
|
|
263
153
|
puts
|
264
154
|
end
|
265
155
|
end
|
156
|
+
|
157
|
+
|
158
|
+
private
|
159
|
+
|
160
|
+
# check if source and destination ip (if given)
|
161
|
+
# are valid ips for this ip version
|
162
|
+
def check_ipversion rule, ipv
|
163
|
+
['source', 'destination', 'to-source'].each do |attr|
|
164
|
+
if rule[attr]
|
165
|
+
rule[attr].each do |addr|
|
166
|
+
return false unless IPAddress(addr).send "ipv#{ipv}?"
|
167
|
+
end
|
168
|
+
end
|
169
|
+
# return false if this ip version was manually disabled
|
170
|
+
return false unless rule['ip-version'].include? ipv if rule['ip-version']
|
171
|
+
end
|
172
|
+
true
|
173
|
+
end
|
174
|
+
|
175
|
+
# map iptables options
|
176
|
+
def parse_rule r
|
177
|
+
with_dashes = {}
|
178
|
+
result = []
|
179
|
+
|
180
|
+
r.each do |k, v|
|
181
|
+
# skip ip-version, since its not iptables option
|
182
|
+
with_dashes[k] = r[k].map { |value| "--#{k} #{value}" } unless k == 'ip-version'
|
183
|
+
end
|
184
|
+
with_dashes.values.each { |a| result = result.combine a }
|
185
|
+
|
186
|
+
# make sure the options are sorted in a way that works.
|
187
|
+
# protocol and match first, jump last
|
188
|
+
sorted = []
|
189
|
+
result.each do |r|
|
190
|
+
r = r.sort_by { |x| if x.include? '--match' then -1 else 1 end }
|
191
|
+
r = r.sort_by { |x| if x.include? '--protocol' then -1 else 1 end }
|
192
|
+
sorted.push(r.sort_by { |x| if x.include? '--jump' then 1 else -1 end })
|
193
|
+
end
|
194
|
+
|
195
|
+
sorted
|
196
|
+
end
|
266
197
|
end
|
267
198
|
|
data/lib/dust/version.rb
CHANGED
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
8
|
-
-
|
9
|
-
version: 0.
|
7
|
+
- 2
|
8
|
+
- 0
|
9
|
+
version: 0.2.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- kris kechagia
|
@@ -65,6 +65,18 @@ dependencies:
|
|
65
65
|
version: "0"
|
66
66
|
type: :runtime
|
67
67
|
version_requirements: *id004
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: ipaddress
|
70
|
+
prerelease: false
|
71
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
segments:
|
76
|
+
- 0
|
77
|
+
version: "0"
|
78
|
+
type: :runtime
|
79
|
+
version_requirements: *id005
|
68
80
|
description: when puppet and chef suck because you want to be in control and sprinkle just cannot do enough for you
|
69
81
|
email:
|
70
82
|
- kk@rndsec.net
|