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