asbestos 0.0.1

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.
Files changed (52) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +18 -0
  3. data/.rspec +3 -0
  4. data/Gemfile +10 -0
  5. data/Guardfile +9 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +461 -0
  8. data/Rakefile +1 -0
  9. data/asbestos.gemspec +26 -0
  10. data/bin/asbestos +112 -0
  11. data/examples/0_simple.rb +5 -0
  12. data/examples/10_kitchen_sink.rb +72 -0
  13. data/examples/1_two_hosts.rb +18 -0
  14. data/examples/2_accept_from_many.rb +19 -0
  15. data/examples/3_groups.rb +39 -0
  16. data/examples/4_host_templates.rb +29 -0
  17. data/examples/5_static_addresses.rb +7 -0
  18. data/examples/6_interface_addresses.rb +19 -0
  19. data/examples/7_services.rb +9 -0
  20. data/examples/8_rule_sets.rb +37 -0
  21. data/examples/9_literal_commands.rb +8 -0
  22. data/lib/asbestos.rb +108 -0
  23. data/lib/asbestos/address.rb +8 -0
  24. data/lib/asbestos/dsl.rb +40 -0
  25. data/lib/asbestos/firewalls/iptables.rb +127 -0
  26. data/lib/asbestos/host.rb +244 -0
  27. data/lib/asbestos/host_template.rb +15 -0
  28. data/lib/asbestos/metadata.rb +4 -0
  29. data/lib/asbestos/rule_set.rb +131 -0
  30. data/lib/asbestos/rule_sets/accept_from_self.rb +19 -0
  31. data/lib/asbestos/rule_sets/allow_related_established.rb +5 -0
  32. data/lib/asbestos/rule_sets/icmp_protection.rb +28 -0
  33. data/lib/asbestos/rule_sets/sanity_check.rb +41 -0
  34. data/lib/asbestos/service.rb +86 -0
  35. data/lib/asbestos/services/chef.rb +4 -0
  36. data/lib/asbestos/services/cube.rb +14 -0
  37. data/lib/asbestos/services/http.rb +8 -0
  38. data/lib/asbestos/services/memcached.rb +4 -0
  39. data/lib/asbestos/services/mongodb.rb +28 -0
  40. data/lib/asbestos/services/monit.rb +4 -0
  41. data/lib/asbestos/services/mysql.rb +4 -0
  42. data/lib/asbestos/services/nfs.rb +5 -0
  43. data/lib/asbestos/services/redis.rb +4 -0
  44. data/lib/asbestos/services/ssh.rb +4 -0
  45. data/spec/asbestos/address_spec.rb +25 -0
  46. data/spec/asbestos/firewalls/iptables_spec.rb +179 -0
  47. data/spec/asbestos/host_spec.rb +173 -0
  48. data/spec/asbestos/host_template_spec.rb +32 -0
  49. data/spec/asbestos/rule_set_spec.rb +55 -0
  50. data/spec/asbestos/service_spec.rb +60 -0
  51. data/spec/spec_helper.rb +20 -0
  52. metadata +159 -0
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ ZDQ2N2U5NmUwMzg1NDdjMTBlNjYxNzM1MjNlY2E3MGM1MTBkNTM3ZA==
5
+ data.tar.gz: !binary |-
6
+ ZDVhODYyOGIxMzRmMDcwOGY3ODE4MDExZmU4YWE0OGUwNDg2MzJmNA==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ MzQ4MjIwOTYxYTFlY2ZmYTk2NjljOWJjZjM3MWJhMmFhZDYwNjE5OWIyMDlh
10
+ MzAxZTZjMDA1Zjk1Mzg0YzY4M2M5NjRiMDRkMjU3ODRmNzVkZmJhMjUyZmZm
11
+ NjMxNzEyYjBlMjRkNWRlN2I2Zjg4MGNlZTU3NTUwYjhlN2I1NTE=
12
+ data.tar.gz: !binary |-
13
+ ZWI5MGVlZjY4YjY3YzZmNGExMGE5YzUyN2JiNWI0N2VlMjAxNjBiM2MxNmU4
14
+ YzJiZjU0ZGU2MzI4ZjYyZWExNDE0NWU1MTY4OWViMjVjMzQ3ZGVhMGE0ZTA3
15
+ YTRlYTIxYWZhZDY4MDg4ZGExNGY2MDgxMjY0NmMzZDYzMmEzMGU=
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.swp
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --format documentation
3
+ --tty
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in asbestos.gemspec
4
+ gemspec
5
+
6
+ group :development, :test do
7
+ unless ENV["CI"]
8
+ gem "guard-rspec"
9
+ end
10
+ end
data/Guardfile ADDED
@@ -0,0 +1,9 @@
1
+ # vim:set filetype=ruby:
2
+ guard(
3
+ "rspec",
4
+ :all_after_pass => false,
5
+ :cli => "--fail-fast --tty") do
6
+
7
+ watch(%r{^spec/.+_spec\.rb$})
8
+ watch(%r{^lib/(.+)\.rb$}) { |match| "spec/#{match[1]}_spec.rb" }
9
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Michael Shapiro
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,461 @@
1
+ # Asbestos
2
+
3
+ Asbestos is a mostly-declarative high-level DSL for firewalls. Show Asbestos where your services run and how they connect and it'll generate your firewall rules for you.
4
+
5
+ Trying to maintain a set of interconnected firewall rules is pretty annoying, hopefully Asbestos can help.
6
+
7
+ At the moment, Asbestos only supports IPTables (the filter table, specifically), but it can be easily expanded for other firewall types.
8
+
9
+ ## Installation
10
+
11
+ Simply:
12
+
13
+ $ gem install asbestos
14
+
15
+ The `asbestos` executable should now be in your PATH.
16
+
17
+ ## Usage
18
+
19
+ Asbestos is Ruby at heart, you'll need to put your Asbestos configuration in files that end with `.rb`.
20
+
21
+ ### Quick Start
22
+
23
+ Check out the examples in the `examples` directory, generate firewall rules for any of the hosts like so:
24
+
25
+ `asbestos rules --host some_hostname_here asbestos_example.rb`
26
+
27
+ ### (not so) Quick Start
28
+
29
+ Let's run through a basic example then expand it out as we go.
30
+
31
+ Here's a simple http server, let's call it "app_host":
32
+
33
+ ```
34
+ host 'app_host' do
35
+ runs :ssh
36
+ runs :http
37
+ end
38
+ ```
39
+
40
+ running `asbestos`, that gives us:
41
+
42
+ ```
43
+ # Generated by Asbestos at 2013-06-16 20:51:13 UTC for app_host
44
+ # http://www.github.com/koudelka/asbestos
45
+ *filter
46
+ :INPUT ACCEPT [0:0]
47
+ :OUTPUT ACCEPT [0:0]
48
+ # Begin [ssh]
49
+ -A INPUT -j ACCEPT -p tcp -m state --state NEW --dport 22 -m comment --comment "allow ssh(tcp port 22) from anyone"
50
+ # End [ssh]
51
+
52
+ # Begin [http]
53
+ -A INPUT -j ACCEPT -p tcp -m state --state NEW --dport 80 -m comment --comment "allow http(tcp port 80) from anyone"
54
+ # End [http]
55
+
56
+ -A INPUT -j DROP -m comment --comment "drop packets that haven't been explicitly accepted"
57
+ COMMIT
58
+ # Asbestos completed at 2013-06-16 20:51:13 UTC
59
+ ```
60
+
61
+ Asbestos has opened tcp port 80 and port 22 to everyone, on all of `app_host`'s interfaces.
62
+
63
+ (The rest of the guide excludes the pre/postamble for brevity).
64
+
65
+
66
+ Let's add another host, our app server needs to talk to our database server, which runs MongoDB. The database server should only listen for traffic from `app_host` and nobody else. To do that, we'll need to tell Asbestos a little about our network interfaces, here's `app_host` again with an interface declared.
67
+
68
+ ```
69
+ host 'app_host' do
70
+ interface :internal, :eth0
71
+ # 'internal' is an arbitrary name, you can make it anything you want
72
+
73
+ runs :ssh
74
+ runs :http
75
+ end
76
+
77
+ host 'db_host' do
78
+ runs :ssh
79
+ runs :mongodb, from: { Host['app_host'] => :internal }
80
+ end
81
+ ```
82
+
83
+ Here's the rule that Asbestos generates for the `mongodb` service on `db_host`:
84
+
85
+ ```
86
+ # Begin [mongodb]
87
+ -A INPUT -j ACCEPT -p tcp -s app_host_internal -m state --state NEW --dport 27017 -m comment --comment "allow mongodb(tcp port 27017) from app_host:eth0 (internal)"
88
+ # End [mongodb]
89
+ ```
90
+
91
+ You'll notice that Asbestos makes an assumption about the address of `app_host`'s "internal" interface, we'll cover that later (don't worry, you can change it).
92
+
93
+ Now `mongodb` is only accepting connections from `app_host`'s internal interface, we can lock it down a little more by requiring that traffic to be through `db_host`'s own internal interface (assuming packets are routable between the two).
94
+
95
+ ```
96
+ host 'db_host' do
97
+ interface :internal, :eth0
98
+
99
+ runs :ssh
100
+ runs :mongodb, on: :internal, from: { Host['app_host'] => :internal }
101
+ end
102
+ ```
103
+
104
+ Asbestos will now give the following more stringent rule for `mongodb` on `db_host`:
105
+
106
+ ```
107
+ # Begin [mongodb]
108
+ -A INPUT -j ACCEPT -i eth0 -p tcp -s app_host_internal -m state --state NEW --dport 27017 -m comment --comment "allow mongodb(tcp port 27017) from app_host:eth0 (internal) on eth0"
109
+ # End [mongodb]
110
+ ```
111
+
112
+ Not only does our app host need to talk to the database, but we've also got a developer that needs to make db dumps, her machine is named `dax`, `mongodb` needs to listen for her connections.
113
+
114
+ ```
115
+ host 'dax' do
116
+ interface :internal, :eth0
117
+ end
118
+
119
+ host 'db_host' do
120
+ interface :internal, :eth0
121
+
122
+ runs :ssh
123
+ runs :mongodb, on: :internal, from: { Host['app_host'] => :internal,
124
+ Host['dax'] => :internal }
125
+ end
126
+ ```
127
+
128
+ Resulting in the additional of a single rule for `dax`.
129
+
130
+ ```
131
+ # Begin [mongodb]
132
+ -A INPUT -j ACCEPT -i eth0 -p tcp -s app_host_internal -m state --state NEW --dport 27017 -m comment --comment "allow mongodb(tcp port 27017) from app_host:eth0 (internal) on eth0"
133
+ -A INPUT -j ACCEPT -i eth0 -p tcp -s dax_internal -m state --state NEW --dport 27017 -m comment --comment "allow mongodb(tcp port 27017) from dax:eth0 (internal) on eth0"
134
+ # End [mongodb]
135
+ ```
136
+
137
+ #### Groups
138
+
139
+ It's pretty unlikely that we'll only have two hosts in our topology, let's add some more hosts and explore how Asbestos' groups work. We'll add a couple app hosts and start using the `group` DSL call.
140
+
141
+ ```
142
+ host 'app_host_0' do
143
+ group :app_hosts
144
+
145
+ interface :internal, :eth0
146
+
147
+ runs :ssh
148
+ runs :http
149
+ end
150
+
151
+ host 'app_host_1' do
152
+ group :app_hosts
153
+
154
+ interface :internal, :eth0
155
+
156
+ runs :ssh
157
+ runs :http
158
+ end
159
+
160
+ host 'app_host_2' do
161
+ group :app_hosts
162
+
163
+ interface :internal, :eth0
164
+
165
+ runs :ssh
166
+ runs :http
167
+ end
168
+
169
+ host 'dax' do
170
+ interface :internal, :eth0
171
+ end
172
+
173
+ host 'db_host' do
174
+ interface :internal, :eth0
175
+
176
+ runs :ssh
177
+ runs :mongodb, on: :internal, from: { :app_hosts => :internal,
178
+ Host['dax'] => :internal }
179
+ end
180
+ ```
181
+
182
+ Our three app hosts belong to the group called `app_hosts`. Now `mongodb` listens for connections from any host in that group, Asbestos gives the following rules for the `mongodb`:
183
+
184
+ ```
185
+ # Begin [mongodb]
186
+ -A INPUT -j ACCEPT -i eth0 -p tcp -s app_host_0_internal -m state --state NEW --dport 27017 -m comment --comment "allow mongodb(tcp port 27017) from app_host_0:eth0 (internal) on eth0"
187
+ -A INPUT -j ACCEPT -i eth0 -p tcp -s app_host_1_internal -m state --state NEW --dport 27017 -m comment --comment "allow mongodb(tcp port 27017) from app_host_1:eth0 (internal) on eth0"
188
+ -A INPUT -j ACCEPT -i eth0 -p tcp -s app_host_2_internal -m state --state NEW --dport 27017 -m comment --comment "allow mongodb(tcp port 27017) from app_host_2:eth0 (internal) on eth0"
189
+ -A INPUT -j ACCEPT -i eth0 -p tcp -s dax_internal -m state --state NEW --dport 27017 -m comment --comment "allow mongodb(tcp port 27017) from dax:eth0 (internal) on eth0"
190
+ # End [mongodb]
191
+ ```
192
+
193
+ ### Host Templates
194
+ So we've got groups now, but wrangling more than a couple machines with common attributes is pretty ungainly, let's DRY up the app hosts with a `host_template` and a little Ruby.
195
+
196
+ ```
197
+ host_template 'app_host' do
198
+ group :app_hosts
199
+
200
+ interface :internal, :eth0
201
+
202
+ runs :ssh
203
+ runs :http
204
+ end
205
+
206
+ 0.upto(2) do |i|
207
+ app_host "app_host_#{i}"
208
+ end
209
+
210
+
211
+ host 'dax' do
212
+ interface :internal, :eth0
213
+ end
214
+
215
+ host 'db_host' do
216
+ interface :internal, :eth0
217
+
218
+ runs :ssh
219
+ runs :mongodb, on: :internal, from: { :app_hosts => :internal,
220
+ Host['dax'] => :internal }
221
+ end
222
+ ```
223
+
224
+ The `host_template` DSL call has created a new `app_host` DSL call for us, it's just like the `host` DSL call. You can pass these custom hosts blocks to extend them further than their template, too:
225
+
226
+ ```
227
+ app_host 'app_host_3' do
228
+ runs :nfs
229
+ end
230
+ ```
231
+
232
+ ### Arbitrary Addresses
233
+
234
+ Sometimes you need to allow traffic from external addresses, you could do that with a `host` entity, but that's a lot of overhead to get a simple address into the mix. To make it dead simple, Asbestos provides the `address` DSL call to maintain a list of named addresses.
235
+
236
+ ```
237
+ address :load_balancers, ['lb0.myprovider.com', 'lb1.myprovider.com']
238
+ address :monitoring, 'pinger.monitoringservice.com'
239
+
240
+ host 'app_host' do
241
+ runs :http, from: [:load_balancers, :monitoring]
242
+ end
243
+ ```
244
+
245
+ Results in:
246
+
247
+ ```
248
+ # Begin [http]
249
+ -A INPUT -j ACCEPT -p tcp -s lb0.myprovider.com -m state --state NEW --dport 80 -m comment --comment "allow http(tcp port 80) from lb0.myprovider.com"
250
+ -A INPUT -j ACCEPT -p tcp -s lb1.myprovider.com -m state --state NEW --dport 80 -m comment --comment "allow http(tcp port 80) from lb1.myprovider.com"
251
+ -A INPUT -j ACCEPT -p tcp -s pinger.monitoringservice.com -m state --state NEW --dport 80 -m comment --comment "allow http(tcp port 80) from pinger.monitoringservice.com"
252
+ # End [http]
253
+ ```
254
+
255
+ ### Interface Addresses
256
+
257
+ By default, Asbestos sets interface addresses to sensible defaults, like so:
258
+
259
+ ```
260
+ host 'kira' do
261
+ interface :external, :eth0 #=> address is "kira_external"
262
+
263
+ interface :dmz, [:eth1, :eth2] #=> addresses are "kira_dmz_eth1" and "kira_dmz_eth2"
264
+ end
265
+ ```
266
+
267
+ You're more than welcome to override Asbestos' defaults:
268
+
269
+ ```
270
+ host 'kira' do
271
+ group :developers
272
+
273
+ interface :external, :eth3 do |host|
274
+ [host.group, host.name, 'foo'].join('_')
275
+ end
276
+ #=> address is "developers_kira_foo"
277
+
278
+ interface :internal, :eth4, 'bar' #=> address is "bar"
279
+ end
280
+ ```
281
+
282
+ Of course, these addresses will need to be resolvable by the hosts executng the rules (via DNS or `/etc/hosts`).
283
+
284
+ ### Services
285
+
286
+ For the most part, Services are simply defined as a set of port numbers (or names, via `/etc/services`), like so:
287
+
288
+ ```
289
+ service :nfs do
290
+ ports :nfs, :sunrpc
291
+ protocols :tcp, :udp
292
+ end
293
+
294
+ host 'myhost' do
295
+ runs :nfs
296
+ end
297
+ ```
298
+
299
+ Results in:
300
+
301
+ ```
302
+ # Begin [nfs]
303
+ -A INPUT -j ACCEPT -p udp -m state --state NEW --dport nfs -m comment --comment "allow nfs(udp port nfs) from anyone"
304
+ -A INPUT -j ACCEPT -p udp -m state --state NEW --dport sunrpc -m comment --comment "allow nfs(udp port sunrpc) from anyone"
305
+ -A INPUT -j ACCEPT -p tcp -m state --state NEW --dport nfs -m comment --comment "allow nfs(tcp port nfs) from anyone"
306
+ -A INPUT -j ACCEPT -p tcp -m state --state NEW --dport sunrpc -m comment --comment "allow nfs(tcp port sunrpc) from anyone"
307
+ # End [nfs]
308
+ ```
309
+
310
+ Asbestos comes with a small set of service definitions, located in `lib/asbestos/services`.
311
+
312
+
313
+ ### RuleSets
314
+
315
+ Simply opening ports between hosts is usually not sufficient for most needs, that's where `RuleSets` come in. `RuleSets` are named chunks of Asbestos/Ruby that are executed in the context of the host. They can easily expose lower level firewall functionality in a clean way. For example, the included `icmp_protection` ruleset:
316
+
317
+ ```
318
+ rule_set :icmp_protection do
319
+
320
+ accept :chain => :output,
321
+ :protocol => :icmp,
322
+ :icmp_type => 'echo-request',
323
+ :comment => "allow us to ping others"
324
+
325
+ accept :protocol => :icmp,
326
+ :icmp_type => 'echo-reply',
327
+ :comment => "allow us to receive ping responses"
328
+
329
+
330
+ interfaces[:external].each do |interface|
331
+ from_each_address(allowed_from) do |address|
332
+ accept :protocol => :icmp,
333
+ :icmp_type => 'echo-request',
334
+ :interface => interface,
335
+ :remote_address => address,
336
+ :limit => '22s',
337
+ :comment => "allow icmp from #{address}"
338
+ end
339
+
340
+ drop :protocol => :icmp,
341
+ :interface => interface,
342
+ :comment => "drop any icmp packets that haven't been explicitly allowed"
343
+ end
344
+ end
345
+
346
+ address :monitoring, 'pinger.monitoringservice.com'
347
+
348
+ host 'kira' do
349
+ interface :external, ['eth1', 'eth1:0']
350
+
351
+ icmp_protection allowed_from: :monitoring
352
+ end
353
+
354
+ ```
355
+
356
+ Results in:
357
+
358
+ ```
359
+ # Begin [icmp_protection]
360
+ -A OUTPUT -j ACCEPT -p icmp --icmp-type echo-request -m comment --comment "allow us to ping others"
361
+ -A INPUT -j ACCEPT -p icmp --icmp-type echo-reply -m comment --comment "allow us to receive ping responses"
362
+ -A INPUT -j ACCEPT -i eth1 -p icmp -s pinger.monitoringservice.com -m limit --limit 22s --icmp-type echo-request -m comment --comment "allow icmp from pinger.monitoringservice.com on eth1"
363
+ -A INPUT -j DROP -i eth1 -p icmp -m comment --comment "drop any icmp packets that haven't been explicitly allowed on eth1"
364
+ -A INPUT -j ACCEPT -i eth1:0 -p icmp -s pinger.monitoringservice.com -m limit --limit 22s --icmp-type echo-request -m comment --comment "allow icmp from pinger.monitoringservice.com on eth1:0"
365
+ -A INPUT -j DROP -i eth1:0 -p icmp -m comment --comment "drop any icmp packets that haven't been explicitly allowed on eth1:0"
366
+ # End [icmp_protection]
367
+ ```
368
+
369
+ The functions available to RuleSets are translated to firewall rules by the appropriate firewall module, just `lib/asbestos/firewalls/iptables.rb` for now.
370
+
371
+ Asbestos comes with a small number of rule sets , located in `lib/asbestos/rule_sets`.
372
+
373
+ ### Overriding Defaults
374
+
375
+ `Services` and `RuleSets` are akin to classes, when a host includes them, a new instance is created. The arguments of any method sent to them (that isn't a firewall module method) get stored as an attribute of the instance and become accessible as a DSL method, this makes `RulesSets` and `Services` easy to write in a DSL-y manner. For example, the `allowed_from` attribute in the `icmp_protection` example above, or the `port` attribute below:
376
+
377
+ ```
378
+ host 'kira' do
379
+ runs :ssh, port: 22022
380
+ end
381
+ ```
382
+
383
+ becomes:
384
+
385
+ ```
386
+ # Begin [ssh]
387
+ -A INPUT -j ACCEPT -p tcp -m state --state NEW --dport 22022 -m comment --comment "allow ssh(tcp port 22022) from anyone"
388
+ # End [ssh]
389
+ ```
390
+
391
+ ### Literal Commands
392
+
393
+ If you just want to add a literal firewall command, you can use the `command` DSL call inside of a `rule_set`:
394
+
395
+ ```
396
+ rule_set :creates_chaos do
397
+ command "-A INPUT -m statistic --mode random --probability 0.01 -j REJECT --reject-with host-unreach"
398
+ end
399
+
400
+ host 'kira' do
401
+ creates_chaos
402
+ end
403
+ ```
404
+
405
+ as expected, will give you:
406
+
407
+ ```
408
+ # Begin [creates_chaos]
409
+ -A INPUT -m statistic --mode random --probability 0.01 -j REJECT --reject-with host-unreach
410
+ # End [creates_chaos]
411
+ ```
412
+
413
+ If you find yourself needing to use `command`, please consider expanding the functionality of `Asbestos::Firewall::IPTables#rule` instead.
414
+
415
+ ### Generating Rules
416
+
417
+ **Development**
418
+
419
+ While you're developing your rules, you can generate the rules for an arbitrary host like so:
420
+
421
+ `asbestos rules --host some_hostname_here asbestos_example.rb`
422
+
423
+ **Production**
424
+
425
+ When you're ready to run your rules against your hosts, you'll need to send the rules from `asbestos`'s stdout somewhere. You can pipe it directly to `iptables-restore`:
426
+
427
+ `asbestos rules asbestos_example.rb | iptables-restore`
428
+
429
+ (Calling the `asbestos` executable without `--host` will generate the corresponding rules for the current host)
430
+
431
+ However, I suggest you send the rules to a file somewhere in `/etc` and have your `if-up` process automatically apply them when the interface is brought up. Your flavor of linux may already have this mechanism built-in, try looking in `/etc/network/if-up.d`.
432
+
433
+ The `asbestos` executable can output your firewall rules on both stdout and stderr, with a slight difference, the rules that are printed on stderr contain line numbers. If there's a problem with your rules, `iptables-restore` will give you the line number of the error. To turn on the stderr output, use the `--debug-stderr` flag. You use this flag to pipe stdout directly to `iptables-restore` and view the rules in your terminal at the same time:
434
+
435
+ `asbestos rules --debug-stderr asbestos_example.rb | iptables-restore`
436
+
437
+
438
+ ### Keeping Things Organized
439
+
440
+ It's probably a good idea to keep various parts of your Asbestos configuration in different files, it's up to you how you do it.
441
+
442
+ You can use Ruby's `require` functionality to assemble your files, or you can provide all the file names to the `asbestos` executable.
443
+
444
+ ### Caveats
445
+
446
+ - As with any framework, you may be sacrificing some degree of low-level control. Asbestos was designed to make firewalls for large networks a little saner, which means a degree of standardization. If you find yourself wishing for more control, try expanding the `#rule` method of the `Asbestos::Firewall::IPTables` module.
447
+
448
+ - Of course, you are a fully qualified operator, conversant in your firewall of choice. You'll read all the rules that Asbestos generates before running them on your machines. Shit happens, it's best to give things a once over before making a mistake. :)
449
+
450
+ - This is just a tool I built to make my life easier, I hope it helps you too. If you take issue with how Asbestos does things, please drop me a line, I'd love hear your thoughts so we can make a better tool for everyone. The infighting in OSS that comes from hiding behind a computer is not cool, let's be excellent to eachother and do kickass work. :)
451
+
452
+ ## Contributing
453
+
454
+ Hug and a beer for anyone submitting pull requests!
455
+
456
+ 1. Fork it
457
+ 2. Fix my bugs or add new features with tests (please). :)
458
+ 3. Create your feature branch (`git checkout -b my-new-feature`)
459
+ 4. Commit your changes (`git commit -am 'Add some feature'`)
460
+ 5. Push to the branch (`git push origin my-new-feature`)
461
+ 6. Create new Pull Request