-
-
Save IOZZYS/3196149f0dc7a43dd139d6bc1652506b to your computer and use it in GitHub Desktop.
Qubes-os port forwarding to allow external connections
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# Neowutran <github@neowutran.ovh> | |
# Adapted previous work to support QubesOS v4.2 | |
# Adapted from previous work: | |
# - https://gist.github.com/fepitre/941d7161ae1150d90e15f778027e3248 | |
# - https://gist.github.com/daktak/f887352d564b54f9e529404cc0eb60d5 | |
# - https://gist.github.com/jpouellet/d8cd0eb8589a5b9bf0c53a28fc530369 | |
# - https://gist.github.com/Joeviocoe/6c4dc0c283f6d6c5b1a3f5af8793292b | |
[ "$DEBUG" = 1 ] && set -x | |
ip() { | |
qvm-prefs -g -- "$1" ip | |
} | |
netvm() { | |
qvm-prefs -g -- "$1" netvm | |
} | |
forward() { | |
local action="$1" | |
local from_qube="$2" | |
local to_qube="$3" | |
local port="$4" | |
local proto="$5" | |
local persistent="$6" | |
local iface | |
local from_ip | |
local to_ip | |
local nft_cmd | |
local nft_handle | |
# TODO: Handle multiple interfaces in sys-net. It currently catches only the first physical interface which is UP. | |
iface=$(qvm-run -p -u root "$from_qube" "ip link | grep -E '^[0-9]' | grep -E 'state UP' | cut -d ':' -f 2 | cut -d ' ' -f 2 | grep -vE '^(vif|lo)' | head -1") | |
#from_ip=$(qvm-run -p -u root "$from_qube" "hostname -I | cut -d ' ' -f 1") | |
from_ip=$(qvm-run -p -u root "$from_qube" "ip addr show dev $iface | grep -Eo 'inet [0-9]+(\.[0-9]+){3}' | cut -d ' ' -f 2 | head -1") | |
to_ip=$(ip "$to_qube") | |
if [ "x$from_ip" = "xNone" ]; then | |
local from_ip= | |
fi | |
if [[ "$action" = "clear" ]]; then | |
echo "$from_qube: Clearing Port Forwarding from $from_qube iptables" >&2 | |
qvm-run -p -u root "$from_qube" "iptables-save | grep -v 'PortFwd $from_qube' | iptables-restore" | |
nft_cmd="nft list table ip qubes-firewall -a | tr -d '\"' | grep 'iifname $iface accept # handle' | awk '{print \$NF}'" | |
nft_handle=$(qvm-run -p -u root "$from_qube" "$nft_cmd") | |
if [[ $nft_handle =~ ^[0-9]+$ ]]; then | |
qvm-run -p -u root "$from_qube" "nft delete rule ip qubes-firewall forward handle $nft_handle" | |
fi | |
qvm-run -p -u root "$from_qube" "sed -i '/PortFwd $from_qube>$to_qube:$proto$port/d' /rw/config/rc.local" | |
qvm-run -p -u root "$from_qube" "sed -i '/PortFwd $from_qube>$to_qube:$proto$port/d' /rw/config/rc.local" | |
if ! qvm-run -p -u root "$from_qube" "grep -q 'PortFwd' /rw/config/rc.local"; then | |
qvm-run -p -u root "$from_qube" "sed -i '/nft add rule ip qubes-firewall forward meta iifname $iface accept/d' /rw/config/rc.local" | |
fi | |
else | |
echo "$from_qube: Forwarding on $iface port $port to $to_qube ($from_ip -> $to_ip)" >&2 | |
forward_rule1="nft create table ip nat; nft -- create chain ip nat prerouting { type nat hook prerouting priority -100 \; }; nft \"add rule ip nat prerouting iifname \\\"$iface\\\" ip daddr $from_ip $proto dport $port counter dnat to $to_ip comment \\\"PortFwd $from_qube>$to_qube:$proto$port\\\"\"" | |
forward_rule2="nft add rule ip qubes custom-forward iifname $iface ip daddr $to_ip $proto dport $port ct state new counter accept" | |
forward_rule3="nft add rule ip qubes-firewall forward meta iifname $iface accept" | |
qvm-run -p -u root "$from_qube" "iptables-nft-save | grep -v 'PortFwd $from_qube>$to_qube:$proto$port' | iptables-nft-restore" | |
qvm-run -p -u root "$from_qube" "$forward_rule1" | |
qvm-run -p -u root "$from_qube" "$forward_rule2" | |
qvm-run -p -u root "$from_qube" "$forward_rule3" | |
if [ "$persistent" = 1 ]; then | |
qvm-run -p -u root "$from_qube" "echo $forward_rule1 >> /rw/config/rc.local" | |
qvm-run -p -u root "$from_qube" "echo $forward_rule2 >> /rw/config/rc.local" | |
if ! qvm-run -p -u root "$from_qube" "grep -q 'nft add rule ip qubes-firewall forward meta iifname $iface accept' /rw/config/rc.local"; then | |
qvm-run -p -u root "$from_qube" "echo $forward_rule3 >> /rw/config/rc.local" | |
# Ensure rc.local is executable | |
qvm-run -p -u root "$from_qube" "chmod +x /rw/config/rc.local" | |
fi | |
fi | |
fi | |
} | |
input() { | |
local action="$1" | |
local qube="$2" | |
local port="$3" | |
local proto="$4" | |
local persistent="$5" | |
if [[ "$action" = "clear" ]]; then | |
echo "$qube: Clearing Port Forwarding from $qube iptables" >&2 | |
qvm-run -p -u root "$qube" "iptables-save | grep -v 'PortFwd $qube' | iptables-restore" | |
qvm-run -p -u root "$qube" "sed -i '/PortFwd $qube:$proto$port/d' /rw/config/rc.local" | |
else | |
echo "$qube: Allowing input to port $port" >&2 | |
qvm-run -p -u root "$qube" "iptables-save | grep -v 'PortFwd $qube:$proto$port' | iptables-restore" | |
input_rule="nft 'insert rule ip qubes custom-input $proto dport $port ct state new counter accept'" | |
qvm-run -p -u root "$qube" "$input_rule" | |
if [ "$persistent" = 1 ]; then | |
qvm-run -p -u root "$qube" "echo $input_rule >> /rw/config/rc.local" | |
# Ensure rc.local is executable | |
qvm-run -p -u root "$qube" "chmod +x /rw/config/rc.local" | |
fi | |
fi | |
} | |
recurse_netvms() { | |
local action="$1" | |
local this_qube="$2" | |
local port="$3" | |
local proto="$4" | |
local persistent="$5" | |
local outer_dom | |
outer_dom=$(netvm "$this_qube") | |
if [[ -n "$outer_dom" && "$outer_dom" != "None" ]]; then | |
forward "$action" "$outer_dom" "$this_qube" "$port" "$proto" "$persistent" | |
recurse_netvms "$action" "$outer_dom" "$port" "$proto" "$persistent" | |
fi | |
} | |
usage() { | |
echo "Usage: ${0##*/} --action ACTION --qube QUBE --port PORT --proto PROTO --persistent" >&2 | |
echo "" >&2 | |
echo "Exemple: " >&2 | |
echo " -> ${0##*/} --action create --qube work --port 22" >&2 | |
echo " -> ${0##*/} --action create --qube work --port 444 --proto udp --persistent" >&2 | |
echo " -> ${0##*/} --action clear --qube work --port 22" >&2 | |
echo " -> ${0##*/} --action clear --qube work --port 444 --proto udp" >&2 | |
echo "" >&2 | |
echo "Default value for PROTO is 'tcp' and iptables are not persistent" | |
exit 1 | |
} | |
if ! OPTS=$(getopt -o a:q:p:n:s --long action:,qube:,port:,proto:,persistent -n "$0" -- "$@"); then | |
echo "An error occurred while parsing options." >&2 | |
exit 1 | |
fi | |
eval set -- "$OPTS" | |
while [[ $# -gt 0 ]]; do | |
case "$1" in | |
-a | --action ) ACTION="$2"; shift ;; | |
-q | --qube ) QUBE="$2"; shift ;; | |
-p | --port ) PORT="$2"; shift ;; | |
-n | --proto ) PROTO="$2"; shift ;; | |
-s | --persistent ) PERSISTENT=1; shift ;; | |
esac | |
shift | |
done | |
if [ -z "$PROTO" ]; then | |
PROTO="tcp" | |
fi | |
if { [ "$ACTION" != "create" ] || [ "$ACTION" == "clear" ]; } && { [ -z "$QUBE" ] || [ -z "$PORT" ]; }; then | |
usage | |
fi | |
if ! qvm-check "$QUBE" > /dev/null 2>&1; then | |
echo "Qube '$QUBE' not found." >&2 | |
exit 1 | |
fi | |
input "$ACTION" "$QUBE" "$PORT" "$PROTO" "$PERSISTENT" | |
recurse_netvms "$ACTION" "$QUBE" "$PORT" "$PROTO" "$PERSISTENT" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment