| #!/bin/sh | |
| # Inspired by https://gist.github.com/daktak/f887352d564b54f9e529404cc0eb60d5 | |
| # Inspired by https://gist.github.com/jpouellet/d8cd0eb8589a5b9bf0c53a28fc530369 | |
| ip() { qvm-prefs -g -- "$1" ip; } | |
| netvm() { qvm-prefs -g -- "$1" netvm; } | |
| forward() { | |
| local from_domain=$1 | |
| local to_domain=$2 | |
| local port=$3 | |
| local type=$4 | |
| local from_ip=$(ip "$from_domain") | |
| local to_ip=$(ip "$to_domain") | |
| local iface=$(qvm-run -p -u root "$from_domain" "ifconfig \ | |
| | grep cast -B 1 --no-group-separator | grep -vE '^(vif|lo)' | grep -oE '^[^: ]+' | head -1") | |
| local from_ip=$(qvm-run -p -u root "$from_domain" "hostname -I | cut -d ' ' -f 1") | |
| if [ X"$from_ip" = XNone ] ; then local from_ip= ; fi | |
| if [[ $3 = "clear" && $4 = "all" ]] | |
| then | |
| echo "$from_domain: Clearing Port Forwarding from $1 iptables" >&2 | |
| qvm-run -p -u root "$from_domain" "iptables-save | grep -v 'PortFwd $1' | iptables-restore" | |
| local nft_cmd="nft list table ip qubes-firewall -a | tr -d '\"' | grep 'iifname $iface accept # handle' | awk '{print \$NF}'" | |
| local nft_handle=$(qvm-run -p -u root "$from_domain" "$nft_cmd") | |
| if [[ $nft_handle =~ ^[0-9]+$ ]] ; then qvm-run -p -u root "$from_domain" "nft delete rule ip qubes-firewall forward handle $nft_handle" ; fi | |
| else | |
| echo "$from_domain: Forwarding on $iface port $port to $to_domain | |
| ($from_ip -> $to_ip)" >&2 | |
| qvm-run -p -u root "$from_domain" "iptables-save | grep -v 'PortFwd $1>$2:$4$3' | iptables-restore" | |
| qvm-run -p -u root "$from_domain" "iptables -t nat -A PREROUTING -i $iface -p $type ${from_ip:+-d} $from_ip --dport $port -j DNAT --to-destination $to_ip \ | |
| -m comment --comment 'PortFwd $1>$2:$4$3'" | |
| qvm-run -p -u root "$from_domain" "iptables -I FORWARD 2 -i $iface -p $type ${to_ip:+-d} $to_ip --dport $port -m conntrack --ctstate NEW -j ACCEPT \ | |
| -m comment --comment 'PortFwd $1>$2:$4$3'" | |
| qvm-run -p -u root "$from_domain" "nft add rule ip qubes-firewall forward meta iifname $iface accept" | |
| fi | |
| } | |
| input() { | |
| local domain=$1 | |
| local port=$2 | |
| local type=$3 | |
| if [[ $2 = "clear" && $3 = "all" ]] | |
| then | |
| echo "$domain: Clearing Port Forwarding from $1 iptables" >&2 | |
| qvm-run -p -u root "$domain" "iptables-save | grep -v 'PortFwd $1' | iptables-restore" | |
| else | |
| echo "$domain: Allowing input to port $port" >&2 | |
| qvm-run -p -u root "$domain" "iptables-save | grep -v 'PortFwd $1:$3$2' | iptables-restore" | |
| qvm-run -p -u root "$domain" "iptables -I INPUT 5 -p $type --dport $port -m conntrack --ctstate NEW -j ACCEPT \ | |
| -m comment --comment 'PortFwd $1:$3$2'" | |
| fi | |
| } | |
| recurse_netvms() { | |
| local this_dom=$1 | |
| local port=$2 | |
| local type=$3 | |
| local outer_dom=$(netvm "$this_dom") | |
| if [[ -n "$outer_dom" && "$outer_dom" != "None" ]]; then | |
| forward "$outer_dom" "$this_dom" "$port" "$type" | |
| recurse_netvms "$outer_dom" "$port" "$type" | |
| fi | |
| } | |
| usage() { | |
| echo "Usage: ${0##*/} <vm> <port> <proto> | <vm> clear all" >&2 | |
| exit 1 | |
| } | |
| [ $# -eq 3 ] || usage | |
| input "$1" "$2" "$3" | |
| recurse_netvms "$1" "$2" "$3" |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
|
Updated version for Qubes 4.0 (RC4 tested) Command line specify the "VM, Port and Protocol"... or just "VM clear all" to undo previous. Works with Fedora 25/26 which uses nft rules along with iptables |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
jpouellet
Jun 25, 2018
[[ ... =~ .. ]] is a bashism, but you're using /bin/sh (which is not guaranteed to be bash)
Also, for safety and portability I'd suggest:
[ X"$foo" = Xbar ]or[ bar = "$foo" ]instead of[[ $foo = bar ]][ ... -a ... ]/[ ... -o ... ]instead of[[ ... && ... ]]/[[ ... || ...]].
Otherwise nice, and your script is probably better than mine.
jpouellet
commented
Jun 25, 2018
|
Also, for safety and portability I'd suggest:
Otherwise nice, and your script is probably better than mine. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
jpouellet
Jun 25, 2018
Also, I don't like how you're reaching into a VM to grab nft_handle, processing the (untrusted!) result in dom0, and then using the result to decide whether to issue another command. A safer way would be to issue only one command which does the checking and conditional command entirely on the VM, discarding any output back to dom0. Might be over-paranoid, but then again, we had shellshock.
... | grep -qx '[0-9]+' && ( ... ) in the command passed to the VM should do it and remove the bashism at the same time.
jpouellet
commented
Jun 25, 2018
•
|
Also, I don't like how you're reaching into a VM to grab nft_handle, processing the (untrusted!) result in dom0, and then using the result to decide whether to issue another command. A safer way would be to issue only one command which does the checking and conditional command entirely on the VM, discarding any output back to dom0. Might be over-paranoid, but then again, we had shellshock.
|
Updated version for Qubes 4.0 (RC4 tested)
Usage:
qvm-portfwd <vm> <port> <proto> | <vm> clear allExample:
qvm-portfwd webserv 8888 tcpCommand line specify the "VM, Port and Protocol"... or just "VM clear all" to undo previous.
Script will recursively configure iptables/nft for all proxyVMs in use.
Now uses comments on iptables to remove previous entries (no duplicates)
Works with Fedora 25/26 which uses nft rules along with iptables
Works with Debian 8/9 too