2 ###############################################################################
4 # IPFire.org - A linux based firewall #
5 # Copyright (C) 2012 IPFire Network Development Team #
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. #
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. #
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/>. #
20 ###############################################################################
22 # High-level function which will create a ruleset for the current firewall
23 # configuration and load it into the kernel.
24 function firewall_start
() {
32 while [ $# -gt 0 ]; do
38 error
"Unrecognized argument: ${1}"
46 log INFO
"Test mode enabled."
47 log INFO
"The firewall ruleset will not be loaded."
52 # Initialize an empty iptables ruleset.
53 iptables_init
"${protocol}" "DROP"
56 firewall_filter_rh0_headers
"${protocol}"
57 firewall_filter_icmp
"${protocol}"
58 firewall_tcp_state_flags
"${protocol}"
59 firewall_custom_chains
"${protocol}"
60 firewall_connection_tracking
"${protocol}"
61 firewall_tcp_clamp_mss
"${protocol}"
63 # Add policies for every zone.
64 firewall_localhost_create_chains
"${protocol}"
67 for zone
in $
(zones_get_all
); do
68 # Create all needed chains for the zone.
69 firewall_zone_create_chains
"${protocol}" "${zone}"
71 # After the chains that are always available have been
72 # created, we will add a custom policy to every single
75 policy_zone_add
"${protocol}" "${zone}"
78 # Load the new ruleset.
80 if enabled testmode
; then
81 list_append args
"--test"
83 iptables_commit
"${protocol}" ${args}
88 function firewall_stop
() {
94 # Initialize an empty firewall ruleset
95 # with default policy ACCEPT.
96 iptables_init
"${protocol}" ACCEPT
99 ipables_load
"${protocol}"
101 firewall_lock_release
104 function firewall_show
() {
105 local protocol
="${1}"
106 assert isset protocol
108 # Shows the ruleset that is currently loaded.
109 iptables_status
"${protocol}"
114 function firewall_panic
() {
115 local protocol
="${1}"
116 assert isset protocol
119 local admin_hosts
="$@"
121 firewall_lock_acquire
"${protocol}"
123 # Drop all communications.
124 iptables_init
"${protocol}" DROP
126 # If an admin host is provided, some administrative
127 # things will be allowed from there.
129 for admin_host
in ${admin_hosts}; do
130 iptables
"${protocol}" -A INPUT
-s "${admin_host}" -j ACCEPT
131 iptables
"${protocol}" -A OUTPUT
-d "${admin_host}" -j ACCEPT
135 iptables_commit
"${protocol}"
137 firewall_lock_release
140 function firewall_lock_acquire
() {
141 lock_acquire
${RUN_DIR}/.firewall_lock
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
147 # Create a directory where we can put our
148 # temporary data in the most secure way as possible.
149 IPTABLES_TMPDIR
=$
(mktemp
-d)
152 function firewall_lock_release
() {
153 if isset IPTABLES_TMPDIR
; then
154 # Remove all temporary data.
155 rm -rf ${IPTABLES_TMPDIR}
157 # Reset the tempdir variable.
162 trap true EXIT TERM KILL
164 lock_release
${RUN_DIR}/.firewall_lock
167 function firewall_custom_chains
() {
168 local protocol
="${1}"
169 assert isset protocol
171 log INFO
"Creating CUSTOM* chains..."
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.
177 iptables_chain_create
"${protocol}" CUSTOMINPUT
178 iptables
"${protocol}" -A INPUT
-j CUSTOMINPUT
180 iptables_chain_create
"${protocol}" CUSTOMFORWARD
181 iptables
"${protocol}" -A FORWARD
-j CUSTOMFORWARD
183 iptables_chain_create
"${protocol}" CUSTOMOUTPUT
184 iptables
"${protocol}" -A OUTPUT
-j CUSTOMOUTPUT
186 iptables_chain_create
"${protocol}" -t nat CUSTOMPREROUTING
187 iptables
"${protocol}" -t nat
-A PREROUTING
-j CUSTOMPREROUTING
189 iptables_chain_create
"${protocol}" -t nat CUSTOMPOSTROUTING
190 iptables
"${protocol}" -t nat
-A POSTROUTING
-j CUSTOMPOSTROUTING
192 iptables_chain_create
"${protocol}" -t nat CUSTOMOUTPUT
193 iptables
"${protocol}" -t nat
-A OUTPUT
-j CUSTOMOUTPUT
196 function firewall_tcp_state_flags
() {
197 local protocol
="${1}"
198 assert isset protocol
200 log INFO
"Creating TCP State Flags chain..."
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
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
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
220 function firewall_tcp_clamp_mss
() {
221 # Do nothing if this has been disabled.
222 enabled FIREWALL_CLAMP_PATH_MTU ||
return ${EXIT_OK}
224 local protocol
="${1}"
225 assert isset protocol
227 log DEBUG
"Adding rules to clamp MSS to path MTU..."
229 iptables
"${protocol}" -t mangle
-A FORWARD \
230 -p tcp
--tcp-flags SYN
,RST SYN
-j TCPMSS
--clamp-mss-to-pmtu
233 function firewall_connection_tracking
() {
234 local protocol
="${1}"
235 assert isset protocol
237 log INFO
"Creating Connection Tracking chain..."
239 iptables_chain_create
"${protocol}" CONNTRACK
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
244 iptables
"${protocol}" -A INPUT
-j CONNTRACK
245 iptables
"${protocol}" -A OUTPUT
-j CONNTRACK
246 iptables
"${protocol}" -A FORWARD
-j CONNTRACK
249 function firewall_localhost_create_chains
() {
250 local protocol
="${1}"
251 assert isset protocol
253 log DEBUG
"Creating firewall chains for localhost..."
255 # Accept everything on lo
256 iptables
"${protocol}" -A INPUT
-i lo
-j ACCEPT
257 iptables
"${protocol}" -A OUTPUT
-o lo
-j ACCEPT
260 function firewall_filter_rh0_headers
() {
261 local protocol
="${1}"
262 assert isset protocol
265 [ "${protocol}" = "ipv6" ] ||
return ${EXIT_OK}
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
272 iptables
"${protocol}" -A INPUT
-j FILTER_RH0
273 iptables
"${protocol}" -A FORWARD
-j FILTER_RH0
274 iptables
"${protocol}" -A OUTPUT
-j FILTER_RH0
277 function firewall_filter_icmp
() {
278 local protocol
="${1}"
279 assert isset protocol
282 [ "${protocol}" = "ipv6" ] ||
return ${EXIT_OK}
284 local chain
="FILTER_ICMPV6"
286 # Create an extra chain for handling ICMP packets.
287 iptables_chain_create
"${protocol}" "${chain}_COMMON"
290 for suffix
in INC FWD OUT
; do
291 iptables_chain_create
"${protocol}" "${chain}_${suffix}"
292 iptables
"${protocol}" -A "${chain}_${suffix}" -j "${chain}_COMMON"
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"
298 # Packets that must always pass the firewall.
299 # Type 4: Parameter Problem
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
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
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
321 # Discard packets from local networks with hop limit smaller than $hoplimit.
322 # Type 148: Path solicitation
323 # Type 149: Path advertisement
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
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
338 function firewall_zone_create_chains
() {
339 local protocol
="${1}"
340 assert isset protocol
345 log DEBUG
"Creating firewall chains for zone '${zone}'."
347 local chain_prefix
="ZONE_${zone^^}"
349 # Create filter chains.
350 iptables_chain_create
"${protocol}" "${chain_prefix}_INPUT"
351 iptables
"${protocol}" -A INPUT -i ${zone} -j "${chain_prefix}_INPUT"
353 iptables_chain_create
"${protocol}" "${chain_prefix}_OUTPUT"
354 iptables
"${protocol}" -A OUTPUT -o ${zone} -j "${chain_prefix}_OUTPUT"
357 iptables_chain_create
"${protocol}" "${chain_prefix}_CUSTOM"
359 # Intrusion Prevention System.
360 iptables_chain_create
"${protocol}" "${chain_prefix}_IPS"
362 # Create a chain for each other zone.
363 # This leaves us with n^2 chains. Duh.
365 local other_zone other_chain_prefix
366 for other_zone
in $
(zones_get_all
); do
367 other_chain_prefix
="${chain_prefix}_${other_zone^^}"
368 iptables_chain_create
"${protocol}" "${other_chain_prefix}"
370 # Connect the chain with the FORWARD chain.
371 iptables
"${protocol}" -A FORWARD -i "${zone}" -o "${other_zone}" \
372 -j "${other_chain_prefix}"
374 # Handle custom rules.
375 iptables
"${protocol}" -A "${other_chain_prefix}" -j "${chain_prefix}_CUSTOM"
378 iptables
"${protocol}" -A "${other_chain_prefix}" -j "${chain_prefix}_IPS"
381 iptables_chain_create
"${protocol}" "${other_chain_prefix}_RULES"
382 iptables
"${protocol}" -A "${other_chain_prefix}" -j "${other_chain_prefix}_RULES"
385 iptables_chain_create
"${protocol}" "${other_chain_prefix}_POLICY"
386 iptables
"${protocol}" -A "${other_chain_prefix}" -j "${other_chain_prefix}_POLICY"
389 ## Create mangle chain.
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}"
394 ## Quality of Service
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"
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}"
405 # Network Address Translation
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"
412 iptables_chain_create
"${protocol}" -t nat
"${chain_prefix}_UPNP"
413 iptables
"${protocol}" -t nat -A "${chain_prefix}" -j "${chain_prefix}_UPNP"
418 function firewall_parse_rules
() {
423 # End if no rule file exists.
424 [ -r "${file}" ] ||
return ${EXIT_OK}
428 local ${FIREWALL_RULES_CONFIG_PARAMS}
430 while read -r line
; do
432 [ -n "${line}" ] ||
continue
434 # Skip commented lines.
435 [ "${line:0:1}" = "#" ] && continue
438 _firewall_parse_rule_line
${line}
439 if [ $?
-ne ${EXIT_OK} ]; then
440 log WARNING
"Skipping invalid line: ${line}"
446 # Source IP address/net.
448 list_append cmd
"-s ${src}"
451 # Destination IP address/net.
453 list_append cmd
"-d ${dst}"
458 list_append cmd
"-p ${proto}"
460 if list_match
${proto} ${FIREWALL_PROTOCOLS_SUPPORTING_PORTS}; then
462 list_append cmd
"--sport ${sport}"
466 list_append cmd
"--dport ${dport}"
471 # Always append the action.
472 list_append cmd
"-j ${action}"
479 function _firewall_parse_rule_line
() {
483 for arg
in ${FIREWALL_RULES_CONFIG_PARAMS}; do
488 while read -r arg
; do
489 key
=$
(cli_get_key
${arg})
491 if ! listmatch
"${key}" ${FIREWALL_RULES_CONFIG_PARAMS}; then
492 log WARNING
"Unrecognized argument: ${arg}"
496 val
=$
(cli_get_val
${arg})
497 assign
"${key}" "${val}"
498 done <<< "$(args $@)"
500 # action must always be set.
501 if ! isset action
; then
502 log WARNING
"'action' is not set: $@"
506 for arg
in src dst
; do
507 isset
${arg} ||
continue
509 # Check for valid IP addresses.
510 if ! ip_is_valid
${!arg}; then
511 log WARNING
"Invalid IP address for '${arg}=${!arg}': $@"
520 if ! list_match
"${proto}" ${FIREWALL_SUPPORTED_PROTOCOLS}; then
521 log WARNING
"Unsupported protocol type 'proto=${proto}': $@"
526 for arg
in sport dport
; do
527 isset
${arg} ||
continue
529 # Check if port is valid.
530 if ! isinteger
${arg}; then
531 log WARNING
"Invalid port '${arg}=${!arg}': $@"