Skip to content

Instantly share code, notes, and snippets.

@u0m3
Last active December 31, 2023 18:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save u0m3/1c72c1dd27a812930d654d2ff144f8d9 to your computer and use it in GitHub Desktop.
Save u0m3/1c72c1dd27a812930d654d2ff144f8d9 to your computer and use it in GitHub Desktop.
Add/Change nftable rules when openvpn server starts
#!/bin/bash
## Add nft rules
# Target is something like:
# - nft insert iifname "enp2s0" oifname "tun0" ip saddr 192.168.22.0/24 ip daddr 192.168.2.0/24 counter accept
# - nft insert iifname "tun0" oifname "enp2s0" ip saddr 192.168.2.0/24 ip daddr 192.168.22.0/24 counter accept
#
# Documentation:
# - https://www.netfilter.org/projects/nftables/manpage.html
# - https://wiki.nftables.org/wiki-nftables/index.php/Main_Page
# - https://wiki.nftables.org/wiki-nftables/index.php/Simple_rule_management
# - https://wiki.nftables.org/wiki-nftables/index.php/Atomic_rule_replacement (this would have been nice if it were usable in my usecase)
# - https://wiki.nftables.org/wiki-nftables/index.php/Monitoring_ruleset_updates (this was fun: apparently replace does an add+delete)
# - https://wiki.nftables.org/wiki-nftables/index.php/Scripting (this might have been another way to go)
# - https://eklitzke.org/bash-$*-and-$@
# - https://coderwall.com/p/85jnpq/bash-built-in-variables
# - https://stackoverflow.com/questions/59895/how-to-get-the-source-directory-of-a-bash-script-from-within-the-script-itself
# - https://www.oreilly.com/library/view/regular-expressions-cookbook/9780596802837/ch07s16.html (for matching subnet)
# - https://openvpn.net/community-resources/reference-manual-for-openvpn-2-4/
# - https://tomlee.co/2011/10/gnu-screen-splitting/ (for help with screen for tmux users)
## Variables
SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
LOGFILE="$SCRIPTDIR/server.up.sh.log"
REGEX_NET='\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}/[0-9]{1,2}\b'
# TODO: try to make LAN dinamic too.
DEV_LAN="enp2s0"
NET_LAN="192.168.22.0/24"
DEV_VPN="" # keep empty to load dinamically from env variables passed by OpenVPN
NET_VPN="" # keep empty to load dinamically from env variables passed by OpenVPN
## Binaries
nft="/usr/sbin/nft"
ipcalc="/usr/bin/ipcalc"
grep="/usr/bin/grep"
(
# Test args and env
# echo '$#' $# ; echo '$@' "$@" ; echo "$( /usr/bin/env )"
## Variables check
if [ -z "$DEV_VPN" ]; then
DEV_VPN=$dev
fi
if [ -z "$NET_VPN" ]; then
NET_VPN=`$ipcalc "$ifconfig_local/$ifconfig_netmask" | grep Network | grep -iPo "$REGEX_NET"`
fi
echo 'DEV_LAN =' $DEV_LAN
echo 'NET_LAN =' $NET_LAN
echo 'DEV_VPN =' $DEV_VPN
echo 'NET_VPN =' $NET_VPN
RULE_DEV_VPN_TO_DEV_LAN="$($nft -a list chain ip filter FORWARD | $grep -P "iifname \"?$DEV_VPN\"?" | $grep -P "oifname \"?$DEV_LAN\"?")"
RULE_DEV_VPN_TO_DEV_LAN_RULE="iifname "$DEV_VPN" oifname "$DEV_LAN" ip saddr "$NET_VPN" ip daddr "$NET_LAN" counter accept"
echo 'RULE_DEV_VPN_TO_DEV_LAN =' $RULE_DEV_VPN_TO_DEV_LAN
echo 'RULE_DEV_VPN_TO_DEV_LAN_RULE =' $RULE_DEV_VPN_TO_DEV_LAN_RULE
if [ -z "$RULE_DEV_VPN_TO_DEV_LAN" ]; then
echo $nft insert rule ip filter FORWARD $RULE_DEV_VPN_TO_DEV_LAN_RULE
$nft insert rule ip filter FORWARD $RULE_DEV_VPN_TO_DEV_LAN_RULE
else
RULE_DEV_VPN_TO_DEV_LAN_SNET="$(echo $RULE_DEV_VPN_TO_DEV_LAN | $grep -Po "ip saddr \"?$REGEX_NET\"?" | $grep -Po "$REGEX_NET")"
RULE_DEV_VPN_TO_DEV_LAN_DNET="$(echo $RULE_DEV_VPN_TO_DEV_LAN | $grep -Po "ip daddr \"?$REGEX_NET\"?" | $grep -Po "$REGEX_NET")"
echo 'RULE_DEV_VPN_TO_DEV_LAN_SNET =' $RULE_DEV_VPN_TO_DEV_LAN_SNET
echo 'RULE_DEV_VPN_TO_DEV_LAN_DNET =' $RULE_DEV_VPN_TO_DEV_LAN_DNET
if [ "$RULE_DEV_VPN_TO_DEV_LAN_SNET" != "$NET_VPN" ] || [ "$RULE_DEV_VPN_TO_DEV_LAN_DNET" != "$NET_LAN" ]; then
RULE_DEV_VPN_TO_DEV_LAN_ID="$(echo $RULE_DEV_VPN_TO_DEV_LAN | $grep -Po '# handle \d+' | $grep -Po '\d+')"
echo 'RULE_DEV_VPN_TO_DEV_LAN_ID =' $RULE_DEV_VPN_TO_DEV_LAN_ID
echo $nft replace rule ip filter FORWARD handle $RULE_DEV_VPN_TO_DEV_LAN_ID $RULE_DEV_VPN_TO_DEV_LAN_RULE
$nft replace rule ip filter FORWARD handle $RULE_DEV_VPN_TO_DEV_LAN_ID $RULE_DEV_VPN_TO_DEV_LAN_RULE
fi
fi
RULE_DEV_LAN_TO_DEV_VPN="$($nft -a list chain ip filter FORWARD | $grep -P "iifname \"?$DEV_LAN\"?" | $grep -P "oifname \"?$DEV_VPN\"?")"
RULE_DEV_LAN_TO_DEV_VPN_RULE="iifname "$DEV_LAN" oifname "$DEV_VPN" ip saddr "$NET_LAN" ip daddr "$NET_VPN" counter accept"
echo 'RULE_DEV_LAN_TO_DEV_VPN =' $RULE_DEV_LAN_TO_DEV_VPN
echo 'RULE_DEV_LAN_TO_DEV_VPN_RULE =' $RULE_DEV_LAN_TO_DEV_VPN_RULE
if [ -z "$RULE_DEV_LAN_TO_DEV_VPN" ]; then
echo $nft insert rule ip filter FORWARD $RULE_DEV_LAN_TO_DEV_VPN_RULE
$nft insert rule ip filter FORWARD $RULE_DEV_LAN_TO_DEV_VPN_RULE
else
RULE_DEV_LAN_TO_DEV_VPN_SNET="$(echo $RULE_DEV_LAN_TO_DEV_VPN | $grep -Po "ip saddr \"?$REGEX_NET\"?" | $grep -Po "$REGEX_NET")"
RULE_DEV_LAN_TO_DEV_VPN_DNET="$(echo $RULE_DEV_LAN_TO_DEV_VPN | $grep -Po "ip daddr \"?$REGEX_NET\"?" | $grep -Po "$REGEX_NET")"
echo 'RULE_DEV_LAN_TO_DEV_VPN_SNET =' $RULE_DEV_LAN_TO_DEV_VPN_SNET
echo 'RULE_DEV_LAN_TO_DEV_VPN_DNET =' $RULE_DEV_LAN_TO_DEV_VPN_DNET
if [ "$RULE_DEV_LAN_TO_DEV_VPN_SNET" != "$NET_LAN" ] || [ "$RULE_DEV_LAN_TO_DEV_VPN_DNET" != "$NET_VPN" ]; then
RULE_DEV_LAN_TO_DEV_VPN_ID="$(echo $RULE_DEV_LAN_TO_DEV_VPN | $grep -Po '# handle \d+' | $grep -Po '\d+')"
echo 'RULE_DEV_LAN_TO_DEV_VPN_ID =' $RULE_DEV_LAN_TO_DEV_VPN_ID
echo $nft replace rule ip filter FORWARD handle $RULE_DEV_LAN_TO_DEV_VPN_ID $RULE_DEV_LAN_TO_DEV_VPN_RULE
$nft replace rule ip filter FORWARD handle $RULE_DEV_LAN_TO_DEV_VPN_ID $RULE_DEV_LAN_TO_DEV_VPN_RULE
fi
fi
) | tee $LOGFILE
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment