From dafbc722de7bf7445a7650e5fe0778ac798dcd18 Mon Sep 17 00:00:00 2001 From: Laurence J. Lane Subject: [PATCH] iptables: update iptables-apply to v1.1 Bug: http://bugs.debian.org/580941 This is GW's update to iptables-apply. It does a code cleanup and adds two options: one runs a command and the other writes the sucessful rules file. I modified the script to use mktemp instead of tempfile. I also fixed a couple of hyphens in the man page addition. Signed-off-by: Laurence J. Lane --- iptables-apply | 310 ++++++++++++++++++++++++++++++++++++---------------- iptables-apply.8.in | 48 +++++--- 2 files changed, 247 insertions(+), 111 deletions(-) --- a/iptables/iptables-apply +++ b/iptables/iptables-apply @@ -1,174 +1,294 @@ #!/bin/bash -# # iptables-apply -- a safer way to update iptables remotely # -# Copyright © Martin F. Krafft +# Usage: +# iptables-apply [-hV] [-t timeout] [-w savefile] {[rulesfile]|-c [runcmd]} +# +# Versions: +# * 1.0 Copyright 2006 Martin F. Krafft +# Original version +# * 1.1 Copyright 2010 GW +# Added parameter -c (run command) +# Added parameter -w (save successfully applied rules to file) +# Major code cleanup +# # Released under the terms of the Artistic Licence 2.0 # set -eu -PROGNAME="${0##*/}"; -VERSION=1.0 +PROGNAME="${0##*/}" +VERSION=1.1 + + +### Default settings + +DEF_TIMEOUT=10 + +MODE=0 # apply rulesfile mode +# MODE=1 # run command mode + +case "$PROGNAME" in + (*6*) + SAVE=ip6tables-save + RESTORE=ip6tables-restore + DEF_RULESFILE="/etc/network/ip6tables.up.rules" + DEF_SAVEFILE="$DEF_RULESFILE" + DEF_RUNCMD="/etc/network/ip6tables.up.run" + ;; + (*) + SAVE=iptables-save + RESTORE=iptables-restore + DEF_RULESFILE="/etc/network/iptables.up.rules" + DEF_SAVEFILE="$DEF_RULESFILE" + DEF_RUNCMD="/etc/network/iptables.up.run" + ;; +esac + -TIMEOUT=10 +### Functions -function blurb() -{ - cat <<-_eof +function blurb() { + cat <<-__EOF__ $PROGNAME $VERSION -- a safer way to update iptables remotely - _eof + __EOF__ } -function copyright() -{ - cat <<-_eof - $PROGNAME is C Martin F. Krafft . - - The program has been published under the terms of the Artistic Licence 2.0 - _eof +function copyright() { + cat <<-__EOF__ + $PROGNAME has been published under the terms of the Artistic Licence 2.0. + + Original version - Copyright 2006 Martin F. Krafft . + Version 1.1 - Copyright 2010 GW . + __EOF__ } -function about() -{ +function about() { blurb echo copyright } -function usage() -{ - cat <<-_eof - Usage: $PROGNAME [options] ruleset - - The script will try to apply a new ruleset (as output by iptables-save/read - by iptables-restore) to iptables, then prompt the user whether the changes - are okay. If the new ruleset cut the existing connection, the user will not - be able to answer affirmatively. In this case, the script rolls back to the - previous ruleset. - - The following options may be specified, using standard conventions: - - -t | --timeout Specify the timeout in seconds (default: $TIMEOUT) - -V | --version Display version information - -h | --help Display this help text - _eof +function usage() { + blurb + echo + cat <<-__EOF__ + Usage: + $PROGNAME [-hV] [-t timeout] [-w savefile] {[rulesfile]|-c [runcmd]} + + The script will try to apply a new rulesfile (as output by iptables-save, + read by iptables-restore) or run a command to configure iptables and then + prompt the user whether the changes are okay. If the new iptables rules cut + the existing connection, the user will not be able to answer affirmatively. + In this case, the script rolls back to the previous working iptables rules + after the timeout expires. + + Successfully applied rules can also be written to savefile and later used + to roll back to this state. This can be used to implement a store last good + configuration mechanism when experimenting with an iptables setup script: + $PROGNAME -w $DEF_SAVEFILE -c $DEF_RUNCMD + + When called as ip6tables-apply, the script will use ip6tables-save/-restore + and IPv6 default values instead. Default value for rulesfile is + '$DEF_RULESFILE'. + + Options: + + -t seconds, --timeout seconds + Specify the timeout in seconds (default: $DEF_TIMEOUT). + -w savefile, --write savefile + Specify the savefile where successfully applied rules will be written to + (default if empty string is given: $DEF_SAVEFILE). + -c runcmd, --command runcmd + Run command runcmd to configure iptables instead of applying a rulesfile + (default: $DEF_RUNCMD). + -h, --help + Display this help text. + -V, --version + Display version information. + + __EOF__ +} + +function checkcommands() { + for cmd in "${COMMANDS[@]}"; do + if ! command -v "$cmd" >/dev/null; then + echo "Error: needed command not found: $cmd" >&2 + exit 127 + fi + done +} + +function revertrules() { + echo -n "Reverting to old iptables rules... " + "$RESTORE" <"$TMPFILE" + echo "done." } -SHORTOPTS="t:Vh"; -LONGOPTS="timeout:,version,help"; + +### Parsing and checking parameters + +TIMEOUT="$DEF_TIMEOUT" +SAVEFILE="" + +SHORTOPTS="t:w:chV"; +LONGOPTS="timeout:,write:,command,help,version"; OPTS=$(getopt -s bash -o "$SHORTOPTS" -l "$LONGOPTS" -n "$PROGNAME" -- "$@") || exit $? for opt in $OPTS; do case "$opt" in - (-*) unset OPT_STATE;; + (-*) + unset OPT_STATE + ;; (*) case "${OPT_STATE:-}" in - (SET_TIMEOUT) - eval TIMEOUT=$opt - case "$TIMEOUT" in - ([0-9]*) :;; - (*) - echo "E: non-numeric timeout value." >&2 - exit 1 - ;; - esac + (SET_TIMEOUT) eval TIMEOUT=$opt;; + (SET_SAVEFILE) + eval SAVEFILE=$opt + [ -z "$SAVEFILE" ] && SAVEFILE="$DEF_SAVEFILE" ;; esac ;; esac case "$opt" in + (-t|--timeout) OPT_STATE="SET_TIMEOUT";; + (-w|--write) OPT_STATE="SET_SAVEFILE";; + (-c|--command) MODE=1;; (-h|--help) usage >&2; exit 0;; (-V|--version) about >&2; exit 0;; - (-t|--timeout) OPT_STATE=SET_TIMEOUT;; (--) break;; esac shift done -case "$PROGNAME" in - (*6*) - SAVE=ip6tables-save - RESTORE=ip6tables-restore - DEFAULT_FILE=/etc/network/ip6tables - ;; - (*) - SAVE=iptables-save - RESTORE=iptables-restore - DEFAULT_FILE=/etc/network/iptables - ;; -esac - -FILE="${1:-$DEFAULT_FILE}"; - -if [[ -z "$FILE" ]]; then - echo "E: missing file argument." >&2 +# Validate parameters +if [ "$TIMEOUT" -ge 0 ] 2>/dev/null; then + TIMEOUT=$(($TIMEOUT)) +else + echo "Error: timeout must be a positive number" >&2 exit 1 fi -if [[ ! -r "$FILE" ]]; then - echo "E: cannot read $FILE" >&2 - exit 2 +if [ -n "$SAVEFILE" -a -e "$SAVEFILE" -a ! -w "$SAVEFILE" ]; then + echo "Error: savefile not writable: $SAVEFILE" >&2 + exit 8 fi -COMMANDS=(tempfile "$SAVE" "$RESTORE") +case "$MODE" in + (1) + # Treat parameter as runcmd (run command mode) + RUNCMD="${1:-$DEF_RUNCMD}" + if [ ! -x "$RUNCMD" ]; then + echo "Error: runcmd not executable: $RUNCMD" >&2 + exit 6 + fi -for cmd in "${COMMANDS[@]}"; do - if ! command -v $cmd >/dev/null; then - echo "E: command not found: $cmd" >&2 - exit 127 - fi -done + # Needed commands + COMMANDS=(mktemp "$SAVE" "$RESTORE" "$RUNCMD") + checkcommands + ;; + (*) + # Treat parameter as rulesfile (apply rulesfile mode) + RULESFILE="${1:-$DEF_RULESFILE}"; + if [ ! -r "$RULESFILE" ]; then + echo "Error: rulesfile not readable: $RULESFILE" >&2 + exit 2 + fi -umask 0700 + # Needed commands + COMMANDS=(mktemp "$SAVE" "$RESTORE") + checkcommands + ;; +esac -TMPFILE=$(tempfile -p iptap) + +### Begin work + +# Store old iptables rules to temporary file +TMPFILE=`mktemp /tmp/$PROGNAME-XXXXXXXX` trap "rm -f $TMPFILE" EXIT HUP INT QUIT ILL TRAP ABRT BUS \ FPE USR1 SEGV USR2 PIPE ALRM TERM if ! "$SAVE" >"$TMPFILE"; then + # An error occured if ! grep -q ipt /proc/modules 2>/dev/null; then - echo "E: iptables support lacking from the kernel." >&2 + echo "Error: iptables support lacking from the kernel" >&2 exit 3 else - echo "E: unknown error saving current iptables ruleset." >&2 + echo "Error: unknown error saving old iptables rules: $TMPFILE" >&2 exit 4 fi fi +# Legacy to stop the fail2ban daemon if present [ -x /etc/init.d/fail2ban ] && /etc/init.d/fail2ban stop -echo -n "Applying new ruleset... " -if ! "$RESTORE" <"$FILE"; then - echo "failed." - echo "E: unknown error applying new iptables ruleset." >&2 - exit 5 -else - echo "done." -fi +# Configure iptables +case "$MODE" in + (1) + # Run command in background and kill it if it times out + echo -n "Running command '$RUNCMD'... " + "$RUNCMD" & + CMD_PID=$! + ( sleep "$TIMEOUT"; kill "$CMD_PID" 2>/dev/null; exit 0 ) & + CMDTIMEOUT_PID=$! + if ! wait "$CMD_PID"; then + echo "failed." + echo "Error: unknown error running command: $RUNCMD" >&2 + revertrules + exit 7 + else + echo "done." + fi + ;; + (*) + # Apply iptables rulesfile + echo -n "Applying new iptables rules from '$RULESFILE'... " + if ! "$RESTORE" <"$RULESFILE"; then + echo "failed." + echo "Error: unknown error applying new iptables rules: $RULESFILE" >&2 + revertrules + exit 5 + else + echo "done." + fi + ;; +esac +# Prompt user for confirmation echo -n "Can you establish NEW connections to the machine? (y/N) " -read -n1 -t "${TIMEOUT:-15}" ret 2>&1 || : +read -n1 -t "$TIMEOUT" ret 2>&1 || : case "${ret:-}" in (y*|Y*) + # Success echo + + if [ ! -z "$SAVEFILE" ]; then + # Write successfully applied rules to the savefile + echo "Writing successfully applied rules to '$SAVEFILE'..." + if ! "$SAVE" >"$SAVEFILE"; then + echo "Error: unknown error writing successfully applied rules: $SAVEFILE" >&2 + exit 9 + fi + fi + echo "... then my job is done. See you next time." ;; (*) - if [[ -z "${ret:-}" ]]; then - echo "apparently not..." + # Failed + echo + if [ -z "${ret:-}" ]; then + echo "Timeout! Something happened (or did not). Better play it safe..." else - echo + echo "No affirmative response! Better play it safe..." fi - echo "Timeout. Something happened (or did not). Better play it safe..." - echo -n "Reverting to old ruleset... " - "$RESTORE" <"$TMPFILE"; - echo "done." + revertrules exit 255 ;; esac +# Legacy to start the fail2ban daemon again [ -x /etc/init.d/fail2ban ] && /etc/init.d/fail2ban start exit 0 --- a/iptables/iptables-apply.8.in +++ b/iptables/iptables-apply.8.in @@ -1,6 +1,6 @@ .\" Title: iptables-apply -.\" Author: Martin F. Krafft -.\" Date: Jun 04, 2006 +.\" Author: Martin F. Krafft, GW +.\" Date: May 10, 2010 .\" .TH IPTABLES\-APPLY 8 "" "@PACKAGE_STRING@" "@PACKAGE_STRING@" .\" disable hyphenation @@ -8,23 +8,37 @@ .SH NAME iptables-apply \- a safer way to update iptables remotely .SH SYNOPSIS -\fBiptables\-apply\fP [\-\fBhV\fP] [\fB-t\fP \fItimeout\fP] \fIruleset\-file\fP +\fBiptables\-apply\fP [\-\fBhV\fP] [\fB-t\fP \fItimeout\fP] [\fB-w\fP \fIsavefile\fP] {[\fIrulesfile]|-c [runcmd]}\fP .SH "DESCRIPTION" .PP -iptables\-apply will try to apply a new ruleset (as output by -iptables\-save/read by iptables\-restore) to iptables, then prompt the -user whether the changes are okay. If the new ruleset cut the existing -connection, the user will not be able to answer affirmatively. In this -case, the script rolls back to the previous ruleset after the timeout -expired. The timeout can be set with \fB\-t\fP. -.PP -When called as \fBip6tables\-apply\fP, the script will use -ip6tables\-save/\-restore instead. +iptables\-apply will try to apply a new rulesfile (as output by +iptables-save, read by iptables-restore) or run a command to configure +iptables and then prompt the user whether the changes are okay. If the +new iptables rules cut the existing connection, the user will not be +able to answer affirmatively. In this case, the script rolls back to +the previous working iptables rules after the timeout expires. +.PP +Successfully applied rules can also be written to savefile and later used +to roll back to this state. This can be used to implement a store last good +configuration mechanism when experimenting with an iptables setup script: +iptables-apply \-w /etc/network/iptables.up.rules \-c /etc/network/iptables.up.run +.PP +When called as ip6tables\-apply, the script will use +ip6tables\-save/\-restore and IPv6 default values instead. Default +value for rulesfile is '/etc/network/iptables.up.rules'. .SH OPTIONS .TP \fB\-t\fP \fIseconds\fR, \fB\-\-timeout\fP \fIseconds\fR -Sets the timeout after which the script will roll back to the previous -ruleset. +Sets the timeout in seconds after which the script will roll back +to the previous ruleset (default: 10). +.TP +\fB\-w\fP \fIsavefile\fR, \fB\-\-write\fP \fIsavefile\fR +Specify the savefile where successfully applied rules will be written to +(default if empty string is given: /etc/network/iptables.up.rules). +.TP +\fB\-c\fP \fIruncmd\fR, \fB\-\-command\fP \fIruncmd\fR +Run command runcmd to configure iptables instead of applying a rulesfile +(default: /etc/network/iptables.up.run). .TP \fB\-h\fP, \fB\-\-help\fP Display usage information. @@ -36,9 +50,11 @@ \fBiptables-restore\fP(8), \fBiptables-save\fP(8), \fBiptables\fR(8). .SH LEGALESE .PP -iptables\-apply is copyright by Martin F. Krafft. +Original iptables-apply - Copyright 2006 Martin F. Krafft . +Version 1.1 - Copyright 2010 GW . .PP -This manual page was written by Martin F. Krafft +This manual page was written by Martin F. Krafft and +extended by GW . .PP Permission is granted to copy, distribute and/or modify this document under the terms of the Artistic License 2.0.