I FIXED Whonix For VPN Users - Can experts look over my work?
by burgler from LinuxQuestions.org on (#5RKFZ)
Hello, everybody!:)
Goal:
Making a guide for safely using Whonix on KVM with a VPN installed on the host, by forwarding all Whonix traffic through the VPN's tun0 interface and easily achieving fail-closed (all traffic cuts off if VPN is disconnected).
If everything checks out I will contribute this on Github.
Method:
Very small changes to XML files and iptables.
Foot Notes:
Whonix Gateway, Worstation & Tor are all working as expected. I just need a second set of eyes to verify if this all ok.
Fix:
Step 1.)
Whonix for KVM comes with 2 XML files named Whonix-External & Whonix-Internal, which will be the virtual networks used by libvirt.
The External XML needs to be edited to include device tun0.
Whonix-External default:
Code: <network>
<name>Whonix-External</name>
<forward mode='nat'/>
<bridge name='virbr1' stp='on' delay='0'/>
<ip address='10.0.2.2' netmask='255.255.255.0'/>
</network>Edited Whonix-External:
Code: <network>
<name>Whonix-External</name>
<forward mode='nat' dev='tun0'/>
<bridge name='virbr1' stp='on' delay='0'/>
<ip address='10.0.2.2' netmask='255.255.255.0'/>
</network>Step 2.)
Libvirt automatically adds iptable rules for virtual networks. Unecessary or unsafe rules/chains need to be removed.
Default rules added by libvirt:
Code: *filter
:INPUT DROP
:FORWARD DROP
:OUTPUT DROP
:LIBVIRT_FWI
:LIBVIRT_FWO
:LIBVIRT_FWX
:LIBVIRT_INP
:LIBVIRT_OUT
-A INPUT -j LIBVIRT_INP
-A FORWARD -j LIBVIRT_FWX
-A FORWARD -j LIBVIRT_FWI
-A FORWARD -j LIBVIRT_FWO
-A OUTPUT -j LIBVIRT_OUT
-A LIBVIRT_FWI -o virbr2 -j REJECT --reject-with icmp-port-unreachable
-A LIBVIRT_FWI -d 10.0.2.0/24 -i tun0 -o virbr1 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A LIBVIRT_FWI -o virbr1 -j REJECT --reject-with icmp-port-unreachable
-A LIBVIRT_FWO -i virbr2 -j REJECT --reject-with icmp-port-unreachable
-A LIBVIRT_FWO -s 10.0.2.0/24 -i virbr1 -o tun0 -j ACCEPT
-A LIBVIRT_FWO -i virbr1 -j REJECT --reject-with icmp-port-unreachable
-A LIBVIRT_FWX -i virbr2 -o virbr2 -j ACCEPT
-A LIBVIRT_FWX -i virbr1 -o virbr1 -j ACCEPT
-A LIBVIRT_INP -i virbr2 -p udp -m udp --dport 53 -j ACCEPT
-A LIBVIRT_INP -i virbr2 -p tcp -m tcp --dport 53 -j ACCEPT
-A LIBVIRT_INP -i virbr2 -p udp -m udp --dport 67 -j ACCEPT
-A LIBVIRT_INP -i virbr2 -p tcp -m tcp --dport 67 -j ACCEPT
-A LIBVIRT_INP -i virbr1 -p udp -m udp --dport 53 -j ACCEPT
-A LIBVIRT_INP -i virbr1 -p tcp -m tcp --dport 53 -j ACCEPT
-A LIBVIRT_INP -i virbr1 -p udp -m udp --dport 67 -j ACCEPT
-A LIBVIRT_INP -i virbr1 -p tcp -m tcp --dport 67 -j ACCEPT
-A LIBVIRT_OUT -o virbr2 -p udp -m udp --dport 53 -j ACCEPT
-A LIBVIRT_OUT -o virbr2 -p tcp -m tcp --dport 53 -j ACCEPT
-A LIBVIRT_OUT -o virbr2 -p udp -m udp --dport 68 -j ACCEPT
-A LIBVIRT_OUT -o virbr2 -p tcp -m tcp --dport 68 -j ACCEPT
-A LIBVIRT_OUT -o virbr1 -p udp -m udp --dport 53 -j ACCEPT
-A LIBVIRT_OUT -o virbr1 -p tcp -m tcp --dport 53 -j ACCEPT
-A LIBVIRT_OUT -o virbr1 -p udp -m udp --dport 68 -j ACCEPT
-A LIBVIRT_OUT -o virbr1 -p tcp -m tcp --dport 68 -j ACCEPT
*mangle
:PREROUTING ACCEPT
:INPUT ACCEPT
:FORWARD ACCEPT
:OUTPUT ACCEPT
:POSTROUTING ACCEPT
:LIBVIRT_PRT
-A POSTROUTING -j LIBVIRT_PRT
*nat
:PREROUTING ACCEPT
:INPUT ACCEPT
:OUTPUT ACCEPT
:POSTROUTING ACCEPT
:LIBVIRT_PRT
-A POSTROUTING -j LIBVIRT_PRT
-A LIBVIRT_PRT -s 10.0.2.0/24 -d 224.0.0.0/24 -o tun0 -j RETURN
-A LIBVIRT_PRT -s 10.0.2.0/24 -d 255.255.255.255/32 -o tun0 -j RETURN
-A LIBVIRT_PRT -s 10.0.2.0/24 ! -d 10.0.2.0/24 -o tun0 -p tcp -j MASQUERADE --to-ports 1024-65535
-A LIBVIRT_PRT -s 10.0.2.0/24 ! -d 10.0.2.0/24 -o tun0 -p udp -j MASQUERADE --to-ports 1024-65535
-A LIBVIRT_PRT -s 10.0.2.0/24 ! -d 10.0.2.0/24 -o tun0 -j MASQUERADEUnecessary rules/chains that need to be flushed/removed:
Code: :LIBVIRT_INP
:LIBVIRT_OUT
-A LIBVIRT_INP -i virbr2 -p udp -m udp --dport 53 -j ACCEPT
-A LIBVIRT_INP -i virbr2 -p tcp -m tcp --dport 53 -j ACCEPT
-A LIBVIRT_INP -i virbr2 -p udp -m udp --dport 67 -j ACCEPT
-A LIBVIRT_INP -i virbr2 -p tcp -m tcp --dport 67 -j ACCEPT
-A LIBVIRT_INP -i virbr1 -p udp -m udp --dport 53 -j ACCEPT
-A LIBVIRT_INP -i virbr1 -p tcp -m tcp --dport 53 -j ACCEPT
-A LIBVIRT_INP -i virbr1 -p udp -m udp --dport 67 -j ACCEPT
-A LIBVIRT_INP -i virbr1 -p tcp -m tcp --dport 67 -j ACCEPT
-A LIBVIRT_OUT -o virbr2 -p udp -m udp --dport 53 -j ACCEPT
-A LIBVIRT_OUT -o virbr2 -p tcp -m tcp --dport 53 -j ACCEPT
-A LIBVIRT_OUT -o virbr2 -p udp -m udp --dport 68 -j ACCEPT
-A LIBVIRT_OUT -o virbr2 -p tcp -m tcp --dport 68 -j ACCEPT
-A LIBVIRT_OUT -o virbr1 -p udp -m udp --dport 53 -j ACCEPT
-A LIBVIRT_OUT -o virbr1 -p tcp -m tcp --dport 53 -j ACCEPT
-A LIBVIRT_OUT -o virbr1 -p udp -m udp --dport 68 -j ACCEPT
-A LIBVIRT_OUT -o virbr1 -p tcp -m tcp --dport 68 -j ACCEPT
:LIBVIRT_FWI
-A LIBVIRT_FWI -o virbr2 -j REJECT --reject-with icmp-port-unreachable
-A LIBVIRT_FWI -d 10.0.2.0/24 -i tun0 -o virbr1 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A LIBVIRT_FWI -o virbr1 -j REJECT --reject-with icmp-port-unreachableChains LIBVIRT_FWI, LIBVIRT_INP & LIBVIRT_OUT have rules that would allow direct traffic between the host and Whonix Gateway/Workstation which is not needed. Only the LIBVIRT_FWO & FWX chains have to be allowed on *filter for Gateway/Tor to work.
Here are the iptable rules after the clean up. Only what's absolutely necessary for Whonix to work:
Code: *filter
:INPUT DROP
:FORWARD DROP
:OUTPUT DROP
:LIBVIRT_FWO
:LIBVIRT_FWX
-A FORWARD -j LIBVIRT_FWX
-A FORWARD -j LIBVIRT_FWO
-A LIBVIRT_FWO -s 10.0.2.0/24 -i virbr1 -o tun0 -j ACCEPT
-A LIBVIRT_FWX -i virbr2 -o virbr2 -j ACCEPT
-A LIBVIRT_FWX -i virbr1 -o virbr1 -j ACCEPT
*mangle
:PREROUTING ACCEPT
:INPUT ACCEPT
:FORWARD ACCEPT
:OUTPUT ACCEPT
:POSTROUTING ACCEPT
:LIBVIRT_PRT
-A POSTROUTING -j LIBVIRT_PRT
*nat
:PREROUTING ACCEPT
:INPUT ACCEPT
:OUTPUT ACCEPT
:POSTROUTING ACCEPT
:LIBVIRT_PRT
-A POSTROUTING -j LIBVIRT_PRT
-A LIBVIRT_PRT -s 10.0.2.0/24 -d 224.0.0.0/24 -o tun0 -j RETURN
-A LIBVIRT_PRT -s 10.0.2.0/24 -d 255.255.255.255/32 -o tun0 -j RETURN
-A LIBVIRT_PRT -s 10.0.2.0/24 ! -d 10.0.2.0/24 -o tun0 -p tcp -j MASQUERADE --to-ports 1024-65535
-A LIBVIRT_PRT -s 10.0.2.0/24 ! -d 10.0.2.0/24 -o tun0 -p udp -j MASQUERADE --to-ports 1024-65535
-A LIBVIRT_PRT -s 10.0.2.0/24 ! -d 10.0.2.0/24 -o tun0 -j MASQUERADEIt is my understanding that *mangle and *nat need to default ACCEPT everything, which won't affect host security, but please correct me if I'm wrong.
Step 3.) Done. The default drop policies on *filter + very tight FORWARD chain should ensure fail-closed if the VPN disconnects, without the need for additional firewalls or other solutions as recommended on [the Whonix page][1].
Tell me what you think and if I missed something please don't hesitate.
Thanks everyone!
Goal:
Making a guide for safely using Whonix on KVM with a VPN installed on the host, by forwarding all Whonix traffic through the VPN's tun0 interface and easily achieving fail-closed (all traffic cuts off if VPN is disconnected).
If everything checks out I will contribute this on Github.
Method:
Very small changes to XML files and iptables.
Foot Notes:
Whonix Gateway, Worstation & Tor are all working as expected. I just need a second set of eyes to verify if this all ok.
Fix:
Step 1.)
Whonix for KVM comes with 2 XML files named Whonix-External & Whonix-Internal, which will be the virtual networks used by libvirt.
The External XML needs to be edited to include device tun0.
Whonix-External default:
Code: <network>
<name>Whonix-External</name>
<forward mode='nat'/>
<bridge name='virbr1' stp='on' delay='0'/>
<ip address='10.0.2.2' netmask='255.255.255.0'/>
</network>Edited Whonix-External:
Code: <network>
<name>Whonix-External</name>
<forward mode='nat' dev='tun0'/>
<bridge name='virbr1' stp='on' delay='0'/>
<ip address='10.0.2.2' netmask='255.255.255.0'/>
</network>Step 2.)
Libvirt automatically adds iptable rules for virtual networks. Unecessary or unsafe rules/chains need to be removed.
Default rules added by libvirt:
Code: *filter
:INPUT DROP
:FORWARD DROP
:OUTPUT DROP
:LIBVIRT_FWI
:LIBVIRT_FWO
:LIBVIRT_FWX
:LIBVIRT_INP
:LIBVIRT_OUT
-A INPUT -j LIBVIRT_INP
-A FORWARD -j LIBVIRT_FWX
-A FORWARD -j LIBVIRT_FWI
-A FORWARD -j LIBVIRT_FWO
-A OUTPUT -j LIBVIRT_OUT
-A LIBVIRT_FWI -o virbr2 -j REJECT --reject-with icmp-port-unreachable
-A LIBVIRT_FWI -d 10.0.2.0/24 -i tun0 -o virbr1 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A LIBVIRT_FWI -o virbr1 -j REJECT --reject-with icmp-port-unreachable
-A LIBVIRT_FWO -i virbr2 -j REJECT --reject-with icmp-port-unreachable
-A LIBVIRT_FWO -s 10.0.2.0/24 -i virbr1 -o tun0 -j ACCEPT
-A LIBVIRT_FWO -i virbr1 -j REJECT --reject-with icmp-port-unreachable
-A LIBVIRT_FWX -i virbr2 -o virbr2 -j ACCEPT
-A LIBVIRT_FWX -i virbr1 -o virbr1 -j ACCEPT
-A LIBVIRT_INP -i virbr2 -p udp -m udp --dport 53 -j ACCEPT
-A LIBVIRT_INP -i virbr2 -p tcp -m tcp --dport 53 -j ACCEPT
-A LIBVIRT_INP -i virbr2 -p udp -m udp --dport 67 -j ACCEPT
-A LIBVIRT_INP -i virbr2 -p tcp -m tcp --dport 67 -j ACCEPT
-A LIBVIRT_INP -i virbr1 -p udp -m udp --dport 53 -j ACCEPT
-A LIBVIRT_INP -i virbr1 -p tcp -m tcp --dport 53 -j ACCEPT
-A LIBVIRT_INP -i virbr1 -p udp -m udp --dport 67 -j ACCEPT
-A LIBVIRT_INP -i virbr1 -p tcp -m tcp --dport 67 -j ACCEPT
-A LIBVIRT_OUT -o virbr2 -p udp -m udp --dport 53 -j ACCEPT
-A LIBVIRT_OUT -o virbr2 -p tcp -m tcp --dport 53 -j ACCEPT
-A LIBVIRT_OUT -o virbr2 -p udp -m udp --dport 68 -j ACCEPT
-A LIBVIRT_OUT -o virbr2 -p tcp -m tcp --dport 68 -j ACCEPT
-A LIBVIRT_OUT -o virbr1 -p udp -m udp --dport 53 -j ACCEPT
-A LIBVIRT_OUT -o virbr1 -p tcp -m tcp --dport 53 -j ACCEPT
-A LIBVIRT_OUT -o virbr1 -p udp -m udp --dport 68 -j ACCEPT
-A LIBVIRT_OUT -o virbr1 -p tcp -m tcp --dport 68 -j ACCEPT
*mangle
:PREROUTING ACCEPT
:INPUT ACCEPT
:FORWARD ACCEPT
:OUTPUT ACCEPT
:POSTROUTING ACCEPT
:LIBVIRT_PRT
-A POSTROUTING -j LIBVIRT_PRT
*nat
:PREROUTING ACCEPT
:INPUT ACCEPT
:OUTPUT ACCEPT
:POSTROUTING ACCEPT
:LIBVIRT_PRT
-A POSTROUTING -j LIBVIRT_PRT
-A LIBVIRT_PRT -s 10.0.2.0/24 -d 224.0.0.0/24 -o tun0 -j RETURN
-A LIBVIRT_PRT -s 10.0.2.0/24 -d 255.255.255.255/32 -o tun0 -j RETURN
-A LIBVIRT_PRT -s 10.0.2.0/24 ! -d 10.0.2.0/24 -o tun0 -p tcp -j MASQUERADE --to-ports 1024-65535
-A LIBVIRT_PRT -s 10.0.2.0/24 ! -d 10.0.2.0/24 -o tun0 -p udp -j MASQUERADE --to-ports 1024-65535
-A LIBVIRT_PRT -s 10.0.2.0/24 ! -d 10.0.2.0/24 -o tun0 -j MASQUERADEUnecessary rules/chains that need to be flushed/removed:
Code: :LIBVIRT_INP
:LIBVIRT_OUT
-A LIBVIRT_INP -i virbr2 -p udp -m udp --dport 53 -j ACCEPT
-A LIBVIRT_INP -i virbr2 -p tcp -m tcp --dport 53 -j ACCEPT
-A LIBVIRT_INP -i virbr2 -p udp -m udp --dport 67 -j ACCEPT
-A LIBVIRT_INP -i virbr2 -p tcp -m tcp --dport 67 -j ACCEPT
-A LIBVIRT_INP -i virbr1 -p udp -m udp --dport 53 -j ACCEPT
-A LIBVIRT_INP -i virbr1 -p tcp -m tcp --dport 53 -j ACCEPT
-A LIBVIRT_INP -i virbr1 -p udp -m udp --dport 67 -j ACCEPT
-A LIBVIRT_INP -i virbr1 -p tcp -m tcp --dport 67 -j ACCEPT
-A LIBVIRT_OUT -o virbr2 -p udp -m udp --dport 53 -j ACCEPT
-A LIBVIRT_OUT -o virbr2 -p tcp -m tcp --dport 53 -j ACCEPT
-A LIBVIRT_OUT -o virbr2 -p udp -m udp --dport 68 -j ACCEPT
-A LIBVIRT_OUT -o virbr2 -p tcp -m tcp --dport 68 -j ACCEPT
-A LIBVIRT_OUT -o virbr1 -p udp -m udp --dport 53 -j ACCEPT
-A LIBVIRT_OUT -o virbr1 -p tcp -m tcp --dport 53 -j ACCEPT
-A LIBVIRT_OUT -o virbr1 -p udp -m udp --dport 68 -j ACCEPT
-A LIBVIRT_OUT -o virbr1 -p tcp -m tcp --dport 68 -j ACCEPT
:LIBVIRT_FWI
-A LIBVIRT_FWI -o virbr2 -j REJECT --reject-with icmp-port-unreachable
-A LIBVIRT_FWI -d 10.0.2.0/24 -i tun0 -o virbr1 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A LIBVIRT_FWI -o virbr1 -j REJECT --reject-with icmp-port-unreachableChains LIBVIRT_FWI, LIBVIRT_INP & LIBVIRT_OUT have rules that would allow direct traffic between the host and Whonix Gateway/Workstation which is not needed. Only the LIBVIRT_FWO & FWX chains have to be allowed on *filter for Gateway/Tor to work.
Here are the iptable rules after the clean up. Only what's absolutely necessary for Whonix to work:
Code: *filter
:INPUT DROP
:FORWARD DROP
:OUTPUT DROP
:LIBVIRT_FWO
:LIBVIRT_FWX
-A FORWARD -j LIBVIRT_FWX
-A FORWARD -j LIBVIRT_FWO
-A LIBVIRT_FWO -s 10.0.2.0/24 -i virbr1 -o tun0 -j ACCEPT
-A LIBVIRT_FWX -i virbr2 -o virbr2 -j ACCEPT
-A LIBVIRT_FWX -i virbr1 -o virbr1 -j ACCEPT
*mangle
:PREROUTING ACCEPT
:INPUT ACCEPT
:FORWARD ACCEPT
:OUTPUT ACCEPT
:POSTROUTING ACCEPT
:LIBVIRT_PRT
-A POSTROUTING -j LIBVIRT_PRT
*nat
:PREROUTING ACCEPT
:INPUT ACCEPT
:OUTPUT ACCEPT
:POSTROUTING ACCEPT
:LIBVIRT_PRT
-A POSTROUTING -j LIBVIRT_PRT
-A LIBVIRT_PRT -s 10.0.2.0/24 -d 224.0.0.0/24 -o tun0 -j RETURN
-A LIBVIRT_PRT -s 10.0.2.0/24 -d 255.255.255.255/32 -o tun0 -j RETURN
-A LIBVIRT_PRT -s 10.0.2.0/24 ! -d 10.0.2.0/24 -o tun0 -p tcp -j MASQUERADE --to-ports 1024-65535
-A LIBVIRT_PRT -s 10.0.2.0/24 ! -d 10.0.2.0/24 -o tun0 -p udp -j MASQUERADE --to-ports 1024-65535
-A LIBVIRT_PRT -s 10.0.2.0/24 ! -d 10.0.2.0/24 -o tun0 -j MASQUERADEIt is my understanding that *mangle and *nat need to default ACCEPT everything, which won't affect host security, but please correct me if I'm wrong.
Step 3.) Done. The default drop policies on *filter + very tight FORWARD chain should ensure fail-closed if the VPN disconnects, without the need for additional firewalls or other solutions as recommended on [the Whonix page][1].
Tell me what you think and if I missed something please don't hesitate.
Thanks everyone!