line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package IPTables::Mangle; |
2
|
1
|
|
|
1
|
|
823
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
38
|
|
3
|
1
|
|
|
1
|
|
5
|
use warnings; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
737
|
|
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
our $VERSION = '0.04'; |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
=head1 NAME |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
IPTables::Mangle - Manage iptables rules with Perl / YAML |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
=head1 SYNOPSIS |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
Given a config file, produces rules for iptables-restore. |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
Example YAML file, for ease of viewing: |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
filter: |
18
|
|
|
|
|
|
|
forward: { default: drop } |
19
|
|
|
|
|
|
|
foo: |
20
|
|
|
|
|
|
|
rules: |
21
|
|
|
|
|
|
|
- src: 9.9.9.9 |
22
|
|
|
|
|
|
|
- src: 10.10.10.10 |
23
|
|
|
|
|
|
|
action: drop |
24
|
|
|
|
|
|
|
input: |
25
|
|
|
|
|
|
|
# by default, do not allow any connections unless authorized |
26
|
|
|
|
|
|
|
# in the rules below |
27
|
|
|
|
|
|
|
default: drop |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
# by default, if no "action" is given to a rule below, accept it |
30
|
|
|
|
|
|
|
default_rule_action: accept |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
rules: |
33
|
|
|
|
|
|
|
# Accept all traffic on loopback interface |
34
|
|
|
|
|
|
|
- in-interface: lo |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
# Don't disconnect existing connections during a rule change. |
37
|
|
|
|
|
|
|
- { match: state, state: 'ESTABLISHED,RELATED' } |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
# Allow for pings (no more than 10 a second) |
40
|
|
|
|
|
|
|
- { protocol: icmp, icmp-type: 8, match: limit, limit: 10/sec } |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
# Allow these IPs, no matter what |
43
|
|
|
|
|
|
|
- src: 123.123.123.123 |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
# example of blocking an IP |
46
|
|
|
|
|
|
|
- { action: drop, src: 8.8.8.8 } |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
# example of allowing ip to connect to port 25 (smtp) (one-line) |
49
|
|
|
|
|
|
|
- { protocol: tcp, dport: 25, src: 4.2.2.2 } |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
# jump to rules defined in "foo" above |
52
|
|
|
|
|
|
|
- action: foo |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
# if there are no more rules, reject the connection with icmp, don't just let it hang |
55
|
|
|
|
|
|
|
- action: reject |
56
|
|
|
|
|
|
|
action_options: |
57
|
|
|
|
|
|
|
reject-with: icmp-admin-prohibited |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
=head1 DESCRIPTION |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
This module allows for the management of iptables rules with Perl / YAML. |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
=head1 TABLES |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
The top hashref is the table for iptables, this can be either mangle, nat, or filter. |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
=head1 CHAINS |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
The hashref under the top hashref is the chain name. For system chains the default chainrule can |
70
|
|
|
|
|
|
|
be set by setting a default hashref in the chain. |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
$VAR1->{filter}{input} would be the input chain for the filter table. |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
=head1 CHAIN RULES |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
Chainrules live in a 'rules' arrayref under the chain, $VAR1->{filter}{input}{rules}, for example. |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
Every rule in the chain is a hashref which builds a rule. By default, the jump in the rules, referenced |
79
|
|
|
|
|
|
|
as 'action' in a rule, is set to accept. The default action can be modified by changing |
80
|
|
|
|
|
|
|
'default_rule_action' in the chain. Every key in the rule's hashref represents a parameter prefixed by two dashes, '--', |
81
|
|
|
|
|
|
|
in an iptables rule. Two things to note here are that 'action' in a rule really maps to 'jump' in iptables, and |
82
|
|
|
|
|
|
|
a special action_options key exists, which references a hashref, which appends options after the iptables jump. This is |
83
|
|
|
|
|
|
|
useful for things like setting '--reject-with' after a jump to reject. |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
Examples of a chain rule: |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
# by default, allow this ip |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
$VAR1->{filter}{input}{rules}[0] = { src => '10.10.10.10' } ; |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
# allow this ip on port 25 tcp, using accept default |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
$VAR1->{filter}{input}{rules}[1] = { protocol: 'tcp', dport: 25, src => '10.10.10.10' } ; |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
# make it explicit |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
$VAR1->{filter}{input}{rules}[2] = { protocol: 'tcp', dport: 25, src => '10.10.10.10', action => 'accept' } ; |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
# blacklist an ip |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
$VAR1->{filter}{input}{rules}[3] = { src => '10.10.10.10', action => 'drop' } ; |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
# reject with icmp message |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
$VAR1->{filter}{input}{rules}[-1] = { |
111
|
|
|
|
|
|
|
action => 'reject', |
112
|
|
|
|
|
|
|
action_options => { |
113
|
|
|
|
|
|
|
reject-with: 'icmp-admin-prohibited', |
114
|
|
|
|
|
|
|
}, |
115
|
|
|
|
|
|
|
}; |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
=cut |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
=head1 METHODS |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
=head2 process_config |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
Given a hashref, produces rules usable by iptables-restore. |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
Returns one string. |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
=cut |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
sub process_config |
130
|
|
|
|
|
|
|
{ |
131
|
1
|
|
|
1
|
1
|
25958
|
my $config = shift; |
132
|
|
|
|
|
|
|
|
133
|
1
|
|
|
|
|
3
|
my $config_out = ''; |
134
|
|
|
|
|
|
|
|
135
|
1
|
|
|
|
|
8
|
$config_out .= _process_table({ |
136
|
|
|
|
|
|
|
table => 'mangle', |
137
|
|
|
|
|
|
|
chains => [ qw(prerouting input forward output postrouting) ], |
138
|
|
|
|
|
|
|
config => $config, |
139
|
|
|
|
|
|
|
}); |
140
|
|
|
|
|
|
|
|
141
|
1
|
|
|
|
|
7
|
$config_out .= _process_table({ |
142
|
|
|
|
|
|
|
table => 'nat', |
143
|
|
|
|
|
|
|
chains => [ qw(prerouting postrouting output) ], |
144
|
|
|
|
|
|
|
config => $config, |
145
|
|
|
|
|
|
|
}); |
146
|
|
|
|
|
|
|
|
147
|
1
|
|
|
|
|
9
|
$config_out .= _process_table({ |
148
|
|
|
|
|
|
|
table => 'filter', |
149
|
|
|
|
|
|
|
chains => [ qw(input forward output) ], |
150
|
|
|
|
|
|
|
config => $config, |
151
|
|
|
|
|
|
|
}); |
152
|
|
|
|
|
|
|
|
153
|
1
|
|
|
|
|
5
|
return $config_out; |
154
|
|
|
|
|
|
|
} |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
sub _process_table |
157
|
|
|
|
|
|
|
{ |
158
|
3
|
|
|
3
|
|
4
|
my $args = shift; |
159
|
|
|
|
|
|
|
|
160
|
3
|
|
|
|
|
5
|
my $table = $args->{table}; |
161
|
3
|
|
|
|
|
4
|
my $config = $args->{config}; |
162
|
|
|
|
|
|
|
|
163
|
3
|
|
|
|
|
4
|
my $table_out = ''; |
164
|
3
|
|
|
|
|
5
|
$table_out .= "*$table\n"; |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
# configure built-in chains |
167
|
3
|
|
|
|
|
35
|
$table_out .= ":" . uc($_) . ' ' . |
168
|
|
|
|
|
|
|
(exists $config->{$table}{$_} ? |
169
|
|
|
|
|
|
|
uc($config->{$table}{$_}{default}) : 'ACCEPT') . "\n" |
170
|
3
|
100
|
|
|
|
4
|
for @{$args->{chains}}; |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
# configure custom chains |
173
|
3
|
50
|
|
|
|
4
|
for my $chain (keys %{$config->{$table} || {} }) |
|
3
|
|
|
|
|
14
|
|
174
|
|
|
|
|
|
|
{ |
175
|
|
|
|
|
|
|
# skip built-in chains |
176
|
2
|
50
|
|
|
|
3
|
next if grep { $chain eq $_ } @{$args->{chains}}; |
|
6
|
|
|
|
|
13
|
|
|
2
|
|
|
|
|
3
|
|
177
|
|
|
|
|
|
|
|
178
|
0
|
|
|
|
|
0
|
$table_out .= ":" . uc($chain) . " -\n"; |
179
|
|
|
|
|
|
|
} |
180
|
|
|
|
|
|
|
|
181
|
3
|
50
|
|
|
|
6
|
for my $chain (keys %{$config->{$table} || {} }) |
|
3
|
|
|
|
|
9
|
|
182
|
|
|
|
|
|
|
{ |
183
|
2
|
100
|
|
|
|
29
|
$table_out .= &_process_rule({ |
184
|
|
|
|
|
|
|
target => ( |
185
|
|
|
|
|
|
|
uc($_->{action} || '') |
186
|
|
|
|
|
|
|
|| uc($config->{$table}{$chain}{default_rule_action} || '') |
187
|
|
|
|
|
|
|
|| 'ACCEPT'), |
188
|
|
|
|
|
|
|
chain => uc($chain), |
189
|
|
|
|
|
|
|
rule => $_ |
190
|
2
|
|
50
|
|
|
3
|
}) . "\n" for @{$config->{filter}{$chain}{rules} || []}; |
191
|
|
|
|
|
|
|
} |
192
|
|
|
|
|
|
|
|
193
|
3
|
|
|
|
|
5
|
$table_out .= "COMMIT\n"; |
194
|
|
|
|
|
|
|
|
195
|
3
|
|
|
|
|
7
|
return $table_out; |
196
|
|
|
|
|
|
|
} |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
sub _process_rule |
199
|
|
|
|
|
|
|
{ |
200
|
8
|
|
|
8
|
|
11
|
my $args = shift; |
201
|
8
|
|
|
|
|
14
|
my $output = "-A $args->{chain} "; |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
# remove action from rule |
204
|
8
|
|
|
|
|
11
|
delete $args->{rule}{action}; |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
# grab action options |
207
|
8
|
|
|
|
|
11
|
my $action_opts = delete $args->{rule}{action_options}; |
208
|
8
|
|
|
|
|
23
|
my $action_opts_str = ''; |
209
|
|
|
|
|
|
|
|
210
|
8
|
50
|
|
|
|
15
|
if ($action_opts) |
211
|
|
|
|
|
|
|
{ |
212
|
|
|
|
|
|
|
$action_opts_str .= "--$_ $action_opts->{$_} " |
213
|
0
|
|
|
|
|
0
|
for keys %$action_opts; |
214
|
|
|
|
|
|
|
} |
215
|
|
|
|
|
|
|
|
216
|
8
|
|
|
|
|
40
|
$output .= "--$_ $args->{rule}{$_} " |
217
|
8
|
|
|
|
|
5
|
for keys %{$args->{rule}}; |
218
|
|
|
|
|
|
|
|
219
|
8
|
|
|
|
|
19
|
$output .= "-j $args->{target} $action_opts_str"; |
220
|
|
|
|
|
|
|
|
221
|
8
|
|
|
|
|
94
|
return $output; |
222
|
|
|
|
|
|
|
} |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
1; |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
=head1 AUTHORS |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
Bizowie |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
Copyright (C) 2013 Bizowie |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
This library is free software. You can redistribute it and/or modify it under the same terms as Perl itself. |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
=cut |