#!/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 is used by various functions # shellcheck disable=SC2034 service_name=natgw loadconfig [ -n "$CTDB_NATGW_NODES" ] || exit 0 export CTDB_NATGW_NODES service_state_dir=$(ctdb_setup_service_state_dir) || exit $? natgw_cfg_new="${service_state_dir}/cfg_new" natgw_cfg_old="${service_state_dir}/cfg_old" natgw_master_old="${service_state_dir}/master_old" ctdb_natgw_slave_only () { _ip_address=$(ctdb_get_ip_address) awk -v my_ip="$_ip_address" \ '$1 == my_ip { if ($2 ~ "slave-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_slave_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" if [ "$CTDB_PARTIALLY_ONLINE_INTERFACES" = "yes" ] ; then die "Invalid configuration: CTDB_PARTIALLY_ONLINE_INTERFACES=yes incompatible with NAT gateway" fi # 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" </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_master () { 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_slave () { _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_master () { # Intentional word splitting here # shellcheck disable=SC2046 set -- $("${CTDB_HELPER_BINDIR}/ctdb_natgw" master) natgwmaster="${1:--1}" # Default is -1, for failure above natgwip="$2" if [ "$natgwmaster" = "-1" ]; then # Fail... die "There is no NATGW master node" fi } natgw_master_has_changed () { if [ -r "$natgw_master_old" ] ; then read _old_natgwmaster <"$natgw_master_old" else _old_natgwmaster="" fi [ "$_old_natgwmaster" != "$natgwmaster" ] } natgw_save_state () { echo "$natgwmaster" >"$natgw_master_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') if grep -q "^${ip_pat}[[:space:]]" \ "${CTDB_PUBLIC_ADDRESSES:-${CTDB_BASE}/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_master natgw_config_has_changed || natgw_master_has_changed || exit 0 natgw_clear pnn=$(ctdb_get_pnn) if [ "$pnn" = "$natgwmaster" ]; then natgw_set_master else natgw_set_slave "$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