X-Git-Url: https://git.distorted.org.uk/~mdw/firewall/blobdiff_plain/775bd287d2eda80ffde35b067429c93223c41bbf..38e85ca3b58ddcf50c7db608f5baa2fd19771f8a:/prologue.m4 diff --git a/prologue.m4 b/prologue.m4 index 4b14578..9f5d084 100644 --- a/prologue.m4 +++ b/prologue.m4 @@ -26,8 +26,21 @@ m4_divert(10)m4_dnl ### Failsafe prologue. revert () { - echo "$1! Retreating to safe version..." - if ! "$firewall_failsafe" revert; then + 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 @@ -40,40 +53,118 @@ finished () { 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 - start | restart | reload | force-reload) +case "$#,${1-update}" in + 1,start | 1,restart | 1,reload | 1,force-reload) echo -n "Starting up firewall... " - "$firewall_script" install || revert "Failed" + "$firewall_script" install || revert "$firewall_failsafe" "Failed" finished ;; - stop) + 1,stop) echo -n "Shutting down firewall... " exit_after_clearing=finished ;; - update) - echo -n "Installing new firewall... " - "$firewall_script" install || revert "Failed" - echo "Done." + 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?" - parent=$$ - (sleep 5; kill $parent; revert "Timeout")& - child=$! + (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 $child - case "$answer" in - y* | Y*) - echo "Cool. We're done here." + 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 - revert "Bogus" ;; - install | revert) + 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) ;; *) - echo >&2 "Usage: firewall start|stop|reload|restart|force-reload|update|install|revert" + cat >&2 <