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