Skip to content

Instantly share code, notes, and snippets.

@imme-emosol
Forked from neowutran/qvm-portfwd-iptables
Last active November 27, 2023 09:25
Show Gist options
  • Save imme-emosol/840ca36b4f098ce3431db43f08288aaa to your computer and use it in GitHub Desktop.
Save imme-emosol/840ca36b4f098ce3431db43f08288aaa to your computer and use it in GitHub Desktop.
Qubes-os port forwarding to allow external connections
#!/usr/bin/env sh
# Adapted from previous work:
# - https://gist.github.com/neowutran/e93ce542ba1e94a5ecbf1a38eef85485
# Adapted previous work to support QubesOS v4.2
# - https://gist.github.com/fepitre/941d7161ae1150d90e15f778027e3248
# - https://gist.github.com/daktak/f887352d564b54f9e529404cc0eb60d5
# - https://gist.github.com/jpouellet/d8cd0eb8589a5b9bf0c53a28fc530369
# - https://gist.github.com/Joeviocoe/6c4dc0c283f6d6c5b1a3f5af8793292b
# for "debugging",
# execute either one of:
# ( set -x ; /path/to/script.sh <arguments> )
# sh -x /path/to/script.sh <arguments>
inform() {
format="${1?}"'\n'
shift
>&2 printf "${format?}" "${@?}"
}
qubePortForwarding() {
# 4.2 (needs adjustment)
# 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 \\\"%s\\\" ip daddr %s %s dport %s counter dnat to %s comment \\\"PortFwd %s>%s:%s%s\\\"\""
#forward_rule2='nft add rule ip qubes custom-forward iifname %s ip daddr %s %s dport %s ct state new counter accept'
#forward_rule3='nft add rule ip qubes-firewall forward meta iifname %s accept'
#input_rule="nft 'insert rule ip qubes custom-input %s dport %s ct state new counter accept'"
# 4.1
forward_rule1='iptables --table nat --append PREROUTING --in-interface %s --protocol %s %s --destination-port %s --jump DNAT --to-destination %s --match comment --comment "PortFwd %s>%s:%s%s"'
forward_rule2='iptables --insert FORWARD 2 --in-interface %s --protocol %s %s --destination-port %s --match conntrack --ctstate NEW --jump ACCEPT --match comment --comment "PortFwd %s>%s:%s%s"'
forward_rule3='nft add rule ip qubes-firewall forward meta iifname %s accept'
input_rule='iptables --insert INPUT 5 --protocol %s --destination-port %s --match conntrack --ctstate NEW --jump ACCEPT --match comment --comment "PortFwd %s:%s%s"'
qube_itsPreference() {
qubeName="${1?}"
shift
prefName="${1?}"
shift
qvm-prefs --get "${qubeName?}" "${prefName?}"
}
qube_itsAddress() {
qubeName="${1?}"
shift
qube_itsPreference "${qubeName?}" ip
}
qube_itsNetworkProvider() {
qubeName="${1?}"
shift
qube_itsPreference "${qubeName?}" netvm | sed -e 's/None//'
}
this_itsInterfaceAddress() {
# TODO: Handle multiple interfaces in sys-net. It currently catches only the first physical interface.
#qube_address="hostname -I | cut -d ' ' -f 1"
qube_address="ip addr show dev %s |
grep -Eo 'inet [0-9]+(\.[0-9]+){3}' |
cut -d ' ' -f 2 |
head -1 |
sed -e 's/None//'"
iface="$( this_itsInterface )"
this_run "${qube_address?}" "${iface?}"
}
this_itsInterface() {
this_run "ip link |
grep -E '^[0-9]' |
cut -d ':' -f 2 |
cut -d ' ' -f 2 |
grep -vE '^(vif|lo)' |
head -1"
}
this_itsAddress() {
qube_itsAddress "${thisVMItsName?}"
}
this_itsNetVM() {
qube_itsNetworkProvider "${thisVMItsName?}"
}
this_run() {
qvm-run \
-p \
-u root \
"${thisVMItsName?}" \
"$( printf "${@?}" )"
}
this_createForward() {
to_qube="${1?}"
shift
thisVMItsInterface="$( this_itsInterface )"
incomingPacketAddress="$( this_itsInterfaceAddress )"
outgoingPacketAddress="$( qube_itsAddress "${to_qube?}" )"
inform '%s: Forwarding on %s port %s to %s (%s -> %s)' \
"${thisVMItsName?}" "${thisVMItsInterface?}" "${thisVMItsPort?}" "${to_qube?}" "${incomingPacketAddress?}" "${outgoingPacketAddress?}"
this_run "iptables-nft-save |
grep -v 'PortFwd %s>%s:%s%s' |
iptables-nft-restore" "$thisVMItsName" "$to_qube" "${thisVMItsProtocol?}" "${thisVMItsPort?}"
this_run "${forward_rule1?}" "${thisVMItsInterface?}" "${thisVMItsProtocol?}" "${incomingPacketAddress:+--destination ${incomingPacketAddress?}}" "${thisVMItsPort?}" "${outgoingPacketAddress?}" "${thisVMItsName?}" "${to_qube?}" "${thisVMItsProtocol?}" "${thisVMItsPort?}"
this_run "${forward_rule2?}" "${thisVMItsInterface?}" "${thisVMItsProtocol?}" "${outgoingPacketAddress:+--destination ${outgoingPacketAddress?}}" "${thisVMItsPort?}" "${thisVMItsName?}" "${to_qube?}" "${thisVMItsProtocol?}" "${thisVMItsPort?}"
this_run "${forward_rule3?}" "${thisVMItsInterface?}"
}
this_createPersistentForward() {
to_qube="${1?}"
shift
this_createForward "${to_qube?}"
thisVMItsInterface="$( this_itsInterface )"
incomingPacketAddress="$( this_itsInterfaceAddress )"
outgoingPacketAddress=$( qube_itsAddress "$to_qube" )
this_run ">>/rw/config/rc.local printf ${forward_rule1?} ${thisVMItsInterface?} ${thisVMItsProtocol?} ${incomingPacketAddress:+--destination ${incomingPacketAddress?}} ${thisVMItsPort?} ${thisVMItsPort?} ${outgoingPacketAddress?} ${thisVMItsName?} ${to_qube?} ${thisVMItsProtocol?} ${thisVMItsPort?} "
this_run ">>/rw/config/rc.local printf ${forward_rule2?} ${thisVMItsInterface?} ${thisVMItsProtocol?} ${outgoingPacketAddress:+--destination ${outgoingPacketAddress?}} ${thisVMItsPort?} ${thisVMItsName?} ${to_qube?} ${thisVMItsProtocol?} ${thisVMItsPort?}"
test this_run "grep -q 'nft add rule ip qubes-firewall forward meta iifname %s accept' /rw/config/rc.local" "$thisVMItsInterface" ||
{
this_run ">>/rw/config/rc.local printf "${forward_rule3?}" "${thisVMItsInterface?}" "
this_run "chmod +x /rw/config/rc.local"
# Ensured rc.local is executable
}
}
this_createForwardNetVMs() {
persistent="${1:-}"
attachedNetwork=$( this_itsNetVM )
test -z "${attachedNetwork?}" ||
{
qubePortForwarding "${attachedNetwork?}" create "${persistent:-}" "${thisVMItsPort?}" "${thisVMItsProtocol?}" Forward "${thisVMItsName?}"
qubePortForwarding "${attachedNetwork?}" create "${persistent:-}" "${thisVMItsPort?}" "${thisVMItsProtocol?}" ForwardNetVMs
}
}
this_createPersistentForwardNetVMs() {
this_createForwardNetVMs "Persistent"
}
this_createPersistentInput() {
this_createInput
this_run "printf '${input_rule?}' "${thisVMItsProtocol?}" "${thisVMItsPort?}" "${thisVMItsName?}" "${thisVMItsProtocol?}" "${thisVMItsPort?}" >> /rw/config/rc.local"
this_run "chmod +x /rw/config/rc.local"
# Ensured rc.local is executable
}
this_createInput() {
inform '%s: Allowing input to port %s' "${thisVMItsName?}" "${thisVMItsPort?}"
this_run "iptables-save |
grep -v 'PortFwd %s:%s%s' |
iptables-restore" "${thisVMItsName?}" "${thisVMItsProtocol?}" "${thisVMItsPort?}"
this_run "${input_rule?}" "${thisVMItsProtocol?}" "${thisVMItsPort?}" "${thisVMItsName?}" "${thisVMItsProtocol?}" "${thisVMItsPort?}"
}
this_clearPortForwarding() {
iface="$( this_itsInterface )"
inform '%s: Clearing Port Forwarding from %s iptables' "${thisVMItsNetVM?}"
this_run "iptables-save |
grep -v 'PortFwd %s' |
iptables-restore" "$thisVMItsNetVM"
# clear_remaining_accept_netfilter "${iface?}"
interfaceAcceptNetFilter="$(
this_run 'nft list table ip qubes-firewall -a |
tr -d \" |
grep '\''iifname %s accept # handle'\'' |
awk '\''{print $NF}'\' "${iface?}"
)"
test "${interfaceAcceptNetFilter?}" == "${interfaceAcceptNetFilter#[0-9]}" ||
this_run 'nft delete rule ip thisVMs-firewall forward handle %s' "${interfaceAcceptNetFilter?}"
this_run "sed -i '/PortFwd $thisVMItsNetVM>$thisVMItsName:${thisVMItsProtocol?}${thisVMItsPort?}/d' /rw/config/rc.local"
this_run "sed -i '/PortFwd $thisVMItsNetVM>$thisVMItsName:${thisVMItsProtocol?}${thisVMItsPort?}/d' /rw/config/rc.local"
this_run "grep -q 'PortFwd' /rw/config/rc.local" ||
{
this_run "sed -i '/nft add rule ip thisVMs-firewall forward meta iifname $iface accept/d' /rw/config/rc.local"
}
}
this_clearForwardNetVMs() {
thisVMItsNetVM="$( this_itsNetVM )"
test -n "${thisVMItsNetVM?}" &&
return 0
qubePortForwarding "${thisVMItsNetVM?}" "clear" "" PortForwarding "${thisVMItsPort?}" "${thisVMItsProtocol?}"
qubePortForwarding "${thisVMItsNetVM?}" "clear" "" ForwardNetVMs "${thisVMItsPort?}" "${thisVMItsProtocol?}"
}
this_clearInput() {
inform '%s: Clearing Port Forwarding from %s iptables' "$thisVMItsName" "$thisVMItsName"
this_run "iptables-save |
grep -v 'PortFwd %s' |
iptables-restore" "$thisVMItsName"
this_run "sed -i '/PortFwd %s:%s%s/d' /rw/config/rc.local" "$thisVMItsName" "${thisVMItsProtocol?}" "${thisVMItsPort?}"
}
# QUBE
thisVMItsName="${1?need a name for the qube to work with}"
shift
action="this_"
# ACTION
action="${action?}${1?}"
shift
# PERSISTENT
action="${action?}${1?}"
shift
# PORT
thisVMItsPort="${1?}"
shift
# PROTO
thisVMItsProtocol="${1?}"
shift
# Routine/Method
action="${action?}${1?}"
shift
##qvm_portfw_iptables "${QUBE?}" "${ACTION?}" "${PERSISTENT?}" "${PORT?}" "${PROTO?}"
#qubePortForwarding "${QUBE?}" "${ACTION?}" "${PERSISTENT?}" "${PORT?}" "${PROTO?}" Input
#qubePortForwarding "${QUBE?}" "${ACTION?}" "${PERSISTENT?}" "${PORT?}" "${PROTO?}" ForwardNetVMs
#
# this_{clear,create{,Persistent}}{Input,ForwardNetVMs}
#
# >> this_clearInput followed by 1+ this_clearForwardNetVMs
# or this_createInput followed by 1+ this_createForwardNetVMs
# or this_createPersistentInput followed by 1+ this_createPersistentForwardNetVMs
# << TroubleInParadise
${action?} "${1:+${@?}}"
}
qvm_portfw_iptables() {
qvm_portfw_iptables_usage() {
inform 'Usage: %s --action ACTION --qube QUBE --port PORT --proto PROTO --persistent
Examples:
-> %s --action create --qube work --port 22
-> %s --action create --qube work --port 444 --proto udp --persistent
-> %s --action clear --qube work --port 22
-> %s --action clear --qube work --port 444 --proto udp
Default value for PROTO is "tcp" and persistent is false, that is: iptables are not persisted.
' "${0##*/}" "${0##*/}" "${0##*/}" "${0##*/}" "${0##*/}"
exit 1
}
: ${PROTO:=tcp}
if {
test "$ACTION" != "create" || test "$ACTION" == "clear" ;
} && {
test -z "$QUBE" || test -z "$PORT" ;
}
then
qvm_portfw_iptables_usage
fi
>/dev/null 2>&1 qvm-check "$QUBE" ||
{
inform 'Qube "%s" not found.' "$QUBE"
exit 1
}
test -z "${ACTION#clear}" ||
{
# ACTION != clear
PERSISTENT="${PERSISTENT:+Persistent}"
}
#qvm_portfw_iptables "${QUBE?}" "${ACTION?}" "${PERSISTENT?}" "${PORT?}" "${PROTO?}"
qubePortForwarding "${QUBE?}" "${ACTION?}" "${PERSISTENT?}" "${PORT?}" "${PROTO?}" Input
qubePortForwarding "${QUBE?}" "${ACTION?}" "${PERSISTENT?}" "${PORT?}" "${PROTO?}" ForwardNetVMs
}
OPTS=$(
getopt -o a:q:p:n:s --long action:,qube:,port:,proto:,persistent -n "$0" -- "$@"
) ||
{
inform 'An error occurred while parsing options.'
exit 1
}
eval set -- "$OPTS"
while [[ $# -gt 0 ]]
do
case "$1" in
-a | --action ) ACTION="$2"; shift ;;
-n | --proto ) PROTO="$2"; shift ;;
-p | --port ) PORT="$2"; shift ;;
-q | --qube ) QUBE="$2"; shift ;;
-s | --persistent ) PERSISTENT=1; shift ;;
esac
shift
done
qvm_portfw_iptables "${QUBE:-}" "${ACTION:-}" "${PERSISTENT:-}" "${PORT:-}" "${PROTO:-}"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment