]> git.ipfire.org Git - people/stevee/network.git/blame - functions.firewall
firewall: Add global ICMP filter table.
[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() {
fe52c5e0
MT
25 local protocol="${1}"
26 assert isset protocol
27 shift
28
afb7d704
MT
29 # Test mode.
30 local test="false"
31
32 while [ $# -gt 0 ]; do
33 case "${1}" in
34 --test)
35 test="true"
36 ;;
b4fc59c7
MT
37 *)
38 error "Unrecognized argument: ${1}"
39 return ${EXIT_ERROR}
40 ;;
afb7d704
MT
41 esac
42 shift
43 done
44
45 if enabled test; then
46 log INFO "Test mode enabled."
47 log INFO "The firewall ruleset will not be loaded."
48 fi
49
98146c00
MT
50 firewall_lock_acquire
51
52 # Initialize an empty iptables ruleset.
fe52c5e0 53 iptables_init "${protocol}" "DROP"
98146c00
MT
54
55 # Add default chains.
a7e23f3c 56 firewall_filter_rh0_headers "${protocol}"
204013de 57 firewall_filter_icmp "${protocol}"
fe52c5e0
MT
58 firewall_tcp_state_flags "${protocol}"
59 firewall_custom_chains "${protocol}"
60 firewall_connection_tracking "${protocol}"
61 firewall_tcp_clamp_mss "${protocol}"
98146c00
MT
62
63 # Add policies for every zone.
fe52c5e0 64 firewall_localhost_create_chains "${protocol}"
98146c00
MT
65
66 local zone
67 for zone in $(zones_get_all); do
a2c9dff5 68 # Create all needed chains for the zone.
fe52c5e0 69 firewall_zone_create_chains "${protocol}" "${zone}"
a2c9dff5
MT
70
71 # After the chains that are always available have been
72 # created, we will add a custom policy to every single
73 # zone.
74
fe52c5e0 75 policy_zone_add "${protocol}" "${zone}"
98146c00
MT
76 done
77
afb7d704 78 # Load the new ruleset.
fe52c5e0
MT
79 local args
80 if enabled testmode; then
81 list_append args "--test"
82 fi
83 iptables_commit "${protocol}" ${args}
98146c00
MT
84
85 firewall_lock_release
86}
87
88function firewall_stop() {
fe52c5e0
MT
89 local protocol="${1}"
90 assert isset protocol
91
98146c00
MT
92 firewall_lock_acquire
93
94 # Initialize an empty firewall ruleset
95 # with default policy ACCEPT.
fe52c5e0 96 iptables_init "${protocol}" ACCEPT
98146c00 97
afb7d704 98 # Load it.
fe52c5e0 99 ipables_load "${protocol}"
afb7d704
MT
100
101 firewall_lock_release
102}
103
104function firewall_show() {
fe52c5e0
MT
105 local protocol="${1}"
106 assert isset protocol
107
afb7d704 108 # Shows the ruleset that is currently loaded.
fe52c5e0 109 iptables_status "${protocol}"
afb7d704
MT
110
111 return ${EXIT_OK}
112}
113
114function firewall_panic() {
fe52c5e0
MT
115 local protocol="${1}"
116 assert isset protocol
117 shift
118
afb7d704
MT
119 local admin_hosts="$@"
120
fe52c5e0 121 firewall_lock_acquire "${protocol}"
afb7d704
MT
122
123 # Drop all communications.
fe52c5e0 124 iptables_init "${protocol}" DROP
afb7d704
MT
125
126 # If an admin host is provided, some administrative
127 # things will be allowed from there.
128 local admin_host
129 for admin_host in ${admin_hosts}; do
fe52c5e0
MT
130 iptables "${protocol}" -A INPUT -s "${admin_host}" -j ACCEPT
131 iptables "${protocol}" -A OUTPUT -d "${admin_host}" -j ACCEPT
afb7d704
MT
132 done
133
134 # Load it.
fe52c5e0 135 iptables_commit "${protocol}"
98146c00
MT
136
137 firewall_lock_release
138}
139
140function firewall_lock_acquire() {
141 lock_acquire ${RUN_DIR}/.firewall_lock
142
143 # Make sure the lock is released after the firewall
144 # script has crashed or exited early.
145 trap firewall_lock_release EXIT TERM KILL
146
147 # Create a directory where we can put our
148 # temporary data in the most secure way as possible.
149 IPTABLES_TMPDIR=$(mktemp -d)
150}
151
152function firewall_lock_release() {
153 if isset IPTABLES_TMPDIR; then
154 # Remove all temporary data.
155 rm -rf ${IPTABLES_TMPDIR}
156
157 # Reset the tempdir variable.
158 IPTABLES_TMPDIR=
159 fi
160
161 # Reset the trap.
162 trap true EXIT TERM KILL
163
164 lock_release ${RUN_DIR}/.firewall_lock
165}
166
3b256a38 167function firewall_custom_chains() {
fe52c5e0
MT
168 local protocol="${1}"
169 assert isset protocol
170
3b256a38
MT
171 log INFO "Creating CUSTOM* chains..."
172
173 # These chains are intened to be filled with
174 # rules by the user. They are processed at the very
175 # beginning so it is possible to overwrite everything.
176
fe52c5e0
MT
177 iptables_chain_create "${protocol}" CUSTOMINPUT
178 iptables "${protocol}" -A INPUT -j CUSTOMINPUT
3b256a38 179
fe52c5e0
MT
180 iptables_chain_create "${protocol}" CUSTOMFORWARD
181 iptables "${protocol}" -A FORWARD -j CUSTOMFORWARD
3b256a38 182
fe52c5e0
MT
183 iptables_chain_create "${protocol}" CUSTOMOUTPUT
184 iptables "${protocol}" -A OUTPUT -j CUSTOMOUTPUT
3b256a38 185
fe52c5e0
MT
186 iptables_chain_create "${protocol}" -t nat CUSTOMPREROUTING
187 iptables "${protocol}" -t nat -A PREROUTING -j CUSTOMPREROUTING
3b256a38 188
fe52c5e0
MT
189 iptables_chain_create "${protocol}" -t nat CUSTOMPOSTROUTING
190 iptables "${protocol}" -t nat -A POSTROUTING -j CUSTOMPOSTROUTING
3b256a38 191
fe52c5e0
MT
192 iptables_chain_create "${protocol}" -t nat CUSTOMOUTPUT
193 iptables "${protocol}" -t nat -A OUTPUT -j CUSTOMOUTPUT
3b256a38
MT
194}
195
98146c00 196function firewall_tcp_state_flags() {
fe52c5e0
MT
197 local protocol="${1}"
198 assert isset protocol
199
98146c00 200 log INFO "Creating TCP State Flags chain..."
fe52c5e0
MT
201
202 iptables_chain_create "${protocol}" BADTCP_LOG
203 iptables "${protocol}" -A BADTCP_LOG -p tcp -j "$(iptables_LOG "Illegal TCP state: ")"
204 iptables "${protocol}" -A BADTCP_LOG -j DROP
205
206 iptables_chain_create "${protocol}" BADTCP
207 iptables "${protocol}" -A BADTCP -p tcp --tcp-flags ALL NONE -j BADTCP_LOG
208 iptables "${protocol}" -A BADTCP -p tcp --tcp-flags SYN,FIN SYN,FIN -j BADTCP_LOG
209 iptables "${protocol}" -A BADTCP -p tcp --tcp-flags SYN,RST SYN,RST -j BADTCP_LOG
210 iptables "${protocol}" -A BADTCP -p tcp --tcp-flags FIN,RST FIN,RST -j BADTCP_LOG
211 iptables "${protocol}" -A BADTCP -p tcp --tcp-flags ACK,FIN FIN -j BADTCP_LOG
212 iptables "${protocol}" -A BADTCP -p tcp --tcp-flags ACK,PSH PSH -j BADTCP_LOG
213 iptables "${protocol}" -A BADTCP -p tcp --tcp-flags ACK,URG URG -j BADTCP_LOG
214
215 iptables "${protocol}" -A INPUT -p tcp -j BADTCP
216 iptables "${protocol}" -A OUTPUT -p tcp -j BADTCP
217 iptables "${protocol}" -A FORWARD -p tcp -j BADTCP
98146c00
MT
218}
219
c2d2d2a9 220function firewall_tcp_clamp_mss() {
fc323fc4
MT
221 # Do nothing if this has been disabled.
222 enabled FIREWALL_CLAMP_PATH_MTU || return ${EXIT_OK}
223
fe52c5e0
MT
224 local protocol="${1}"
225 assert isset protocol
226
c2d2d2a9 227 log DEBUG "Adding rules to clamp MSS to path MTU..."
fe52c5e0
MT
228
229 iptables "${protocol}" -t mangle -A FORWARD \
c2d2d2a9
MT
230 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
231}
232
98146c00 233function firewall_connection_tracking() {
fe52c5e0
MT
234 local protocol="${1}"
235 assert isset protocol
236
98146c00 237 log INFO "Creating Connection Tracking chain..."
fe52c5e0
MT
238
239 iptables_chain_create "${protocol}" CONNTRACK
85b52db6
MT
240 iptables "${protocol}" -A CONNTRACK -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
241 iptables "${protocol}" -A CONNTRACK -m conntrack --ctstate INVALID -j "$(iptables_LOG "INVALID packet: ")"
242 iptables "${protocol}" -A CONNTRACK -m conntrack --ctstate INVALID -j DROP
fe52c5e0
MT
243
244 iptables "${protocol}" -A INPUT -j CONNTRACK
245 iptables "${protocol}" -A OUTPUT -j CONNTRACK
246 iptables "${protocol}" -A FORWARD -j CONNTRACK
98146c00 247}
3647b19f 248
a2c9dff5 249function firewall_localhost_create_chains() {
fe52c5e0
MT
250 local protocol="${1}"
251 assert isset protocol
252
a2c9dff5
MT
253 log DEBUG "Creating firewall chains for localhost..."
254
255 # Accept everything on lo
fe9bf26b
MT
256 iptables "${protocol}" -A INPUT -i lo -j ACCEPT
257 iptables "${protocol}" -A OUTPUT -o lo -j ACCEPT
a2c9dff5
MT
258}
259
a7e23f3c
MT
260function firewall_filter_rh0_headers() {
261 local protocol="${1}"
262 assert isset protocol
263
264 # Only IPv6.
265 [ "${protocol}" = "ipv6" ] || return ${EXIT_OK}
266
267 # Filter all packets that have RH0 headers
268 # http://www.ietf.org/rfc/rfc5095.txt
269 iptables_chain_create "${protocol}" FILTER_RH0
270 iptables "${protocol}" -A FILTER_RH0 -m rt --rt-type 0 -j DROP
271
272 iptables "${protocol}" -A INPUT -j FILTER_RH0
273 iptables "${protocol}" -A FORWARD -j FILTER_RH0
274 iptables "${protocol}" -A OUTPUT -j FILTER_RH0
275}
276
204013de
MT
277function firewall_filter_icmp() {
278 local protocol="${1}"
279 assert isset protocol
280
281 # Only IPv6.
282 [ "${protocol}" = "ipv6" ] || return ${EXIT_OK}
283
284 local chain="FILTER_ICMPV6"
285
286 # Create an extra chain for handling ICMP packets.
287 iptables_chain_create "${protocol}" "${chain}_COMMON"
288
289 local suffix
290 for suffix in INC FWD OUT; do
291 iptables_chain_create "${protocol}" "${chain}_${suffix}"
292 iptables "${protocol}" -A "${chain}_${suffix}" -j "${chain}_COMMON"
293 done
294 iptables "${protocol}" -A INPUT -p icmpv6 -j "${chain}_INC"
295 iptables "${protocol}" -A FORWARD -p icmpv6 -j "${chain}_FWD"
296 iptables "${protocol}" -A OUTPUT -p icmpv6 -j "${chain}_OUT"
297
298 # Packets that must always pass the firewall.
299 # Type 4: Parameter Problem
300 local type
301 for type in ttl-zero-during-reassembly bad-header; do
302 iptables "${protocol}" -A "${chain}_COMMON" \
303 -p icmpv6 --icmpv6-type "${type}" -j ACCEPT
304 done
305
306 # Packets that are accepted if they belong to an existing connection.
307 for type in echo-reply destination-unreachable packet-too-big \
308 unknown-header-type unknown-option; do
309 iptables "${protocol}" -A "${chain}_COMMON" \
310 -m conntrack --ctstate ESTABLISHED,RELATED \
311 -p icmpv6 --icmpv6-type "${type}" -j ACCEPT
312 done
313
314 # Packets that are always discarded.
315 # Type 100, 101, 200, 201: Private Experimentation
316 for type in 100 101 200 201; do
317 iptables "${protocol}" -A "${chain}_COMMON" \
318 -p icmpv6 --icmpv6-type "${type}" -j DROP
319 done
320
321 # Discard packets from local networks with hop limit smaller than $hoplimit.
322 # Type 148: Path solicitation
323 # Type 149: Path advertisement
324 local hoplimit=255
325 for type in {router,neighbour}-{advertisement,solicitation} 148 149; do
326 iptables "${protocol}" -A "${chain}_INC" \
327 -p icmpv6 --icmpv6-type "${type}" \
328 -m hl --hl-lt "${hoplimit}" -j DROP
329 done
330
331 # The firewall is always allowed to send ICMP echo requests.
332 iptables "${protocol}" -A "${chain}_OUT" \
333 -p icmpv6 --icmpv6-type echo-request -j ACCEPT
334
335 return ${EXIT_OK}
336}
337
a2c9dff5 338function firewall_zone_create_chains() {
fe52c5e0
MT
339 local protocol="${1}"
340 assert isset protocol
341
342 local zone="${2}"
a2c9dff5
MT
343 assert isset zone
344
345 log DEBUG "Creating firewall chains for zone '${zone}'."
346
347 local chain_prefix="ZONE_${zone^^}"
348
349 # Create filter chains.
fe52c5e0
MT
350 iptables_chain_create "${protocol}" "${chain_prefix}_INPUT"
351 iptables "${protocol}" -A INPUT -i ${zone} -j "${chain_prefix}_INPUT"
a2c9dff5 352
fe52c5e0
MT
353 iptables_chain_create "${protocol}" "${chain_prefix}_OUTPUT"
354 iptables "${protocol}" -A OUTPUT -o ${zone} -j "${chain_prefix}_OUTPUT"
a2c9dff5
MT
355
356 # Custom rules.
fe52c5e0 357 iptables_chain_create "${protocol}" "${chain_prefix}_CUSTOM"
a2c9dff5
MT
358
359 # Intrusion Prevention System.
fe52c5e0 360 iptables_chain_create "${protocol}" "${chain_prefix}_IPS"
a2c9dff5
MT
361
362 # Create a chain for each other zone.
363 # This leaves us with n^2 chains. Duh.
364
365 local other_zone other_chain_prefix
366 for other_zone in $(zones_get_all); do
367 other_chain_prefix="${chain_prefix}_${other_zone^^}"
fe52c5e0 368 iptables_chain_create "${protocol}" "${other_chain_prefix}"
a2c9dff5
MT
369
370 # Connect the chain with the FORWARD chain.
fe52c5e0 371 iptables "${protocol}" -A FORWARD -i "${zone}" -o "${other_zone}" \
a2c9dff5
MT
372 -j "${other_chain_prefix}"
373
374 # Handle custom rules.
fe52c5e0 375 iptables "${protocol}" -A "${other_chain_prefix}" -j "${chain_prefix}_CUSTOM"
a2c9dff5
MT
376
377 # Link IPS.
fe52c5e0 378 iptables "${protocol}" -A "${other_chain_prefix}" -j "${chain_prefix}_IPS"
a2c9dff5
MT
379
380 # Rules.
fe52c5e0
MT
381 iptables_chain_create "${protocol}" "${other_chain_prefix}_RULES"
382 iptables "${protocol}" -A "${other_chain_prefix}" -j "${other_chain_prefix}_RULES"
a2c9dff5
MT
383
384 # Policy.
fe52c5e0
MT
385 iptables_chain_create "${protocol}" "${other_chain_prefix}_POLICY"
386 iptables "${protocol}" -A "${other_chain_prefix}" -j "${other_chain_prefix}_POLICY"
a2c9dff5
MT
387 done
388
389 ## Create mangle chain.
fe52c5e0
MT
390 #iptables_chain_create "${protocol}" -t mangle "${chain_prefix}"
391 #iptables "${protocol}" -t mangle -A PREROUTING -i "${zone}" -j "${chain_prefix}"
392 #iptables "${protocol}" -t mangle -A POSTROUTING -o "${zone}" -j "${chain_prefix}"
a2c9dff5
MT
393
394 ## Quality of Service
fe52c5e0
MT
395 #iptables_chain_create "${protocol}" -t mangle "${chain_prefix}_QOS_INC"
396 #iptables "${protocol}" -t mangle -A "${chain_prefix}" -i "${zone}" -j "${chain_prefix}_QOS_INC"
397 #iptables_chain_create "${protocol}" -t mangle "${chain_prefix}_QOS_OUT"
398 #iptables "${protocol}" -t mangle -A "${chain_prefix}" -o "${zone}" -j "${chain_prefix}_QOS_OUT"
a2c9dff5
MT
399
400 # Create NAT chain.
fe52c5e0
MT
401 iptables_chain_create "${protocol}" -t nat "${chain_prefix}"
402 iptables "${protocol}" -t nat -A PREROUTING -i "${zone}" -j "${chain_prefix}"
403 iptables "${protocol}" -t nat -A POSTROUTING -o "${zone}" -j "${chain_prefix}"
a2c9dff5
MT
404
405 # Network Address Translation
fe52c5e0
MT
406 iptables_chain_create "${protocol}" -t nat "${chain_prefix}_DNAT"
407 iptables "${protocol}" -t nat -A PREROUTING -i "${zone}" -j "${chain_prefix}_DNAT"
408 iptables_chain_create "${protocol}" -t nat "${chain_prefix}_SNAT"
409 iptables "${protocol}" -t nat -A POSTROUTING -o "${zone}" -j "${chain_prefix}_SNAT"
a2c9dff5
MT
410
411 # UPnP
fe52c5e0
MT
412 iptables_chain_create "${protocol}" -t nat "${chain_prefix}_UPNP"
413 iptables "${protocol}" -t nat -A "${chain_prefix}" -j "${chain_prefix}_UPNP"
a2c9dff5
MT
414
415 return ${EXIT_OK}
416}
417
418function firewall_parse_rules() {
419 local file=${1}
420 assert isset file
3647b19f
MT
421 shift
422
a2c9dff5
MT
423 # End if no rule file exists.
424 [ -r "${file}" ] || return ${EXIT_OK}
3647b19f 425
a2c9dff5
MT
426 local cmd
427
428 local ${FIREWALL_RULES_CONFIG_PARAMS}
429 local line
430 while read -r line; do
431 # Skip empty lines.
432 [ -n "${line}" ] || continue
433
434 # Skip commented lines.
435 [ "${line:0:1}" = "#" ] && continue
436
437 # Parse the rule.
438 _firewall_parse_rule_line ${line}
439 if [ $? -ne ${EXIT_OK} ]; then
440 log WARNING "Skipping invalid line: ${line}"
441 continue
442 fi
443
444 cmd="iptables $@"
445
446 # Source IP address/net.
447 if isset src; then
448 list_append cmd "-s ${src}"
449 fi
450
451 # Destination IP address/net.
452 if isset dst; then
453 list_append cmd "-d ${dst}"
454 fi
455
456 # Protocol.
457 if isset proto; then
458 list_append cmd "-p ${proto}"
459
460 if list_match ${proto} ${FIREWALL_PROTOCOLS_SUPPORTING_PORTS}; then
461 if isset sport; then
462 list_append cmd "--sport ${sport}"
463 fi
464
465 if isset dport; then
466 list_append cmd "--dport ${dport}"
467 fi
468 fi
469 fi
470
471 # Always append the action.
472 list_append cmd "-j ${action}"
473
474 # Execute command.
475 ${cmd}
476 done < ${file}
477}
478
479function _firewall_parse_rule_line() {
480 local arg
481
482 # Clear all values.
483 for arg in ${FIREWALL_RULES_CONFIG_PARAMS}; do
484 assign "${arg}" ""
3647b19f
MT
485 done
486
a2c9dff5
MT
487 local key val
488 while read -r arg; do
489 key=$(cli_get_key ${arg})
3647b19f 490
a2c9dff5
MT
491 if ! listmatch "${key}" ${FIREWALL_RULES_CONFIG_PARAMS}; then
492 log WARNING "Unrecognized argument: ${arg}"
493 return ${EXIT_ERROR}
494 fi
3647b19f 495
a2c9dff5
MT
496 val=$(cli_get_val ${arg})
497 assign "${key}" "${val}"
498 done <<< "$(args $@)"
499
500 # action must always be set.
501 if ! isset action; then
502 log WARNING "'action' is not set: $@"
503 return ${EXIT_ERROR}
504 fi
505
506 for arg in src dst; do
507 isset ${arg} || continue
508
509 # Check for valid IP addresses.
510 if ! ip_is_valid ${!arg}; then
511 log WARNING "Invalid IP address for '${arg}=${!arg}': $@"
512 return ${EXIT_ERROR}
513 fi
514 done
515
516 if isset proto; then
517 # Make lowercase.
518 proto=${proto,,}
519
520 if ! list_match "${proto}" ${FIREWALL_SUPPORTED_PROTOCOLS}; then
521 log WARNING "Unsupported protocol type 'proto=${proto}': $@"
522 return ${EXIT_ERROR}
523 fi
524 fi
525
526 for arg in sport dport; do
527 isset ${arg} || continue
528
529 # Check if port is valid.
530 if ! isinteger ${arg}; then
531 log WARNING "Invalid port '${arg}=${!arg}': $@"
532 return ${EXIT_ERROR}
533 fi
534 done
535
536 return ${EXIT_OK}
3647b19f 537}