diff options
Diffstat (limited to 'ctdb/config/events/legacy/11.natgw.script')
-rwxr-xr-x | ctdb/config/events/legacy/11.natgw.script | 242 |
1 files changed, 242 insertions, 0 deletions
diff --git a/ctdb/config/events/legacy/11.natgw.script b/ctdb/config/events/legacy/11.natgw.script new file mode 100755 index 0000000..fb93dea --- /dev/null +++ b/ctdb/config/events/legacy/11.natgw.script @@ -0,0 +1,242 @@ +#!/bin/sh +# Script to set up one of the nodes as a NAT gateway for all other nodes. +# This is used to ensure that all nodes in the cluster can still originate +# traffic to the external network even if there are no public addresses +# available. +# + +[ -n "$CTDB_BASE" ] || \ + CTDB_BASE=$(d=$(dirname "$0") && cd -P "$d" && dirname "$PWD") + +. "${CTDB_BASE}/functions" + +service_name="natgw" + +load_script_options + +[ -n "$CTDB_NATGW_NODES" ] || exit 0 +export CTDB_NATGW_NODES + +ctdb_setup_state_dir "failover" "$service_name" + +# script_state_dir set by ctdb_setup_state_dir() +# shellcheck disable=SC2154 +natgw_cfg_new="${script_state_dir}/cfg_new" +natgw_cfg_old="${script_state_dir}/cfg_old" +natgw_leader_old="${script_state_dir}/leader_old" + +ctdb_natgw_follower_only () +{ + _ip_address=$(ctdb_get_ip_address) + + awk -v my_ip="$_ip_address" \ + '$1 == my_ip { if ($2 ~ "follower-only") { exit 0 } else { exit 1 } }' \ + "$CTDB_NATGW_NODES" +} + +natgw_check_config () +{ + [ -r "$CTDB_NATGW_NODES" ] || \ + die "error: CTDB_NATGW_NODES=${CTDB_NATGW_NODES} unreadable" + if ! ctdb_natgw_follower_only ; then + [ -n "$CTDB_NATGW_PUBLIC_IP" ] || \ + die "Invalid configuration: CTDB_NATGW_PUBLIC_IP not set" + [ -n "$CTDB_NATGW_PUBLIC_IFACE" ] || \ + die "Invalid configuration: CTDB_NATGW_PUBLIC_IFACE not set" + fi + [ -n "$CTDB_NATGW_PRIVATE_NETWORK" ] || \ + die "Invalid configuration: CTDB_NATGW_PRIVATE_NETWORK not set" + + # The default is to create a single default route + [ -n "$CTDB_NATGW_STATIC_ROUTES" ] || CTDB_NATGW_STATIC_ROUTES="0.0.0.0/0" +} + +natgw_write_config () +{ + _f="$1" + + cat >"$_f" <<EOF +CTDB_NATGW_NODES="$CTDB_NATGW_NODES" +CTDB_NATGW_PUBLIC_IP="$CTDB_NATGW_PUBLIC_IP" +CTDB_NATGW_PUBLIC_IFACE="$CTDB_NATGW_PUBLIC_IFACE" +CTDB_NATGW_DEFAULT_GATEWAY="$CTDB_NATGW_DEFAULT_GATEWAY" +CTDB_NATGW_PRIVATE_NETWORK="$CTDB_NATGW_PRIVATE_NETWORK" +CTDB_NATGW_STATIC_ROUTES="$CTDB_NATGW_STATIC_ROUTES" +EOF +} + +natgw_config_has_changed () +{ + natgw_write_config "$natgw_cfg_new" + + # Non-existent old returns true, no log message + if [ ! -f "$natgw_cfg_old" ] ; then + return 0 + fi + + # Handle no change + if cmp "$natgw_cfg_old" "$natgw_cfg_new" >/dev/null 2>&1 ; then + return 1 + fi + + echo "NAT gateway configuration has changed" + return 0 +} + +_natgw_clear () +{ + _ip="${CTDB_NATGW_PUBLIC_IP%/*}" + _maskbits="${CTDB_NATGW_PUBLIC_IP#*/}" + + delete_ip_from_iface \ + "$CTDB_NATGW_PUBLIC_IFACE" "$_ip" "$_maskbits" >/dev/null 2>&1 + for _net_gw in $CTDB_NATGW_STATIC_ROUTES ; do + _net="${_net_gw%@*}" + ip route del "$_net" metric 10 >/dev/null 2>/dev/null + done + + # Delete the masquerading setup from a previous iteration where we + # were the NAT-GW + iptables -D POSTROUTING -t nat \ + -s "$CTDB_NATGW_PRIVATE_NETWORK" ! -d "$CTDB_NATGW_PRIVATE_NETWORK" \ + -j MASQUERADE >/dev/null 2>/dev/null + + iptables -D INPUT -p tcp --syn -d "${_ip}/32" -j REJECT 2>/dev/null +} + +natgw_clear () +{ + if [ -r "$natgw_cfg_old" ] ; then + (. "$natgw_cfg_old" ; _natgw_clear) + else + _natgw_clear + fi +} + +natgw_set_leader () +{ + set_proc sys/net/ipv4/ip_forward 1 + iptables -A POSTROUTING -t nat \ + -s "$CTDB_NATGW_PRIVATE_NETWORK" ! -d "$CTDB_NATGW_PRIVATE_NETWORK" \ + -j MASQUERADE + + # block all incoming connections to the NATGW IP address + ctdb_natgw_public_ip_host="${CTDB_NATGW_PUBLIC_IP%/*}/32" + iptables -D INPUT -p tcp --syn \ + -d "$ctdb_natgw_public_ip_host" -j REJECT 2>/dev/null + iptables -I INPUT -p tcp --syn \ + -d "$ctdb_natgw_public_ip_host" -j REJECT 2>/dev/null + + ip addr add "$CTDB_NATGW_PUBLIC_IP" dev "$CTDB_NATGW_PUBLIC_IFACE" + for _net_gw in $CTDB_NATGW_STATIC_ROUTES ; do + _net="${_net_gw%@*}" + if [ "$_net" != "$_net_gw" ] ; then + _gw="${_net_gw#*@}" + else + _gw="$CTDB_NATGW_DEFAULT_GATEWAY" + fi + + [ -n "$_gw" ] || continue + ip route add "$_net" metric 10 via "$_gw" + done +} + +natgw_set_follower () +{ + _natgwip="$1" + + for _net_gw in $CTDB_NATGW_STATIC_ROUTES ; do + _net="${_net_gw%@*}" + ip route add "$_net" via "$_natgwip" metric 10 + done +} + +natgw_ensure_leader () +{ + # Intentional word splitting here + # shellcheck disable=SC2046 + set -- $("${CTDB_HELPER_BINDIR}/ctdb_natgw" leader) + natgwleader="${1:--1}" # Default is -1, for failure above + natgwip="$2" + + if [ "$natgwleader" = "-1" ]; then + # Fail... + die "There is no NATGW leader node" + fi +} + +natgw_leader_has_changed () +{ + if [ -r "$natgw_leader_old" ] ; then + read _old_natgwleader <"$natgw_leader_old" + else + _old_natgwleader="" + fi + [ "$_old_natgwleader" != "$natgwleader" ] +} + +natgw_save_state () +{ + echo "$natgwleader" >"$natgw_leader_old" + # Created by natgw_config_has_changed() + mv "$natgw_cfg_new" "$natgw_cfg_old" +} + + +case "$1" in +setup) + natgw_check_config + ;; + +startup) + natgw_check_config + + # Error if CTDB_NATGW_PUBLIC_IP is listed in public addresses + ip_pat=$(echo "$CTDB_NATGW_PUBLIC_IP" | sed -e 's@\.@\\.@g') + ctdb_public_addresses="${CTDB_BASE}/public_addresses" + if grep -q "^${ip_pat}[[:space:]]" "$ctdb_public_addresses" ; then + die "ERROR: CTDB_NATGW_PUBLIC_IP same as a public address" + fi + + # do not send out arp requests from loopback addresses + set_proc sys/net/ipv4/conf/all/arp_announce 2 + ;; + +updatenatgw|ipreallocated) + natgw_check_config + + natgw_ensure_leader + + natgw_config_has_changed || natgw_leader_has_changed || exit 0 + + natgw_clear + + pnn=$(ctdb_get_pnn) + if [ "$pnn" = "$natgwleader" ]; then + natgw_set_leader + else + natgw_set_follower "$natgwip" + fi + + # flush our route cache + set_proc sys/net/ipv4/route/flush 1 + + # Only update saved state when NATGW successfully updated + natgw_save_state + ;; + +shutdown|removenatgw) + natgw_check_config + natgw_clear + ;; + +monitor) + natgw_check_config + + if [ -n "$CTDB_NATGW_PUBLIC_IFACE" ] ; then + interface_monitor "$CTDB_NATGW_PUBLIC_IFACE" || exit 1 + fi + ;; +esac + +exit 0 |