asbestos 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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