|
2 | 2 | # |
3 | 3 | # Copyright (c) Citrix Systems. All rights reserved. |
4 | 4 |
|
5 | | -set -e |
6 | | - |
7 | 5 | ############################################################## |
8 | 6 | # Use this script to open/close the specified interfaces |
9 | | -# (IP addresses) for incoming TCP connections to the NBD port. |
| 7 | +# for incoming TCP connections to the NBD port. |
10 | 8 | # |
11 | 9 | # Usage: |
12 | | -# ./nbd-firewall-config set [address...] |
| 10 | +# ./nbd-firewall-config set [interface...] |
13 | 11 | # |
14 | 12 | ############################################################## |
15 | 13 |
|
| 14 | +# Historical note: the original version of this file filtered |
| 15 | +# by local IP addresses rather than by network interfaces; |
| 16 | +# that version might one day be a useful reference for something. |
| 17 | + |
16 | 18 | # Require the "set" argument because otherwise there would be a risk |
17 | 19 | # of someone running the script with no arguments and accidentally |
18 | | -# setting NBD to be available on no addresses. |
| 20 | +# setting NBD to be available on no interfaces. |
19 | 21 | # This way, omitting "set" (or supplying "--help") causes the script |
20 | 22 | # to print usage instructions. |
21 | 23 |
|
| 24 | +set -e |
22 | 25 | OP="$1" |
23 | 26 | if [ _"${OP}" != _set ]; then |
24 | | - echo "Usage: $(basename ${0}) set [address...] |
| 27 | + echo "Usage: $(basename ${0}) set [interface...] |
25 | 28 | will set the firewall to allow incoming TCP connections |
26 | | -to the NBD port on only the specified addresses (if any)." 1>&2 |
| 29 | +to the NBD port on only the specified interfaces (if any)." 1>&2 |
27 | 30 | exit 1 |
28 | 31 | fi |
29 | 32 | shift 1 |
30 | 33 |
|
31 | 34 | set -eu |
32 | | - |
33 | | -TMPSET=xapi_nbd_ipset_ephemeral |
34 | | -NBDSET=xapi_nbd_ipset |
| 35 | +# No automatic cleanup on error because it would depend too |
| 36 | +# critically on the stage at which the error occurs. |
| 37 | + |
| 38 | +# If this script has been run already then there will be existing chains and |
| 39 | +# rules in place already. Therefore the approach is: |
| 40 | +# 1. Make new nbd-specific chains with names ending in "_new". |
| 41 | +# 2. Insert rules into main INPUT/OUTPUT chains to use our new chains. |
| 42 | +# 3. Remove preexisting rules that use preexisting nbd chains (if present). |
| 43 | +# 4. Remove preexisting nbd chains (if present). |
| 44 | +# 5. Rename the new chains so they no longer end in "_new". |
| 45 | + |
| 46 | +# Note that chain names must be under 29 characters. |
| 47 | +NewChainIn=xapi_nbd_input_chain_new |
| 48 | +NbdChainIn=xapi_nbd_input_chain |
| 49 | +NewChainOut=xapi_nbd_output_chain_new |
| 50 | +NbdChainOut=xapi_nbd_output_chain |
| 51 | + |
| 52 | +# IANA-assigned port: the NBD protocol specifies that it SHOULD be used. |
35 | 53 | NBDPORT=10809 |
36 | | -SETTYPE=hash:ip |
37 | | - |
38 | | -# Rule to accept new NBD connections on the appropriate addresses |
39 | | -NBD_ACCEPT="-p tcp --dport $NBDPORT -m conntrack --ctstate NEW -m set --match-set $NBDSET dst -j ACCEPT" |
40 | 54 |
|
41 | | -# Rules to reject NBD packets on disallowed addresses, |
42 | | -# inbound and outbound, |
43 | | -# even if part of an established connection. |
44 | | -NBD_REJECT_IN="-p tcp --dport $NBDPORT -m set ! --match-set $NBDSET dst -j REJECT" |
45 | | -NBD_REJECT_OUT="-p tcp --sport $NBDPORT -m set ! --match-set $NBDSET src -j REJECT" |
46 | | - |
47 | | -function destroy_tmp { |
48 | | - ipset destroy $TMPSET 2>/dev/null || true |
49 | | -} |
50 | | - |
51 | | -destroy_tmp |
52 | | -trap destroy_tmp ERR EXIT |
| 55 | +# Rule fragments for sending NBD packets to NBD chains |
| 56 | +# from INPUT and OUTPUT chains. |
| 57 | +NBD_INPUT_JUMP_TO="-p tcp --dport $NBDPORT -j " |
| 58 | +NBD_OUTPUT_JUMP_TO="-p tcp --sport $NBDPORT -j " |
53 | 59 |
|
54 | 60 | # Same principle as double-buffering a display: |
55 | | -# add the items one by one to a temporary ipset, |
56 | | -# then swap it into use atomically. |
57 | | -ipset create $TMPSET $SETTYPE |
58 | | - |
| 61 | +# add the items one by one to temporary chains, |
| 62 | +# then swap them into use atomically, or as near as possible. |
| 63 | +iptables --new-chain $NewChainIn 2>/dev/null || iptables --flush $NewChainIn |
| 64 | +iptables --new-chain $NewChainOut 2>/dev/null || iptables --flush $NewChainOut |
59 | 65 | while [ "$#" -ne 0 ]; do |
60 | | - addr="${1}" |
| 66 | + intf="${1}" |
| 67 | + # (Each positional param is forgotten for ever at the end of the loop.) |
61 | 68 | shift 1 |
62 | | - ipset add $TMPSET "${addr}" |
| 69 | + iptables -A $NewChainIn -m conntrack --ctstate NEW,ESTABLISHED --in-interface "${intf}" -j ACCEPT |
| 70 | + iptables -A $NewChainOut --out-interface "${intf}" -j RETURN |
63 | 71 | done |
| 72 | +iptables --append $NewChainIn -j REJECT |
| 73 | +iptables --append $NewChainOut -j REJECT |
64 | 74 |
|
65 | | -ipset create -exist $NBDSET $SETTYPE |
66 | | -ipset swap $TMPSET $NBDSET |
67 | | -ipset destroy $TMPSET |
| 75 | +function insert_rule_if_absent { |
| 76 | + chain="${1}" |
| 77 | + shift 1 |
| 78 | + rule="${*}" |
| 79 | + iptables --check "${chain}" $rule 2>/dev/null || \ |
| 80 | + iptables --insert "${chain}" $rule |
| 81 | +} |
68 | 82 |
|
69 | | -# Now create the rules if they do not exist already. |
70 | | -# Note: the ip set must exist before we can add an iptables rule that uses it. |
71 | | -for rule in "$NBD_ACCEPT" "$NBD_REJECT_IN"; do |
72 | | - if ! iptables --check INPUT $rule 2>/dev/null |
73 | | - then |
74 | | - iptables --insert INPUT $rule |
75 | | - fi |
76 | | -done |
| 83 | +# Start using the new chains. |
| 84 | +insert_rule_if_absent INPUT "${NBD_INPUT_JUMP_TO} ${NewChainIn}" |
| 85 | +insert_rule_if_absent OUTPUT "${NBD_OUTPUT_JUMP_TO} ${NewChainOut}" |
77 | 86 |
|
78 | | -if ! iptables --check OUTPUT $NBD_REJECT_OUT 2>/dev/null |
79 | | -then |
80 | | - iptables --insert OUTPUT $NBD_REJECT_OUT |
81 | | -fi |
| 87 | +# If old chains are present, stop using them and then delete them. |
| 88 | +iptables --delete INPUT $NBD_INPUT_JUMP_TO $NbdChainIn 2>/dev/null || true |
| 89 | +iptables --flush "${NbdChainIn}" 2>/dev/null && \ |
| 90 | + iptables --delete-chain "${NbdChainIn}" |
| 91 | + |
| 92 | +iptables --delete OUTPUT $NBD_OUTPUT_JUMP_TO $NbdChainOut 2>/dev/null || true |
| 93 | +iptables --flush "${NbdChainOut}" 2>/dev/null && \ |
| 94 | + iptables --delete-chain "${NbdChainOut}" |
82 | 95 |
|
| 96 | +# Rename our rules so they no longer appear new. |
| 97 | +# (References remain valid, pointing to the renamed chains.) |
| 98 | +iptables --rename-chain "${NewChainIn}" "${NbdChainIn}" |
| 99 | +iptables --rename-chain "${NewChainOut}" "${NbdChainOut}" |
83 | 100 | exit 0 |
0 commit comments