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,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
|