ipscriptables 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +17 -0
  3. data/.rubocop.yml +15 -0
  4. data/.travis.yml +10 -0
  5. data/CHANGELOG.md +6 -0
  6. data/CONTRIBUTING.md +43 -0
  7. data/Gemfile +13 -0
  8. data/LICENSE +20 -0
  9. data/README.md +54 -0
  10. data/Rakefile +22 -0
  11. data/bin/ipscriptables +6 -0
  12. data/cookbook/.gitignore +2 -0
  13. data/cookbook/.kitchen.yml +28 -0
  14. data/cookbook/Berksfile +6 -0
  15. data/cookbook/README.md +53 -0
  16. data/cookbook/attributes/default.rb +3 -0
  17. data/cookbook/chefignore +96 -0
  18. data/cookbook/libraries/default.rb +35 -0
  19. data/cookbook/metadata.rb +9 -0
  20. data/cookbook/providers/rules.rb +21 -0
  21. data/cookbook/recipes/default.rb +10 -0
  22. data/cookbook/recipes/load.rb +8 -0
  23. data/cookbook/resources/rules.rb +17 -0
  24. data/cookbook/test/cookbooks/ipscriptables-test/#metadata.rb# +8 -0
  25. data/cookbook/test/cookbooks/ipscriptables-test/metadata.rb +11 -0
  26. data/cookbook/test/cookbooks/ipscriptables-test/recipes/default.rb +23 -0
  27. data/cookbook/test/cookbooks/ipscriptables-test/recipes/prepare.rb +5 -0
  28. data/cookbook/test/data/.gitignore +1 -0
  29. data/cookbook/test/integration/default/bats/default.bats +9 -0
  30. data/doc/iptables-switches.txt +342 -0
  31. data/ipscriptables.gemspec +38 -0
  32. data/lib/ipscriptables.rb +14 -0
  33. data/lib/ipscriptables/chain.rb +83 -0
  34. data/lib/ipscriptables/cli.rb +19 -0
  35. data/lib/ipscriptables/helpers.rb +39 -0
  36. data/lib/ipscriptables/pretty_print.rb +58 -0
  37. data/lib/ipscriptables/rule.rb +95 -0
  38. data/lib/ipscriptables/ruleset.rb +103 -0
  39. data/lib/ipscriptables/ruleset/class_methods.rb +67 -0
  40. data/lib/ipscriptables/runtime.rb +97 -0
  41. data/lib/ipscriptables/table.rb +77 -0
  42. data/lib/ipscriptables/version.rb +5 -0
  43. data/spec/fixtures/clyhq.txt +40 -0
  44. data/spec/fixtures/docker-plus.txt +31 -0
  45. data/spec/fixtures/drumknott.txt +67 -0
  46. data/spec/fixtures/falcor.txt +39 -0
  47. data/spec/fixtures/ghq.txt +102 -0
  48. data/spec/fixtures/ip6tables-empty.txt +7 -0
  49. data/spec/fixtures/only-docker-c.txt +23 -0
  50. data/spec/fixtures/only-docker.txt +23 -0
  51. data/spec/fixtures/only_docker.rb +22 -0
  52. data/spec/fixtures/runtime.rb +7 -0
  53. data/spec/fixtures/runtime2.rb +16 -0
  54. data/spec/ipscriptables/dsl_spec.rb +74 -0
  55. data/spec/ipscriptables/helpers_spec.rb +58 -0
  56. data/spec/ipscriptables/rule_spec.rb +41 -0
  57. data/spec/ipscriptables/ruleset/class_methods_spec.rb +52 -0
  58. data/spec/ipscriptables/ruleset_spec.rb +199 -0
  59. data/spec/ipscriptables/runtime_spec.rb +227 -0
  60. data/spec/ipscriptables/table_spec.rb +32 -0
  61. data/spec/ipscriptables/version_spec.rb +12 -0
  62. data/spec/spec_helper.rb +60 -0
  63. metadata +350 -0
@@ -0,0 +1,7 @@
1
+ # Generated by ip6tables-save v1.4.12 on Thu Feb 20 14:32:49 2014
2
+ *filter
3
+ :INPUT ACCEPT [128:11760]
4
+ :FORWARD ACCEPT [0:0]
5
+ :OUTPUT ACCEPT [75:12168]
6
+ COMMIT
7
+ # Completed on Thu Feb 20 14:32:49 2014
@@ -0,0 +1,23 @@
1
+ # Generated by iptables-save v1.4.12 on Wed Feb 19 13:37:35 2014
2
+ *filter
3
+ :INPUT ACCEPT [211:14626]
4
+ :FORWARD ACCEPT [0:0]
5
+ :OUTPUT ACCEPT [122:11280]
6
+ [1:2] -A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
7
+ [1:2] -A FORWARD -i docker0 ! -o docker0 -j ACCEPT
8
+ [1:2] -A FORWARD -i docker0 -o docker0 -j ACCEPT
9
+ COMMIT
10
+ # Completed on Wed Feb 19 13:37:35 2014
11
+ # Generated by iptables-save v1.4.12 on Wed Feb 19 13:37:35 2014
12
+ *nat
13
+ :PREROUTING ACCEPT [5:1208]
14
+ :INPUT ACCEPT [5:1208]
15
+ :OUTPUT ACCEPT [42:3215]
16
+ :POSTROUTING ACCEPT [42:3215]
17
+ :DOCKER - [0:0]
18
+ [1:2] -A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
19
+ [1:2] -A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
20
+ [1:2] -A POSTROUTING -s 172.17.0.0/16 ! -d 172.17.0.0/16 -j MASQUERADE
21
+ [1:2] -A POSTROUTING -s 10.0.3.0/24 ! -d 10.0.3.0/24 -j MASQUERADE
22
+ COMMIT
23
+ # Completed on Wed Feb 19 13:37:35 2014
@@ -0,0 +1,23 @@
1
+ # Generated by iptables-save v1.4.12 on Wed Feb 19 13:37:35 2014
2
+ *filter
3
+ :INPUT ACCEPT [211:14626]
4
+ :FORWARD ACCEPT [0:0]
5
+ :OUTPUT ACCEPT [122:11280]
6
+ -A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
7
+ -A FORWARD -i docker0 ! -o docker0 -j ACCEPT
8
+ -A FORWARD -i docker0 -o docker0 -j ACCEPT
9
+ COMMIT
10
+ # Completed on Wed Feb 19 13:37:35 2014
11
+ # Generated by iptables-save v1.4.12 on Wed Feb 19 13:37:35 2014
12
+ *nat
13
+ :PREROUTING ACCEPT [5:1208]
14
+ :INPUT ACCEPT [5:1208]
15
+ :OUTPUT ACCEPT [42:3215]
16
+ :POSTROUTING ACCEPT [42:3215]
17
+ :DOCKER - [0:0]
18
+ -A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
19
+ -A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
20
+ -A POSTROUTING -s 172.17.0.0/16 ! -d 172.17.0.0/16 -j MASQUERADE
21
+ -A POSTROUTING -s 10.0.3.0/24 ! -d 10.0.3.0/24 -j MASQUERADE
22
+ COMMIT
23
+ # Completed on Wed Feb 19 13:37:35 2014
@@ -0,0 +1,22 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ table :filter do
4
+ chain :FORWARD do
5
+ rule '-o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT'
6
+ rule '-i docker0 ! -o docker0 -j ACCEPT'
7
+ rule '-i docker0 -o docker0 -j ACCEPT'
8
+ end
9
+ end
10
+
11
+ table :nat do
12
+ chain :PREROUTING do
13
+ rule '-m addrtype --dst-type LOCAL -j DOCKER'
14
+ end
15
+ chain :OUTPUT do
16
+ rule '! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER'
17
+ end
18
+ chain :POSTROUTING do
19
+ rule '-s 172.17.0.0/16 ! -d 172.17.0.0/16 -j MASQUERADE'
20
+ rule '-s 10.0.3.0/24 ! -d 10.0.3.0/24 -j MASQUERADE'
21
+ end
22
+ end
@@ -0,0 +1,7 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ iptables do
4
+ table :filter do
5
+ inherit(:FORWARD) { |rule| rule[:i] == 'docker0' || rule[:o] == 'docker0' }
6
+ end
7
+ end
@@ -0,0 +1,16 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ iptables do
4
+ table :nat do
5
+ chain :PREROUTING do
6
+ rule '-m addrtype --dst-type LOCAL -j DOCKER'
7
+ end
8
+ chain :OUTPUT do
9
+ rule '! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER'
10
+ end
11
+ chain :POSTROUTING do
12
+ rule '-s 172.17.0.0/16 ! -d 172.17.0.0/16 -j MASQUERADE'
13
+ rule '-s 10.0.3.0/24 ! -d 10.0.3.0/24 -j MASQUERADE'
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,74 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ module IPScriptables
5
+ describe 'Chain#rule' do
6
+ THREE_PORTS_RULES = <<EOF
7
+ *filter
8
+ :INPUT ACCEPT [0:0]
9
+ :FORWARD ACCEPT [0:0]
10
+ :OUTPUT ACCEPT [0:0]
11
+ -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
12
+ -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
13
+ -A INPUT -p tcp -m tcp --dport 443 -j ACCEPT
14
+ -A INPUT -j REJECT --reject-with icmp-port-unreachable
15
+ COMMIT
16
+ EOF
17
+
18
+ def expect_three_ports_from(&block)
19
+ rs = Ruleset.new do
20
+ table :filter do
21
+ chain :INPUT do
22
+ instance_eval(&block)
23
+ rule '-j REJECT --reject-with icmp-port-unreachable'
24
+ end
25
+ end
26
+ end
27
+ expect { rs.render == THREE_PORTS_RULES }
28
+ end
29
+
30
+ it 'allows to describe rulesets' do
31
+ expect_three_ports_from do
32
+ rule '-p tcp -m tcp --dport 22 -j ACCEPT'
33
+ rule '-p tcp -m tcp --dport 80 -j ACCEPT'
34
+ rule '-p tcp -m tcp --dport 443 -j ACCEPT'
35
+ end
36
+ end
37
+
38
+ it 'Allows nesting for DRY' do
39
+ expect_three_ports_from do
40
+ rule '-p tcp -m tcp' do
41
+ rule '--dport 22 -j ACCEPT'
42
+ rule '--dport 80 -j ACCEPT'
43
+ rule '--dport 443 -j ACCEPT'
44
+ end
45
+ end
46
+ end
47
+
48
+ it 'accepts iterable of elements for iteration' do
49
+ expect_three_ports_from do
50
+ rule [
51
+ '-p tcp -m tcp --dport 22 -j ACCEPT',
52
+ '-p tcp -m tcp --dport 80 -j ACCEPT',
53
+ '-p tcp -m tcp --dport 443 -j ACCEPT'
54
+ ]
55
+ end
56
+ end
57
+
58
+ it 'accepts multiple of arguments and processes them as if nested' do
59
+ expect_three_ports_from do
60
+ rule '-p tcp -m tcp --dport', [22, 80, 443], '-j ACCEPT'
61
+ end
62
+ end
63
+
64
+ it 'understands parameters provided as hashes' do
65
+ expect_three_ports_from do
66
+ rule p: :tcp, m: :tcp, dport: [22, 80, 443], j: :ACCEPT
67
+ end
68
+
69
+ expect_three_ports_from do
70
+ rule '-p' => :tcp, '-m' => :tcp, '--dport' => [22, 80, 443], '-j' => :ACCEPT # rubocop:disable LineLength
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,58 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ module IPScriptables
5
+ describe 'Helpers.run_command' do
6
+ def mock_status(success = true)
7
+ rv = mock('Process::Status')
8
+ rv.expects(:success?).at_least(0).returns(success)
9
+ rv
10
+ end
11
+
12
+ it 'runs external command and returns its stdout' do
13
+ Helpers.expects('systemu').with(%w(echo foo))
14
+ .returns([mock_status, "foo\n", ''])
15
+ expect { Helpers.run_command('echo', 'foo') == "foo\n" }
16
+ end
17
+
18
+ it 'raises RuntimError on failure status' do
19
+ Helpers.expects('systemu').with(%w(false))
20
+ .returns([mock_status(false), '', ''])
21
+ expect { rescuing { Helpers.run_command('false') }.is_a?(RuntimeError) }
22
+ end
23
+
24
+ it 'prints command\'s stderr on stderr' do
25
+ Helpers.expects('systemu').with(%w(cmd))
26
+ .returns([mock_status, "bar\n", "foo\n"])
27
+ out, err = capture_io { @res = Helpers.run_command('cmd') }
28
+ expect { out == '' }
29
+ expect { err == "cmd: foo\n" }
30
+ expect { @res == "bar\n" }
31
+ end
32
+
33
+ it 'prints stdout on stderr in case of failure' do
34
+ Helpers.expects('systemu').with(%w(cmd))
35
+ .returns([mock_status(false), "foo\n", ''])
36
+ out, err = capture_io do
37
+ @rescued = rescuing { Helpers.run_command('cmd') }
38
+ end
39
+ expect { out == '' }
40
+ expect { err == "cmd: foo\n" }
41
+ expect { @rescued.is_a?(RuntimeError) }
42
+ end
43
+ end
44
+
45
+ describe 'Helpers.ohai' do
46
+ before { fauxhai! }
47
+
48
+ it 'returns a configured instance of Ohai' do
49
+ expect { Helpers.ohai['hostname'] == 'Fauxhai' }
50
+ end
51
+
52
+ it 'caches the ohai instance for better performance' do
53
+ ohai1 = Helpers.ohai
54
+ ohai2 = Helpers.ohai
55
+ expect { ohai1.object_id == ohai2.object_id }
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,41 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ module IPScriptables
5
+ describe 'Rule' do
6
+ let(:chain) do
7
+ ch = mock(Chain.name)
8
+ ch.expects(:original).at_least_once.returns(nil)
9
+ ch.expects(:opts).at_least(0).returns({})
10
+ ch
11
+ end
12
+
13
+ let(:rule) do
14
+ Rule.new(chain, '-s 1.1.1.1/32 -p tcp -m tcp -m multiport --dports 4949,5666 -j ACCEPT') # rubocop:disable LineLength
15
+ end
16
+
17
+ it 'allows accessing individual switches by dict access' do
18
+ expect { rule['--dports'] == '4949,5666' }
19
+ expect { rule['dports'] == '4949,5666' }
20
+ expect { rule[:dports] == '4949,5666' }
21
+ expect { rule[:destination_ports] == '4949,5666' }
22
+ expect { rule[:j] == 'ACCEPT' }
23
+ expect { rule[:jump] == 'ACCEPT' }
24
+ end
25
+
26
+ it 'rolls multiple options into arrays' do
27
+ expect { rule[:m] == %w(tcp multiport) }
28
+ end
29
+
30
+ it 'has convenience aliases' do
31
+ expect { rule.match == %w(tcp multiport) }
32
+ expect { rule.proto == 'tcp' }
33
+ expect { rule.target == 'ACCEPT' }
34
+ end
35
+
36
+ it 'can be also matched by regexp' do
37
+ expect { rule =~ /multiport/ }
38
+ deny { rule =~ /udp/ }
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,52 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ module IPScriptables
5
+ describe 'Ruleset.from_file, .from_s' do
6
+ Dir[fixture('**/*.txt')].each do |fixture|
7
+ it "parses #{File.basename(fixture)} and renders it back" do
8
+ fixture_text = File.open(fixture).lines.grep(/^[^#]/).join
9
+ expect { Ruleset.from_file(fixture).render == fixture_text }
10
+ expect { Ruleset.from_s(File.read(fixture)).render == fixture_text }
11
+ end
12
+ end
13
+
14
+ it 'fails on invalid input' do
15
+ expect { rescuing { Ruleset.from_s('Invalid!') }.is_a?(RuntimeError) }
16
+ end
17
+ end
18
+
19
+ describe 'Ruleset.from_command' do
20
+ let(:fixture_content) { File.read(fixture('ghq.txt')) }
21
+ let(:fixture_text) { fixture_content.lines.grep(/^[^#]/).join }
22
+
23
+ it 'executes external command and parses its output as ruleset' do
24
+ Helpers.expects(:run_command)
25
+ .with('a', 'command', 'with', 'arguments')
26
+ .returns(fixture_content)
27
+ rs = Ruleset.from_command('a', 'command', 'with', 'arguments')
28
+ expect { rs.render == fixture_text }
29
+ expect { rs.command == %w(a command with arguments) }
30
+ end
31
+
32
+ it 'has a convenience alias .from_iptables' do
33
+ Helpers.expects(:run_command)
34
+ .with('iptables-save', '-c')
35
+ .returns(fixture_content)
36
+ rs = Ruleset.from_iptables
37
+ expect { rs.render == fixture_text }
38
+ expect { rs.command == %w(iptables-save -c) }
39
+ expect { rs.family == :inet }
40
+ end
41
+
42
+ it 'has a convenience alias .from_ip6tables' do
43
+ Helpers.expects(:run_command)
44
+ .with('ip6tables-save', '-c')
45
+ .returns(fixture_content)
46
+ rs = Ruleset.from_ip6tables
47
+ expect { rs.render == fixture_text }
48
+ expect { rs.command == %w(ip6tables-save -c) }
49
+ expect { rs.family == :inet6 }
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,199 @@
1
+ # -*- coding: utf-8 -*-
2
+ # rubocop:disable LineLength
3
+ require 'spec_helper'
4
+
5
+ module IPScriptables
6
+ describe 'Ruleset#method_missing' do
7
+ let(:rs) { Ruleset.new(foo: 23) }
8
+ it 'forwards calls to ruleset\'s options' do
9
+ expect { rs.foo == 23 }
10
+ expect { rs.respond_to? :foo }
11
+ end
12
+
13
+ it 'rejects methods that are not in options' do
14
+ expect { rescuing { rs.bar }.is_a?(NoMethodError) }
15
+ deny { rs.respond_to? :bar }
16
+ end
17
+ end
18
+
19
+ describe 'Ruleset#bud' do
20
+ let(:drumknott) { Ruleset.from_file(fixture('drumknott.txt')) }
21
+ let(:ghq) { Ruleset.from_file(fixture('ghq.txt')) }
22
+
23
+ it 'creates a child ruleset with identical tables and chains, but no rules' do
24
+ expected_rules = <<-EOF
25
+ *mangle
26
+ :PREROUTING ACCEPT [9070264:2761485141]
27
+ :INPUT ACCEPT [5794:541194]
28
+ :FORWARD ACCEPT [9064470:2760943947]
29
+ :OUTPUT ACCEPT [4447:1027385]
30
+ :POSTROUTING ACCEPT [9068917:2761971332]
31
+ COMMIT
32
+ *nat
33
+ :PREROUTING ACCEPT [936831:58138468]
34
+ :INPUT ACCEPT [383149:28442596]
35
+ :OUTPUT ACCEPT [188115:19311882]
36
+ :POSTROUTING ACCEPT [88176135:5298607741]
37
+ :DOCKER - [0:0]
38
+ COMMIT
39
+ *filter
40
+ :INPUT ACCEPT [419:18560]
41
+ :FORWARD ACCEPT [5802508472:1613710597740]
42
+ :OUTPUT ACCEPT [2072879:485657573]
43
+ :FWR - [0:0]
44
+ COMMIT
45
+ EOF
46
+ child = drumknott.bud
47
+ expect { child.render == expected_rules }
48
+ expect { child.original == drumknott }
49
+ end
50
+
51
+ it 'allows chain inheritance' do
52
+ child = drumknott.bud do
53
+ inherit :nat, :DOCKER
54
+ end
55
+ expected_rules = <<-EOF
56
+ *mangle
57
+ :PREROUTING ACCEPT [9070264:2761485141]
58
+ :INPUT ACCEPT [5794:541194]
59
+ :FORWARD ACCEPT [9064470:2760943947]
60
+ :OUTPUT ACCEPT [4447:1027385]
61
+ :POSTROUTING ACCEPT [9068917:2761971332]
62
+ COMMIT
63
+ *nat
64
+ :PREROUTING ACCEPT [936831:58138468]
65
+ :INPUT ACCEPT [383149:28442596]
66
+ :OUTPUT ACCEPT [188115:19311882]
67
+ :POSTROUTING ACCEPT [88176135:5298607741]
68
+ :DOCKER - [0:0]
69
+ -A DOCKER ! -i docker0 -p tcp -m tcp --dport 6379 -j DNAT --to-destination 172.17.0.2:6379
70
+ COMMIT
71
+ *filter
72
+ :INPUT ACCEPT [419:18560]
73
+ :FORWARD ACCEPT [5802508472:1613710597740]
74
+ :OUTPUT ACCEPT [2072879:485657573]
75
+ :FWR - [0:0]
76
+ COMMIT
77
+ EOF
78
+ expect { child.render == expected_rules }
79
+ end
80
+
81
+ it 'allows filtering inherited chains' do
82
+ child = drumknott.bud do
83
+ inherit(:filter, :FWR) { |rule| !rule[:source] }
84
+ end
85
+ expected_rules = <<-EOF
86
+ *mangle
87
+ :PREROUTING ACCEPT [9070264:2761485141]
88
+ :INPUT ACCEPT [5794:541194]
89
+ :FORWARD ACCEPT [9064470:2760943947]
90
+ :OUTPUT ACCEPT [4447:1027385]
91
+ :POSTROUTING ACCEPT [9068917:2761971332]
92
+ COMMIT
93
+ *nat
94
+ :PREROUTING ACCEPT [936831:58138468]
95
+ :INPUT ACCEPT [383149:28442596]
96
+ :OUTPUT ACCEPT [188115:19311882]
97
+ :POSTROUTING ACCEPT [88176135:5298607741]
98
+ :DOCKER - [0:0]
99
+ COMMIT
100
+ *filter
101
+ :INPUT ACCEPT [419:18560]
102
+ :FORWARD ACCEPT [5802508472:1613710597740]
103
+ :OUTPUT ACCEPT [2072879:485657573]
104
+ :FWR - [0:0]
105
+ -A FWR -i lo -j ACCEPT
106
+ -A FWR -m state --state RELATED,ESTABLISHED -j ACCEPT
107
+ -A FWR -p icmp -j ACCEPT
108
+ -A FWR -i docker+ -j ACCEPT
109
+ -A FWR -p tcp -m tcp --dport 22 -j ACCEPT
110
+ -A FWR -p tcp -m tcp --tcp-flags SYN,RST,ACK SYN -j REJECT --reject-with icmp-port-unreachable
111
+ -A FWR -p udp -j REJECT --reject-with icmp-port-unreachable
112
+ COMMIT
113
+ EOF
114
+ expect { child.render == expected_rules }
115
+ expect { drumknott.render =~ /^-A FWR -s 1\.1\.1\.1/ }
116
+ end
117
+
118
+ it 'copies original\'s chain with counters' do
119
+ child = ghq.bud do
120
+ inherit(:nat, :DOCKER)
121
+ table :filter do
122
+ chain :INPUT do
123
+ rule '-j FWR'
124
+ end
125
+ chain :FWR do
126
+ rule '-i lo -j ACCEPT'
127
+ end
128
+ end
129
+ end
130
+
131
+ expected_rules = <<-EOF
132
+ *nat
133
+ :PREROUTING ACCEPT [732601:44001989]
134
+ :INPUT ACCEPT [376018:22538408]
135
+ :OUTPUT ACCEPT [3131507:229597576]
136
+ :POSTROUTING ACCEPT [20476198:1943580383]
137
+ :DOCKER - [0:0]
138
+ [2:120] -A DOCKER ! -i docker0 -p tcp -m tcp --dport 2003 -j DNAT --to-destination 172.17.0.4:2003
139
+ [0:0] -A DOCKER ! -i docker0 -p tcp -m tcp --dport 2004 -j DNAT --to-destination 172.17.0.4:2004
140
+ [0:0] -A DOCKER ! -i docker0 -p tcp -m tcp --dport 49153 -j DNAT --to-destination 172.17.0.8:9000
141
+ [95:5580] -A DOCKER ! -i docker0 -p tcp -m tcp --dport 5000 -j DNAT --to-destination 172.17.0.5:5000
142
+ [0:0] -A DOCKER -d 127.0.0.1/32 ! -i docker0 -p tcp -m tcp --dport 49154 -j DNAT --to-destination 172.17.0.9:8080
143
+ [17011603:1693997647] -A DOCKER ! -i docker0 -p udp -m udp --dport 8125 -j DNAT --to-destination 172.17.0.10:8125
144
+ [0:0] -A DOCKER -d 127.0.0.1/32 ! -i docker0 -p tcp -m tcp --dport 49155 -j DNAT --to-destination 172.17.0.10:8126
145
+ COMMIT
146
+ *filter
147
+ :INPUT ACCEPT [1602:65593]
148
+ :FORWARD ACCEPT [79892700:14079015733]
149
+ :OUTPUT ACCEPT [173177551:46244981637]
150
+ :FWR - [0:0]
151
+ [162824485:36484450187] -A INPUT -j FWR
152
+ [104747465:21902005069] -A FWR -i lo -j ACCEPT
153
+ COMMIT
154
+ EOF
155
+
156
+ expect { child.render == expected_rules }
157
+ end
158
+ end
159
+
160
+ describe 'Ruleset#diff' do
161
+ it 'returns a Diffy::Diff from the original ruleset' do
162
+ child = Ruleset.from_file(fixture('only-docker.txt')).bud
163
+
164
+ expect { child.diff.to_s.each_line.grep(/^\S/).length == 7 }
165
+
166
+ child.dsl_eval do
167
+ inherit :filter, :FORWARD
168
+ end
169
+
170
+ expect { child.diff.to_s.each_line.grep(/^\S/).length == 4 }
171
+
172
+ child.dsl_eval do
173
+ table :nat do
174
+ chain :PREROUTING do
175
+ rule '-m addrtype --dst-type LOCAL -j DOCKER'
176
+ end
177
+ chain :OUTPUT do
178
+ rule '! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER'
179
+ end
180
+ chain :POSTROUTING do
181
+ rule '-s 172.17.0.0/16 ! -d 172.17.0.0/16 -j MASQUERADE'
182
+ rule '-s 10.0.3.0/24 ! -d 10.0.3.0/24 -j MASQUERADE'
183
+ end
184
+ end
185
+ end
186
+
187
+ expect { child.diff.to_s.empty? }
188
+ end
189
+ end
190
+
191
+ describe 'Ruleset#load_file' do
192
+ it 'Loads a ruleset from file' do
193
+ child = Ruleset.from_file(fixture('only-docker.txt')).bud
194
+ deny { child.diff.to_s.empty? }
195
+ child.load_file fixture('only_docker.rb')
196
+ expect { child.diff.to_s.empty? }
197
+ end
198
+ end
199
+ end