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,40 @@
|
|
1
|
+
{
|
2
|
+
"primitives": {
|
3
|
+
"iana_reserved": [
|
4
|
+
"0.0.0.0/8",
|
5
|
+
"5.0.0.0/8",
|
6
|
+
"10.0.0.0/8",
|
7
|
+
"36.0.0.0/7",
|
8
|
+
"39.0.0.0/8",
|
9
|
+
"42.0.0.0/8",
|
10
|
+
"49.0.0.0/8",
|
11
|
+
"100.0.0.0/6",
|
12
|
+
"104.0.0.0/7",
|
13
|
+
"106.0.0.0/8",
|
14
|
+
"127.0.0.0/8",
|
15
|
+
"179.0.0.0/8",
|
16
|
+
"185.0.0.0/8",
|
17
|
+
"240.0.0.0/4",
|
18
|
+
"169.254.0.0/16",
|
19
|
+
"172.16.0.0/12",
|
20
|
+
"192.0.2.0/24",
|
21
|
+
"192.88.99.0/24",
|
22
|
+
"192.168.0.0/16"
|
23
|
+
],
|
24
|
+
"internet": {
|
25
|
+
"address": "198.51.100.10/32",
|
26
|
+
"broadcast": "198.51.100.255/32",
|
27
|
+
"device": "eth0",
|
28
|
+
"subnet": {
|
29
|
+
"internet": "198.51.100.0/24",
|
30
|
+
"vpn": "203.0.113.0/24"
|
31
|
+
}
|
32
|
+
},
|
33
|
+
"storage": {
|
34
|
+
"address": "192.0.2.100/32",
|
35
|
+
"device": "eth1",
|
36
|
+
"subnet": "192.0.2.0/24"
|
37
|
+
}
|
38
|
+
}
|
39
|
+
}
|
40
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
{
|
2
|
+
"rules": {
|
3
|
+
"nat": {
|
4
|
+
"INPUT": {
|
5
|
+
"policy": "ACCEPT"
|
6
|
+
},
|
7
|
+
"OUTPUT": {
|
8
|
+
"policy": "ACCEPT"
|
9
|
+
},
|
10
|
+
"POSTROUTING": {
|
11
|
+
"policy": "ACCEPT"
|
12
|
+
},
|
13
|
+
"PREROUTING": {
|
14
|
+
"policy": "ACCEPT"
|
15
|
+
}
|
16
|
+
},
|
17
|
+
"filter": {
|
18
|
+
"in_*": {
|
19
|
+
"additions": []
|
20
|
+
},
|
21
|
+
"in_peers": {
|
22
|
+
"additions": []
|
23
|
+
},
|
24
|
+
"in_public": {
|
25
|
+
"additions": []
|
26
|
+
}
|
27
|
+
}
|
28
|
+
}
|
29
|
+
}
|
30
|
+
|
@@ -0,0 +1,81 @@
|
|
1
|
+
{
|
2
|
+
"services": {
|
3
|
+
"dhcp": "-p udp -m udp --sport 67:68 --dport 67:68 -m state --state NEW,ESTABLISHED -j ACCEPT",
|
4
|
+
"domain": [
|
5
|
+
"-p udp -m udp --sport 53 --dport 53 -m state --state NEW,ESTABLISHED -j ACCEPT",
|
6
|
+
{
|
7
|
+
"service_udp": "53"
|
8
|
+
},
|
9
|
+
"-p tcp -m tcp --sport 53 --dport 53 -m state --state NEW,ESTABLISHED -j ACCEPT",
|
10
|
+
{
|
11
|
+
"service_tcp": "53"
|
12
|
+
}
|
13
|
+
],
|
14
|
+
"http": 80,
|
15
|
+
"https": 443,
|
16
|
+
"icmp": "-p icmp -m state --state NEW,ESTABLISHED -j ACCEPT",
|
17
|
+
"ipp": 631,
|
18
|
+
"jabber": [
|
19
|
+
{
|
20
|
+
"service_tcp": "5222"
|
21
|
+
},
|
22
|
+
{
|
23
|
+
"service_tcp": "5223"
|
24
|
+
}
|
25
|
+
],
|
26
|
+
"ldap": 389,
|
27
|
+
"ldaps": 636,
|
28
|
+
"mysql": 3306,
|
29
|
+
"nfs": [
|
30
|
+
{
|
31
|
+
"service_tcp": "2049"
|
32
|
+
},
|
33
|
+
{
|
34
|
+
"service_udp": "2049"
|
35
|
+
}
|
36
|
+
],
|
37
|
+
"ntp": [
|
38
|
+
"-p udp -m udp --sport 123 --dport 123 -m state --state NEW,ESTABLISHED -j ACCEPT",
|
39
|
+
{
|
40
|
+
"service_udp": "123"
|
41
|
+
},
|
42
|
+
"-p tcp -m tcp --sport 123 --dport 123 -m state --state NEW,ESTABLISHED -j ACCEPT",
|
43
|
+
{
|
44
|
+
"service_tcp": "123"
|
45
|
+
}
|
46
|
+
],
|
47
|
+
"portmap": [
|
48
|
+
{
|
49
|
+
"service_tcp": "111"
|
50
|
+
},
|
51
|
+
{
|
52
|
+
"service_udp": "111"
|
53
|
+
}
|
54
|
+
],
|
55
|
+
"postgres": 5432,
|
56
|
+
"rabbitmq": [
|
57
|
+
{
|
58
|
+
"service_tcp": "5670:5680"
|
59
|
+
},
|
60
|
+
{
|
61
|
+
"service_udp": "5670:5680"
|
62
|
+
}
|
63
|
+
],
|
64
|
+
"smtp": 25,
|
65
|
+
"snmp": 161,
|
66
|
+
"ssh": 22,
|
67
|
+
"syslog": [
|
68
|
+
"-p udp -m udp --sport 514 --dport 514 -m state --state NEW,ESTABLISHED -j ACCEPT",
|
69
|
+
{
|
70
|
+
"service_udp": "514"
|
71
|
+
},
|
72
|
+
"-p tcp -m tcp --sport 514 --dport 514 -m state --state NEW,ESTABLISHED -j ACCEPT",
|
73
|
+
{
|
74
|
+
"service_tcp": "514"
|
75
|
+
}
|
76
|
+
],
|
77
|
+
"tftp": {
|
78
|
+
"service_udp": "69"
|
79
|
+
}
|
80
|
+
}
|
81
|
+
}
|
data/lib/iptables.rb
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module IPTables
|
4
|
+
class Configuration
|
5
|
+
@@json_pattern = /\.js(on)?$/
|
6
|
+
|
7
|
+
def initialize(*args)
|
8
|
+
@parsed_hash = {}
|
9
|
+
self.parse_files(*args)
|
10
|
+
end
|
11
|
+
|
12
|
+
def parse_files(*args)
|
13
|
+
args.each{ |arg|
|
14
|
+
$log.debug("reading arg #{arg}")
|
15
|
+
case arg
|
16
|
+
when @@json_pattern
|
17
|
+
handle_json(arg)
|
18
|
+
else
|
19
|
+
raise "don't know how to handle #{arg.inspect}"
|
20
|
+
end
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
def policy(in_policy = nil)
|
25
|
+
@policy ||= nil
|
26
|
+
return @policy unless @policy.nil?
|
27
|
+
unless in_policy.nil?
|
28
|
+
@policy = in_policy
|
29
|
+
return @policy
|
30
|
+
end
|
31
|
+
raise 'missing policy' unless @parsed_hash.has_key? 'policy'
|
32
|
+
@policy = IPTables::Tables.new(@parsed_hash['policy'], self)
|
33
|
+
end
|
34
|
+
|
35
|
+
def policy6(in_policy = nil)
|
36
|
+
@policy6 ||= nil
|
37
|
+
return @policy6 unless @policy6.nil?
|
38
|
+
unless in_policy.nil?
|
39
|
+
@policy6 = in_policy
|
40
|
+
return @policy6
|
41
|
+
end
|
42
|
+
raise 'missing policy6' unless @parsed_hash.has_key? 'policy6'
|
43
|
+
@policy6 = IPTables::Tables.new(@parsed_hash['policy6'], self)
|
44
|
+
end
|
45
|
+
|
46
|
+
def interpolations(in_interpolations = nil)
|
47
|
+
@interpolations ||= nil
|
48
|
+
return @interpolations unless @interpolations.nil?
|
49
|
+
unless in_interpolations.nil?
|
50
|
+
@interpolations = in_interpolations
|
51
|
+
return @interpolations
|
52
|
+
end
|
53
|
+
@interpolations = IPTables::Interpolations.new(self.primitives)
|
54
|
+
end
|
55
|
+
|
56
|
+
def primitives(in_primitives = nil)
|
57
|
+
@primitives ||= nil
|
58
|
+
return @primitives unless @primitives.nil?
|
59
|
+
unless in_primitives.nil?
|
60
|
+
@primitives = in_primitives
|
61
|
+
return @primitives
|
62
|
+
end
|
63
|
+
raise 'missing primitives' unless @parsed_hash.has_key? 'primitives'
|
64
|
+
@primitives = IPTables::Primitives.new(@parsed_hash['primitives'])
|
65
|
+
end
|
66
|
+
|
67
|
+
def rules(in_rules = nil)
|
68
|
+
@rules ||= nil
|
69
|
+
return @rules unless @rules.nil?
|
70
|
+
unless in_rules.nil?
|
71
|
+
@rules = in_rules
|
72
|
+
return @rules
|
73
|
+
end
|
74
|
+
raise 'missing rules' unless @parsed_hash.has_key? 'rules'
|
75
|
+
@rules = IPTables::Tables.new(@parsed_hash['rules'], self)
|
76
|
+
end
|
77
|
+
|
78
|
+
def services(in_services = nil)
|
79
|
+
@services ||= nil
|
80
|
+
return @services unless @services.nil?
|
81
|
+
unless in_services.nil?
|
82
|
+
@services = in_services
|
83
|
+
return @services
|
84
|
+
end
|
85
|
+
raise 'missing services' unless @parsed_hash.has_key? 'services'
|
86
|
+
@services = IPTables::Services.new(@parsed_hash['services'])
|
87
|
+
end
|
88
|
+
|
89
|
+
def macros(in_macros = nil)
|
90
|
+
@macros ||= nil
|
91
|
+
return @macros unless @macros.nil?
|
92
|
+
unless in_macros.nil?
|
93
|
+
@macros = in_macros
|
94
|
+
return @macros
|
95
|
+
end
|
96
|
+
raise 'missing macros' unless @parsed_hash.has_key? 'macros'
|
97
|
+
@macros = IPTables::Macros.new(@parsed_hash['macros'])
|
98
|
+
end
|
99
|
+
|
100
|
+
def handle_json(file_name)
|
101
|
+
json = File.read(file_name)
|
102
|
+
JSON.parse(json).each{ |key, value|
|
103
|
+
$log.debug("reading #{key} from file #{file_name}")
|
104
|
+
raise "duplicate key: #{key}" if @parsed_hash.has_key? key
|
105
|
+
@parsed_hash[key] = value
|
106
|
+
}
|
107
|
+
end
|
108
|
+
|
109
|
+
def converge_firewall()
|
110
|
+
policy_fw = self.policy
|
111
|
+
rules_fw = self.rules
|
112
|
+
policy_fw.merge(rules_fw)
|
113
|
+
return policy_fw
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,189 @@
|
|
1
|
+
require 'iptables/logger'
|
2
|
+
|
3
|
+
module IPTables
|
4
|
+
class Macros
|
5
|
+
attr_reader :named
|
6
|
+
def initialize(expansion_hash)
|
7
|
+
@expansion_hash = expansion_hash
|
8
|
+
@named = {}
|
9
|
+
@expansion_hash.each{ |name, info|
|
10
|
+
@named[name] = Macro.new(name, info)
|
11
|
+
}
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class Macro
|
16
|
+
attr_reader :name, :children
|
17
|
+
def initialize(name, info)
|
18
|
+
@name = name
|
19
|
+
@info = info
|
20
|
+
@children = []
|
21
|
+
|
22
|
+
case info
|
23
|
+
when Array
|
24
|
+
self.handle_array()
|
25
|
+
when Hash
|
26
|
+
self.handle_hash()
|
27
|
+
when String
|
28
|
+
self.handle_string()
|
29
|
+
else
|
30
|
+
raise "don't know how to handle info: #{info.inspect}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def add_child(rule_hash)
|
35
|
+
@children.push(rule_hash)
|
36
|
+
end
|
37
|
+
|
38
|
+
def handle_array()
|
39
|
+
@info.each{ |macro_hash|
|
40
|
+
self.add_child(macro_hash)
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
def handle_hash()
|
45
|
+
self.add_child( @info )
|
46
|
+
end
|
47
|
+
|
48
|
+
def handle_string()
|
49
|
+
self.add_child( {'raw' => @info} )
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class Services
|
54
|
+
attr_reader :named
|
55
|
+
def initialize(expansion_hash)
|
56
|
+
@expansion_hash = expansion_hash
|
57
|
+
@named = {}
|
58
|
+
@expansion_hash.each{ |name, info|
|
59
|
+
@named[name] = Service.new(name, info)
|
60
|
+
}
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class Service
|
65
|
+
attr_reader :name, :children
|
66
|
+
def initialize(name, info)
|
67
|
+
@name = name
|
68
|
+
@info = info
|
69
|
+
@children = []
|
70
|
+
|
71
|
+
case info
|
72
|
+
when Array
|
73
|
+
self.handle_array()
|
74
|
+
when Hash
|
75
|
+
self.handle_hash()
|
76
|
+
when Integer
|
77
|
+
self.handle_integer()
|
78
|
+
when String
|
79
|
+
self.handle_string()
|
80
|
+
else
|
81
|
+
raise "don't know how to handle info: #{info.inspect}"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def add_child(rule_hash)
|
86
|
+
@children.push(rule_hash)
|
87
|
+
end
|
88
|
+
|
89
|
+
def handle_array()
|
90
|
+
self.add_child( { 'comment' => "_ #{@name}" } )
|
91
|
+
raise 'empty @info' if @info.empty?
|
92
|
+
@info.each{ |service_info_hash|
|
93
|
+
self.add_child(service_info_hash)
|
94
|
+
}
|
95
|
+
end
|
96
|
+
|
97
|
+
def handle_hash()
|
98
|
+
raise 'empty @info' if @info.empty?
|
99
|
+
@info['service_name'] = @name
|
100
|
+
self.add_child( @info )
|
101
|
+
end
|
102
|
+
|
103
|
+
def handle_integer()
|
104
|
+
self.add_child( { 'comment' => "_ Port #{@info} - #{@name}" } )
|
105
|
+
self.add_child({
|
106
|
+
'raw' =>
|
107
|
+
"-p tcp -m tcp --sport 1024:65535 --dport #{@info} -m state --state NEW,ESTABLISHED -j ACCEPT"
|
108
|
+
})
|
109
|
+
end
|
110
|
+
|
111
|
+
def handle_string()
|
112
|
+
self.add_child( { 'comment' => "_ #{@name}" } )
|
113
|
+
self.add_child( {'raw' => @info} )
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
class Interpolations
|
118
|
+
# interpret strings such as "<% foo.bar %>" into equivalent primitives
|
119
|
+
attr_reader :primitives, :named
|
120
|
+
def initialize(primitives)
|
121
|
+
@primitives = primitives
|
122
|
+
$log.debug("interpolations primitives: #{@primitives}")
|
123
|
+
@named = {}
|
124
|
+
end
|
125
|
+
|
126
|
+
def add(interpolation_string)
|
127
|
+
@named[interpolation_string] = Interpolation.new(self, interpolation_string)
|
128
|
+
end
|
129
|
+
|
130
|
+
def children(interpolation_string)
|
131
|
+
self.add(interpolation_string) unless @named.has_key? interpolation_string
|
132
|
+
strings = @named[interpolation_string].children()
|
133
|
+
returned_array = []
|
134
|
+
case strings
|
135
|
+
when Array
|
136
|
+
strings.each{ |result|
|
137
|
+
returned_array.push({'raw' => result})
|
138
|
+
}
|
139
|
+
else
|
140
|
+
returned_array.push({'raw' => strings})
|
141
|
+
end
|
142
|
+
return returned_array
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
class Interpolation
|
147
|
+
@@interpolation_regex = /(.*?)<%\s*(\S+)\s*%>(.*)/
|
148
|
+
|
149
|
+
def initialize(interpolations, initial_string)
|
150
|
+
@interpolations = interpolations
|
151
|
+
@initial_string = initial_string
|
152
|
+
@child = nil
|
153
|
+
if @initial_string =~ @@interpolation_regex
|
154
|
+
self.add_child($1, $2, $3)
|
155
|
+
else
|
156
|
+
$log.debug("completed substitution: #{@initial_string}")
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def add_child(first, identifier, last)
|
161
|
+
interpolated = @interpolations.primitives.substitute(identifier)
|
162
|
+
case interpolated
|
163
|
+
when Array
|
164
|
+
@child = []
|
165
|
+
interpolated.each{ |value|
|
166
|
+
@child.push(Interpolation.new(@interpolations, "#{first}#{value}#{last}"))
|
167
|
+
}
|
168
|
+
else
|
169
|
+
@child = Interpolation.new(@interpolations, "#{first}#{interpolated}#{last}")
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def children()
|
174
|
+
return_value = nil
|
175
|
+
case @child
|
176
|
+
when nil
|
177
|
+
return_value = @initial_string
|
178
|
+
when Array
|
179
|
+
return_value = []
|
180
|
+
@child.each{ |value|
|
181
|
+
return_value.push(value.children())
|
182
|
+
}
|
183
|
+
else
|
184
|
+
return_value = @child.children()
|
185
|
+
end
|
186
|
+
return return_value
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|