#!/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 ## Copyright (C) 2014 - 2015 Jason Mehring ## 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