-
-
Save Joeviocoe/6c4dc0c283f6d6c5b1a3f5af8793292b to your computer and use it in GitHub Desktop.
#!/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" |
[[ ... =~ .. ]]
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. 🍻
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.
how do i use/install this on Qubes 4.0 I need to port forward correctly on Qubes 4.0 and and also I have a router so kinda confusing... any help is appreciated! thanks
Simpy put it in ~/bin in dom0 or any other directory in your PATH and use it. Here is an example how I use it for samba:
#!/bin/sh
# Configure ports for samba server
qvm-portfwd server 445 tcp
qvm-portfwd server 445 udp
qvm-portfwd server 139 tcp
qvm-portfwd server 138 udp
qvm-portfwd server 137 udp
# and make conncections between VMS
qvm-portfwd server 445 tcp work
qvm-portfwd server 445 udp work
qvm-portfwd server 139 tcp work
qvm-portfwd server 138 udp work
qvm-portfwd server 137 udp work
I hope that I didn't make any changes. But I don't think so.
this sounds really stupid but what is the exact command to do that?
`
qvm-portfwd PTFAssaultMachine 443 tcp
isn't working lol i can get the file into dom0 but it wont recognize the command
for the command
sudo nano /rw/config/rc.local
#!/bin/sh
####################
# My service routing
# Create a new firewall natting chain for my service
if iptables -t nat -N MY-HTTPS; then
# Add a natting rule if it did not exit (to avoid cluter if script executed multiple times)
iptables -t nat -A MY-HTTPS -j DNAT --to-destination 10.137.1.x
fi
# If no prerouting rule exist for my service
if ! iptables -t nat -n -L PREROUTING | grep --quiet MY-HTTPS; then
# add a natting rule for the traffic (same reason)
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 443 -d 192.168.0.x -j MY-HTTPS
fi
######################
# My service filtering
# Create a new firewall filtering chain for my service
if iptables -N MY-HTTPS; then
# Add a filtering rule if it did not exit (to avoid cluter if script executed multiple times)
iptables -A MY-HTTPS -s 192.168.x.0/24 -j ACCEPT
fi
# If no forward rule exist for my service
if ! iptables -n -L FORWARD | grep --quiet MY-HTTPS; then
# add a forward rule for the traffic (same reason)
iptables -I FORWARD 2 -d 10.137.1.x -p tcp --dport 443 -m conntrack --ctstate NEW -j MY-HTTPS
fi
Note: Again in R4 the following needs to be added:
#############
# In Qubes R4
# If not already present
if nft -nn list table ip qubes-firewall | grep "tcp dport 443 ct state new"; then
# Add a filtering rule
nft add rule ip qubes-firewall forward meta iifname eth0 ip daddr 10.137.0.x tcp dport 443 ct state new counter accept
fi
Finally make this file executable, so it runs at each boot
sudo chmod +x /rw/config/rc.local
what is MY-HTTPS referring to? it is confusing me lol its on
https://www.qubes-os.org/doc/firewall/#port-forwarding-to-a-qube-from-the-outside-world
also what does this mean for me(at the bottom of the same page mentioned):
Where to put firewall rules (R4.0)
Implicit in the above example scripts, but worth calling attention to: for all qubes except AppVMs supplying networking, iptables commands should be added to the /rw/config/rc.local script. For AppVMs supplying networking (sys-firewall inclusive), iptables commands should be added to /rw/config/qubes-firewall-user-script.
if i have a basic configuration with app machines : firewall sys-net and PTFAssaultMachine with all three being App machines, also i have a WiFi router and I am wondering if that matters
This is awesome! - worked like a charm.
Thanks
This works well for me. As the instructions said, put it in dom0's ~/bin directory. I used the following instructions:
https://www.qubes-os.org/doc/copy-from-dom0/
From my debian-9 vm, it was:
qvm-run --pass-io debian-9 'cat /home/user/Downloads/qvm-portfwd' > ~/bin/qvm-portfwd
chmod +x ~/bin/qvm-portfwd
Good luck to all others.
[[ ... =~ .. ]]
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. beers
Also, I don't like how you're reaching into a VM to grab nft_handle, processing the (untrusted!) result in dom0....
Thanks. I haven't had time to implement these changes but they are good changes to make.
Honestly, I use a new script that is more in line with Qubism (not Picasso). Everything I need for port fowarding is TCP based... so this script is much safer and uses socat over Qubes own RPC. It connects the net-vm directly to the app-vm without the need for either NFT or IPtables on a proxy-vm/firewall.
It requires some 'install' commands outside/prior to the script... but that could be scripted as well.
https://gist.github.com/Joeviocoe/90ec9fd9a0769b4671a8ae9c87584187
Dear @Joeviocoe are the changes applied persistent upon next reboot or even beyond that?
Why this double fw thing tho?
nf and ip tables
In case someone wants to test, I improved a little bit the current version (https://gist.github.com/fepitre/941d7161ae1150d90e15f778027e3248), notably by adding option for persistence. I'll do some review again (and from others I hope) and we will probably refactor it a little bit again for putting it in @QubesOS-contrib
Updated version for Qubes 4.0 (RC4 tested)
Usage:
qvm-portfwd <vm> <port> <proto> | <vm> clear all
Example:
qvm-portfwd webserv 8888 tcp
Command 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