[Qubes OS 4.2] Easily NAT qubes port to external network

@solene

I’ve modified your script for my own purposes and thought I’d share it here as well, if someone has the same use-case (forwarding simple broadcast packets).

The modifications include:

  1. Fixing the default value for TYPE (not important)
  2. Adding support for forwarding simple broadcast packets using the -b flag. I’m not sure if it will work for something more complex that e.g. requires two-way communication, but it’s completely enough for Syncthing’s local discovery protocol to actually start working.

DISCLAIMER: I am not well-versed in nft/iptables, and as such, I have used an LLM to help me write those rules. If there’s any security vulnerabilities introduced because of that, please let me know. I tried to keep the amount of lines changed to the minimum.

I attach the script below:

#!/bin/sh

END_QUBE="$1"
PORT="$2"
TYPE="${3:-tcp}"
BFLAG="$4"

usage() {
    echo "usage: $0 qube_name port_number [tcp|udp] [-b]"
    exit 0
}

find_netvm() {
    QUBE="$1"
    NET_VM="$(qvm-ls -n | awk -v qube="${QUBE}" '$1 == qube { print $3 }')"
    DESTINATION="$(qvm-ls -n | awk -v qube="${QUBE}" '$1 == qube { print $4 }')"
    echo "Qube $QUBE has $NET_VM for netvm"

    if [ "$NET_VM" != "-" ]
    then
        # new: only the netvm holding the uplink sees a real L2 broadcast
        if [ "$BFLAG" = "-b" ] && \
           [ "$(qvm-ls -n | awk -v qube="${NET_VM}" '$1 == qube { print $3 }')" = "-" ]
        then BCAST=yes ; else BCAST=no ; fi

        cat <<EOF | qvm-run --no-gui -u root --pass-io "$NET_VM" "/bin/sh"
if ! nft -nn list table ip qubes | grep "chain nat {" >/dev/null ; then
    nft add chain qubes nat { type nat hook prerouting priority dstnat\; }
fi

if [ "${BCAST}" = "yes" ] ; then
    nft add chain qubes bcast { type filter hook prerouting priority mangle\; }
    nft insert rule qubes bcast iifname != \"vif*\" meta pkttype broadcast "${TYPE}" dport "${PORT}" counter meta pkttype set host
fi

nft insert rule qubes custom-input "${TYPE}" dport "${PORT}" accept
nft insert rule qubes custom-forward "${TYPE}" dport "${PORT}" accept
nft insert rule qubes nat iifname != \"vif*\" "${TYPE}" dport "${PORT}" dnat "${DESTINATION}"
EOF
        if [ $? -eq 0 ] ; then echo "$NET_VM -> $QUBE: [OK]" ; else echo "$NET_VM -> $QUBE: [KO]" ; exit 1 ; fi
        find_netvm "$NET_VM"
    fi
}

if [ -z "$1" ] || [ -z "$2" ]
then
    usage
fi

if qvm-ls --running | grep ^"${END_QUBE} " >/dev/null
then
    echo "qube is running"
    find_netvm "$END_QUBE"
    cat <<EOF | qvm-run --no-gui -u root --pass-io "$END_QUBE" "/bin/sh"
nft add rule qubes custom-input "${TYPE}" dport "${PORT}" accept
EOF
else
    echo "qube ${END_QUBE} is not running"
fi