Adding spec file
[iptables.git] / iptables-apply
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 #
8 set -eu
9
10 PROGNAME="${0##*/}";
11 VERSION=1.0
12
13 TIMEOUT=10
14 DEFAULT_FILE=/etc/network/iptables
15
16 function blurb()
17 {
18         cat <<-_eof
19         $PROGNAME $VERSION -- a safer way to update iptables remotely
20         _eof
21 }
22
23 function 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
32 function about()
33 {
34         blurb
35         echo
36         copyright
37 }
38
39 function 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
58 SHORTOPTS="t:Vh";
59 LONGOPTS="timeout:,version,help";
60
61 OPTS=$(getopt -s bash -o "$SHORTOPTS" -l "$LONGOPTS" -n "$PROGNAME" -- "$@") || exit $?
62 for 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
88 done
89
90 FILE="${1:-$DEFAULT_FILE}";
91
92 if [[ -z "$FILE" ]]; then
93         echo "E: missing file argument." >&2
94         exit 1
95 fi
96
97 if [[ ! -r "$FILE" ]]; then
98         echo "E: cannot read $FILE" >&2
99         exit 2
100 fi
101
102 case "${0##*/}" in
103         (*6*)
104                 SAVE=ip6tables-save
105                 RESTORE=ip6tables-restore
106                 ;;
107         (*)
108                 SAVE=iptables-save
109                 RESTORE=iptables-restore
110                 ;;
111 esac
112
113 COMMANDS=(tempfile "$SAVE" "$RESTORE")
114
115 for 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
120 done
121
122 umask 0700
123
124 TMPFILE=$(tempfile -p iptap)
125 trap "rm -f $TMPFILE" EXIT 1 2 3 4 5 6 7 8 10 11 12 13 14 15
126
127 if ! "$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
135 fi
136
137 [ -x /etc/init.d/fail2ban ] && /etc/init.d/fail2ban stop
138
139 echo -n "Applying new ruleset... "
140 if ! "$RESTORE" <"$FILE"; then
141         echo "failed."
142         echo "E: unknown error applying new iptables ruleset." >&2
143         exit 5
144 else
145         echo done.
146 fi
147
148 echo -n "Can you establish NEW connections to the machine? (y/N) "
149
150 read -n1 -t "${TIMEOUT:-15}" ret 2>&1 || :
151 case "${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                 ;;
168 esac
169
170 [ -x /etc/init.d/fail2ban ] && /etc/init.d/fail2ban start
171
172 exit 0
173
174 # vim:noet:sw=8