proxy_role/overlay/Linux/usr/local/sbin/privacy_whonix-gateway-firewall.bash

828 lines
32 KiB
Bash
Raw Permalink Normal View History

2024-01-06 03:08:22 +00:00
#!/bin/bash
# -*-mode: sh; tab-width: 8; coding: utf-8-unix -*-
prog=$( basename $0 .bash )
PREFIX=/usr/local
ROLE=privacy
[ -f /usr/local/etc/testforge/testforge.bash ] && . /usr/local/etc/testforge/testforge.bash \
|| { echo >&2 ERROR: $prog "/usr/local/etc/testforge/testforge.bash" ; exit 1 ; }
[ -z "$PRIV_TOR_OWNER" ] && grep -q ^debian-tor /etc/passwd && PRIV_TOR_OWNER=debian-tor
[ -z "$PRIV_TOR_OWNER" ] && grep -q ^tor /etc/passwd && PRIV_TOR_OWNER=tor
#set -- -x
DEBUG=
WHONIX_HOST=0
WHONIX_GATE=1
SSH_SERVICE=22
BOOTPC_SERVICE=68
BOOTPS_SERVICE=67
NETBIOSNS_SERVICE=137
NETBIOSDG_SERVICE=138
PRIV_WHONIX_EXTERNAL_NET=10.0.2.0/24
# 10.152.152.10 gateway
# 10.152.152.11 work
PRIV_WHONIX_INTERNAL_NET=10.152.152.0/24
## Copyright (C) 2012 - 2020 ENCRYPTED SUPPORT LP <adrelanos@riseup.net>
## Copyright (C) 2014 - 2015 Jason Mehring <nrgaway@gmail.com>
## See the file COPYING for copying conditions.
ALLOW_GATEWAY_USER_USER=1
GATEWAY_ALLOW_INCOMING_SSH=0
GATEWAY_ALLOW_INCOMING_ICMP=0
#### meta start
#### project Whonix
#### category networking and firewall
#### description
## firewall script
#### meta end
## --reject-with
## http://ubuntuforums.org/showthread.php?p=12011099
## Set to icmp-admin-prohibited because icmp-port-unreachable caused
## confusion. icmp-port-unreachable looks like a bug while
## icmp-admin-prohibited hopefully makes clear it is by design.
set -e
error_handler() {
echo "$0 ##################################################"
echo "$0 ERROR: Whonix firewall script failed!"
echo "$0 ##################################################"
exit 1
}
trap "error_handler" ERR
init() {
output_cmd "OK: Loading Whonix firewall..."
set -o pipefail
set -o errtrace
}
source_config_folder() {
shopt -s nullglob
local i
for i in \
/etc/whonix_firewall.d/*.conf \
/rw/config/whonix_firewall.d/*.conf \
/usr/local/etc/whonix_firewall.d/*.conf \
; do
bash_n_exit_code="0"
bash_n_output="$(bash -n "$i" 2>&1)" || { bash_n_exit_code="$?" ; true; };
if [ ! "$bash_n_exit_code" = "0" ]; then
output_cmd "ERROR: Invalid config file: $i
bash_n_exit_code: $bash_n_exit_code
bash_n_output:
$bash_n_output" >&2
exit 1
fi
source "$i"
done
}
variables_defaults() {
[ -n "$iptables_cmd" ] || iptables_cmd="iptables --wait"
[ -n "$ip6tables_cmd" ] || ip6tables_cmd="ip6tables --wait"
[ -n "$WORKSTATION_TRANSPARENT_TCP" ] || WORKSTATION_TRANSPARENT_TCP=1
[ -n "$WORKSTATION_TRANSPARENT_DNS" ] || WORKSTATION_TRANSPARENT_DNS=1
[ -n "$WORKSTATION_ALLOW_SOCKSIFIED" ] || WORKSTATION_ALLOW_SOCKSIFIED=1
[ -n "$CONTROL_PORT_FILTER_PROXY_ENABLE" ] || CONTROL_PORT_FILTER_PROXY_ENABLE=1
[ -n "$GATEWAY_ALLOW_INCOMING_DIR_PORT" ] || GATEWAY_ALLOW_INCOMING_DIR_PORT=0
[ -n "$GATEWAY_ALLOW_INCOMING_OR_PORT" ] || GATEWAY_ALLOW_INCOMING_OR_PORT=0
[ -n "$DIR_PORT" ] || DIR_PORT=80
[ -n "$OR_PORT" ] || OR_PORT=443
[ -n "$GATEWAY_TRANSPARENT_TCP" ] || GATEWAY_TRANSPARENT_TCP=0
[ -n "$GATEWAY_TRANSPARENT_UDP" ] || GATEWAY_TRANSPARENT_UDP=0
[ -n "$GATEWAY_TRANSPARENT_DNS" ] || GATEWAY_TRANSPARENT_DNS=0
[ -n "$ALLOW_GATEWAY_ROOT_USER" ] || ALLOW_GATEWAY_ROOT_USER=0
[ -n "$ALLOW_GATEWAY_USER_USER" ] || ALLOW_GATEWAY_USER_USER=0
[ -n "$GATEWAY_ALLOW_INCOMING_SSH" ] || GATEWAY_ALLOW_INCOMING_SSH=0
[ -n "$GATEWAY_ALLOW_INCOMING_ICMP" ] || GATEWAY_ALLOW_INCOMING_ICMP=0
## Get Tor username, distro specific!
[ -n "$TOR_USER" ] || TOR_USER=$PRIV_TOR_OWNER
## Get user uids.
#!? [ -n "$CLEARNET_USER" ] || CLEARNET_USER="$(id -u clearnet)"
[ -n "$USER_USER" ] || USER_USER="$(id -u user)" || true
[ -n "$ROOT_USER" ] || ROOT_USER="$(id -u root)"
#!? [ -n "$TUNNEL_USER" ] || TUNNEL_USER="$(id -u tunnel)"
[ -n "$SDWDATE_USER" ] || SDWDATE_USER="$(id -u sdwdate)"
[ -n "$WHONIXCHECK_USER" ] || WHONIXCHECK_USER="$(id -u whonixcheck)"
## No NAT for clearnet user.
[ -n "$CLEARNET_USER" ] && NO_NAT_USERS+=" $CLEARNET_USER"
## No NAT for tunnel user.
[ -n "$TUNNEL_USER" ] && NO_NAT_USERS+=" $TUNNEL_USER"
## No NAT for user user.
## DISABLED BY DEFAULT. For testing/debugging only.
if [ "$ALLOW_GATEWAY_USER_USER" = "1" ]; then
if [ "$USER_USER" = "" ]; then
output_cmd "INFO: USER_USER is unset. Not adding USER_USER to NO_NAT_USERS."
else
NO_NAT_USERS+=" $USER_USER"
fi
fi
## No NAT for root user.
## DISABLED BY DEFAULT. For testing/debugging only.
if [ "$ALLOW_GATEWAY_ROOT_USER" = "1" ]; then
NO_NAT_USERS+=" $ROOT_USER"
fi
## Whonix-Gateway firewall does not support TUNNEL_FIREWALL_ENABLE=true yet.
## It only supports VPN_FIREWALL="1".
## In case someone confused this setting, i.e. using TUNNEL_FIREWALL_ENABLE=true
## since this is how it is done on Whonix-Workstation, then gracefully enable
## VPN_FIREWALL="1" to prevent users shooting their own feet.
if [ "$TUNNEL_FIREWALL_ENABLE" = "true" ]; then
VPN_FIREWALL="1"
fi
## No NAT for Tor itself,
## unless VPN_FIREWALL mode is enabled.
if [ "$VPN_FIREWALL" = "1" ]; then
true
else
NO_NAT_USERS+=" $TOR_USER"
fi
if command -v "qubesdb-read" >/dev/null 2>&1 ; then
[ -n "$INT_IF" ] || INT_IF="vif+"
[ -n "$INT_TIF" ] || INT_TIF="vif+"
fi
## External interface
[ -n "$EXT_IF" ] || EXT_IF="eth0"
## Internal interface
[ -n "$INT_IF" ] || INT_IF="eth1"
## Internal "tunnel" interface, usually the same as
## the Internal interface unless using vpn tunnels
## between workstations and gateway
[ -n "$INT_TIF" ] || INT_TIF="eth1"
if [ "$NON_TOR_GATEWAY" = "" ]; then
if command -v "qubesdb-read" >/dev/null 2>&1 ; then
NON_TOR_GATEWAY=""
else
## 10.0.2.2-10.0.2.24: VirtualBox DHCP
NON_TOR_GATEWAY="\
127.0.0.0-127.0.0.24 \
192.168.0.0-192.168.0.24 \
192.168.1.0-192.168.1.24 \
10.152.152.0-10.152.152.24 \
10.0.2.2-10.0.2.24 \
"
fi
fi
[ -n "$VPN_INTERFACE" ] || VPN_INTERFACE="tun0"
## Destinations you do not routed through VPN, only for Whonix-Gateway.
if [ "$LOCAL_NET" = "" ]; then
if command -v "qubesdb-read" >/dev/null 2>&1 ; then
LOCAL_NET="\
127.0.0.0-127.0.0.24 \
10.137.0.0-10.138.255.255 \
"
else
## 10.0.2.2/24: VirtualBox DHCP
LOCAL_NET="\
127.0.0.0-127.0.0.24 \
192.168.0.0-192.168.0.24 \
192.168.1.0-192.168.1.24 \
10.152.152.0-10.152.152.24 \
10.0.2.2-10.0.2.24 \
"
fi
fi
if [ "$WORKSTATION_DEST_SOCKSIFIED" = "" ]; then
## 10.152.152.10 - Non-Qubes-Whonix-Gateway IP
##
## 10.137.0.0/8 - persistent Qubes-Whonix-Gateway IP range
## 10.138.0.0/8 - DispVM Qubes-Whonix-Gateway IP range
if command -v "qubesdb-read" >/dev/null 2>&1 ; then
## https://forums.whonix.org/t/whonix-gateway-not-reachable/7484/16
## 10.152.152.10 is hardcoded in some places.
WORKSTATION_DEST_SOCKSIFIED="10.137.0.0/16,10.138.0.0/16,10.152.152.10"
else
WORKSTATION_DEST_SOCKSIFIED="10.152.152.10"
fi
fi
## The following ports are used
## - here in /usr/bin/whonix_firewall (package: whonix-gw-firewall)
## - by Tor in /usr/share/tor/tor-service-defaults-torrc (package: anon-gw-anonymizer-config)
##
## The following applications will be separated, preventing identity
## correlation through circuit sharing.
## Transparent Proxy Ports for Whonix-Workstation
[ -n "$TRANS_PORT_WORKSTATION" ] || TRANS_PORT_WORKSTATION="9040"
[ -n "$DNS_PORT_WORKSTATION" ] || DNS_PORT_WORKSTATION="5300"
## Transparent Proxy Ports for Whonix-Gateway
[ -n "$TRANS_PORT_GATEWAY" ] || TRANS_PORT_GATEWAY="9041"
[ -n "$DNS_PORT_GATEWAY" ] || DNS_PORT_GATEWAY="5400"
## Control Port Filter Proxy Port
[ -n "$CONTROL_PORT_FILTER_PROXY_PORT" ] || CONTROL_PORT_FILTER_PROXY_PORT="9051"
[ -n "$GATEWAY_ALLOW_INCOMING_FLASHPROXY" ] || GATEWAY_ALLOW_INCOMING_FLASHPROXY="0"
[ -n "$FLASHPROXY_PORT" ] || FLASHPROXY_PORT="9000"
## Socks Ports for per application circuits.
[ -n "$SOCKS_PORT_TOR_DEFAULT" ] || SOCKS_PORT_TOR_DEFAULT="9050"
[ -n "$SOCKS_PORT_TB" ] || SOCKS_PORT_TB="9100"
[ -n "$SOCKS_PORT_IRC" ] || SOCKS_PORT_IRC="9101"
[ -n "$SOCKS_PORT_TORBIRDY" ] || SOCKS_PORT_TORBIRDY="9102"
[ -n "$SOCKS_PORT_IM" ] || SOCKS_PORT_IM="9103"
[ -n "$SOCKS_PORT_APT_GET" ] || SOCKS_PORT_APT_GET="9104"
[ -n "$SOCKS_PORT_GPG" ] || SOCKS_PORT_GPG="9105"
[ -n "$SOCKS_PORT_SSH" ] || SOCKS_PORT_SSH="9106"
[ -n "$SOCKS_PORT_GIT" ] || SOCKS_PORT_GIT="9107"
[ -n "$SOCKS_PORT_SDWDATE" ] || SOCKS_PORT_SDWDATE="9108"
[ -n "$SOCKS_PORT_WGET" ] || SOCKS_PORT_WGET="9109"
[ -n "$SOCKS_PORT_WHONIXCHECK" ] || SOCKS_PORT_WHONIXCHECK="9110"
[ -n "$SOCKS_PORT_BITCOIN" ] || SOCKS_PORT_BITCOIN="9111"
[ -n "$SOCKS_PORT_PRIVOXY" ] || SOCKS_PORT_PRIVOXY="9112"
[ -n "$SOCKS_PORT_POLIPO" ] || SOCKS_PORT_POLIPO="9113"
[ -n "$SOCKS_PORT_WHONIX_NEWS" ] || SOCKS_PORT_WHONIX_NEWS="9114"
[ -n "$SOCKS_PORT_TBB_DOWNLOAD" ] || SOCKS_PORT_TBB_DOWNLOAD="9115"
[ -n "$SOCKS_PORT_TBB_GPG" ] || SOCKS_PORT_TBB_GPG="9116"
[ -n "$SOCKS_PORT_CURL" ] || SOCKS_PORT_CURL="9117"
[ -n "$SOCKS_PORT_RSS" ] || SOCKS_PORT_RSS="9118"
[ -n "$SOCKS_PORT_TORCHAT" ] || SOCKS_PORT_TORCHAT="9119"
[ -n "$SOCKS_PORT_MIXMASTERUPDATE" ] || SOCKS_PORT_MIXMASTERUPDATE="9120"
[ -n "$SOCKS_PORT_MIXMASTER" ] || SOCKS_PORT_MIXMASTER="9121"
[ -n "$SOCKS_PORT_KDE" ] || SOCKS_PORT_KDE="9122"
[ -n "$SOCKS_PORT_GNOME" ] || SOCKS_PORT_GNOME="9123"
[ -n "$SOCKS_PORT_APTITUDE" ] || SOCKS_PORT_APTITUDE="9124"
[ -n "$SOCKS_PORT_YUM" ] || SOCKS_PORT_YUM="9125"
[ -n "$SOCKS_PORT_TBB_DEFAULT" ] || SOCKS_PORT_TBB_DEFAULT="9150"
## For testing purposes only.
## To test if prerouting redirection rules for socksified interfere with transparent torification.
## https://phabricator.whonix.org/T462
#[ -n "$SOCKS_PORT_HTTP" ] || SOCKS_PORT_HTTP="80"
#[ -n "$SOCKS_PORT_SSL" ] || SOCKS_PORT_SSL="443"
## Adding more Socks Ports here should no longer be necessary.
## There are already lots of custom ports prepared that you can use.
## See documentation:
## https://www.whonix.org/wiki/Stream_Isolation
##
## Additional Socks Ports for per application circuits could be
## added here, but you would have to:
## - Edit '/usr/local/etc/torrc.d/50_user.conf' to add more 'SocksPort's.
## - And 'sudo service tor@default reload' afterwards.
## - Add more socks port variables to Whonix firewall configuration.
## (For example to '/etc/whonix_firewall.d/50_user.conf'.)
## Follow the 'SOCKS_PORT_...' naming scheme.
## (For example 'SOCKS_PORT_CUSTOM_ONE', 'SOCKS_PORT_CUSTOM_TWO', etc.)
## - And issue "sudo /usr/bin/whonix_firewall" afterwards.
socks_ports_list="$(compgen -v | grep SOCKS\_PORT\_)"
}
ipv4_defaults() {
lsmod | grep -q iptable_filter || modprobe iptable_filter
## Set secure defaults.
$iptables_cmd -P INPUT DROP
## FORWARD rules does not actually do anything if forwarding is disabled. Better be safe just in case.
$iptables_cmd -P FORWARD DROP
## Will be lifted below.
$iptables_cmd -P OUTPUT DROP
}
ipv4_preparation() {
lsmod | grep -q nf_nat || modprobe nf_nat
lsmod | grep -q iptable_mangle || modprobe iptable_mangle
## Flush old rules.
$iptables_cmd -F
$iptables_cmd -X
$iptables_cmd -t nat -F
$iptables_cmd -t nat -X
$iptables_cmd -t mangle -F
$iptables_cmd -t mangle -X
}
ipv4_drop_invalid_incoming_packages() {
lsmod | grep -q nf_conntrack || modprobe nf_conntrack
## DROP MARTIANS
## https://www.cyberciti.biz/faq/linux-log-suspicious-martian-packets-un-routable-source-addresses/
$iptables_cmd -A INPUT -i $WLAN_IF -s 10.0.0.0/8 -j LOG --log-prefix "iptables_martian_DROP SPOOF A: "
$iptables_cmd -A INPUT -i $WLAN_IF -s 172.16.0.0/12 -j LOG --log-prefix "iptables_martian_DROP SPOOF B: "
$iptables_cmd -A INPUT -i $WLAN_IF -s 192.168.0.0/16 -j LOG --log-prefix "iptables_martian_DROP SPOOF C: "
$iptables_cmd -A INPUT -i $WLAN_IF -s 224.0.0.0/4 -j LOG --log-prefix "iptables_martian_DROP MULTICAST D: "
$iptables_cmd -A INPUT -i $WLAN_IF -s 240.0.0.0/5 -j LOG --log-prefix "iptables_martian_DROP SPOOF E: "
$iptables_cmd -A INPUT -i $WLAN_IF -d 127.0.0.0/8 -j LOG --log-prefix "iptables_martian_DROP LOOPBACK: "
$iptables_cmd -A INPUT -i $WLAN_IF -s 10.0.0.0/8 -j DROP
$iptables_cmd -A INPUT -i $WLAN_IF -s 172.16.0.0/12 -j DROP
$iptables_cmd -A INPUT -i $WLAN_IF -s 192.168.0.0/16 -j DROP
$iptables_cmd -A INPUT -i $WLAN_IF -s 224.0.0.0/4 -j DROP
$iptables_cmd -A INPUT -i $WLAN_IF -s 240.0.0.0/5 -j DROP
$iptables_cmd -A INPUT -i $WLAN_IF -d 127.0.0.0/8 -j DROP
## DROP INVALID
$iptables_cmd -A INPUT -m conntrack --ctstate INVALID -j DROP
$iptables_cmd -A INPUT -m state --state INVALID -j DROP
## DROP INVALID SYN PACKETS
$iptables_cmd -A INPUT -p tcp --tcp-flags ALL ACK,RST,SYN,FIN -j DROP
$iptables_cmd -A INPUT -p tcp --tcp-flags SYN,FIN SYN,FIN -j DROP
$iptables_cmd -A INPUT -p tcp --tcp-flags SYN,RST SYN,RST -j DROP
## DROP PACKETS WITH INCOMING FRAGMENTS. THIS ATTACK ONCE RESULTED IN KERNEL PANICS
$iptables_cmd -A INPUT -f -j DROP
## DROP INCOMING MALFORMED XMAS PACKETS
$iptables_cmd -A INPUT -p tcp --tcp-flags ALL ALL -j DROP
## DROP INCOMING MALFORMED NULL PACKETS
$iptables_cmd -A INPUT -p tcp --tcp-flags ALL NONE -j DROP
}
qubes() {
lsmod | grep -q xt_owner || modprobe xt_owner
if [ "$firewall_mode" = "timesync-fail-closed" ]; then
true "timesync-fail-closed mode, skipping rest of function $FUNCNAME"
return 0
fi
if [ -e /run/qubes/this-is-netvm ] || [ -e /run/qubes/this-is-proxyvm ]; then
local int_if_item
for int_if_item in $INT_IF; do
## Allow connections from port 8082 of internal vif interface for tinyproxy
## tinyproxy is responsible to handle TemplateVMs updates.
$iptables_cmd -A INPUT -i "$int_if_item" -p tcp -m tcp --dport 8082 -j ACCEPT
$iptables_cmd -A OUTPUT -o "$int_if_item" -p tcp -m tcp --sport 8082 -j ACCEPT
done
## Qubes pre-routing. Will be able to intercept traffic destined for
## 10.137.255.254 to be re-routed to tinyproxy.
$iptables_cmd -t nat -N PR-QBS-SERVICES
$iptables_cmd -t nat -A PREROUTING -j PR-QBS-SERVICES
for int_if_item in $INT_IF; do
## Redirects traffic destined for 10.137.255.154 to port 8082 (tinyproxy).
$iptables_cmd -t nat -A PR-QBS-SERVICES -d 10.137.255.254/32 -i "$int_if_item" -p tcp -m tcp --dport 8082 -j REDIRECT
done
## Forward tinyproxy output to port 5300/9040 on internal (Tor) interface (eth1) to be
## able to connect to Internet (via Tor) to proxy updates for TemplateVM.
$iptables_cmd -t nat -A OUTPUT -p udp -m owner --uid-owner tinyproxy -m conntrack --ctstate NEW -j DNAT --to "127.0.0.1:${DNS_PORT_GATEWAY}"
$iptables_cmd -t nat -A OUTPUT -p tcp -m owner --uid-owner tinyproxy -m conntrack --ctstate NEW -j DNAT --to "127.0.0.1:${TRANS_PORT_GATEWAY}"
## The same for squid from qubes-updates-cache, which runs as user vm-updates.
if getent passwd vm-updates >/dev/null; then
$iptables_cmd -t nat -A OUTPUT -p udp -m owner --uid-owner vm-updates -m conntrack --ctstate NEW -j DNAT --to "127.0.0.1:${DNS_PORT_GATEWAY}"
$iptables_cmd -t nat -A OUTPUT -p tcp -m owner --uid-owner vm-updates -m conntrack --ctstate NEW -j DNAT --to "127.0.0.1:${TRANS_PORT_GATEWAY}"
fi
## https://github.com/QubesOS/qubes-issues/issues/3201#issuecomment-338646742
$iptables_cmd -A OUTPUT -p udp -m owner --uid-owner tinyproxy -m conntrack --ctstate NEW -d 127.0.0.1 --dport "${DNS_PORT_GATEWAY}" -j ACCEPT
$iptables_cmd -A OUTPUT -p tcp -m owner --uid-owner tinyproxy -m conntrack --ctstate NEW -d 127.0.0.1 --dport "${TRANS_PORT_GATEWAY}" -j ACCEPT
fi
}
ipv4_input_rules() {
## Traffic on the loopback interface is accepted.
$iptables_cmd -A INPUT -i lo -j ACCEPT
## Established incoming connections are accepted.
$iptables_cmd -A INPUT -m state --state ESTABLISHED -j ACCEPT
## Drop all incoming ICMP traffic by default.
## All incoming connections are dropped by default anyway, but should a user
## allow incoming ports (such as for incoming SSH or FlashProxy), ICMP should
## still be dropped to filter for example ICMP time stamp requests.
if [ ! "$GATEWAY_ALLOW_INCOMING_ICMP" = "1" ]; then
$iptables_cmd -A INPUT -p icmp -j DROP
fi
## Allow all incoming connections on the virtual VPN network interface,
## when VPN_FIREWALL mode is enabled.
## DISABLED BY DEFAULT.
if [ "$VPN_FIREWALL" = "1" ]; then
$iptables_cmd -A INPUT -i "$VPN_INTERFACE" -j ACCEPT
fi
local ext_if_item
for ext_if_item in $EXT_IF; do
## Allow incoming SSH connections on the external interface.
## DISABLED BY DEFAULT. For testing/debugging only.
if [ "$GATEWAY_ALLOW_INCOMING_SSH" = "1" ]; then
$iptables_cmd -A INPUT -i "$ext_if_item" -p tcp --dport 22 -j ACCEPT
fi
## Allow incoming Flash Proxy connections on the external interface.
## This has NOTHING to do with Adobe Flash.
## DISABLED BY DEFAULT.
if [ "$GATEWAY_ALLOW_INCOMING_FLASHPROXY" = "1" ]; then
$iptables_cmd -A INPUT -i "$ext_if_item" -p tcp --dport "$FLASHPROXY_PORT" -j ACCEPT
fi
local local_port_to_open
for local_port_to_open in $EXTERNAL_OPEN_PORTS; do
$iptables_cmd -A INPUT -i "$ext_if_item" -p tcp --dport "$local_port_to_open" -j ACCEPT
done
local local_udp_port_to_open
for local_udp_port_to_open in $EXTERNAL_UDP_OPEN_PORTS; do
$iptables_cmd -A INPUT -p udp --dport "$local_udp_port_to_open" -j ACCEPT
done
if [ "$EXTERNAL_OPEN_ALL" = "true" ]; then
$iptables_cmd -A INPUT -j ACCEPT
fi
done
if [ "$firewall_mode" = "timesync-fail-closed" ]; then
true "timesync-fail-closed mode, skipping rest of function $FUNCNAME"
return 0
fi
for ext_if_item in $EXT_IF; do
## Allow incoming DIRPORT connections for an optional Tor relay.
## DISABLED BY DEFAULT.
if [ "$GATEWAY_ALLOW_INCOMING_DIR_PORT" = "1" ]; then
$iptables_cmd -A INPUT -i "$ext_if_item" -p tcp --dport "$DIR_PORT" -j ACCEPT
fi
## Allow incoming ORPORT connections for an optional Tor relay.
## DISABLED BY DEFAULT.
if [ "$GATEWAY_ALLOW_INCOMING_OR_PORT" = "1" ]; then
$iptables_cmd -A INPUT -i "$ext_if_item" -p tcp --dport "$OR_PORT" -j ACCEPT
fi
## Custom Open Ports on external interface
## - untested, should work
## - Replace 22,9050,9051,9150,9151 with any ports you like to be open, example: 9050,9051
## or just 9050
## - $iptables_cmd v1.4.14: multiport needs -p tcp, -p udp, -p udplite, -p sctp or -p dccp
#$iptables_cmd -A INPUT -i "$ext_if_item" -p tcp --match multiport --dports 22,9050,9051,9150,9151 -j ACCEPT
#$iptables_cmd -A INPUT -i "$ext_if_item" -p udp --match multiport --dports 22,9050,9051,9150,9151 -j ACCEPT
## OPTIONAL Allow incoming OpenVPN connections on the external interface.
#$iptables_cmd -A INPUT -i "$ext_if_item" -p tcp --dport 1194 -j ACCEPT
done
local int_tif_item
local int_if_item
for int_tif_item in $INT_TIF; do
if [ "$WORKSTATION_TRANSPARENT_DNS" = "1" ]; then
## Allow DNS traffic to DnsPort.
$iptables_cmd -A INPUT -i "$int_tif_item" -p udp --dport "$DNS_PORT_WORKSTATION" -j ACCEPT
fi
done
for int_if_item in $INT_IF; do
if [ "$WORKSTATION_TRANSPARENT_TCP" = "1" ]; then
## Allow TCP traffic TransPort.
$iptables_cmd -A INPUT -i "$int_if_item" -p tcp --dport "$TRANS_PORT_WORKSTATION" -j ACCEPT
fi
done
for int_tif_item in $INT_TIF; do
## Allow TCP traffic to Control Port Filter Proxy.
if [ "$CONTROL_PORT_FILTER_PROXY_ENABLE" = "1" ]; then
$iptables_cmd -A INPUT -i "$int_tif_item" -p tcp --dport "$CONTROL_PORT_FILTER_PROXY_PORT" -j ACCEPT
fi
## Allow socksified applications.
if [ "$WORKSTATION_ALLOW_SOCKSIFIED" = "1" ]; then
for socks_port in $socks_ports_list; do
true "$socks_port: ${!socks_port}"
$iptables_cmd -A INPUT -i "$int_tif_item" -p tcp --dport "${!socks_port}" -j ACCEPT
done
## Accept ports 9152-9189 prepared for user custom applications.
## See /usr/share/tor/tor-service-defaults-torrc for more comments.
$iptables_cmd -A INPUT -i "$int_tif_item" -p tcp --match multiport --dports 9152:9189 -j ACCEPT
fi
done
for int_if_item in $INT_IF; do
## Redirect Control Port Filter Proxy to Control Port Filter Proxy port.
if [ "$CONTROL_PORT_FILTER_PROXY_ENABLE" = "1" ]; then
$iptables_cmd -t nat -A PREROUTING -i "$int_if_item" -d "$WORKSTATION_DEST_SOCKSIFIED" -p tcp --dport "$CONTROL_PORT_FILTER_PROXY_PORT" -j REDIRECT --to-ports "$CONTROL_PORT_FILTER_PROXY_PORT"
fi
if [ "$WORKSTATION_ALLOW_SOCKSIFIED" = "1" ]; then
for socks_port in $socks_ports_list; do
true "$socks_port: ${!socks_port}"
## Redirect Browser/IRC/TorBirdy, etc. to SocksPort.
$iptables_cmd -t nat -A PREROUTING -i "$int_if_item" -d "$WORKSTATION_DEST_SOCKSIFIED" -p tcp --dport "${!socks_port}" -j REDIRECT --to-ports "${!socks_port}"
done
## Redirect ports 9152-9189 prepared for user custom applications.
$iptables_cmd -t nat -A PREROUTING -i "$int_if_item" -d "$WORKSTATION_DEST_SOCKSIFIED" -p tcp --dport 9152:9189 -j REDIRECT
fi
if [ "$WORKSTATION_TRANSPARENT_DNS" = "1" ]; then
## Redirect remaining DNS traffic to DNS_PORT_WORKSTATION.
## Only user installed applications not configured to use a SocksPort are affected.
$iptables_cmd -t nat -A PREROUTING -i "$int_if_item" -p udp --dport 53 -j REDIRECT --to-ports "$DNS_PORT_WORKSTATION"
fi
if [ "$WORKSTATION_TRANSPARENT_TCP" = "1" ]; then
## Catch all remaining TCP and redirect to TransPort.
## Only user installed applications not configured to use a SocksPort are affected.
$iptables_cmd -t nat -A PREROUTING -i "$int_if_item" -p tcp --syn -j REDIRECT --to-ports "$TRANS_PORT_WORKSTATION"
## Optionally restrict TransPort.
## Replace above rule with a more restrictive one, e.g.:
#$iptables_cmd -t nat -A PREROUTING -i "$int_if_item" -p tcp --match multiport --dports 80,443 --syn -j REDIRECT --to-ports "$TRANS_PORT_WORKSTATION"
fi
done
}
ipv4_input_defaults() {
## Log.
#$iptables_cmd -A INPUT -j LOG --log-prefix "Whonix blocked input4: "
## Reject anything not explicitly allowed above.
## Drop is better than reject here, because we do not want to reveal it's a Whonix-Gateway.
## (In case someone running Whonix-Gateway on bare metal.)
$iptables_cmd -A INPUT -j DROP
}
ipv4_forward() {
## Log.
#$iptables_cmd -A FORWARD -j LOG --log-prefix "Whonix blocked forward4: "
## Reject everything.
$iptables_cmd -A FORWARD -j REJECT --reject-with icmp-admin-prohibited
}
ipv4_reject_invalid_outgoing_packages() {
## Drop invalid outgoing packages,
## unless NO_REJECT_INVALID_OUTGOING_PACKAGES is set to 1.
if [ ! "$NO_REJECT_INVALID_OUTGOING_PACKAGES" = "1" ]; then
## https://lists.torproject.org/pipermail/tor-talk/2014-March/032507.html
$iptables_cmd -A OUTPUT -m conntrack --ctstate INVALID -j REJECT --reject-with icmp-admin-prohibited
$iptables_cmd -A OUTPUT -m state --state INVALID -j REJECT --reject-with icmp-admin-prohibited
#$iptables_cmd -A OUTPUT ! -o lo ! -d 127.0.0.1 ! -s 127.0.0.1 -p tcp -m tcp --tcp-flags ACK,FIN ACK,FIN -j REJECT --reject-with icmp-admin-prohibited
#$iptables_cmd -A OUTPUT ! -o lo ! -d 127.0.0.1 ! -s 127.0.0.1 -p tcp -m tcp --tcp-flags ACK,RST ACK,RST -j REJECT --reject-with icmp-admin-prohibited
## DROP INVALID SYN PACKETS
$iptables_cmd -A OUTPUT -p tcp --tcp-flags ALL ACK,RST,SYN,FIN -j REJECT --reject-with icmp-admin-prohibited
$iptables_cmd -A OUTPUT -p tcp --tcp-flags SYN,FIN SYN,FIN -j REJECT --reject-with icmp-admin-prohibited
$iptables_cmd -A OUTPUT -p tcp --tcp-flags SYN,RST SYN,RST -j REJECT --reject-with icmp-admin-prohibited
## DROP PACKETS WITH INCOMING FRAGMENTS. THIS ATTACK ONCE RESULTED IN KERNEL PANICS
$iptables_cmd -A OUTPUT -f -j REJECT --reject-with icmp-admin-prohibited
## DROP INCOMING MALFORMED XMAS PACKETS
$iptables_cmd -A OUTPUT -p tcp --tcp-flags ALL ALL -j REJECT --reject-with icmp-admin-prohibited
## DROP INCOMING MALFORMED NULL PACKETS
$iptables_cmd -A OUTPUT -p tcp --tcp-flags ALL NONE -j REJECT --reject-with icmp-admin-prohibited
fi
}
ipv4_output() {
lsmod | grep -q xt_owner || modprobe xt_owner
## Allow outgoing traffic on VPN interface,
## if VPN_FIREWALL mode is enabled.
## DISABLED BY DEFAULT.
if [ "$VPN_FIREWALL" = "1" ]; then
$iptables_cmd -A OUTPUT -o "$VPN_INTERFACE" -j ACCEPT
fi
local no_nat_user
for no_nat_user in $NO_NAT_USERS ; do
$iptables_cmd -t nat -A OUTPUT -m owner --uid-owner "$no_nat_user" -j RETURN
done
if [ "$firewall_mode" = "full" ]; then
## Redirect of Gateway DNS traffic to DNS_PORT_GATEWAY.
## DISABLED BY DEFAULT. default. Using SocksPort instead.
if [ "$GATEWAY_TRANSPARENT_DNS" = "1" ]; then
$iptables_cmd -t nat -A OUTPUT -p udp --dport 53 -j REDIRECT --to-ports "$DNS_PORT_GATEWAY"
fi
fi
if [ "$firewall_mode" = "full" ]; then
## Exclude connections to local network, Whonix-Workstation, VirtualBox from being redirected through Tor,
## unless VPN_FIREWALL mode is enabled.
## ENABLED BY DEFAULT.
if [ ! "$VPN_FIREWALL" = "1" ]; then
local non_tor_gateway_item
for non_tor_gateway_item in $NON_TOR_GATEWAY; do
$iptables_cmd -t nat -A OUTPUT -m iprange --dst-range "$non_tor_gateway_item" -j RETURN
done
fi
fi
if [ "$firewall_mode" = "full" ]; then
## Redirect all Gateway TCP traffic to TRANS_PORT_GATEWAY.
## DISABLED BY DEFAULT. Using SocksPort instead.
if [ "$GATEWAY_TRANSPARENT_TCP" = "1" ]; then
$iptables_cmd -t nat -A OUTPUT -p tcp --syn -j REDIRECT --to-ports "$TRANS_PORT_GATEWAY"
fi
fi
## Existing connections are accepted.
$iptables_cmd -A OUTPUT -m state --state ESTABLISHED -j ACCEPT
if [ "$firewall_mode" = "full" ]; then
## Accept outgoing connections to local network, Whonix-Workstation and VirtualBox,
## unless VPN_FIREWALL mode is enabled.
## ENABLED BY DEFAULT.
if [ ! "$VPN_FIREWALL" = "1" ]; then
for non_tor_gateway_item in $NON_TOR_GATEWAY; do
$iptables_cmd -A OUTPUT -m iprange --dst-range "$non_tor_gateway_item" -j ACCEPT
done
fi
fi
if [ "$firewall_mode" = "full" ]; then
## Accept outgoing connections to local network,
## when VPN_FIREWALL mode is enabled.
## DISABLED BY DEFAULT.
if [ "$VPN_FIREWALL" = "1" ]; then
local local_net_item
for local_net_item in $LOCAL_NET; do
$iptables_cmd -A OUTPUT -m iprange --dst-range "$local_net_item" -j ACCEPT
done
fi
fi
## Prevent connections to Tor SocksPorts.
## https://phabricator.whonix.org/T533#11025
if [ "$firewall_mode" = "timesync-fail-closed" ]; then
local socks_port_item
for socks_port_item in $socks_ports_list; do
true "$socks_port_item: ${!socks_port_item}"
if [ "$SOCKS_PORT_SDWDATE" = "${!socks_port_item}" ]; then
continue
fi
$iptables_cmd -A OUTPUT -p tcp --dport "${!socks_port_item}" --dst "127.0.0.1" -j REJECT
done
fi
## Access to localhost is required even in timesync-fail-closed mode,
## otherwise breaks applications such as konsole and kwrite.
$iptables_cmd -A OUTPUT -o lo -j ACCEPT
for no_nat_user in $NO_NAT_USERS ; do
$iptables_cmd -A OUTPUT -m owner --uid-owner "$no_nat_user" -j ACCEPT
done
if [ "$firewall_mode" = "timesync-fail-closed" ]; then
## Allow sdwdate talking to localhost and Tor in Whonix firewall timesync-fail-closed mode.
## Otherwise in Whonix firewall full mode this rule is redundant.
$iptables_cmd -A OUTPUT -m owner --uid-owner "$SDWDATE_USER" -m iprange --dst-range "127.0.0.1" -j ACCEPT
$iptables_cmd -A OUTPUT -m owner --uid-owner "$WHONIXCHECK_USER" -m iprange --dst-range "127.0.0.1" -j ACCEPT
$iptables_cmd -A OUTPUT -p tcp --dport "$CONTROL_PORT_FILTER_PROXY_PORT" --dst "127.0.0.1" -j ACCEPT
fi
## Log.
#$iptables_cmd -A OUTPUT -j LOG --log-prefix "Whonix blocked output4: "
## Reject all other outgoing traffic.
$iptables_cmd -A OUTPUT -j REJECT --reject-with icmp-admin-prohibited
}
ipv6() {
## Policy DROP for all traffic as fallback.
$ip6tables_cmd -P INPUT DROP
$ip6tables_cmd -P OUTPUT DROP
$ip6tables_cmd -P FORWARD DROP
## Flush old rules.
$ip6tables_cmd -F
$ip6tables_cmd -X
$ip6tables_cmd -t mangle -F
$ip6tables_cmd -t mangle -X
## Allow unlimited access on loopback.
## Not activated, since we do not need it.
#$ip6tables_cmd -A INPUT -i lo -j ACCEPT
#$ip6tables_cmd -A OUTPUT -o lo -j ACCEPT
## Log.
#$ip6tables_cmd -A INPUT -j LOG --log-prefix "Whonix blocked input6: "
#$ip6tables_cmd -A OUTPUT -j LOG --log-prefix "Whonix blocked output6: "
#$ip6tables_cmd -A FORWARD -j LOG --log-prefix "Whonix blocked forward6: "
## Drop/reject all other traffic.
$ip6tables_cmd -A INPUT -j DROP
## --reject-with icmp-admin-prohibited not supported by ip6tables
$ip6tables_cmd -A OUTPUT -j REJECT
## --reject-with icmp-admin-prohibited not supported by ip6tables
$ip6tables_cmd -A FORWARD -j REJECT
}
status_files() {
mkdir --parents /run/whonix_firewall
if [ -e /run/whonix_firewall/first_run_current_boot.status ]; then
touch /run/whonix_firewall/consecutive_run.status
return 0
fi
touch /run/whonix_firewall/first_run_current_boot.status
}
date_cmd(){
date -u +"%Y-%m-%d %T"
}
output_cmd() {
echo "$(date_cmd) - $0 - $@"
}
firewall_mode_detection() {
if [ ! "$firewall_mode" = "" ]; then
output_cmd "OK: Skipping firewall mode detection since already set to '$firewall_mode'."
if [ "$firewall_mode" = "timesync-fail-closed" ]; then
output_cmd "OK: (Only local Tor control port connections and torified sdwdate allowed.)"
return 0
elif [ "$firewall_mode" = "full" ]; then
output_cmd "OK: (Full torified network access allowed.)"
return 0
else
output_cmd "ERROR: firewall_mode must be set to either 'full' or 'timesync-fail-closed'."
error_handler
fi
fi
## Run Whonix firewall in full mode if sdwdate already succeeded.
if [ -e /run/sdwdate/first_success ]; then
firewall_mode=full
output_cmd "OK: (/run/sdwdate/first_success exists.)"
elif [ -e /run/sdwdate/success ]; then
firewall_mode=full
output_cmd "OK: (/run/sdwdate/success exists.)"
## /run/whonix_firewall/first_run_current_boot.status already exists,
## therefore have Whonix firewall run in full mode.
elif [ -e /run/whonix_firewall/first_run_current_boot.status ]; then
firewall_mode=full
output_cmd "OK: (/run/whonix_firewall/first_run_current_boot.status exists.)"
else
## /run/whonix_firewall/first_run_current_boot.status does not yet exist,
## therefore return 'yes, timesync-fail-closed'.
firewall_mode=timesync-fail-closed
fi
if [ "$firewall_mode" = "timesync-fail-closed" ]; then
output_cmd "OK: First run during current boot, therefore running in timesync-fail-closed mode."
output_cmd "OK: (Only local Tor control port connections and torified sdwdate allowed.)"
else
output_cmd "OK: Consecutive run during current boot, therefore running in full mode."
output_cmd "OK: (Full torified network access allowed.)"
fi
}
end() {
output_cmd "OK: Whonix firewall loaded."
exit 0
}
main() {
init
firewall_mode_detection
variables_defaults
ipv4_defaults
ipv4_preparation
ipv4_drop_invalid_incoming_packages
qubes
ipv4_input_rules
ipv4_input_defaults
ipv4_forward
ipv4_reject_invalid_outgoing_packages
ipv4_output
if [ -d /proc/sys/net/ipv6/ ]; then
ipv6
fi
status_files
end
}
source_config_folder
main