iptables-ruby 0.2.4
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.
- checksums.yaml +7 -0
- data/.gitignore +6 -0
- data/CHANGELOG +23 -0
- data/Gemfile +6 -0
- data/Generate.md +278 -0
- data/LICENSE +20 -0
- data/README.md +140 -0
- data/Rakefile +44 -0
- data/bin/check_firewall.rb +220 -0
- data/examples/policy/macros.json +92 -0
- data/examples/policy/policy.json +208 -0
- data/examples/policy/policy6.json +8 -0
- data/examples/policy/primitives.json +40 -0
- data/examples/policy/rules.json +30 -0
- data/examples/policy/services.json +81 -0
- data/lib/iptables.rb +5 -0
- data/lib/iptables/configuration.rb +116 -0
- data/lib/iptables/expansions.rb +189 -0
- data/lib/iptables/logger.rb +5 -0
- data/lib/iptables/primitives.rb +51 -0
- data/lib/iptables/tables.rb +851 -0
- data/test/common.rb +22 -0
- data/test/tc_all.rb +7 -0
- data/test/tc_comparison.rb +751 -0
- data/test/tc_configuration.rb +72 -0
- data/test/tc_expansions.rb +116 -0
- data/test/tc_primitives.rb +35 -0
- data/test/tc_tables.rb +652 -0
- metadata +94 -0
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'common'
|
2
|
+
|
3
|
+
class TestConfiguration < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
@test_config = IPTables::Configuration.new()
|
6
|
+
end
|
7
|
+
|
8
|
+
def test_initialize
|
9
|
+
assert_nothing_raised( RuntimeError ) { IPTables::Configuration.new() }
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_parse_files
|
13
|
+
assert_raise( RuntimeError ) { IPTables::Configuration.new('foobar') }
|
14
|
+
# don't know how to test reading .json files
|
15
|
+
# without being able to test reading json, can not complete testing
|
16
|
+
#assert_nothing_raised( RuntimeError ) { IPTables::Configuration.new('test/test_config.json') }
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_policy
|
20
|
+
assert_raise( RuntimeError ) { @test_config.policy }
|
21
|
+
assert_equal({}, @test_config.policy({}))
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_policy6
|
25
|
+
assert_raise( RuntimeError ) { @test_config.policy6 }
|
26
|
+
assert_equal({}, @test_config.policy6({}))
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_interpolations
|
30
|
+
assert_raise( RuntimeError ) { @test_config.interpolations }
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_primitives
|
34
|
+
assert_raise( RuntimeError ) { @test_config.primitives }
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_rules
|
38
|
+
assert_raise( RuntimeError ) { @test_config.rules }
|
39
|
+
assert_equal({}, @test_config.rules({}))
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_services
|
43
|
+
assert_raise( RuntimeError ) { @test_config.services }
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_macros
|
47
|
+
assert_raise( RuntimeError ) { @test_config.macros }
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_converge_firewall
|
51
|
+
assert_raise( RuntimeError ) { @test_config.converge_firewall() }
|
52
|
+
|
53
|
+
test_policy = IPTables::Tables.new({
|
54
|
+
'table1' => {
|
55
|
+
'chain1' => {
|
56
|
+
'policy' => 'ACCEPT',
|
57
|
+
'rules' => [
|
58
|
+
'-j ACCEPT'
|
59
|
+
]
|
60
|
+
}
|
61
|
+
}
|
62
|
+
}, @test_config)
|
63
|
+
@test_config.policy(test_policy)
|
64
|
+
|
65
|
+
test_rules = IPTables::Tables.new({
|
66
|
+
'table1' => { 'chain1' => { 'policy' => 'DROP' } }
|
67
|
+
}, @test_config)
|
68
|
+
@test_config.rules(test_policy)
|
69
|
+
|
70
|
+
assert_instance_of(IPTables::Tables, @test_config.converge_firewall)
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'common'
|
2
|
+
|
3
|
+
class TestMacros < Test::Unit::TestCase
|
4
|
+
def test_initialize
|
5
|
+
assert_equal({}, IPTables::Macros.new({}).named)
|
6
|
+
assert_instance_of(IPTables::Macro, IPTables::Macros.new({'test_macro' => {}}).named['test_macro'])
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class TestMacro < Test::Unit::TestCase
|
11
|
+
def test_initialize
|
12
|
+
assert_raise( RuntimeError ) { IPTables::Macro.new('test_macro', 1) }
|
13
|
+
|
14
|
+
assert_equal('test_macro', IPTables::Macro.new('test_macro', {}).name)
|
15
|
+
assert_equal([], IPTables::Macro.new('test_macro', []).children)
|
16
|
+
assert_equal([{}], IPTables::Macro.new('test_macro', {}).children)
|
17
|
+
assert_equal([{'raw' => ''}], IPTables::Macro.new('test_macro', '').children)
|
18
|
+
assert_equal([{'raw' => ''}], IPTables::Macro.new('test_macro', [{'raw' => ''}]).children)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class TestServices < Test::Unit::TestCase
|
23
|
+
def test_initialize
|
24
|
+
assert_equal({}, IPTables::Services.new({}).named)
|
25
|
+
assert_instance_of(IPTables::Service, IPTables::Services.new({'test_service' => 1}).named['test_service'])
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class TestService < Test::Unit::TestCase
|
30
|
+
def test_initialize
|
31
|
+
assert_raise( RuntimeError ) { IPTables::Service.new('test_service', 1.0) }
|
32
|
+
assert_raise( RuntimeError ) { IPTables::Service.new('test_service', []) }
|
33
|
+
assert_raise( RuntimeError ) { IPTables::Service.new('test_service', {}) }
|
34
|
+
|
35
|
+
assert_equal('test_service', IPTables::Service.new('test_service', 1).name)
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_handle_string
|
39
|
+
assert_equal([{"comment"=>"_ test_service"}, {"raw"=>"-j ACCEPT"}], IPTables::Service.new('test_service', '-j ACCEPT').children)
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_handle_array
|
43
|
+
assert_equal([{"comment"=>"_ test_service"}, {"raw"=>"-j ACCEPT"}], IPTables::Service.new('test_service', [{'raw' => '-j ACCEPT'}]).children)
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_handle_hash
|
47
|
+
assert_equal([{"raw"=>"-j ACCEPT", "service_name"=>"test_service"}], IPTables::Service.new('test_service', {'raw' => '-j ACCEPT'}).children)
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_handle_integer
|
51
|
+
assert_equal(
|
52
|
+
[
|
53
|
+
{"comment"=>"_ Port 1 - test_service"},
|
54
|
+
{"raw"=> "-p tcp -m tcp --sport 1024:65535 --dport 1 -m state --state NEW,ESTABLISHED -j ACCEPT"}
|
55
|
+
],
|
56
|
+
IPTables::Service.new('test_service', 1).children
|
57
|
+
)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class TestInterpolations < Test::Unit::TestCase
|
62
|
+
def setup
|
63
|
+
@new = IPTables::Interpolations.new({})
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_initialize
|
67
|
+
assert_equal({}, @new.named)
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_add
|
71
|
+
@new.add('test_interpolation')
|
72
|
+
assert_instance_of(IPTables::Interpolation, @new.named['test_interpolation'])
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_children
|
76
|
+
@new.add('test_interpolation')
|
77
|
+
assert_equal([{"raw"=>"test_interpolation"}], @new.children('test_interpolation'))
|
78
|
+
assert_equal([{"raw"=>"test_interpolation"}], @new.children(['test_interpolation']))
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
class TestInterpolation < Test::Unit::TestCase
|
83
|
+
def setup
|
84
|
+
@config = IPTables::Configuration.new
|
85
|
+
@config.primitives(
|
86
|
+
IPTables::Primitives.new({
|
87
|
+
'branch' => { 'leaf1' => 'leaf1_value' },
|
88
|
+
'leaf2' => 'leaf2_value',
|
89
|
+
'array1' => [ 'array_value1', 'array_value2' ]
|
90
|
+
})
|
91
|
+
)
|
92
|
+
@config.interpolations(
|
93
|
+
IPTables::Interpolations.new( @config.primitives )
|
94
|
+
)
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_add_child
|
98
|
+
assert_raise( RuntimeError ) { IPTables::Interpolation.new(@config.interpolations, '<% missing %>') }
|
99
|
+
#assert_raise( RuntimeError ) { IPTables::Interpolation.new(@config.interpolations, '<% branch %>') }
|
100
|
+
end
|
101
|
+
|
102
|
+
def test_children
|
103
|
+
assert_equal(
|
104
|
+
'junk leaf1_value',
|
105
|
+
IPTables::Interpolation.new(@config.interpolations, 'junk <% branch.leaf1 %>').children
|
106
|
+
)
|
107
|
+
assert_equal(
|
108
|
+
'leaf2_value stuff',
|
109
|
+
IPTables::Interpolation.new(@config.interpolations, '<% leaf2 %> stuff').children
|
110
|
+
)
|
111
|
+
assert_equal(
|
112
|
+
["before array_value1 after", "before array_value2 after"],
|
113
|
+
IPTables::Interpolation.new(@config.interpolations, 'before <% array1 %> after').children
|
114
|
+
)
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'common'
|
2
|
+
|
3
|
+
class TestPrimitives < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
test_data = {
|
6
|
+
'first' => {
|
7
|
+
'second' => 'blah'
|
8
|
+
}
|
9
|
+
}
|
10
|
+
@primitives = IPTables::Primitives.new(test_data)
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_initialize
|
14
|
+
assert_raise( RuntimeError ) { IPTables::Primitives.new(1) }
|
15
|
+
assert_raise( RuntimeError ) { IPTables::Primitives.new({'test_primitive', 1}) }
|
16
|
+
|
17
|
+
assert_equal({}, IPTables::Primitives.new({}).children)
|
18
|
+
assert_equal({'test_primitive' => []}, IPTables::Primitives.new({'test_primitive' => []}).children)
|
19
|
+
|
20
|
+
primitives = IPTables::Primitives.new({'test_primitive' => {}})
|
21
|
+
assert_kind_of(IPTables::Primitives, primitives.children['test_primitive'])
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_substitute
|
25
|
+
assert_raise( RuntimeError, 'a missing substitution is an error' ) { @primitives.substitute('missing') }
|
26
|
+
assert_raise( RuntimeError, 'a partial substitution is an error' ) { @primitives.substitute('first') }
|
27
|
+
assert_equal('blah', @primitives.substitute('first.second'))
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_has_primitive
|
31
|
+
assert(@primitives.has_primitive?('first.second'), 'An existing primitive should say it exists')
|
32
|
+
assert_equal(false, @primitives.has_primitive?('missing'), 'A missing primitive should say it does not exist')
|
33
|
+
assert_equal(false, @primitives.has_primitive?('first'), 'A partial primitive should say it does not exist')
|
34
|
+
end
|
35
|
+
end
|
data/test/tc_tables.rb
ADDED
@@ -0,0 +1,652 @@
|
|
1
|
+
require 'common'
|
2
|
+
|
3
|
+
class TestTables < Test::Unit::TestCase
|
4
|
+
def test_initialize
|
5
|
+
assert_raise( RuntimeError ) { IPTables::Tables.new(1) }
|
6
|
+
|
7
|
+
test_iptables = IPTables::Tables.new({
|
8
|
+
'table1' => {},
|
9
|
+
'table2' => nil
|
10
|
+
})
|
11
|
+
assert_kind_of(IPTables::Table, test_iptables.tables['table1'])
|
12
|
+
assert_nil(test_iptables.tables['table2'])
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_null_table_as_array
|
16
|
+
test_iptables1 = IPTables::Tables.new( {
|
17
|
+
'nat' => nil,
|
18
|
+
})
|
19
|
+
assert_equal(
|
20
|
+
[],
|
21
|
+
test_iptables1.as_array,
|
22
|
+
'null table should produce no policy and empty ruleset'
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_as_array_without_comments
|
27
|
+
config = IPTables::Configuration.new()
|
28
|
+
tables = IPTables::Tables.new({
|
29
|
+
'filter' => {
|
30
|
+
'INPUT' => {
|
31
|
+
'policy' => 'ACCEPT',
|
32
|
+
'rules' => [
|
33
|
+
{ 'comment' => 'foobar' }
|
34
|
+
]
|
35
|
+
}
|
36
|
+
}
|
37
|
+
}, config)
|
38
|
+
assert_equal(
|
39
|
+
[
|
40
|
+
"*filter",
|
41
|
+
":INPUT ACCEPT",
|
42
|
+
"COMMIT"
|
43
|
+
],
|
44
|
+
tables.as_array(comments = false),
|
45
|
+
'if comments are excluded, they should not appear in output'
|
46
|
+
)
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_ignore_comments_in_policy_fw
|
50
|
+
config = IPTables::Configuration.new
|
51
|
+
config.primitives(
|
52
|
+
IPTables::Primitives.new({
|
53
|
+
'branch' => { 'leaf1' => 'leaf1_value' },
|
54
|
+
'leaf2' => 'leaf2_value',
|
55
|
+
})
|
56
|
+
)
|
57
|
+
config.interpolations(
|
58
|
+
IPTables::Interpolations.new( config.primitives )
|
59
|
+
)
|
60
|
+
config.services(
|
61
|
+
IPTables::Services.new({
|
62
|
+
'service1' => 1111,
|
63
|
+
})
|
64
|
+
)
|
65
|
+
config.macros(
|
66
|
+
IPTables::Macros.new({
|
67
|
+
'macro1' => [
|
68
|
+
{ 'comment' => 'A comment in a macro' }
|
69
|
+
]
|
70
|
+
})
|
71
|
+
)
|
72
|
+
config.policy(
|
73
|
+
IPTables::Tables.new({
|
74
|
+
'filter' => {
|
75
|
+
'INPUT' => {
|
76
|
+
'policy' => 'ACCEPT',
|
77
|
+
'rules' => [
|
78
|
+
{ 'comment' => 'foobar' },
|
79
|
+
'-j ACCEPT',
|
80
|
+
{ 'node_addition_points' => [ 'INPUT' ] },
|
81
|
+
{ 'macro' => 'macro1' },
|
82
|
+
{ 'service' => 'service1' },
|
83
|
+
]
|
84
|
+
}
|
85
|
+
}
|
86
|
+
}, config)
|
87
|
+
)
|
88
|
+
config.rules(
|
89
|
+
IPTables::Tables.new({
|
90
|
+
'filter' => {
|
91
|
+
'INPUT' => {
|
92
|
+
'additions' => [
|
93
|
+
{ 'comment' => 'a comment from an addition' },
|
94
|
+
{
|
95
|
+
'service_name' => 'a test service',
|
96
|
+
'service_tcp' => 2222
|
97
|
+
},
|
98
|
+
]
|
99
|
+
}
|
100
|
+
}
|
101
|
+
}, config)
|
102
|
+
)
|
103
|
+
converged_fw = config.converge_firewall
|
104
|
+
assert_equal(
|
105
|
+
[
|
106
|
+
'*filter',
|
107
|
+
':INPUT ACCEPT',
|
108
|
+
'-A INPUT -j ACCEPT',
|
109
|
+
'-A INPUT -p tcp -m tcp --sport 1024:65535 --dport 2222 -m state --state NEW,ESTABLISHED -j ACCEPT',
|
110
|
+
'-A INPUT -p tcp -m tcp --sport 1024:65535 --dport 1111 -m state --state NEW,ESTABLISHED -j ACCEPT',
|
111
|
+
'COMMIT'
|
112
|
+
],
|
113
|
+
converged_fw.as_array(false),
|
114
|
+
'when excluding comments from a converged firewall, should see no comments'
|
115
|
+
)
|
116
|
+
end
|
117
|
+
|
118
|
+
def test_two_tables_as_array
|
119
|
+
test_iptables1 = IPTables::Tables.new( {
|
120
|
+
'nat' => {
|
121
|
+
'INPUT' => {
|
122
|
+
'policy' => 'ACCEPT'
|
123
|
+
}
|
124
|
+
},
|
125
|
+
'filter' => {
|
126
|
+
'INPUT' => {
|
127
|
+
'policy' => 'ACCEPT'
|
128
|
+
}
|
129
|
+
},
|
130
|
+
})
|
131
|
+
assert_equal(
|
132
|
+
[
|
133
|
+
"*filter",
|
134
|
+
":INPUT ACCEPT",
|
135
|
+
"COMMIT",
|
136
|
+
"*nat",
|
137
|
+
":INPUT ACCEPT",
|
138
|
+
"COMMIT"
|
139
|
+
],
|
140
|
+
test_iptables1.as_array,
|
141
|
+
'two tables should produce consistent array output'
|
142
|
+
)
|
143
|
+
end
|
144
|
+
|
145
|
+
def test_table_only_with_policy_as_array
|
146
|
+
test_iptables1 = IPTables::Tables.new( {
|
147
|
+
'nat' => {
|
148
|
+
'INPUT' => {
|
149
|
+
'policy' => 'ACCEPT'
|
150
|
+
}
|
151
|
+
},
|
152
|
+
})
|
153
|
+
assert_equal(
|
154
|
+
[
|
155
|
+
"*nat",
|
156
|
+
":INPUT ACCEPT",
|
157
|
+
"COMMIT"
|
158
|
+
],
|
159
|
+
test_iptables1.as_array,
|
160
|
+
'null table to array should produce an empty array'
|
161
|
+
)
|
162
|
+
end
|
163
|
+
|
164
|
+
def test_merge_table_to_null_table
|
165
|
+
test_iptables1 = IPTables::Tables.new( {
|
166
|
+
'table1' => nil,
|
167
|
+
})
|
168
|
+
test_iptables2 = IPTables::Tables.new( {
|
169
|
+
'table1' => {
|
170
|
+
'INPUT' => {
|
171
|
+
'policy' => 'ACCEPT'
|
172
|
+
}
|
173
|
+
},
|
174
|
+
})
|
175
|
+
test_iptables1.merge(test_iptables2)
|
176
|
+
assert_kind_of(IPTables::Table, test_iptables1.tables['table1'], 'after merge, table1 should be a Table')
|
177
|
+
assert_equal(
|
178
|
+
[
|
179
|
+
"*table1",
|
180
|
+
":INPUT ACCEPT",
|
181
|
+
"COMMIT"
|
182
|
+
],
|
183
|
+
test_iptables1.as_array,
|
184
|
+
'after merge, table1 should include a complete ruleset'
|
185
|
+
)
|
186
|
+
end
|
187
|
+
|
188
|
+
def test_custom_service_additions
|
189
|
+
config = IPTables::Configuration.new()
|
190
|
+
policy_table = IPTables::Tables.new({
|
191
|
+
'filter' => {
|
192
|
+
'INPUT' => {
|
193
|
+
'policy' => 'ACCEPT',
|
194
|
+
'rules' => [
|
195
|
+
{
|
196
|
+
'node_addition_points' => [ 'INPUT' ]
|
197
|
+
},
|
198
|
+
]
|
199
|
+
}
|
200
|
+
}
|
201
|
+
}, config)
|
202
|
+
rules_table = IPTables::Tables.new({
|
203
|
+
'filter' => {
|
204
|
+
'INPUT' => {
|
205
|
+
'additions' => [
|
206
|
+
{
|
207
|
+
'service_name' => 'svc1',
|
208
|
+
'service_tcp' => 2222
|
209
|
+
},
|
210
|
+
{
|
211
|
+
'service_name' => 'svc2',
|
212
|
+
'service_udp' => 2223
|
213
|
+
},
|
214
|
+
{
|
215
|
+
'service_name' => 'svc3',
|
216
|
+
'service_tcp' => 2224,
|
217
|
+
'service_udp' => 2225
|
218
|
+
}
|
219
|
+
]
|
220
|
+
}
|
221
|
+
}
|
222
|
+
}, config)
|
223
|
+
policy_table.merge(rules_table)
|
224
|
+
assert_equal(
|
225
|
+
[
|
226
|
+
"*filter",
|
227
|
+
":INPUT ACCEPT",
|
228
|
+
'-A INPUT -m comment --comment "_ Port 2222 - svc1"',
|
229
|
+
'-A INPUT -p tcp -m tcp --sport 1024:65535 --dport 2222 -m state --state NEW,ESTABLISHED -j ACCEPT',
|
230
|
+
'-A INPUT -m comment --comment "_ Port 2223 - svc2"',
|
231
|
+
'-A INPUT -p udp -m udp --sport 1024:65535 --dport 2223 -m state --state NEW,ESTABLISHED -j ACCEPT',
|
232
|
+
'-A INPUT -m comment --comment "_ svc3"',
|
233
|
+
'-A INPUT -p tcp -m tcp --sport 1024:65535 --dport 2224 -m state --state NEW,ESTABLISHED -j ACCEPT',
|
234
|
+
'-A INPUT -p udp -m udp --sport 1024:65535 --dport 2225 -m state --state NEW,ESTABLISHED -j ACCEPT',
|
235
|
+
"COMMIT"
|
236
|
+
],
|
237
|
+
policy_table.as_array,
|
238
|
+
'adding custom services on a node_addition_point should produce known results'
|
239
|
+
)
|
240
|
+
end
|
241
|
+
|
242
|
+
def test_merge
|
243
|
+
test_iptables1 = IPTables::Tables.new(
|
244
|
+
<<-EOS.dedent
|
245
|
+
*table1
|
246
|
+
*table2
|
247
|
+
COMMIT
|
248
|
+
EOS
|
249
|
+
)
|
250
|
+
test_iptables2 = IPTables::Tables.new( {
|
251
|
+
'table1' => {},
|
252
|
+
'table2' => false,
|
253
|
+
'table3' => nil,
|
254
|
+
'table4' => {}
|
255
|
+
})
|
256
|
+
test_iptables1.merge(test_iptables2)
|
257
|
+
assert_kind_of(IPTables::Table, test_iptables1.tables['table1'], 'after merge, table1 should still be a Table')
|
258
|
+
assert_nil(test_iptables1.tables['table2'], 'after merge, should have no table2')
|
259
|
+
assert_nil(test_iptables1.tables['table3'], 'after merge, should still have no table3')
|
260
|
+
assert_kind_of(IPTables::Table, test_iptables1.tables['table4'], 'after merge, table4 should be a new Table')
|
261
|
+
|
262
|
+
# a huge merge test, because I am lazy
|
263
|
+
config = IPTables::Configuration.new()
|
264
|
+
policy_table = IPTables::Tables.new({
|
265
|
+
'filter' => {
|
266
|
+
'INPUT' => {
|
267
|
+
'policy' => 'ACCEPT',
|
268
|
+
'rules' => [
|
269
|
+
'-J INPUT_rule1',
|
270
|
+
{
|
271
|
+
'node_addition_points' => [ 'INPUT', 'chain_INOUT' ]
|
272
|
+
},
|
273
|
+
'-J INPUT_rule3'
|
274
|
+
]
|
275
|
+
},
|
276
|
+
'OUTPUT' => {
|
277
|
+
'policy' => 'ACCEPT',
|
278
|
+
'rules' => [
|
279
|
+
'-J OUTPUT_rule1',
|
280
|
+
{
|
281
|
+
'node_addition_points' => [ 'OUTPUT', 'chain_INOUT' ]
|
282
|
+
},
|
283
|
+
'-J OUTPUT_rule3'
|
284
|
+
]
|
285
|
+
}
|
286
|
+
}
|
287
|
+
}, config)
|
288
|
+
rules_table = IPTables::Tables.new({
|
289
|
+
'filter' => {
|
290
|
+
'INPUT' => {
|
291
|
+
'policy' => 'DROP',
|
292
|
+
'additions' => [
|
293
|
+
'-J INPUT_addition'
|
294
|
+
]
|
295
|
+
},
|
296
|
+
'FORWARD' => {
|
297
|
+
'policy' => 'REJECT',
|
298
|
+
'rules' => [
|
299
|
+
'-J FORWARD_rule1'
|
300
|
+
]
|
301
|
+
},
|
302
|
+
'chain_INOUT' => {
|
303
|
+
'additions' => [
|
304
|
+
'-J chain_INOUT_addition'
|
305
|
+
]
|
306
|
+
},
|
307
|
+
'nonexistent' => {
|
308
|
+
'additions' => [
|
309
|
+
'-J nonexistent_addition'
|
310
|
+
]
|
311
|
+
}
|
312
|
+
}
|
313
|
+
}, config)
|
314
|
+
policy_table.merge(rules_table)
|
315
|
+
assert_equal(
|
316
|
+
[
|
317
|
+
"*filter",
|
318
|
+
":INPUT DROP",
|
319
|
+
":FORWARD REJECT",
|
320
|
+
":OUTPUT ACCEPT",
|
321
|
+
"-A INPUT -J INPUT_rule1",
|
322
|
+
"-A INPUT -J INPUT_addition",
|
323
|
+
"-A INPUT -J chain_INOUT_addition",
|
324
|
+
"-A INPUT -J INPUT_rule3",
|
325
|
+
"-A FORWARD -J FORWARD_rule1",
|
326
|
+
"-A OUTPUT -J OUTPUT_rule1",
|
327
|
+
"-A OUTPUT -J chain_INOUT_addition",
|
328
|
+
"-A OUTPUT -J OUTPUT_rule3",
|
329
|
+
"COMMIT"
|
330
|
+
],
|
331
|
+
policy_table.as_array
|
332
|
+
)
|
333
|
+
end
|
334
|
+
|
335
|
+
def test_get_node_additions
|
336
|
+
# not testing this directly here?
|
337
|
+
end
|
338
|
+
|
339
|
+
def test_parse
|
340
|
+
assert_raise( RuntimeError ) { IPTables::Tables.new('garbage') }
|
341
|
+
assert_raise( RuntimeError, 'should not allow empty iptables parsed rules' ) { IPTables::Tables.new('') }
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
class TestTable < Test::Unit::TestCase
|
346
|
+
def test_initialize
|
347
|
+
test_iptables = IPTables::Tables.new({
|
348
|
+
'table1' => {
|
349
|
+
'INPUT' => {
|
350
|
+
'rules' => [
|
351
|
+
'-j ACCEPT'
|
352
|
+
]
|
353
|
+
}
|
354
|
+
}
|
355
|
+
})
|
356
|
+
table1 = test_iptables.tables['table1']
|
357
|
+
assert_kind_of(IPTables::Tables, table1.my_iptables)
|
358
|
+
assert_kind_of(IPTables::Chain, table1.chains['INPUT'])
|
359
|
+
assert_equal('table1', table1.name)
|
360
|
+
|
361
|
+
assert_raise( RuntimeError ) {
|
362
|
+
IPTables::Tables.new({
|
363
|
+
'table1' => {
|
364
|
+
'INPUT' => 1
|
365
|
+
}
|
366
|
+
})
|
367
|
+
}
|
368
|
+
end
|
369
|
+
|
370
|
+
def test_path
|
371
|
+
test_iptables = IPTables::Tables.new({
|
372
|
+
'table1' => {
|
373
|
+
'INPUT' => {
|
374
|
+
'rules' => [
|
375
|
+
'-j ACCEPT'
|
376
|
+
]
|
377
|
+
}
|
378
|
+
}
|
379
|
+
})
|
380
|
+
table1 = test_iptables.tables['table1']
|
381
|
+
assert_equal('table1', table1.path)
|
382
|
+
end
|
383
|
+
|
384
|
+
def test_merge
|
385
|
+
test_iptables1 = IPTables::Tables.new(
|
386
|
+
<<-EOS.dedent
|
387
|
+
*table1
|
388
|
+
:chain1 ACCEPT [0:0]
|
389
|
+
:chain2 ACCEPT [0:0]
|
390
|
+
-A chain1 -j ACCEPT
|
391
|
+
-A chain2 -j ACCEPT
|
392
|
+
COMMIT
|
393
|
+
EOS
|
394
|
+
)
|
395
|
+
table1 = test_iptables1.tables['table1']
|
396
|
+
|
397
|
+
test_iptables2 = IPTables::Tables.new({
|
398
|
+
'table1' => {
|
399
|
+
'chain1' => {},
|
400
|
+
'chain2' => false,
|
401
|
+
'chain3' => nil,
|
402
|
+
'chain4' => {},
|
403
|
+
}
|
404
|
+
})
|
405
|
+
table2 = test_iptables2.tables['table1']
|
406
|
+
|
407
|
+
table1.merge(table2)
|
408
|
+
assert_kind_of(IPTables::Chain, table1.chains['chain1'])
|
409
|
+
assert_nil(table1.chains['chain2'], 'after merge, should have no chain2')
|
410
|
+
assert_nil(table1.chains['chain3'], 'after merge, should have no chain3')
|
411
|
+
assert_kind_of(IPTables::Chain, table1.chains['chain4'])
|
412
|
+
end
|
413
|
+
end
|
414
|
+
|
415
|
+
class TestChain < Test::Unit::TestCase
|
416
|
+
def setup
|
417
|
+
@test_iptables = IPTables::Tables.new(
|
418
|
+
<<-EOS.dedent
|
419
|
+
*table1
|
420
|
+
:chain1 ACCEPT [0:0]
|
421
|
+
-A chain1 -m comment --comment "BEGIN: in-bound traffic"
|
422
|
+
-A chain1 -j ACCEPT
|
423
|
+
COMMIT
|
424
|
+
EOS
|
425
|
+
)
|
426
|
+
@chain1 = @test_iptables.tables['table1'].chains['chain1']
|
427
|
+
end
|
428
|
+
|
429
|
+
def test_output_policy
|
430
|
+
assert_equal('ACCEPT', @chain1.output_policy)
|
431
|
+
end
|
432
|
+
|
433
|
+
def test_as_array
|
434
|
+
assert_equal(
|
435
|
+
[
|
436
|
+
'-A chain1 -m comment --comment "BEGIN: in-bound traffic"',
|
437
|
+
'-A chain1 -j ACCEPT'
|
438
|
+
],
|
439
|
+
@chain1.as_array,
|
440
|
+
'chain as array should produce known output'
|
441
|
+
)
|
442
|
+
end
|
443
|
+
|
444
|
+
def test_all_as_array
|
445
|
+
assert_equal(
|
446
|
+
[
|
447
|
+
':chain1 ACCEPT',
|
448
|
+
'-A chain1 -m comment --comment "BEGIN: in-bound traffic"',
|
449
|
+
'-A chain1 -j ACCEPT'
|
450
|
+
],
|
451
|
+
@chain1.all_as_array,
|
452
|
+
'chain as array including its policy should produce known output'
|
453
|
+
)
|
454
|
+
end
|
455
|
+
|
456
|
+
def test_as_array_without_comments
|
457
|
+
assert_equal(
|
458
|
+
@chain1.rules[0].type,
|
459
|
+
'comment',
|
460
|
+
'a chain rule that is known to be a comment should have type comment'
|
461
|
+
)
|
462
|
+
assert_equal(
|
463
|
+
[ '-A chain1 -j ACCEPT' ],
|
464
|
+
@chain1.as_array(comments = false),
|
465
|
+
'chain as array without comments should produce known output'
|
466
|
+
)
|
467
|
+
end
|
468
|
+
|
469
|
+
def test_path
|
470
|
+
assert_equal('table1.chain1', @chain1.path)
|
471
|
+
end
|
472
|
+
|
473
|
+
def test_complete?
|
474
|
+
assert(
|
475
|
+
IPTables::Chain.new(
|
476
|
+
'test_chain',
|
477
|
+
{ 'rules' => [ '-j ACCEPT' ] },
|
478
|
+
@test_iptables
|
479
|
+
).complete?,
|
480
|
+
'chain with rules should say it is complete'
|
481
|
+
)
|
482
|
+
assert(
|
483
|
+
IPTables::Chain.new( 'test_chain', {}, @test_iptables).complete?,
|
484
|
+
'chain without any rules or additions should say it is complete'
|
485
|
+
)
|
486
|
+
assert_equal(
|
487
|
+
false,
|
488
|
+
IPTables::Chain.new( 'test_chain', { 'additions' => [] }, @test_iptables).complete?,
|
489
|
+
'chain with only additions should not say it is complete'
|
490
|
+
)
|
491
|
+
end
|
492
|
+
end
|
493
|
+
|
494
|
+
class TestRule < Test::Unit::TestCase
|
495
|
+
def setup
|
496
|
+
config = IPTables::Configuration.new
|
497
|
+
config.primitives(
|
498
|
+
IPTables::Primitives.new({
|
499
|
+
'branch' => { 'leaf1' => 'leaf1_value' },
|
500
|
+
'leaf2' => 'leaf2_value',
|
501
|
+
})
|
502
|
+
)
|
503
|
+
config.interpolations(
|
504
|
+
IPTables::Interpolations.new( config.primitives )
|
505
|
+
)
|
506
|
+
config.services(
|
507
|
+
IPTables::Services.new({
|
508
|
+
'service1' => 1111,
|
509
|
+
})
|
510
|
+
)
|
511
|
+
config.macros(
|
512
|
+
IPTables::Macros.new({
|
513
|
+
'macro1' => '-j macro1',
|
514
|
+
})
|
515
|
+
)
|
516
|
+
@test_iptables = IPTables::Tables.new({
|
517
|
+
'table1' => {
|
518
|
+
'chain1' => {
|
519
|
+
'policy' => 'ACCEPT',
|
520
|
+
'rules' => [
|
521
|
+
'-j ACCEPT'
|
522
|
+
]
|
523
|
+
}
|
524
|
+
}
|
525
|
+
}, config)
|
526
|
+
@chain1 = @test_iptables.tables['table1'].chains['chain1']
|
527
|
+
end
|
528
|
+
|
529
|
+
def test_initialize
|
530
|
+
assert_raise( RuntimeError ) { IPTables::Rule.new( 1, @chain1 ) }
|
531
|
+
assert_equal(0, @chain1.rules[0].position)
|
532
|
+
assert_equal(
|
533
|
+
["-A chain1 -j ACCEPT"],
|
534
|
+
IPTables::Rule.new( {'raw' => '-j ACCEPT'}, @chain1 ).as_array
|
535
|
+
)
|
536
|
+
assert_equal(
|
537
|
+
[
|
538
|
+
"-A chain1 -m comment --comment \"_ Port 1337 - foo\"",
|
539
|
+
"-A chain1 -p tcp -m tcp --sport 1024:65535 --dport 1337 -m state --state NEW,ESTABLISHED -j ACCEPT"
|
540
|
+
],
|
541
|
+
IPTables::Rule.new( {'service_name' => 'foo', 'service_tcp' => 1337}, @chain1 ).as_array
|
542
|
+
)
|
543
|
+
assert_equal(
|
544
|
+
[
|
545
|
+
"-A chain1 -m comment --comment \"_ Port 1337 - foo\"",
|
546
|
+
"-A chain1 -p tcp -m tcp --sport 1024:65535 --dport 1337 -m state --state NEW,ESTABLISHED -j ACCEPT",
|
547
|
+
"-A chain1 -p udp -m udp --sport 1024:65535 --dport 1337 -m state --state NEW,ESTABLISHED -j ACCEPT"
|
548
|
+
],
|
549
|
+
IPTables::Rule.new( {'service_name' => 'foo', 'service_tcp' => 1337, 'service_udp' => 1337}, @chain1 ).as_array
|
550
|
+
)
|
551
|
+
assert_raise( RuntimeError ) {
|
552
|
+
IPTables::Rule.new( {'service_name' => 'foo', 'service_tcp' => 1337, 'service_udp' => 1337, 'fake' => 1}, @chain1 ).as_array
|
553
|
+
}
|
554
|
+
assert_raise( RuntimeError ) { IPTables::Rule.new( {'bad' => 1}, @chain1 ) }
|
555
|
+
end
|
556
|
+
|
557
|
+
def test_handle_comment
|
558
|
+
rule = IPTables::Rule.new( {'comment' => 'a comment'}, @chain1 )
|
559
|
+
assert_equal(
|
560
|
+
{'comment' => 'a comment'},
|
561
|
+
rule.rule_hash,
|
562
|
+
'comment attributes should be handled as comments'
|
563
|
+
)
|
564
|
+
assert_equal(
|
565
|
+
rule.type,
|
566
|
+
'comment',
|
567
|
+
'comment attributes should have their type set as "comment"'
|
568
|
+
)
|
569
|
+
end
|
570
|
+
|
571
|
+
def test_parse_comment
|
572
|
+
rule = IPTables::Rule.new( '-m comment --comment "BEGIN: in-bound traffic"', @chain1 )
|
573
|
+
assert_equal(
|
574
|
+
{'comment' => 'BEGIN: in-bound traffic'},
|
575
|
+
rule.rule_hash,
|
576
|
+
'parsed comments should have their rule_hash set properly'
|
577
|
+
)
|
578
|
+
assert_equal(
|
579
|
+
rule.type,
|
580
|
+
'comment',
|
581
|
+
'parsed comments should have their type set as "comment"'
|
582
|
+
)
|
583
|
+
assert_equal(
|
584
|
+
[],
|
585
|
+
rule.as_array(comments = false),
|
586
|
+
'parsed comments should not display when displayed with comments turned off'
|
587
|
+
)
|
588
|
+
end
|
589
|
+
|
590
|
+
def test_handle_node_addition_points
|
591
|
+
rule = IPTables::Rule.new( {'node_addition_points' => ['chain1']}, @chain1 )
|
592
|
+
assert_equal( [], rule.as_array )
|
593
|
+
end
|
594
|
+
|
595
|
+
def test_handle_interpolated
|
596
|
+
assert_equal(
|
597
|
+
["-A chain1 -j leaf1_value"],
|
598
|
+
IPTables::Rule.new( {'interpolated' => '-j <% branch.leaf1 %>'}, @chain1 ).as_array
|
599
|
+
)
|
600
|
+
end
|
601
|
+
|
602
|
+
def test_handle_macro
|
603
|
+
assert_equal(
|
604
|
+
["-A chain1 -j macro1"],
|
605
|
+
IPTables::Rule.new( {'macro' => 'macro1'}, @chain1 ).as_array
|
606
|
+
)
|
607
|
+
end
|
608
|
+
|
609
|
+
def test_handle_service
|
610
|
+
assert_equal(
|
611
|
+
[
|
612
|
+
"-A chain1 -m comment --comment \"_ Port 1111 - service1\"",
|
613
|
+
"-A chain1 -p tcp -m tcp --sport 1024:65535 --dport 1111 -m state --state NEW,ESTABLISHED -j ACCEPT"
|
614
|
+
],
|
615
|
+
IPTables::Rule.new( {'service' => 'service1'}, @chain1 ).as_array
|
616
|
+
)
|
617
|
+
end
|
618
|
+
|
619
|
+
def test_handle_requires_primitive
|
620
|
+
assert_equal(
|
621
|
+
[],
|
622
|
+
IPTables::Rule.new(
|
623
|
+
{
|
624
|
+
'raw' => '-j ACCEPT',
|
625
|
+
'requires_primitive' => 'nonexistent'
|
626
|
+
},
|
627
|
+
@chain1
|
628
|
+
).as_array
|
629
|
+
)
|
630
|
+
assert_equal(
|
631
|
+
['-A chain1 -j ACCEPT'],
|
632
|
+
IPTables::Rule.new(
|
633
|
+
{
|
634
|
+
'raw' => '-j ACCEPT',
|
635
|
+
'requires_primitive' => 'leaf2'
|
636
|
+
},
|
637
|
+
@chain1
|
638
|
+
).as_array
|
639
|
+
)
|
640
|
+
end
|
641
|
+
|
642
|
+
def test_as_array
|
643
|
+
assert_equal(
|
644
|
+
["-A chain1 -p tcp -m limit --limit 1/sec --limit-burst 2 -j ULOG --ulog-prefix \"chain1:\""],
|
645
|
+
IPTables::Rule.new( {'ulog' => '-p tcp'}, @chain1 ).as_array
|
646
|
+
)
|
647
|
+
end
|
648
|
+
|
649
|
+
def test_path
|
650
|
+
assert_equal('table1.chain1.0', @chain1.rules[0].path)
|
651
|
+
end
|
652
|
+
end
|