]> git.ipfire.org Git - people/stevee/network.git/blame - functions.firewall
aiccu: Add documentation.
[people/stevee/network.git] / functions.firewall
CommitLineData
98146c00
MT
1#!/bin/bash
2###############################################################################
3# #
4# IPFire.org - A linux based firewall #
5# Copyright (C) 2012 IPFire Network Development Team #
6# #
7# This program is free software: you can redistribute it and/or modify #
8# it under the terms of the GNU General Public License as published by #
9# the Free Software Foundation, either version 3 of the License, or #
10# (at your option) any later version. #
11# #
12# This program is distributed in the hope that it will be useful, #
13# but WITHOUT ANY WARRANTY; without even the implied warranty of #
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
15# GNU General Public License for more details. #
16# #
17# You should have received a copy of the GNU General Public License #
18# along with this program. If not, see <http://www.gnu.org/licenses/>. #
19# #
20###############################################################################
21
22# High-level function which will create a ruleset for the current firewall
23# configuration and load it into the kernel.
24function firewall_start() {
afb7d704
MT
25 # Test mode.
26 local test="false"
27
28 while [ $# -gt 0 ]; do
29 case "${1}" in
30 --test)
31 test="true"
32 ;;
b4fc59c7
MT
33 *)
34 error "Unrecognized argument: ${1}"
35 return ${EXIT_ERROR}
36 ;;
afb7d704
MT
37 esac
38 shift
39 done
40
41 if enabled test; then
42 log INFO "Test mode enabled."
43 log INFO "The firewall ruleset will not be loaded."
44 fi
45
98146c00
MT
46 firewall_lock_acquire
47
48 # Initialize an empty iptables ruleset.
49 iptables_init DROP
50
51 # Add default chains.
52 firewall_tcp_state_flags
3b256a38 53 firewall_custom_chains
98146c00 54 firewall_connection_tracking
c2d2d2a9 55 firewall_tcp_clamp_mss
98146c00
MT
56
57 # Add policies for every zone.
a2c9dff5 58 firewall_localhost_create_chains
98146c00
MT
59
60 local zone
61 for zone in $(zones_get_all); do
a2c9dff5
MT
62 # Create all needed chains for the zone.
63 firewall_zone_create_chains ${zone}
64
65 # After the chains that are always available have been
66 # created, we will add a custom policy to every single
67 # zone.
68
478de6f9 69 policy_zone_add ${zone}
98146c00
MT
70 done
71
afb7d704
MT
72 # Load the new ruleset.
73 iptables_load ${test}
98146c00
MT
74
75 firewall_lock_release
76}
77
78function firewall_stop() {
79 firewall_lock_acquire
80
81 # Initialize an empty firewall ruleset
82 # with default policy ACCEPT.
83 iptables_init ACCEPT
84
afb7d704
MT
85 # Load it.
86 iptables_load
87
88 firewall_lock_release
89}
90
91function firewall_show() {
92 # Shows the ruleset that is currently loaded.
93 iptables_status
94
95 return ${EXIT_OK}
96}
97
98function firewall_panic() {
99 local admin_hosts="$@"
100
101 firewall_lock_acquire
102
103 # Drop all communications.
104 iptables_init DROP
105
106 # If an admin host is provided, some administrative
107 # things will be allowed from there.
108 local admin_host
109 for admin_host in ${admin_hosts}; do
110 iptables -A INPUT -s ${admin_host} -j ACCEPT
111 iptables -A OUTPUT -d ${admin_host} -j ACCEPT
112 done
113
114 # Load it.
115 iptables_load
98146c00
MT
116
117 firewall_lock_release
118}
119
120function firewall_lock_acquire() {
121 lock_acquire ${RUN_DIR}/.firewall_lock
122
123 # Make sure the lock is released after the firewall
124 # script has crashed or exited early.
125 trap firewall_lock_release EXIT TERM KILL
126
127 # Create a directory where we can put our
128 # temporary data in the most secure way as possible.
129 IPTABLES_TMPDIR=$(mktemp -d)
130}
131
132function firewall_lock_release() {
133 if isset IPTABLES_TMPDIR; then
134 # Remove all temporary data.
135 rm -rf ${IPTABLES_TMPDIR}
136
137 # Reset the tempdir variable.
138 IPTABLES_TMPDIR=
139 fi
140
141 # Reset the trap.
142 trap true EXIT TERM KILL
143
144 lock_release ${RUN_DIR}/.firewall_lock
145}
146
3b256a38
MT
147function firewall_custom_chains() {
148 log INFO "Creating CUSTOM* chains..."
149
150 # These chains are intened to be filled with
151 # rules by the user. They are processed at the very
152 # beginning so it is possible to overwrite everything.
153
154 iptables_chain_create CUSTOMINPUT
155 iptables -A INPUT -j CUSTOMINPUT
156
157 iptables_chain_create CUSTOMFORWARD
158 iptables -A FORWARD -j CUSTOMFORWARD
159
160 iptables_chain_create CUSTOMOUTPUT
161 iptables -A OUTPUT -j CUSTOMOUTPUT
162
163 iptables_chain_create -4 -t nat CUSTOMPREROUTING
164 iptables -4 -t nat -A PREROUTING -j CUSTOMPREROUTING
165
166 iptables_chain_create -4 -t nat CUSTOMPOSTROUTING
167 iptables -4 -t nat -A POSTROUTING -j CUSTOMPOSTROUTING
168
169 iptables_chain_create -4 -t nat CUSTOMOUTPUT
170 iptables -4 -t nat -A OUTPUT -j CUSTOMOUTPUT
171}
172
98146c00
MT
173function firewall_tcp_state_flags() {
174 log INFO "Creating TCP State Flags chain..."
175 iptables_chain_create BADTCP_LOG
176 iptables -A BADTCP_LOG -p tcp -j $(iptables_LOG "Illegal TCP state: ")
177 iptables -A BADTCP_LOG -j DROP
178
179 iptables_chain_create BADTCP
180 iptables -A BADTCP -p tcp --tcp-flags ALL NONE -j BADTCP_LOG
181 iptables -A BADTCP -p tcp --tcp-flags SYN,FIN SYN,FIN -j BADTCP_LOG
182 iptables -A BADTCP -p tcp --tcp-flags SYN,RST SYN,RST -j BADTCP_LOG
183 iptables -A BADTCP -p tcp --tcp-flags FIN,RST FIN,RST -j BADTCP_LOG
184 iptables -A BADTCP -p tcp --tcp-flags ACK,FIN FIN -j BADTCP_LOG
185 iptables -A BADTCP -p tcp --tcp-flags ACK,PSH PSH -j BADTCP_LOG
186 iptables -A BADTCP -p tcp --tcp-flags ACK,URG URG -j BADTCP_LOG
187
188 iptables -A INPUT -p tcp -j BADTCP
189 iptables -A OUTPUT -p tcp -j BADTCP
190 iptables -A FORWARD -p tcp -j BADTCP
191}
192
c2d2d2a9 193function firewall_tcp_clamp_mss() {
fc323fc4
MT
194 # Do nothing if this has been disabled.
195 enabled FIREWALL_CLAMP_PATH_MTU || return ${EXIT_OK}
196
c2d2d2a9
MT
197 log DEBUG "Adding rules to clamp MSS to path MTU..."
198 iptables -t mangle -A FORWARD \
199 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
200}
201
98146c00
MT
202function firewall_connection_tracking() {
203 log INFO "Creating Connection Tracking chain..."
204 iptables_chain_create CONNTRACK
205 iptables -A CONNTRACK -m state --state ESTABLISHED,RELATED -j ACCEPT
206 iptables -A CONNTRACK -m state --state INVALID -j $(iptables_LOG "INVALID packet: ")
207 iptables -A CONNTRACK -m state --state INVALID -j DROP
208
209 iptables -A INPUT -j CONNTRACK
210 iptables -A OUTPUT -j CONNTRACK
211 iptables -A FORWARD -j CONNTRACK
212}
3647b19f 213
a2c9dff5
MT
214function firewall_localhost_create_chains() {
215 log DEBUG "Creating firewall chains for localhost..."
216
217 # Accept everything on lo
218 iptables -A INPUT -i lo -m state --state NEW -j ACCEPT
219 iptables -A OUTPUT -o lo -m state --state NEW -j ACCEPT
220}
221
222function firewall_zone_create_chains() {
3647b19f 223 local zone=${1}
a2c9dff5
MT
224 assert isset zone
225
226 log DEBUG "Creating firewall chains for zone '${zone}'."
227
228 local chain_prefix="ZONE_${zone^^}"
229
230 # Create filter chains.
231 iptables_chain_create "${chain_prefix}_INPUT"
232 iptables -A INPUT -i ${zone} -j "${chain_prefix}_INPUT"
233
234 iptables_chain_create "${chain_prefix}_OUTPUT"
235 iptables -A OUTPUT -o ${zone} -j "${chain_prefix}_OUTPUT"
236
237 # Custom rules.
238 iptables_chain_create "${chain_prefix}_CUSTOM"
239
240 # Intrusion Prevention System.
241 iptables_chain_create "${chain_prefix}_IPS"
242
243 # Create a chain for each other zone.
244 # This leaves us with n^2 chains. Duh.
245
246 local other_zone other_chain_prefix
247 for other_zone in $(zones_get_all); do
248 other_chain_prefix="${chain_prefix}_${other_zone^^}"
249 iptables_chain_create ${other_chain_prefix}
250
251 # Connect the chain with the FORWARD chain.
252 iptables -A FORWARD -i ${zone} -o ${other_zone} \
253 -j "${other_chain_prefix}"
254
255 # Handle custom rules.
256 iptables -A ${other_chain_prefix} -j "${chain_prefix}_CUSTOM"
257
258 # Link IPS.
259 iptables -A ${other_chain_prefix} -j "${chain_prefix}_IPS"
260
261 # Rules.
262 iptables_chain_create "${other_chain_prefix}_RULES"
263 iptables -A ${other_chain_prefix} -j "${other_chain_prefix}_RULES"
264
265 # Policy.
266 iptables_chain_create "${other_chain_prefix}_POLICY"
267 iptables -A ${other_chain_prefix} -j "${other_chain_prefix}_POLICY"
268 done
269
270 ## Create mangle chain.
271 #iptables_chain_create -t mangle ${chain_prefix}
272 #iptables -t mangle -A PREROUTING -i ${zone} -j ${chain_prefix}
273 #iptables -t mangle -A POSTROUTING -o ${zone} -j ${chain_prefix}
274
275 ## Quality of Service
276 #iptables_chain_create -t mangle "${chain_prefix}_QOS_INC"
277 #iptables -t mangle -A ${chain_prefix} -i ${zone} -j "${chain_prefix}_QOS_INC"
278 #iptables_chain_create -t mangle "${chain_prefix}_QOS_OUT"
279 #iptables -t mangle -A ${chain_prefix} -o ${zone} -j "${chain_prefix}_QOS_OUT"
280
281 # Create NAT chain.
282 iptables_chain_create -4 -t nat ${chain_prefix}
283 iptables -4 -t nat -A PREROUTING -i ${zone} -j ${chain_prefix}
284 iptables -4 -t nat -A POSTROUTING -o ${zone} -j ${chain_prefix}
285
286 # Network Address Translation
287 iptables_chain_create -4 -t nat "${chain_prefix}_DNAT"
288 iptables -4 -t nat -A PREROUTING -i ${zone} -j "${chain_prefix}_DNAT"
289 iptables_chain_create -4 -t nat "${chain_prefix}_SNAT"
290 iptables -4 -t nat -A POSTROUTING -o ${zone} -j "${chain_prefix}_SNAT"
291
292 # UPnP
293 iptables_chain_create -4 -t nat "${chain_prefix}_UPNP"
294 iptables -4 -t nat -A ${chain_prefix} -j "${chain_prefix}_UPNP"
295
296 return ${EXIT_OK}
297}
298
299function firewall_parse_rules() {
300 local file=${1}
301 assert isset file
3647b19f
MT
302 shift
303
a2c9dff5
MT
304 # End if no rule file exists.
305 [ -r "${file}" ] || return ${EXIT_OK}
3647b19f 306
a2c9dff5
MT
307 local cmd
308
309 local ${FIREWALL_RULES_CONFIG_PARAMS}
310 local line
311 while read -r line; do
312 # Skip empty lines.
313 [ -n "${line}" ] || continue
314
315 # Skip commented lines.
316 [ "${line:0:1}" = "#" ] && continue
317
318 # Parse the rule.
319 _firewall_parse_rule_line ${line}
320 if [ $? -ne ${EXIT_OK} ]; then
321 log WARNING "Skipping invalid line: ${line}"
322 continue
323 fi
324
325 cmd="iptables $@"
326
327 # Source IP address/net.
328 if isset src; then
329 list_append cmd "-s ${src}"
330 fi
331
332 # Destination IP address/net.
333 if isset dst; then
334 list_append cmd "-d ${dst}"
335 fi
336
337 # Protocol.
338 if isset proto; then
339 list_append cmd "-p ${proto}"
340
341 if list_match ${proto} ${FIREWALL_PROTOCOLS_SUPPORTING_PORTS}; then
342 if isset sport; then
343 list_append cmd "--sport ${sport}"
344 fi
345
346 if isset dport; then
347 list_append cmd "--dport ${dport}"
348 fi
349 fi
350 fi
351
352 # Always append the action.
353 list_append cmd "-j ${action}"
354
355 # Execute command.
356 ${cmd}
357 done < ${file}
358}
359
360function _firewall_parse_rule_line() {
361 local arg
362
363 # Clear all values.
364 for arg in ${FIREWALL_RULES_CONFIG_PARAMS}; do
365 assign "${arg}" ""
3647b19f
MT
366 done
367
a2c9dff5
MT
368 local key val
369 while read -r arg; do
370 key=$(cli_get_key ${arg})
3647b19f 371
a2c9dff5
MT
372 if ! listmatch "${key}" ${FIREWALL_RULES_CONFIG_PARAMS}; then
373 log WARNING "Unrecognized argument: ${arg}"
374 return ${EXIT_ERROR}
375 fi
3647b19f 376
a2c9dff5
MT
377 val=$(cli_get_val ${arg})
378 assign "${key}" "${val}"
379 done <<< "$(args $@)"
380
381 # action must always be set.
382 if ! isset action; then
383 log WARNING "'action' is not set: $@"
384 return ${EXIT_ERROR}
385 fi
386
387 for arg in src dst; do
388 isset ${arg} || continue
389
390 # Check for valid IP addresses.
391 if ! ip_is_valid ${!arg}; then
392 log WARNING "Invalid IP address for '${arg}=${!arg}': $@"
393 return ${EXIT_ERROR}
394 fi
395 done
396
397 if isset proto; then
398 # Make lowercase.
399 proto=${proto,,}
400
401 if ! list_match "${proto}" ${FIREWALL_SUPPORTED_PROTOCOLS}; then
402 log WARNING "Unsupported protocol type 'proto=${proto}': $@"
403 return ${EXIT_ERROR}
404 fi
405 fi
406
407 for arg in sport dport; do
408 isset ${arg} || continue
409
410 # Check if port is valid.
411 if ! isinteger ${arg}; then
412 log WARNING "Invalid port '${arg}=${!arg}': $@"
413 return ${EXIT_ERROR}
414 fi
415 done
416
417 return ${EXIT_OK}
3647b19f 418}