-
-
Save imme-emosol/840ca36b4f098ce3431db43f08288aaa to your computer and use it in GitHub Desktop.
Qubes-os port forwarding to allow external connections
This file contains hidden or 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
#!/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