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