mirror of
https://github.com/ben-grande/qusal.git
synced 2025-02-02 10:24:56 -05:00
feat: port forwarder
If persistent rules are chosen, it can deal with disposable sys-net, but not with disposable sys-firewall, as the qube ip will change, the rule won't work. Applying the rule to the disposable template is a "try it all", but it's usage is discouraged.
This commit is contained in:
parent
c3937e881e
commit
80638d64b5
@ -7,13 +7,6 @@ Salt Formulas for Qubes OS.
|
|||||||
**Warning**: Not ready for production, development only. Breaking changes can
|
**Warning**: Not ready for production, development only. Breaking changes can
|
||||||
and will be introduced in the meantime. You've been warned.
|
and will be introduced in the meantime. You've been warned.
|
||||||
|
|
||||||
The following projects are unfinished (not a complete list):
|
|
||||||
|
|
||||||
- sys-syncthing: broken firewall script due to nftables and disposable netvm
|
|
||||||
|
|
||||||
Other projects might also have drastic changes, the above are simply not ready
|
|
||||||
at all.
|
|
||||||
|
|
||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
|
||||||
* [Description](#description)
|
* [Description](#description)
|
||||||
|
@ -29,8 +29,8 @@ qubesctl top.enable sys-syncthing browser
|
|||||||
qubesctl --targets=tpl-browser,sys-syncthing-browser,tpl-sys-syncthing,sys-syncthing state.apply
|
qubesctl --targets=tpl-browser,sys-syncthing-browser,tpl-sys-syncthing,sys-syncthing state.apply
|
||||||
qubesctl top.disable sys-syncthing browser
|
qubesctl top.disable sys-syncthing browser
|
||||||
qubesctl state.apply sys-syncthing.appmenus
|
qubesctl state.apply sys-syncthing.appmenus
|
||||||
/srv/salt/qusal/sys-syncthing/files/admin/firewall/in.sh -a -p add sys-syncthing tcp 22000
|
qvm-port-forward -a add -q sys-syncthing -n tcp -p 22000
|
||||||
/srv/salt/qusal/sys-syncthing/files/admin/firewall/in.sh -a -p add sys-syncthing udp 22000
|
qvm-port-forward -a add -q sys-syncthing -n udp -p 22000
|
||||||
```
|
```
|
||||||
|
|
||||||
- State:
|
- State:
|
||||||
@ -42,8 +42,8 @@ qubesctl --skip-dom0 --targets=tpl-sys-syncthing state.apply sys-syncthing.insta
|
|||||||
qubesctl --skip-dom0 --targets=sys-syncthing state.apply sys-syncthing.configure
|
qubesctl --skip-dom0 --targets=sys-syncthing state.apply sys-syncthing.configure
|
||||||
qubesctl --skip-dom0 --targets=sys-syncthing-browser state.apply sys-syncthing.configure-browser
|
qubesctl --skip-dom0 --targets=sys-syncthing-browser state.apply sys-syncthing.configure-browser
|
||||||
qubesctl state.apply sys-syncthing.appmenus
|
qubesctl state.apply sys-syncthing.appmenus
|
||||||
/srv/salt/qusal/sys-syncthing/files/admin/firewall/in.sh -a -p add sys-syncthing tcp 22000
|
qvm-port-forward -a add -q sys-syncthing -n tcp -p 22000
|
||||||
/srv/salt/qusal/sys-syncthing/files/admin/firewall/in.sh -a -p add sys-syncthing udp 22000
|
qvm-port-forward -a add -q sys-syncthing -n udp -p 22000
|
||||||
```
|
```
|
||||||
<!-- pkg:end:post-install -->
|
<!-- pkg:end:post-install -->
|
||||||
|
|
||||||
@ -99,10 +99,10 @@ If sys-net has more than one network card the first external interface will
|
|||||||
be used by default.
|
be used by default.
|
||||||
If this is incorrect, you must change it manually. In Dom0 run:
|
If this is incorrect, you must change it manually. In Dom0 run:
|
||||||
```sh
|
```sh
|
||||||
/srv/salt/qusal/sys-syncthing/files/admin/firewall/in.sh delete sys-syncthing tcp 22000 -a -p
|
qvm-port-forward -a del -q sys-syncthing -n udp -p 22000
|
||||||
/srv/salt/qusal/sys-syncthing/files/admin/firewall/in.sh delete sys-syncthing udp 22000 -a -p
|
qvm-port-forward -a del -q sys-syncthing -n tcp -p 22000
|
||||||
/srv/salt/qusal/sys-syncthing/files/admin/firewall/in.sh add sys-syncthing tcp 22000 -p
|
qvm-port-forward -a add -q sys-syncthing -n udp -p 22000
|
||||||
/srv/salt/qusal/sys-syncthing/files/admin/firewall/in.sh add sys-syncthing udp 22000 -p
|
qvm-port-forward -a add -q sys-syncthing -n tcp -p 22000
|
||||||
```
|
```
|
||||||
This will let you choose the NIC.
|
This will let you choose the NIC.
|
||||||
|
|
||||||
@ -117,8 +117,8 @@ Syncthing between qubes.
|
|||||||
Uninstallation procedure:
|
Uninstallation procedure:
|
||||||
<!-- pkg:begin:preun-uninstall -->
|
<!-- pkg:begin:preun-uninstall -->
|
||||||
```sh
|
```sh
|
||||||
/srv/salt/qusal/sys-syncthing/files/admin/firewall/in.sh -a -p delete sys-syncthing tcp 22000
|
qvm-port-forward -a del -q sys-syncthing -n tcp -p 22000
|
||||||
/srv/salt/qusal/sys-syncthing/files/admin/firewall/in.sh -a -p delete sys-syncthing udp 22000
|
qvm-port-forward -a del -q sys-syncthing -n udp -p 22000
|
||||||
qubesctl --skip-dom0 --targets=sys-syncthing state.apply sys-syncthing.cancel
|
qubesctl --skip-dom0 --targets=sys-syncthing state.apply sys-syncthing.cancel
|
||||||
qubesctl state.apply sys-syncthing.clean
|
qubesctl state.apply sys-syncthing.clean
|
||||||
```
|
```
|
||||||
|
@ -97,3 +97,12 @@ features:
|
|||||||
|
|
||||||
{% from 'utils/macros/policy.sls' import policy_set with context -%}
|
{% from 'utils/macros/policy.sls' import policy_set with context -%}
|
||||||
{{ policy_set(sls_path, '80') }}
|
{{ policy_set(sls_path, '80') }}
|
||||||
|
|
||||||
|
"{{ slsdotpath }}-qvm-port-forward":
|
||||||
|
file.managed:
|
||||||
|
- name: /usr/local/bin/qvm-port-forward
|
||||||
|
- source: salt://{{ slsdotpath }}/files/admin/firewall/qvm-port-forward
|
||||||
|
- user: root
|
||||||
|
- group: root
|
||||||
|
- mode: '0755'
|
||||||
|
- makedirs: True
|
||||||
|
@ -1,312 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# SPDX-FileCopyrightText: 2022 unman <unman@thirdeyesecurity.org>
|
|
||||||
# SPDX-FileCopyrightText: 2023 Benjamin Grande M. S. <ben.grande.b@gmail.com>
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
|
|
||||||
## Credits: https://github.com/unman/shaker/blob/main/i2p/in.sh
|
|
||||||
## Recursively open ports through the firewall to allow remote access to a qube.
|
|
||||||
|
|
||||||
## TODO: remove iptables in favor of nft. It doesn't work if the upstream net
|
|
||||||
## qubes are disposables, instead, the rule should be applied on the
|
|
||||||
## disposable template. This would work well if users used our project that
|
|
||||||
## creates a template per service, but if user is using a default diposable
|
|
||||||
## template for that, such as debian-XX-dvm, the firewall would allow many
|
|
||||||
## qubes to be exposed.
|
|
||||||
|
|
||||||
me="${0##*/}"
|
|
||||||
rc="/rw/config/rc.local.d/50-port-forwarder.rc"
|
|
||||||
|
|
||||||
usage(){
|
|
||||||
cat <<HERE
|
|
||||||
Usage: ${me} [-h|a|p] [add|delete] [target] [tcp|udp] [port number|service] [external port]
|
|
||||||
|
|
||||||
Options:
|
|
||||||
h print this help
|
|
||||||
a auto mode, a port will be opened on the first external interface
|
|
||||||
p permanent rules, takes effect in each qube start up
|
|
||||||
Action add, delete
|
|
||||||
Protocol tcp, udp
|
|
||||||
Target Port port number or service name (e.g. ssh)
|
|
||||||
External Port port number or service name (e.g. ssh) (default: target port)
|
|
||||||
|
|
||||||
Example:
|
|
||||||
${me} OPTIONS ACTION TARGET_QUBE PROTOCOL TARGET_PORT EXTERNAL_PORT
|
|
||||||
${me} add QUBE tcp 80 80
|
|
||||||
${me} add QUBE tcp ssh ssh
|
|
||||||
${me} delete QUBE tcp https https
|
|
||||||
|
|
||||||
DO NOT use this script for qubes behind a Tor or VPN proxy.
|
|
||||||
At a minimum you risk breaking the security of those proxies.
|
|
||||||
HERE
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
## Check input port
|
|
||||||
check_port(){
|
|
||||||
if test "$2" != "$2";then
|
|
||||||
status=1
|
|
||||||
else
|
|
||||||
if test "$2" -lt 65536; then
|
|
||||||
status=0
|
|
||||||
portnum="$2"
|
|
||||||
else
|
|
||||||
status=1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
if [ "$status" -ne 0 ]; then
|
|
||||||
if ! grep -q -w "^$2 " /etc/services; then
|
|
||||||
echo "Specify usable port number or service name"
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
portnum="$(getent services "$2" | awk '{split($2,a,"/");print a[1]}')"
|
|
||||||
if test -z "$portnum"; then
|
|
||||||
echo "Specify usable port number or service name"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
echo "$portnum"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
get_handle(){
|
|
||||||
local my_handle
|
|
||||||
my_handle="$(qvm-run -q -u root -p "$1" -- "nft -a list table $2 | awk 'BEGIN{c=0} /$3/{c++; if (c==$4) print \$NF}'")"
|
|
||||||
echo "$my_handle"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
## Tunnel through netvms
|
|
||||||
tunnel(){
|
|
||||||
declare -a my_netvms=("${!1}")
|
|
||||||
declare -a my_ips=("${!2}")
|
|
||||||
declare -i numhops
|
|
||||||
numhops="${#my_ips[@]}"
|
|
||||||
lasthop=$((numhops-1))
|
|
||||||
local i=1
|
|
||||||
iface="eth0"
|
|
||||||
if qvm-run -q -u root "${my_netvms[$lasthop]}" " nft list table nat|grep ' $proto dport $portnum dnat to ${my_ips[$numhops-1]}'"
|
|
||||||
then
|
|
||||||
echo "Are rules already set?"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
while test "$i" != "$numhops"; do
|
|
||||||
if test "$i" = "1"; then
|
|
||||||
portnum_used=$external_portnum
|
|
||||||
portnum_target=$portnum
|
|
||||||
else
|
|
||||||
portnum_used=$external_portnum
|
|
||||||
portnum_target=$external_portnum
|
|
||||||
fi
|
|
||||||
echo "${my_netvms[$i]} $portnum_used"
|
|
||||||
if [ $i -eq $lasthop ]; then
|
|
||||||
iface=$external_iface
|
|
||||||
fi
|
|
||||||
# Is it nft or iptables?
|
|
||||||
local found
|
|
||||||
found="$(qvm-run -p -q -u root "${my_netvms[$i]}" -- nft list table nat 2>/dev/null)"
|
|
||||||
if test -z "$found"; then
|
|
||||||
qvm-run -q -u root "${my_netvms[$i]}" -- "iptables -I QBS-FORWARD -i $iface -p $proto --dport $portnum_target -d ${my_ips[$i-1]} -j ACCEPT"
|
|
||||||
qvm-run -q -u root "${my_netvms[$i]}" -- "iptables -t nat -I PR-QBS-SERVICES -i $iface -p $proto --dport $portnum_used -j DNAT --to-destination ${my_ips[$i-1]}:$portnum_target"
|
|
||||||
if test "$permanent" = "1"; then
|
|
||||||
qvm-run -q -u root "${my_netvms[$i]}" -- "echo iptables -I QBS-FORWARD -i $iface -p $proto --dport $portnum_target -d ${my_ips[$i-1]} -j ACCEPT >> ${rc}"
|
|
||||||
qvm-run -q -u root "${my_netvms[$i]}" -- "echo iptables -t nat -I PR-QBS-SERVICES -i $iface -p $proto --dport $portnum_used -j DNAT --to-destination ${my_ips[$i-1]}:$portnum_target >> ${rc}"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
qvm-run -q -u root "${my_netvms[$i]}" -- nft insert rule nat PR-QBS-SERVICES meta iifname "$iface" "$proto" dport "$portnum_used" dnat to "${my_ips[$i-1]}:$portnum_target"
|
|
||||||
qvm-run -q -u root "${my_netvms[$i]}" -- nft insert rule filter QBS-FORWARD meta iifname "$iface" ip daddr "${my_ips[$i-1]}" "$proto" dport "$portnum_target" ct state new accept
|
|
||||||
if test "$permanent" = "1"; then
|
|
||||||
qvm-run -q -u root "${my_netvms[$i]}" -- "echo nft insert rule nat PR-QBS-SERVICES meta iifname $iface $proto dport $portnum_used dnat to ${my_ips[$i-1]}:$portnum_target >> ${rc}"
|
|
||||||
qvm-run -q -u root "${my_netvms[$i]}" -- "echo nft insert rule filter QBS-FORWARD meta iifname $iface ip daddr ${my_ips[$i-1]} $proto dport $portnum_target ct state new accept >> ${rc}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
((i++))
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
## Teardown from top netvm down
|
|
||||||
teardown(){
|
|
||||||
declare -a my_netvms=("${!1}")
|
|
||||||
declare -a my_ips=("${!2}")
|
|
||||||
declare -i numhops
|
|
||||||
numhops=${#my_ips[@]}
|
|
||||||
numhops=$((numhops-1))
|
|
||||||
local i=$numhops
|
|
||||||
iface="eth0"
|
|
||||||
echo "Removing firewall rules"
|
|
||||||
while [ $i -gt 0 ]; do
|
|
||||||
if [ $i -eq 1 ]; then
|
|
||||||
portnum_used=$external_portnum
|
|
||||||
portnum_target=$portnum
|
|
||||||
else
|
|
||||||
portnum_used=$external_portnum
|
|
||||||
portnum_target=$external_portnum
|
|
||||||
fi
|
|
||||||
# Is it nft or iptables?
|
|
||||||
echo "${my_netvms[$i]}"
|
|
||||||
local found
|
|
||||||
found="$( qvm-run -p -q -u root "${my_netvms[$i]}" -- "nft list table nat 2>/dev/null" )"
|
|
||||||
if test -z "$found"; then
|
|
||||||
qvm-run -q -u root "${my_netvms[$i]}" -- "iptables -D QBS-FORWARD -i $iface -p $proto --dport $portnum_target -d ${my_ips[$i-1]} -j ACCEPT"
|
|
||||||
qvm-run -q -u root "${my_netvms[$i]}" -- "iptables -t nat -D PR-QBS-SERVICES -i $iface -p $proto --dport $external_portnum -j DNAT --to-destination ${my_ips[$i-1]}:$portnum_target"
|
|
||||||
if [ "$permanent" -eq 1 ]; then
|
|
||||||
qvm-run -q -u root "${my_netvms[$i]}" -- "sed -i '/iptables -D QBS-FORWARD -i $iface -p $proto --dport $portnum_target -d ${my_ips[$i-1]} -j ACCEPT/d' ${rc}"
|
|
||||||
qvm-run -q -u root "${my_netvms[$i]}" -- "sed -i '/iptables -t nat -D PR-QBS-SERVICES -i $iface -p $proto --dport $external_portnum -j DNAT --to-destination ${my_ips[$i-1]}:$portnum_target/d' ${rc}"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
local handle
|
|
||||||
handle="$( get_handle "${my_netvms[$i]}" nat "dport $external_portnum " 1 )"
|
|
||||||
qvm-run -q -u root "${my_netvms[$i]}" -- "nft delete rule nat PR-QBS-SERVICES handle $handle"
|
|
||||||
local handle
|
|
||||||
handle="$( get_handle "${my_netvms[$i]}" filter "dport $external_portnum " 1 )"
|
|
||||||
qvm-run -q -u root "${my_netvms[$i]}" -- "nft delete rule filter QBS-FORWARD handle $handle"
|
|
||||||
if [ "$permanent" -eq 1 ]; then
|
|
||||||
qvm-run -q -u root "${my_netvms[$i]}" -- "sed -i '/nft insert rule nat PR-QBS-SERVICES meta iifname $iface $proto dport $portnum_used dnat to ${my_ips[$i-1]}:$portnum_target/d' ${rc}"
|
|
||||||
qvm-run -q -u root "${my_netvms[$i]}" -- "sed -i '/nft insert rule filter QBS-FORWARD meta iifname $iface ip daddr ${my_ips[$i-1]} $proto dport $portnum_target ct state new accept/d' ${rc}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
((i--))
|
|
||||||
done
|
|
||||||
local found
|
|
||||||
found="$( qvm-run -p -q -u root "${my_netvms[$i]}" -- nft list table nat 2>/dev/null )"
|
|
||||||
if test -z "$found"; then
|
|
||||||
qvm-run -q -u root "${my_netvms[$i]}" " iptables -D INPUT -p $proto --dport $external_portnum -j ACCEPT"
|
|
||||||
else
|
|
||||||
handle=$( get_handle "${my_netvms[$i]}" filter "dport $portnum " 1 )
|
|
||||||
qvm-run -q -u root "${my_netvms[$i]}" -- nft delete rule filter INPUT handle "$handle"
|
|
||||||
fi
|
|
||||||
exit
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
list(){
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
## Defaults
|
|
||||||
auto=0
|
|
||||||
permanent=0
|
|
||||||
|
|
||||||
## Get options
|
|
||||||
optstring=":hap"
|
|
||||||
while getopts ${optstring} option ; do
|
|
||||||
case $option in
|
|
||||||
h) usage;;
|
|
||||||
a) auto=1;;
|
|
||||||
p) permanent=1;;
|
|
||||||
?) usage;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
shift $((OPTIND -1))
|
|
||||||
|
|
||||||
## Check inputs
|
|
||||||
test "$#" -lt 4 && usage
|
|
||||||
if ! qvm-check -q "$2" 2>/dev/null; then
|
|
||||||
echo "$2 is not the name of any qube"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
qube_name="$2"
|
|
||||||
if test "$3" != "tcp" && test "$3" != "udp"; then
|
|
||||||
echo "Specify tcp or udp"
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
proto="$3"
|
|
||||||
portnum="$(check_port "$3" "$4")"
|
|
||||||
|
|
||||||
if [ $# -eq 5 ]; then
|
|
||||||
external_portnum="$(check_port "$3" "$5")"
|
|
||||||
else
|
|
||||||
external_portnum=$portnum
|
|
||||||
fi
|
|
||||||
|
|
||||||
## Get all netvms
|
|
||||||
declare -a netvms
|
|
||||||
declare -a ips
|
|
||||||
declare -a external_ips
|
|
||||||
hop=0
|
|
||||||
# shellcheck disable=SC2004
|
|
||||||
netvms[${hop}]="$qube_name"
|
|
||||||
IFS='|' read -r netvms[$hop+1] ips[$hop] <<< "$(qvm-ls "$qube_name" --raw-data -O netvm,IP)"
|
|
||||||
while [ "${netvms[hop+1]}" != "-" ]
|
|
||||||
do
|
|
||||||
((hop++))
|
|
||||||
IFS='|' read -r netvms[$hop+1] ips[$hop] <<< "$(qvm-ls "${netvms[$hop]}" --raw-data -O netvm,IP)"
|
|
||||||
done
|
|
||||||
|
|
||||||
if test "$1" = "delete"; then
|
|
||||||
teardown netvms[@] ips[@]
|
|
||||||
elif test "$1" = "add"; then
|
|
||||||
if [ "$hop" -eq 0 ]; then
|
|
||||||
echo "$qube_name is not network connected"
|
|
||||||
echo "Cannot set up a tunnel"
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check last hop has external IP address
|
|
||||||
readarray -t external_ips < <( qvm-run -p "${netvms[$hop]}" "ip -4 -o a|grep -wv 'lo\|vif[0-9]*.*'"|awk '{print $2,$4}')
|
|
||||||
#readarray -t external_ips < <( qvm-run -p ${netvms[$hop]} "ip -4 -o a|grep -wv 'vif[0-9]'"|awk '{print $2,$4}')
|
|
||||||
num_ifs=${#external_ips[@]}
|
|
||||||
if [ "$num_ifs" -eq 1 ]; then
|
|
||||||
interface=0
|
|
||||||
elif [ $auto -eq 1 ]; then
|
|
||||||
interface=0
|
|
||||||
elif [ "$num_ifs" -gt 1 ]; then
|
|
||||||
echo "${netvms[$hop]} has more than 1 external interface"
|
|
||||||
echo "Which one do you want to use?"
|
|
||||||
for i in $(seq "$num_ifs"); do
|
|
||||||
echo "$i. ${external_ips[$i-1]}"
|
|
||||||
done
|
|
||||||
read -r interface
|
|
||||||
if ! [ "$interface" -eq "$interface" ] 2> /dev/null; then
|
|
||||||
echo "No such interface"
|
|
||||||
exit
|
|
||||||
elif [ "$interface" -gt "$num_ifs" ] || [ "$interface" -lt 1 ]; then
|
|
||||||
echo "No such interface"
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
((interface--))
|
|
||||||
else
|
|
||||||
echo "${netvms[$hop]} does not have an external interface"
|
|
||||||
echo "Cannot set up a tunnel"
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
external_ip="${external_ips[$interface]}"
|
|
||||||
external_iface="${external_ip%[[:space:]]*}"
|
|
||||||
ip="${external_ip#*[0-9]}"
|
|
||||||
ip="${ip%%/*}"
|
|
||||||
# shellcheck disable=SC2004,SC2034
|
|
||||||
ips[$hop]="$ip"
|
|
||||||
|
|
||||||
# Create tunnel
|
|
||||||
found="$(qvm-run -p -q -u root "$qube_name" -- nft list table nat 2>/dev/null)"
|
|
||||||
if test -z "$found"; then
|
|
||||||
found=$(qvm-run -p -u root "$qube_name" "iptables -L -nv | grep -c '.*ACCEPT.*$proto dpt:$portnum' ")
|
|
||||||
if [ "$found" -gt 0 ]; then
|
|
||||||
echo "Input rule in $qube_name already exists"
|
|
||||||
echo "Please check configuration - exiting now."
|
|
||||||
exit
|
|
||||||
else
|
|
||||||
qvm-run -q -u root "$qube_name" "iptables -I INPUT -p $proto --dport $portnum -j ACCEPT "
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
if qvm-run -q -u root "$qube_name" "nft list table filter | grep '$proto dport $portnum accept' "
|
|
||||||
then
|
|
||||||
echo "Input rule in $qube_name already exists"
|
|
||||||
echo "Please check configuration - exiting now."
|
|
||||||
exit
|
|
||||||
else
|
|
||||||
handle="$(get_handle "$qube_name" filter related,established 1)"
|
|
||||||
qvm-run -q -u root "$qube_name" -- nft add rule filter INPUT position "$handle" iifname eth0 "$proto" dport "$portnum" accept
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
if ! tunnel netvms[@] ips[@]; then
|
|
||||||
teardown netvms[@] ips[@]
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
usage
|
|
||||||
fi
|
|
266
salt/sys-syncthing/files/admin/firewall/qvm-port-forward
Normal file
266
salt/sys-syncthing/files/admin/firewall/qvm-port-forward
Normal file
@ -0,0 +1,266 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# SPDX-FileCopyrightText: 2017 Jean-Philippe Ouellet <jpo@vt.edu>
|
||||||
|
# SPDX-FileCopyrightText: 2022 daktak <daktak@gmail.com>
|
||||||
|
# SPDX-FileCopyrightText: 2023 Frederic Pierret <frederic.pierret@qubes-os.org>
|
||||||
|
# SPDX-FileCopyrightText: 2024 Benjamin Grande M. S. <ben.grande.b@gmail.com>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
#
|
||||||
|
# Credits: https://gist.github.com/daktak/f887352d564b54f9e529404cc0eb60d5
|
||||||
|
# Credits: https://gist.github.com/jpouellet/d8cd0eb8589a5b9bf0c53a28fc530369
|
||||||
|
# Credits: https://gist.github.com/fepitre/941d7161ae1150d90e15f778027e3248
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
run_qube(){
|
||||||
|
qube="${1}"
|
||||||
|
shift
|
||||||
|
qvm-run --pass-io --user=root "${qube}" -- "${@}"
|
||||||
|
}
|
||||||
|
|
||||||
|
create_net_dir(){
|
||||||
|
qube="${1}"
|
||||||
|
run_qube "${qube}" mkdir -p "${hook_dir}"
|
||||||
|
}
|
||||||
|
|
||||||
|
get_rule_handle(){
|
||||||
|
qube="${1}"
|
||||||
|
chain="${2}"
|
||||||
|
rule="${3}"
|
||||||
|
run_qube "${qube}" "nft --handle --stateless list chain ip qubes ${chain} | tr -d '\"' | grep '^\s\+${rule} # handle ' | awk '{print \$NF}' | tr '\n' ' '" 2>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
delete_rule_handle(){
|
||||||
|
qube="${1}"
|
||||||
|
chain="${2}"
|
||||||
|
handle="${3}"
|
||||||
|
run_qube "${qube}" "nft delete rule ip qubes ${chain} handle ${handle}"
|
||||||
|
}
|
||||||
|
|
||||||
|
delete_rule(){
|
||||||
|
qube="${1}"
|
||||||
|
chain="${2}"
|
||||||
|
rule="${3}"
|
||||||
|
handle="$(get_rule_handle "${qube}" "${chain}" "${rule}")"
|
||||||
|
if test -n "${handle}"; then
|
||||||
|
for h in ${handle}; do
|
||||||
|
delete_rule_handle "${qube}" "${chain}" "${h}"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
forward() {
|
||||||
|
from_qube="${1}"
|
||||||
|
to_qube="${2}"
|
||||||
|
create_net_dir "${from_qube}"
|
||||||
|
|
||||||
|
## TODO: Handle multiple interfaces in upstream.
|
||||||
|
dev="$(run_qube "${from_qube}" ip -4 r \
|
||||||
|
| awk '/^default via /{print $5}' | head -1)"
|
||||||
|
from_ip="$(run_qube "${from_qube}" ip -4 -o a show dev "${dev}" \
|
||||||
|
| awk '{print $4}' | cut -d "/" -f 1)"
|
||||||
|
to_ip="$(qvm-prefs --get -- "${to_qube}" ip)"
|
||||||
|
to_ip_escaped="$(echo "${to_ip}" | tr "." "-")"
|
||||||
|
hook="${hook_prefix}${to_ip}-${proto}-${port}.sh"
|
||||||
|
|
||||||
|
if test "${from_ip}" = "None"; then
|
||||||
|
from_ip=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
dnat_chain="custom-pf-${to_ip_escaped}"
|
||||||
|
dnat_rule="iifname ${dev} ip saddr ${lan_ip} ${proto} dport ${port} ct state established,related,new counter dnat to ${to_ip}"
|
||||||
|
forward_chain="custom-forward"
|
||||||
|
forward_rule="iifname ${dev} ip saddr ${lan_ip} ip daddr ${to_ip} ${proto} dport ${port} ct state established,related,new counter accept"
|
||||||
|
full_rule="nft 'add chain ip qubes ${dnat_chain} { type nat hook prerouting priority filter +1; policy accept; }
|
||||||
|
add rule ip qubes ${dnat_chain} ${dnat_rule}
|
||||||
|
add rule ip qubes ${forward_chain} ${forward_rule}'"
|
||||||
|
|
||||||
|
delete_rule "${from_qube}" "${forward_chain}" "${forward_rule}"
|
||||||
|
delete_rule "${from_qube}" "${dnat_chain}" "${dnat_rule}"
|
||||||
|
if test "${action}" = "del"; then
|
||||||
|
echo "info: ${from_qube}: deleting rules" >&2
|
||||||
|
run_qube "${from_qube}" "rm -f ${hook}"
|
||||||
|
else
|
||||||
|
echo "info: ${from_qube}: adding forward rule dev ${dev} saddr ${lan_ip} daddr ${to_ip}" >&2
|
||||||
|
run_qube "${from_qube}" "${full_rule}"
|
||||||
|
|
||||||
|
if test "${persistent}" = "1"; then
|
||||||
|
if test "$(qvm-prefs --get -- "${from_qube}" klass)" = "DispVM"; then
|
||||||
|
from_qube="$(qvm-prefs --get -- "${from_qube}" template)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
full_rule="#!/bin/sh
|
||||||
|
get_handle(){
|
||||||
|
chain=\\\${1}
|
||||||
|
rule=\\\${2}
|
||||||
|
nft --handle --stateless list chain ip qubes \\\${chain} | tr -d '\\\"' | grep '^\\\s\\\+\\\${rule} \\# handle ' | awk '{print \\\$NF}' | tr \\\"\\\n\\\" \\\" \\\"
|
||||||
|
}
|
||||||
|
|
||||||
|
forward_handle=\\\$(get_handle ${forward_chain} \\\"${forward_rule}\\\")
|
||||||
|
if test -n \\\"\\\${forward_handle:-}\\\"; then
|
||||||
|
for h in \\\${forward_handle}; do
|
||||||
|
nft delete rule ip qubes ${forward_chain} handle \\\${h}
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
dnat_handle=\\\$(get_handle ${dnat_chain} \\\"${dnat_rule}\\\")
|
||||||
|
if test -n \\\"\\\${dnat_handle:-}\\\"; then
|
||||||
|
for h in \\\${dnat_handle}; do
|
||||||
|
nft delete rule ip qubes ${dnat_chain} handle \\\${h}
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
${full_rule}"
|
||||||
|
|
||||||
|
create_net_dir "${from_qube}"
|
||||||
|
run_qube "${from_qube}" "echo \"${full_rule}\" | tee \"${hook}\" >/dev/null"
|
||||||
|
run_qube "${from_qube}" "chmod +x ${hook}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
input() {
|
||||||
|
qube="${1}"
|
||||||
|
to_ip="$(qvm-prefs --get -- "${qube}" ip)"
|
||||||
|
hook="${hook_prefix}${to_ip}-${proto}-${port}.sh"
|
||||||
|
create_net_dir "${qube}"
|
||||||
|
|
||||||
|
custom_input_rule="${proto} dport ${port} ip daddr ${to_ip} ct state new counter accept"
|
||||||
|
input_rule="nft add rule ip qubes custom-input ${custom_input_rule}"
|
||||||
|
|
||||||
|
delete_rule "${qube}" "custom-input" "${custom_input_rule}"
|
||||||
|
if test "${action}" = "del"; then
|
||||||
|
echo "info: ${qube}: deleting rules" >&2
|
||||||
|
run_qube "${qube}" "rm -f ${hook}"
|
||||||
|
else
|
||||||
|
echo "info: ${qube}: adding input rule daddr ${to_ip}" >&2
|
||||||
|
run_qube "${qube}" "${input_rule}"
|
||||||
|
if test "${persistent}" = "1"; then
|
||||||
|
input_rule="#!/bin/sh
|
||||||
|
get_handle(){
|
||||||
|
chain=\\\${1}
|
||||||
|
rule=\\\${2}
|
||||||
|
nft --handle --stateless list chain ip qubes \\\${chain} | tr -d '\\\"' | grep '^\\\s\\\+\\\${rule} \\# handle ' | awk '{print \\\$NF}' | tr \\\"\\\n\\\" \\\" \\\"
|
||||||
|
}
|
||||||
|
|
||||||
|
input_handle=\\\$(get_handle custom-input \\\"${custom_input_rule}\\\")
|
||||||
|
if test -n \\\"\\\${input_handle:-}\\\"; then
|
||||||
|
for h in \\\${input_handle}; do
|
||||||
|
nft delete rule ip qubes custom-input handle \\\${h}
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
${input_rule}"
|
||||||
|
|
||||||
|
run_qube "${qube}" "echo \"${input_rule}\" | tee \"${hook}\" >/dev/null"
|
||||||
|
run_qube "${qube}" "chmod +x ${hook}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
get_lan(){
|
||||||
|
qube="${1}"
|
||||||
|
## TODO: Handle multiple interfaces in upstream.
|
||||||
|
dev="$(run_qube "${qube}" ip -4 route \
|
||||||
|
| awk '/^default via /{print $5}' | head -1)"
|
||||||
|
if test -z "${dev}"; then
|
||||||
|
echo "error: ${qube}: could not find any device that is up" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
lan_ip="$(run_qube "${qube}" ip -4 r show dev "${dev}" prot kernel \
|
||||||
|
| cut -d " " -f 1)"
|
||||||
|
if test -z "${lan_ip}"; then
|
||||||
|
echo "error: ${qube}: could not find LAN from device ${dev}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
recurse_netvms() {
|
||||||
|
cmd="${1}"
|
||||||
|
rec_qube="${2}"
|
||||||
|
rec_netvm="$(qvm-prefs --get -- "${rec_qube}" netvm)"
|
||||||
|
if test -n "${rec_netvm}" && test "${rec_netvm}" != "None"; then
|
||||||
|
case "${cmd}" in
|
||||||
|
show-upstream);;
|
||||||
|
apply-rules) forward "${rec_netvm}" "${rec_qube}";;
|
||||||
|
esac
|
||||||
|
recurse_netvms "${cmd}" "${rec_netvm}"
|
||||||
|
fi
|
||||||
|
case "${cmd}" in
|
||||||
|
show-upstream) get_lan "${rec_qube}";;
|
||||||
|
apply-rules) ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
echo "Usage: ${0##*/} --action ACTION --qube QUBE --port PORT --proto PROTO --persistent
|
||||||
|
Example:
|
||||||
|
${0##*/} --action add --qube work --port 22 --proto tcp
|
||||||
|
${0##*/} --action add --qube work --port 444 --proto udp --persistent
|
||||||
|
${0##*/} --action del --qube work --port 22 --proto tcp
|
||||||
|
${0##*/} --action del --qube work --port 444 --proto udp
|
||||||
|
Note: Defaults to temporary rules
|
||||||
|
Warn: If persistent is and and a netvm is disposable, the rule will be saved in the disposable template" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
check_opt(){
|
||||||
|
case "${action:-}" in
|
||||||
|
add|del);;
|
||||||
|
*) echo "error: action must be either 'add' or 'del'" >&2; exit 1;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
case "${proto:-}" in
|
||||||
|
tcp|udp);;
|
||||||
|
*) echo "error: protocol must be only 'tcp' or 'udp'" >&2; exit 1;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
case "${port:-}" in
|
||||||
|
""|*[!0-9]*) echo "error: port must be only numbers" >&2; exit 1;;
|
||||||
|
*)
|
||||||
|
esac
|
||||||
|
|
||||||
|
if test "${port}" -ge 1 && test "${port}" -le 65535; then
|
||||||
|
true
|
||||||
|
else
|
||||||
|
echo "error: port must be in range 1-65535" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test -z "${target_qube:-}"; then
|
||||||
|
echo "error: qube name not provided" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! qvm-check "${target_qube}" >/dev/null 2>&1; then
|
||||||
|
echo "error: qube '${target_qube}' not found." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
hook_dir="/rw/config/network-hooks.d"
|
||||||
|
hook_prefix="${hook_dir}/90-port-forward-"
|
||||||
|
persistent=""
|
||||||
|
|
||||||
|
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 test "${#}" -gt "0"; do
|
||||||
|
case "${1}" in
|
||||||
|
-a|--action) action="${2}"; shift;;
|
||||||
|
-q|--qube) target_qube="${2}"; shift;;
|
||||||
|
-p|--port) port="${2}"; shift;;
|
||||||
|
-n|--proto) proto="${2}"; shift;;
|
||||||
|
-s|--persistent) persistent=1; shift;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
check_opt
|
||||||
|
recurse_netvms show-upstream "${target_qube}"
|
||||||
|
input "${target_qube}"
|
||||||
|
recurse_netvms apply-rules "${target_qube}"
|
Loading…
x
Reference in New Issue
Block a user