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