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
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/asbestos.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'asbestos/metadata'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "asbestos"
8
+ spec.version = Asbestos::VERSION
9
+ spec.authors = ["Michael Shapiro"]
10
+ spec.email = ["koudelka@ryoukai.org"]
11
+ spec.description = %q{Asbestos is a declarative DSL for building firewall rules (iptables, at this point)}
12
+ spec.summary = %q{Declarative firewall(iptables) DSL.}
13
+ spec.homepage = Asbestos::HOMEPAGE
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec"
24
+
25
+ spec.add_dependency "system-getifaddrs", "~> 0.1.5"
26
+ end
data/bin/asbestos ADDED
@@ -0,0 +1,112 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'asbestos'
4
+ require 'optparse'
5
+ require 'pathname'
6
+
7
+ options = {
8
+ :host => Asbestos.hostname
9
+ }
10
+
11
+ parser = \
12
+ OptionParser.new do |o|
13
+ o.banner = "Usage: #{o.program_name} COMMAND [OPTIONS] FILES"
14
+ o.separator ""
15
+ o.separator "Commands"
16
+ o.separator " rules: generates firewall rules for the given host"
17
+ #o.separator " graph: geneates a graphviz visualization of your topology"
18
+ o.separator ""
19
+ o.separator "Options"
20
+
21
+ o.on("-h", "--host HOST","the host to generate rules for") do |host|
22
+ options[:host] = host
23
+ end
24
+
25
+ o.on("-D","--debug","dumps lots of information about your topology's hosts/templates/services") do
26
+ $ASBESTOSDEBUG = true
27
+ end
28
+
29
+ o.on("--debug-stderr","suppresses the line-numbered output on stderr") do
30
+ options[:stderr] = true
31
+ end
32
+
33
+ o.on("--help","displays this help information") do
34
+ puts o
35
+ end
36
+
37
+ o.separator ""
38
+ o.separator "Examples"
39
+ o.separator " Generate rules for the current host:"
40
+ o.separator " $ #{o.program_name} rules my_services.rb my_hosts.rb"
41
+ o.separator ""
42
+ o.separator " Generate rules for an arbitrary host:"
43
+ o.separator " $ #{o.program_name} rules --host some_host my_services.rb my_hosts.rb"
44
+ o.separator ""
45
+ o.separator "DSL Examples"
46
+ o.separator " Please see #{Asbestos::HOMEPAGE}"
47
+
48
+ o.parse!
49
+ end
50
+
51
+ command = \
52
+ if ARGV.first.end_with? '.rb'
53
+ 'rules'
54
+ else
55
+ ARGV.shift
56
+ end
57
+
58
+ files = ARGV
59
+
60
+ files.each do |file|
61
+ require Pathname.new(file).realpath.to_s
62
+ end
63
+
64
+ if $ASBESTOSDEBUG
65
+ $stderr.puts "-"*10 + "Asbestos Debug Enabled--" + "-"*10
66
+ $stderr.puts
67
+ $stderr.puts "Known RuleSets #{Asbestos::RuleSet.all.keys}"
68
+ $stderr.puts "Known Services #{Asbestos::Service.all.keys}"
69
+ $stderr.puts
70
+ $stderr.puts "Dumping all Hosts:"
71
+ $stderr.puts
72
+ Asbestos::Host.all.each do |_, host|
73
+ $stderr.puts "-"*20 + host.name.to_s + "-"*20
74
+ $stderr.puts host.debug
75
+ $stderr.puts host.rules
76
+ $stderr.puts
77
+ end
78
+ end
79
+
80
+
81
+
82
+ case command
83
+ when "rules"
84
+ if $ASBESTOSDEBUG
85
+ $stderr.puts '-' * 20
86
+ $stderr.puts "Running command 'rules' for host #{options[:host]}"
87
+ $stderr.puts '-' * 20
88
+ end
89
+
90
+ Host.all[options[:host]].tap do |host|
91
+ unless host
92
+ puts "Asbestos doesn't know about host '#{options[:host]}'!"
93
+ puts
94
+ puts "You've defined hosts:"
95
+ Host.all.keys.each do |name|
96
+ puts " - #{name}"
97
+ end
98
+ puts
99
+ puts "Try `asbestos rules --host #{Host.all.keys.first} #{files.join(' ')}`"
100
+ exit
101
+ end
102
+ host.rules.tap do |rules|
103
+ rules.each_with_index do |rule, line_number|
104
+ puts rule
105
+ $stderr.puts "#{line_number.to_s.rjust(rules.length.to_s.length)}: #{rule}" if options[:stderr]
106
+ end
107
+ end
108
+ end
109
+ #when "graph"
110
+ else
111
+ puts parser
112
+ end
@@ -0,0 +1,5 @@
1
+
2
+ host 'app_host' do
3
+ runs :ssh
4
+ runs :http
5
+ end
@@ -0,0 +1,72 @@
1
+ #
2
+ # This is just a large contrived example. :)
3
+ #
4
+
5
+ address :the_office, "1.2.3.4"
6
+
7
+
8
+
9
+ host_template "squidco_host" do
10
+ interface :loopback, :lo0
11
+ interface :internal, :bond0
12
+ interface :external, :bond1
13
+
14
+ accept_from_self
15
+ allow_related_established
16
+ icmp_protection allowed_from: :the_office
17
+ sanity_check
18
+
19
+ runs :ssh, on: :internal, port: 22022
20
+ runs :ssh, on: :external, port: 22022, from: :the_office
21
+ runs :monit, on: :external, from: :the_office
22
+ end
23
+
24
+ host_template "vps_host" do
25
+ interface :external, :eth0
26
+
27
+ runs :ssh, on: :external, port: 22022
28
+ runs :worker_status, on: :external, from: :the_office
29
+ end
30
+
31
+
32
+
33
+ service :worker_status do
34
+ port 1337
35
+ end
36
+
37
+
38
+
39
+ 2.times do |i|
40
+ squidco_host "loadbalancer_#{i}" do
41
+ group :loadbalancers
42
+
43
+ runs :http, on: :external
44
+ end
45
+ end
46
+
47
+ 5.times do |i|
48
+ squidco_host "app_#{i}" do
49
+ group :app_hosts
50
+
51
+ runs :http, on: :internal, from: {:loadbalancers => :internal}
52
+ runs :http, on: :external, from: :the_office
53
+ end
54
+ end
55
+
56
+ 3.times do |i|
57
+ squidco_host "db_#{i}" do
58
+ group :db_hosts
59
+
60
+ runs :mongodb, on: :internal, from: {:app_hosts => :internal}
61
+ end
62
+ end
63
+
64
+ squidco_host "background_queue" do
65
+ runs :redis, on: :external, from: [:the_office, {:background_workers => :external}]
66
+ end
67
+
68
+ 5.times do |i|
69
+ vps_host "worker_#{i}" do
70
+ group :background_workers
71
+ end
72
+ end
@@ -0,0 +1,18 @@
1
+
2
+ host 'app_host' do
3
+ interface :internal, :eth0
4
+ # 'internal' is an arbitrary name, you can make it anything you want
5
+
6
+ runs :ssh
7
+ runs :http
8
+ end
9
+
10
+ host 'db_host' do
11
+ runs :ssh
12
+ runs :mongodb, from: { Host['app_host'] => :internal }
13
+ end
14
+
15
+ host 'db_host_more_specific' do
16
+ runs :ssh
17
+ runs :mongodb, from: { Host['app_host'] => :internal }
18
+ end
@@ -0,0 +1,19 @@
1
+
2
+ host 'app_host' do
3
+ interface :internal, :eth0
4
+
5
+ runs :ssh
6
+ runs :http
7
+ end
8
+
9
+ host 'dax' do
10
+ interface :internal, :eth0
11
+ end
12
+
13
+ host 'db_host' do
14
+ interface :internal, :eth0
15
+
16
+ runs :ssh
17
+ runs :mongodb, on: :internal, from: { Host['app_host'] => :internal,
18
+ Host['dax'] => :internal }
19
+ end
@@ -0,0 +1,39 @@
1
+
2
+ host 'app_host_0' do
3
+ group :app_hosts
4
+
5
+ interface :internal, :eth0
6
+
7
+ runs :ssh
8
+ runs :http
9
+ end
10
+
11
+ host 'app_host_1' do
12
+ group :app_hosts
13
+
14
+ interface :internal, :eth0
15
+
16
+ runs :ssh
17
+ runs :http
18
+ end
19
+
20
+ host 'app_host_2' do
21
+ group :app_hosts
22
+
23
+ interface :internal, :eth0
24
+
25
+ runs :ssh
26
+ runs :http
27
+ end
28
+
29
+ host 'dax' do
30
+ interface :internal, :eth0
31
+ end
32
+
33
+ host 'db_host' do
34
+ interface :internal, :eth0
35
+
36
+ runs :ssh
37
+ runs :mongodb, on: :internal, from: { :app_hosts => :internal,
38
+ Host['dax'] => :internal }
39
+ end
@@ -0,0 +1,29 @@
1
+
2
+ host_template 'app_host' do
3
+ group :app_hosts
4
+
5
+ interface :internal, :eth0
6
+
7
+ runs :ssh
8
+ runs :http
9
+ end
10
+
11
+ 0.upto(2) do |i|
12
+ app_host "app_host_#{i}"
13
+ end
14
+
15
+ app_host 'app_host_3' do
16
+ runs :nfs
17
+ end
18
+
19
+ host 'dax' do
20
+ interface :internal, :eth0
21
+ end
22
+
23
+ host 'db_host' do
24
+ interface :internal, :eth0
25
+
26
+ runs :ssh
27
+ runs :mongodb, on: :internal, from: { :app_hosts => :internal,
28
+ Host['dax'] => :internal }
29
+ end
@@ -0,0 +1,7 @@
1
+
2
+ address :load_balancers, ['lb0.myprovider.com', 'lb1.myprovider.com']
3
+ address :monitoring, 'pinger.monitoringservice.com'
4
+
5
+ host 'app_host' do
6
+ runs :http, from: [:load_balancers, :monitoring]
7
+ end
@@ -0,0 +1,19 @@
1
+
2
+ host 'dax' do
3
+ interface :external, :eth0 #=> address is "dax_external"
4
+ interface :dmz, [:eth1, :eth2] #=> addresses are "dax_dmz_eth1" and "dax_dmz_eth2"
5
+
6
+ runs :ssh, from: {Host['kira'] => :external}
7
+ end
8
+
9
+
10
+ host 'kira' do
11
+ group :developers
12
+
13
+ interface :external, :eth3 do |host|
14
+ [host.groups.join, host.name, 'foo'].join('_')
15
+ end
16
+ #=> address is "developers_kira_foo"
17
+
18
+ interface :internal, :eth4, 'bar' #=> address is "bar"
19
+ end
@@ -0,0 +1,9 @@
1
+
2
+ service :my_service do
3
+ ports 1337
4
+ protocols :tcp, :udp
5
+ end
6
+
7
+ host 'app_host' do
8
+ runs :my_service
9
+ end
@@ -0,0 +1,37 @@
1
+ rule_set :icmp_protection do
2
+ accept :chain => :output,
3
+ :protocol => :icmp,
4
+ :icmp_type => 'echo-request',
5
+ :comment => "allow us to ping others"
6
+
7
+ accept :protocol => :icmp,
8
+ :icmp_type => 'echo-reply',
9
+ :comment => "allow us to receive ping responses"
10
+
11
+
12
+ interfaces[:external].each do |interface|
13
+ from_each_address(allowed_from) do |address|
14
+ accept :protocol => :icmp,
15
+ :icmp_type => 'echo-request',
16
+ :interface => interface,
17
+ :remote_address => address,
18
+ :limit => '22s',
19
+ :comment => "allow icmp from #{address}"
20
+ end
21
+
22
+ drop :protocol => :icmp,
23
+ :interface => interface,
24
+ :comment => "drop any icmp packets that haven't been explicitly allowed"
25
+ end
26
+ end
27
+
28
+ address :monitoring, 'pinger.monitoringservice.com'
29
+
30
+ host 'app_host' do
31
+ interface :external, ['eth1', 'eth1:0']
32
+
33
+ icmp_protection allowed_from: :monitoring
34
+
35
+ runs :ssh
36
+ end
37
+
@@ -0,0 +1,8 @@
1
+
2
+ rule_set :creates_chaos do
3
+ command "-A INPUT -m statistic --mode random --probability 0.01 -j REJECT --reject-with host-unreach"
4
+ end
5
+
6
+ host 'app_host' do
7
+ creates_chaos
8
+ end
data/lib/asbestos.rb ADDED
@@ -0,0 +1,108 @@
1
+ require 'asbestos/metadata'
2
+
3
+ require 'socket'
4
+ require 'system/getifaddrs'
5
+
6
+ require 'forwardable'
7
+
8
+
9
+ module Asbestos
10
+
11
+ def self.hostname
12
+ Socket.gethostname[/[^.]*/]
13
+ end
14
+
15
+ def self.interfaces
16
+ System.get_ifaddrs
17
+ end
18
+
19
+ def self.os
20
+ case
21
+ when RUBY_PLATFORM[/linux/i]
22
+ :linux
23
+ when RUBY_PLATFORM[/darwin/i]
24
+ :darwin
25
+ end
26
+ end
27
+
28
+ def self.firewall
29
+ case os
30
+ when :linux
31
+ Asbestos::Firewall::IPTables
32
+ when :darwin
33
+ #FIXME
34
+ Asbestos::Firewall::IPTables
35
+ end
36
+ end
37
+
38
+ def self.reset!
39
+ [
40
+ Host.all,
41
+ Host.groups,
42
+ HostTemplate.all,
43
+ Address.all,
44
+ RuleSet.all,
45
+ Service.all,
46
+ ].each do |collection|
47
+ collection.delete_if {|_| true}
48
+ end
49
+ end
50
+
51
+ #
52
+ # Didn't want to monkeypatch the Hash class.
53
+ #
54
+ def self.with_indifferent_access!(hash)
55
+ class << hash
56
+ def [](key)
57
+ fetch key.to_sym
58
+ rescue KeyError # key not found
59
+ nil
60
+ end
61
+
62
+ def []=(key, value)
63
+ store key.to_sym, value
64
+ end
65
+ end
66
+ end
67
+
68
+ module ClassCollection
69
+ def self.included(base)
70
+ base.extend ClassMethods
71
+ end
72
+
73
+ module ClassMethods
74
+ def class_collection(name, base = self)
75
+ # this is a little nasty, the 'name' variable isn't available
76
+ # in the scope of the eigenclass, so we have to class_eval
77
+ # the eigenclass
78
+ (class << base; self; end).instance_eval do
79
+ extend ::Forwardable
80
+
81
+ attr_accessor name
82
+ def_delegators name, :[], :[]= if name == :all
83
+ end
84
+
85
+ Hash.new.tap do |hash|
86
+ Asbestos.with_indifferent_access! hash
87
+ base.instance_variable_set "@#{name}", hash
88
+ end
89
+ end
90
+ end
91
+
92
+ end
93
+
94
+ end
95
+
96
+
97
+
98
+
99
+ require 'asbestos/rule_set'
100
+ require 'asbestos/service'
101
+ require 'asbestos/host_template'
102
+ require 'asbestos/host'
103
+ require 'asbestos/address'
104
+ require 'asbestos/dsl'
105
+
106
+ %w{firewalls services rule_sets}.each do |dir|
107
+ Dir["#{File.dirname(__FILE__)}/asbestos/#{dir}/*.rb"].each { |f| require File.expand_path(f) }
108
+ end