iptables-apply revision e37d45ce390c2f5a7f1e64742b9100ecef0def54
1#!/bin/bash
2#
3# iptables-apply -- a safer way to update iptables remotely
4#
5# Copyright © Martin F. Krafft <madduck@madduck.net>
6# Released under the terms of the Artistic Licence 2.0
7#
8set -eu
9
10PROGNAME="${0##*/}";
11VERSION=1.0
12
13TIMEOUT=10
14DEFAULT_FILE=/etc/network/iptables
15
16function blurb()
17{
18	cat <<-_eof
19	$PROGNAME $VERSION -- a safer way to update iptables remotely
20	_eof
21}
22
23function copyright()
24{
25	cat <<-_eof
26	$PROGNAME is C Martin F. Krafft <madduck@madduck.net>.
27
28	The program has been published under the terms of the Artistic Licence 2.0
29	_eof
30}
31
32function about()
33{
34	blurb
35	echo
36	copyright
37}
38
39function usage()
40{
41	cat <<-_eof
42	Usage: $PROGNAME [options] ruleset
43
44	The script will try to apply a new ruleset (as output by iptables-save/read
45	by iptables-restore) to iptables, then prompt the user whether the changes
46	are okay. If the new ruleset cut the existing connection, the user will not
47	be able to answer affirmatively. In this case, the script rolls back to the
48	previous ruleset.
49
50	The following options may be specified, using standard conventions:
51
52	-t | --timeout	Specify the timeout in seconds (default: $TIMEOUT)
53	-V | --version	Display version information
54	-h | --help	Display this help text
55	_eof
56}
57
58SHORTOPTS="t:Vh";
59LONGOPTS="timeout:,version,help";
60
61OPTS=$(getopt -s bash -o "$SHORTOPTS" -l "$LONGOPTS" -n "$PROGNAME" -- "$@") || exit $?
62for opt in $OPTS; do
63	case "$opt" in
64		(-*) unset OPT_STATE;;
65		(*)
66			case "${OPT_STATE:-}" in
67				(SET_TIMEOUT)
68					eval TIMEOUT=$opt
69					case "$TIMEOUT" in
70						([0-9]*) :;;
71						(*)
72							echo "E: non-numeric timeout value." >&2
73							exit 1
74							;;
75					esac
76					;;
77			esac
78			;;
79	esac
80
81	case "$opt" in
82		(-h|--help) usage >&2; exit 0;;
83		(-V|--version) about >&2; exit 0;;
84		(-t|--timeout) OPT_STATE=SET_TIMEOUT;;
85		(--) break;;
86	esac
87	shift
88done
89
90FILE="${1:-$DEFAULT_FILE}";
91
92if [[ -z "$FILE" ]]; then
93	echo "E: missing file argument." >&2
94	exit 1
95fi
96
97if [[ ! -r "$FILE" ]]; then
98	echo "E: cannot read $FILE" >&2
99	exit 2
100fi
101
102case "${0##*/}" in
103	(*6*)
104		SAVE=ip6tables-save
105		RESTORE=ip6tables-restore
106		;;
107	(*)
108		SAVE=iptables-save
109		RESTORE=iptables-restore
110		;;
111esac
112
113COMMANDS=(tempfile "$SAVE" "$RESTORE")
114
115for cmd in "${COMMANDS[@]}"; do
116	if ! command -v $cmd >/dev/null; then
117		echo "E: command not found: $cmd" >&2
118		exit 127
119	fi
120done
121
122umask 0700
123
124TMPFILE=$(tempfile -p iptap)
125trap "rm -f $TMPFILE" EXIT 1 2 3 4 5 6 7 8 10 11 12 13 14 15
126
127if ! "$SAVE" >"$TMPFILE"; then
128	if ! grep -q ipt /proc/modules 2>/dev/null; then
129		echo "E: iptables support lacking from the kernel." >&2
130		exit 3
131	else
132		echo "E: unknown error saving current iptables ruleset." >&2
133		exit 4
134	fi
135fi
136
137[ -x /etc/init.d/fail2ban ] && /etc/init.d/fail2ban stop
138
139echo -n "Applying new ruleset... "
140if ! "$RESTORE" <"$FILE"; then
141	echo "failed."
142	echo "E: unknown error applying new iptables ruleset." >&2
143	exit 5
144else
145	echo done.
146fi
147
148echo -n "Can you establish NEW connections to the machine? (y/N) "
149
150read -n1 -t "${TIMEOUT:-15}" ret 2>&1 || :
151case "${ret:-}" in
152	(y*|Y*)
153		echo
154		echo ... then my job is done. See you next time.
155		;;
156	(*)
157		if [[ -z "${ret:-}" ]]; then
158			echo "apparently not..."
159		else
160			echo
161		fi
162		echo "Timeout. Something happened (or did not). Better play it safe..."
163		echo -n "Reverting to old ruleset... "
164		"$RESTORE" <"$TMPFILE";
165		echo done.
166		exit 255
167		;;
168esac
169
170[ -x /etc/init.d/fail2ban ] && /etc/init.d/fail2ban start
171
172exit 0
173
174# vim:noet:sw=8
175