#!/bin/sh # the following environment variables are set: # - 'ACTION' - 'up' or 'down' # - 'TUN' - tunnel device name # - 'SETUP_MODE' is 'full_script' or 'firewall_script' # - 'FWMARK_PROTECTED' - packets allready inspected by network protection # - 'FWMARK_TUN' - packets that shall enter tun device for inspection # - 'ROUTING_TABLE_ID' - packets with FWMARK_TUN are routed into this table. When 'SETUP_MODE' is 'full_script' this creates an entry under /etc/iproute2/rt_tables # - 'ALTERNATE_FIREWALL' - on some systems this variable is set to yes when the nftables lacks certain features. # When mode is 'simple' or 'default' and network protection fails to configure the firewall via nftables this script # is called with ALTERNATE_FIREWALL set to 'iptables' # - 'GROUP_ID' - protected group # - 'HAVE_IPV6' - configure with IPV6 support # - "TCP_DSTPORT_BYPASS" - a comma separated list of destination tcp ports to skip # - "UDP_DSTPORT_BYPASS" - a comma separated list of destination tcp ports to skip set -e set -x oif=$(ls /sys/class/net/ | grep -E "en|eth|wl") # a list of comma separated network interface names (ex: oif='"eth0","eth1"') oifset=$(echo $oif | xargs printf '"%s"\n' | paste -s -d ",") setup_fwbins() { case $ALTERNATE_FIREWALL in iptables) fw=iptables ipt4=`which iptables` if [ "$HAVE_IPV6" = "yes" ]; then ipt6=`which ip6tables` fi ;; *) fw=nft nftcmd=`which nft` ;; esac } # these are for nftables that have support for inet chain. # not currently used but available in no-script modes do_fw_up_nft_inet() { $nftcmd delete table inet wdnetp 2>/dev/null || true $nftcmd add table inet wdnetp $nftcmd add set inet wdnetp oifset '{ type ifname;}' $nftcmd add element inet wdnetp oifset {$oifset} $nftcmd 'add chain inet wdnetp vpnbr { type route hook output priority 0 ; }' $nftcmd add rule inet wdnetp vpnbr meta skgid $GROUP_ID return $nftcmd add rule inet wdnetp vpnbr meta mark $FWMARK_PROTECTED return if [ ! x"$TCP_DSTPORT_BYPASS" = x ]; then $nftcmd add rule inet wdnetp vpnbr "tcp dport {$TCP_DSTPORT_BYPASS} return" fi if [ ! x"$UDP_DSTPORT_BYPASS" = x ]; then $nftcmd add rule inet wdnetp vpnbr "udp dport {$UDP_DSTPORT_BYPASS} return" fi $nftcmd add rule inet wdnetp vpnbr "oifname @oifset ip protocol udp ct mark set $FWMARK_TUN counter" if [ x"$ENABLE_ICMP" = x"yes" ]; then $nftcmd add rule inet wdnetp vpnbr "oifname @oifset ip protocol icmp ct mark set $FWMARK_TUN counter" $nftcmd add rule inet wdnetp vpnbr "oifname @oifset ip6 nexthdr icmpv6 ct mark set $FWMARK_TUN counter" fi $nftcmd add rule inet wdnetp vpnbr "oifname @oifset tcp flags == syn ct mark set $FWMARK_TUN counter" $nftcmd add rule inet wdnetp vpnbr "oifname @oifset meta mark set ct mark" } do_fw_down_nft_inet() { $nftcmd delete table inet wdnetp 2>/dev/null || true } # default configure ip and ip4 separate chains. (ex: RHEL8.1) do_fw_up_nft_ip() { $nftcmd delete table ip wdnetp 2>/dev/null || true $nftcmd add table ip wdnetp $nftcmd add set ip wdnetp oifset '{ type ifname;}' $nftcmd add element ip wdnetp oifset {$oifset} $nftcmd add chain ip 'wdnetp vpnbr { type route hook output priority 0 ; }' $nftcmd add rule ip wdnetp vpnbr meta skgid $GROUP_ID return $nftcmd add rule ip wdnetp vpnbr meta mark $FWMARK_PROTECTED return if [ ! x"$TCP_DSTPORT_BYPASS" = x ]; then $nftcmd add rule ip wdnetp vpnbr "tcp dport {$TCP_DSTPORT_BYPASS} return" fi if [ ! x"$UDP_DSTPORT_BYPASS" = x ]; then $nftcmd add rule ip wdnetp vpnbr "udp dport {$UDP_DSTPORT_BYPASS} return" fi $nftcmd add rule ip wdnetp vpnbr "oifname @oifset ip protocol udp ct mark set $FWMARK_TUN counter" if [ x"$ENABLE_ICMP" = x"yes" ]; then $nftcmd add rule ip wdnetp vpnbr "oifname @oifset ip protocol icmp ct mark set $FWMARK_TUN counter" fi $nftcmd add rule ip wdnetp vpnbr "oifname @oifset tcp flags == syn ct mark set $FWMARK_TUN counter" $nftcmd add rule ip wdnetp vpnbr "oifname @oifset meta mark set ct mark" } do_fw_up_nft_ip6() { $nftcmd delete table ip6 wdnetp 2>/dev/null || true $nftcmd add table ip6 wdnetp $nftcmd add set ip6 wdnetp oifset '{ type ifname;}' $nftcmd add element ip6 wdnetp oifset {$oifset} $nftcmd add chain ip6 'wdnetp vpnbr { type route hook output priority 0 ; }' $nftcmd add rule ip6 wdnetp vpnbr meta skgid $GROUP_ID return $nftcmd add rule ip6 wdnetp vpnbr meta mark $FWMARK_PROTECTED return if [ ! x"$TCP_DSTPORT_BYPASS" = x ]; then $nftcmd add rule ip6 wdnetp vpnbr "tcp dport {$TCP_DSTPORT_BYPASS} return" fi if [ ! x"$UDP_DSTPORT_BYPASS" = x ]; then $nftcmd add rule ip6 wdnetp vpnbr "udp dport {$UDP_DSTPORT_BYPASS} return" fi $nftcmd add rule ip6 wdnetp vpnbr "oifname @oifset ip6 nexthdr udp ct mark set $FWMARK_TUN counter" if [ x"$ENABLE_ICMP" = x"yes" ]; then $nftcmd add rule ip6 wdnetp vpnbr "oifname @oifset ip6 nexthdr icmpv6 ct mark set $FWMARK_TUN counter" fi $nftcmd add rule ip6 wdnetp vpnbr "oifname @oifset tcp flags == syn ct mark set $FWMARK_TUN counter" $nftcmd add rule ip6 wdnetp vpnbr "oifname @oifset meta mark set ct mark" } do_fw_down_nft_ip() { $nftcmd delete table ip wdnetp 2>/dev/null || true if [ "$HAVE_IPV6" = "yes" ]; then $nftcmd delete table ip6 wdnetp 2>/dev/null || true fi } fw_up_nft() { do_fw_up_nft_ip if [ "$HAVE_IPV6" = "yes" ]; then do_fw_up_nft_ip6 fi } fw_down_nft() { do_fw_down_nft_ip } do_fw_up_iptables() { ipt=$1 chain=$2 do_fw_down_iptables $ipt $chain $ipt -N $chain -t mangle $ipt -A OUTPUT -t mangle -j $chain $ipt -A $chain -t mangle -m owner --gid-owner $GROUP_ID -j RETURN $ipt -A $chain -t mangle -m mark --mark $FWMARK_PROTECTED -j RETURN if [ ! x"$TCP_DSTPORT_BYPASS" = x ]; then $ipt -A $chain -t mangle -p tcp --match multiport --dports $TCP_DSTPORT_BYPASS -j RETURN fi if [ ! x"$UDP_DSTPORT_BYPASS" = x ]; then $ipt -A $chain -t mangle -p udp --match multiport --dports $UDP_DSTPORT_BYPASS -j RETURN fi for eth in $oif; do $ipt -A $chain -t mangle -o $eth -p udp -j CONNMARK --set-mark $FWMARK_TUN if [ x"$ENABLE_ICMP" = x"yes" ]; then $ipt -A $chain -t mangle -o $eth -p icmp -j CONNMARK --set-mark $FWMARK_TUN fi $ipt -A $chain -t mangle -o $eth -p tcp --syn -j CONNMARK --set-mark $FWMARK_TUN $ipt -A $chain -t mangle -o $eth -j CONNMARK --restore-mark done $ipt -A $chain -t mangle -j RETURN } do_fw_down_iptables() { ipt=$1 chain=$2 if $ipt -L $chain -t mangle; then $ipt -D OUTPUT -t mangle -j $chain || true $ipt -t mangle --flush $chain $ipt -t mangle -X $chain fi } fw_up_iptables() { do_fw_up_iptables $ipt4 out_wdnetp4 if [ "$HAVE_IPV6" = "yes" ]; then do_fw_up_iptables $ipt6 out_wdnetp6 fi } fw_down_iptables() { do_fw_down_iptables $ipt4 out_wdnetp4 if [ "$HAVE_IPV6" = "yes" ]; then do_fw_down_iptables $ipt6 out_wdnetp6 fi } tun_up() { ip address add 10.9.0.1/24 dev $TUN if [ "$HAVE_IPV6" = "yes" ]; then ip address add fd00::0/64 dev $TUN fi ip link set dev $TUN up } tun_down() { : } # make sure the /etc/iproute2/rt_tables has a custom entry named wdnissrv rt_tables_fix() { if ! grep -q -E '[[:space:]]+wdnissrv$' /etc/iproute2/rt_tables ; then printf "%d\twdnissrv\n" $ROUTING_TABLE_ID >> /etc/iproute2/rt_tables fi } iprules_clean() { for r in $(ip rule show table $ROUTING_TABLE_ID | awk '{ print $1;}' | cut -d ':' -f 1); do ip rule del pref $r done if [ "$HAVE_IPV6" = "yes" ]; then for r in $(ip -6 rule show table $ROUTING_TABLE_ID | awk '{ print $1;}' | cut -d ':' -f 1); do ip -6 rule del pref $r done fi } iprules_up() { iprules_clean ip rule add fwmark $FWMARK_TUN table wdnissrv ip route add default dev $TUN table wdnissrv ip route flush cache if [ "$HAVE_IPV6" = "yes" ]; then ip -6 rule add fwmark $FWMARK_TUN table wdnissrv ip -6 route add default dev $TUN table wdnissrv ip -6 route flush cache fi } iprules_down() { iprules_clean } up() { # if in 'full_script' mode setup the tun device and routing rules if [ "$SETUP_MODE" = "full_script" ]; then tun_up rt_tables_fix iprules_up fi fw_up_${fw} } down() { fw_down_${fw} if [ "$SETUP_MODE" = "full_script" ]; then iprules_down tun_down fi } fwreset() { fw_down_${fw} fw_up_${fw} } echo "ACTION = $ACTION" echo "SETUP_MODE = $SETUP_MODE" echo "TUN = $TUN" echo "ALTERNATE_FIREWALL = $ALTERNATE_FIREWALL" echo "FWMARK_PROTECTED = $FWMARK_PROTECTED" echo "FWMARK_TUN = $FWMARK_TUN" echo "ROUTING_TABLE_ID = $ROUTING_TABLE_ID" echo "GROUP_ID = $GROUP_ID" echo "NFT_MODE = $NFT_MODE" echo "HAVE_IPV6 = $HAVE_IPV6" echo "TCP_DSTPORT_BYPASS = $TCP_DSTPORT_BYPASS" echo "UDP_DSTPORT_BYPASS = $UDP_DSTPORT_BYPASS" setup_fwbins case "$ACTION" in up) up ;; down) down ;; fwreset) fwreset ;; *) exit 1 esac echo "DONE $ACTION"