### -*-sh-*- ### ### Failsafe prologue for firewall scripts ### ### (c) 2008 Mark Wooding ### ###----- Licensing notice --------------------------------------------------- ### ### This program is free software; you can redistribute it and/or modify ### it under the terms of the GNU General Public License as published by ### the Free Software Foundation; either version 2 of the License, or ### (at your option) any later version. ### ### This program is distributed in the hope that it will be useful, ### but WITHOUT ANY WARRANTY; without even the implied warranty of ### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ### GNU General Public License for more details. ### ### You should have received a copy of the GNU General Public License ### along with this program; if not, write to the Free Software Foundation, ### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. m4_divert(10)m4_dnl ###-------------------------------------------------------------------------- ### Failsafe prologue. revert () { escape=$1 badness=$2 ## Report a firewall script failure and retreat to a safe place. echo "$2! Retreating to safe version..." if [ -f /var/run/firewall.save ] && [ -f /var/run/firewall6.save ]; then echo "Trying to loading saved firewall state..." if iptables-restore &2 "Safe firewall failed. You're screwed. Good luck." exit 1 fi echo >&2 "Phew! Fallback to safe version successful." exit 1 } finished () { echo "Done." exit 0 } try () { old=$1 new=$2 ## Install the NEW firewall rules. If it fails, revert to the OLD ones. ## Updating firewall rules can fail spectacularly, so be careful. Leave a ## timebomb in the form of NEW.errors: if this isn't removed in 10 seconds ## after the NEW rules complete successfully, then revert. Write errors to ## NEW.errors. ## Make sure we have an escape route. iptables-save >/var/run/firewall.save.new ip6tables-save >/var/run/firewall6.save.new mv /var/run/firewall.save.new /var/run/firewall.save mv /var/run/firewall6.save.new /var/run/firewall6.save ## Clear the air and make the errors file. rm -f "$new.errors" "$new.timebomb" "$new.grabbed" exec >"$new.errors" 2>&1 ## Now try to install the new firewall. "$new" install || revert "$old" "Failed" ## Set up the time bomb. Leave the errors file there if we failed. (sleep 10 if [ -f "$new.errors" ]; then mv "$new.errors" "$new.timebomb" revert "$old" "Time bomb" fi)& } catch () { new=$1 ## Report successful installation of the script. if mv "$new.errors" "$new.grabbed" 2>/dev/null; then rc=0 echo "Installed OK." else mv "$new.timebomb" "$new.grabbed" echo "Timebomb went off." rc=1 fi cat "$new.grabbed" >&2 rm -f "$new.grabbed" return $rc } exit_after_clearing=: export FWCOOKIE=magical case "$#,${1-update}" in 1,start | 1,restart | 1,reload | 1,force-reload) echo -n "Starting up firewall... " "$firewall_script" install || revert "$firewall_failsafe" "Failed" finished ;; 1,stop) echo -n "Shutting down firewall... " exit_after_clearing=finished ;; 1,replace | 1,test) echo -n "Running new firewall... " if ! (try "$firewall_script" "$0"); then echo "FAILED." cat "$0.errors" >&2 exit fi echo "done." echo "Can you hear me?" (trap 'exit 127' TERM while :; do if [ -f "$0.timebomb" ]; then kill $$ echo "Timebomb went off!" cat "$0.timebomb" exit 1 fi sleep 1 done)& read answer kill $! catch "$0" case "$1,$answer" in replace,y* | replace,Y*) install -m755 "$0" "$firewall_script" echo "Cool. Firewall script replaced." exit 0 ;; test,y* | test,Y*) echo "Cool. Everything seems good." exit 0 ;; *) revert "$firewall_script" "Bogus" ;; esac ;; 1,remote-prepare) try "$firewall_script" "$0" exit 0 ;; 1,remote-commit) catch "$0" install -m755 "$0" "$firewall_script" exit 0 ;; 1,install | 1,revert) ;; *) cat >&2 <