ipscriptables 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 (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