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