mirror of
https://github.com/QubesOS/qubes-doc.git
synced 2024-12-26 07:49:34 -05:00
435 lines
16 KiB
Markdown
435 lines
16 KiB
Markdown
---
|
|
layout: doc
|
|
title: The Qubes Firewall
|
|
permalink: /doc/firewall/
|
|
redirect_from:
|
|
- /doc/qubes-firewall/
|
|
- /en/doc/qubes-firewall/
|
|
- /doc/QubesFirewall/
|
|
- /wiki/QubesFirewall/
|
|
---
|
|
|
|
The Qubes Firewall
|
|
==================
|
|
|
|
|
|
Understanding firewalling in Qubes
|
|
----------------------------------
|
|
|
|
Every qube in Qubes is connected to the network via a FirewallVM, which is used to
|
|
enforce network-level policies. By default there is one default FirewallVM, but
|
|
the user is free to create more, if needed.
|
|
|
|
For more information, see the following:
|
|
|
|
- [https://groups.google.com/group/qubes-devel/browse\_thread/thread/9e231b0e14bf9d62](https://groups.google.com/group/qubes-devel/browse_thread/thread/9e231b0e14bf9d62)
|
|
- [https://blog.invisiblethings.org/2011/09/28/playing-with-qubes-networking-for-fun.html](https://blog.invisiblethings.org/2011/09/28/playing-with-qubes-networking-for-fun.html)
|
|
|
|
|
|
How to edit rules
|
|
-----------------
|
|
|
|
In order to edit rules for a given qube, select it in the Qubes
|
|
Manager and press the "firewall" button:
|
|
|
|
![r2b1-manager-firewall.png](/attachment/wiki/QubesFirewall/r2b1-manager-firewall.png)
|
|
|
|
*R4.0 note:* ICMP and DNS are no longer accessible in the GUI, but can be changed
|
|
via `qvm-firewall` described below. Connections to Updates Proxy are no longer made
|
|
over network so can not be allowed or blocked with firewall rules
|
|
(see [R4.0 Updates proxy](https://www.qubes-os.org/doc/software-update-vm/) for more detail.
|
|
|
|
Note that if you specify a rule by DNS name it will be resolved to IP(s)
|
|
*at the moment of applying the rules*, and not on the fly for each new
|
|
connection. This means it will not work for servers using load balancing. More
|
|
on this in the message quoted below.
|
|
|
|
Alternatively, one can use the `qvm-firewall` command from Dom0 to edit the
|
|
firewall rules by hand. The firewall rules for each VM are saved in an XML file
|
|
in that VM's directory in dom0:
|
|
|
|
/var/lib/qubes/appvms/<vm-name>/firewall.xml
|
|
|
|
Please note that there is a 3 kB limit to the size of the `iptables` script in Qubes versions before R4.0.
|
|
This equates to somewhere between 35 and 39 rules.
|
|
If this limit is exceeded, the qube will not start.
|
|
The limit was removed in R4.0.
|
|
|
|
It is possible to work around this limit by enforcing the rules on the qube itself
|
|
by putting appropriate rules in `/rw/config`.
|
|
See [Where to put firewall rules](#where-to-put-firewall-rules).
|
|
In complex cases, it might be appropriate to load a ruleset using `iptables-restore`
|
|
called from `/rw/config/rc.local`.
|
|
|
|
|
|
Reconnecting VMs after a NetVM reboot
|
|
-------------------------------------
|
|
|
|
Normally Qubes doesn't let the user stop a NetVM if there are other qubes
|
|
running which use it as their own NetVM. But in case the NetVM stops for
|
|
whatever reason (e.g. it crashes, or the user forces its shutdown via qvm-kill
|
|
via terminal in Dom0), Qubes R4.0 will often automatically repair the
|
|
connection. If it does not, then there is an easy way to restore the connection to
|
|
the NetVM by issuing:
|
|
|
|
` qvm-prefs <vm> netvm <netvm> `
|
|
|
|
Normally qubes do not connect directly to the actual NetVM which has networking
|
|
devices, but rather to the default sys-firewall first, and in most cases it would
|
|
be the NetVM that will crash, e.g. in response to S3 sleep/restore or other
|
|
issues with WiFi drivers. In that case it is only necessary to issue the above
|
|
command once, for the sys-firewall (this assumes default VM-naming used by the
|
|
default Qubes installation):
|
|
|
|
` qvm-prefs sys-firewall netvm sys-net `
|
|
|
|
|
|
Network service qubes
|
|
---------------------
|
|
|
|
Qubes does not support running any networking services (e.g. VPN, local DNS server, IPS, ...) directly in a qube that is used to run the Qubes firewall service (usually sys-firewall) for good reasons.
|
|
In particular, if one wants to ensure proper functioning of the Qubes firewall, one should not tinker with iptables or nftables rules in such qubes.
|
|
|
|
Instead, one should deploy a network infrastructure such as
|
|
~~~
|
|
sys-net <--> sys-firewall-1 <--> network service qube <--> sys-firewall-2 <--> [client qubes]
|
|
~~~
|
|
Thereby sys-firewall-1 is only needed if one has client qubes connected there as well or wants to manage the traffic of the local network service qube.
|
|
The sys-firewall-2 proxy ensures that:
|
|
1. Firewall changes done in the network service qube cannot render the Qubes firewall ineffective.
|
|
2. Changes to the Qubes firewall by the Qubes maintainers cannot lead to unwanted information leakage in combination with user rules deployed in the network service qube.
|
|
3. A compromise of the network service qube does not compromise the Qubes firewall.
|
|
|
|
For the VPN service please also look at the [VPN documentation](/doc/vpn).
|
|
|
|
|
|
Enabling networking between two qubes
|
|
-------------------------------------
|
|
|
|
Normally any networking traffic between qubes is prohibited for security reasons.
|
|
However, in special situations, one might want to selectively allow specific qubes
|
|
to establish networking connectivity between each other. For example,
|
|
this might be useful in some development work, when one wants to test
|
|
networking code, or to allow file exchange between HVM domains (which do not
|
|
have Qubes tools installed) via SMB/scp/NFS protocols.
|
|
|
|
In order to allow networking between qubes A and B follow these steps:
|
|
|
|
* Make sure both A and B are connected to the same firewall vm (by default all
|
|
VMs use the same firewall VM).
|
|
* Note the Qubes IP addresses assigned to both qubes. This can be done using the
|
|
`qvm-ls -n` command, or via the Qubes Manager preferences pane for each qube.
|
|
* Start both qubes, and also open a terminal in the firewall VM
|
|
* In the firewall VM's terminal enter the following iptables rule:
|
|
|
|
~~~
|
|
sudo iptables -I FORWARD 2 -s <IP address of A> -d <IP address of B> -j ACCEPT
|
|
~~~
|
|
|
|
* In qube B's terminal enter the following iptables rule:
|
|
|
|
~~~
|
|
sudo iptables -I INPUT -s <IP address of A> -j ACCEPT
|
|
~~~
|
|
|
|
* Now you should be able to reach B from A -- test it using e.g. ping
|
|
issued from A. Note however, that this doesn't allow you to reach A from
|
|
B -- for this you would need two more rules, with A and B swapped.
|
|
* If everything works as expected, then the above iptables rules should be
|
|
written into firewallVM's `qubes-firewall-user-script` script which is run
|
|
on every firewall update, and A and B's `rc.local` script which is run when
|
|
the qube is launched. The `qubes-firewall-user-script` is necessary because Qubes
|
|
orders every firewallVM to update all the rules whenever a new connected qube is
|
|
started. If we didn't enter our rules into this "hook" script, then shortly
|
|
our custom rules would disappear and inter-VM networking would stop working.
|
|
Here's an example how to update the script (note that, by default, there is no
|
|
script file present, so we will probably be creating it, unless we had some other
|
|
custom rules defined earlier in this firewallVM):
|
|
|
|
~~~
|
|
[user@sys-firewall ~]$ sudo bash
|
|
[root@sys-firewall user]# echo "iptables -I FORWARD 2 -s 10.137.2.25 -d 10.137.2.6 -j ACCEPT" >> /rw/config/qubes-firewall-user-script
|
|
[root@sys-firewall user]# chmod +x /rw/config/qubes-firewall-user-script
|
|
~~~
|
|
|
|
* Here is an example how to update `rc.local`:
|
|
|
|
~~~
|
|
[user@B ~]$ sudo bash
|
|
[root@B user]# echo "iptables -I INPUT -s 10.137.2.25 -j ACCEPT" >> /rw/config/rc.local
|
|
[root@B user]# chmod +x /rw/config/rc.local
|
|
~~~
|
|
|
|
|
|
Port forwarding to a qube from the outside world
|
|
------------------------------------------------
|
|
|
|
In order to allow a service present in a qube to be exposed to the outside world
|
|
in the default setup (where the qube has sys-firewall as network VM, which in
|
|
turn has sys-net as network VM)
|
|
the following needs to be done:
|
|
|
|
* In the sys-net VM:
|
|
* Route packets from the outside world to the sys-firewall VM
|
|
* Allow packets through the sys-net VM firewall
|
|
* In the sys-firewall VM:
|
|
* Route packets from the sys-net VM to the VM
|
|
* Allow packets through the sys-firewall VM firewall
|
|
* In the qube:
|
|
* Allow packets through the qube firewall to reach the service
|
|
|
|
As an example we can take the use case of a web server listening on port 443
|
|
that we want to expose on our physical interface eth0, but only to our local
|
|
network 192.168.x.0/24.
|
|
|
|
> Note: To have all interfaces available and configured, make sure the 3 qubes are up and running
|
|
|
|
> Note: [Issue #4028](https://github.com/QubesOS/qubes-issues/issues/4028) discusses adding a command to automate exposing the port.
|
|
|
|
**1. Route packets from the outside world to the FirewallVM**
|
|
|
|
From a Terminal window in sys-net VM, take note of the 'Interface name' and
|
|
'IP address' on which you want to expose your service (i.e. ens5, 192.168.x.x)
|
|
|
|
` ifconfig | grep -i cast `
|
|
|
|
> Note: The vifx.0 interface is the one connected to your sys-firewall VM so it
|
|
is _not_ an outside world interface...
|
|
|
|
From a Terminal window in sys-firewall VM, take note of the 'IP address' for
|
|
interface Eth0 (10.137.1.x or 10.137.0.x in Qubes R4)
|
|
|
|
` ifconfig | grep -i cast `
|
|
|
|
Back into the sys-net VM's Terminal, code a natting firewall rule to route
|
|
traffic on the outside interface for the service to the sys-firewall VM
|
|
|
|
` iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 443 -d 192.168.x.x -j DNAT --to-destination 10.137.1.x `
|
|
|
|
Code the appropriate new filtering firewall rule to allow new connections for
|
|
the service
|
|
|
|
` iptables -I FORWARD 2 -i eth0 -d 10.137.1.x -p tcp --dport 443 -m conntrack --ctstate NEW -j ACCEPT `
|
|
|
|
> Note: If you want to expose the service on multiple interfaces, repeat the
|
|
steps described in part 1 for each interface
|
|
|
|
> Note: In Qubes R4, at the moment ([QubesOS/qubes-issues#3644](https://github.com/QubesOS/qubes-issues/issues/3644)), nftables is also used which imply that additional rules need to be set in a `qubes-firewall` nft table with a forward chain.
|
|
|
|
`nft add rule ip qubes-firewall forward meta iifname eth0 ip daddr 10.137.0.x tcp dport 443 ct state new counter accept`
|
|
|
|
Verify you are cutting through the sys-net VM firewall by looking at its
|
|
counters (column 2)
|
|
|
|
` iptables -t nat -L -v -n `
|
|
|
|
` iptables -L -v -n `
|
|
|
|
> Note: On Qubes R4, you can also check the nft counters
|
|
|
|
`nft list table ip qubes-firewall`
|
|
|
|
Send a test packet by trying to connect to the service from an external device
|
|
|
|
` telnet 192.168.x.x 443 `
|
|
|
|
Once you have confirmed that the counters increase, store these command in
|
|
`/rw/config/rc.local` so they get set on sys-net start-up
|
|
|
|
` sudo nano /rw/config/rc.local `
|
|
|
|
~~~
|
|
#!/bin/sh
|
|
|
|
|
|
####################
|
|
# My service routing
|
|
|
|
# Create a new firewall natting chain for my service
|
|
if iptables -t nat -N MY-HTTPS; then
|
|
|
|
# Add a natting rule if it did not exit (to avoid cluter if script executed multiple times)
|
|
iptables -t nat -A MY-HTTPS -j DNAT --to-destination 10.137.1.x
|
|
|
|
fi
|
|
|
|
|
|
# If no prerouting rule exist for my service
|
|
if ! iptables -t nat -n -L PREROUTING | grep --quiet MY-HTTPS; then
|
|
|
|
# add a natting rule for the traffic (same reason)
|
|
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 443 -d 192.168.0.x -j MY-HTTPS
|
|
fi
|
|
|
|
|
|
######################
|
|
# My service filtering
|
|
|
|
# Create a new firewall filtering chain for my service
|
|
if iptables -N MY-HTTPS; then
|
|
|
|
# Add a filtering rule if it did not exit (to avoid cluter if script executed multiple times)
|
|
iptables -A MY-HTTPS -s 192.168.x.0/24 -j ACCEPT
|
|
|
|
fi
|
|
|
|
# If no forward rule exist for my service
|
|
if ! iptables -n -L FORWARD | grep --quiet MY-HTTPS; then
|
|
|
|
# add a forward rule for the traffic (same reason)
|
|
iptables -I FORWARD 2 -d 10.137.1.x -p tcp --dport 443 -m conntrack --ctstate NEW -j MY-HTTPS
|
|
|
|
fi
|
|
~~~
|
|
|
|
> Note: Again in R4 the following needs to be added:
|
|
|
|
~~~
|
|
#############
|
|
# In Qubes R4
|
|
|
|
# If not already present
|
|
if nft -nn list table ip qubes-firewall | grep "tcp dport 443 ct state new"; then
|
|
|
|
# Add a filtering rule
|
|
nft add rule ip qubes-firewall forward meta iifname eth0 ip daddr 10.137.0.x tcp dport 443 ct state new counter accept
|
|
|
|
fi
|
|
~~~
|
|
|
|
Finally make this file executable, so it runs at each boot
|
|
|
|
` sudo chmod +x /rw/config/rc.local `
|
|
|
|
**2. Route packets from the FirewallVM to the VM**
|
|
|
|
From a Terminal window in the VM where the service to be exposed is running, take note of the 'IP address' for
|
|
interface Eth0 (i.e. 10.137.2.y, 10.137.0.y in Qubes R4)
|
|
|
|
` ifconfig | grep -i cast `
|
|
|
|
Back into the sys-firewall VM's Terminal, code a natting firewall rule to route
|
|
traffic on its outside interface for the service to the qube
|
|
|
|
` iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 443 -d 10.137.1.x -j DNAT --to-destination 10.137.2.y `
|
|
|
|
Code the appropriate new filtering firewall rule to allow new connections for
|
|
the service
|
|
|
|
` iptables -I FORWARD 2 -i eth0 -s 192.168.x.0/24 -d 10.137.2.y -p tcp --dport 443 -m conntrack --ctstate NEW -j ACCEPT `
|
|
|
|
> Note: If you do not wish to limit the IP addresses connecting to the service,
|
|
remove the ` -s 192.168.0.1/24 `
|
|
|
|
> Note: On Qubes R4
|
|
|
|
`nft add rule ip qubes-firewall forward meta iifname eth0 ip saddr 192.168.x.0/24 ip daddr 10.137.0.y tcp dport 443 ct state new counter accept`
|
|
|
|
Once you have confirmed that the counters increase, store these command in `/rw/config/qubes-firewall-user-script`
|
|
|
|
` sudo nano /rw/config/qubes-firewall-user-script `
|
|
|
|
~~~
|
|
#!/bin/sh
|
|
|
|
|
|
####################
|
|
# My service routing
|
|
|
|
# Create a new firewall natting chain for my service
|
|
if iptables -t nat -N MY-HTTPS; then
|
|
|
|
# Add a natting rule if it did not exit (to avoid cluter if script executed multiple times)
|
|
iptables -t nat -A MY-HTTPS -j DNAT --to-destination 10.137.2.y
|
|
|
|
fi
|
|
|
|
|
|
# If no prerouting rule exist for my service
|
|
if ! iptables -t nat -n -L PREROUTING | grep --quiet MY-HTTPS; then
|
|
|
|
# add a natting rule for the traffic (same reason)
|
|
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 443 -d 10.137.1.x -j MY-HTTPS
|
|
fi
|
|
|
|
|
|
######################
|
|
# My service filtering
|
|
|
|
# Create a new firewall filtering chain for my service
|
|
if iptables -N MY-HTTPS; then
|
|
|
|
# Add a filtering rule if it did not exit (to avoid cluter if script executed multiple times)
|
|
iptables -A MY-HTTPS -s 192.168.x.0/24 -j ACCEPT
|
|
|
|
fi
|
|
|
|
# If no forward rule exist for my service
|
|
if ! iptables -n -L FORWARD | grep --quiet MY-HTTPS; then
|
|
|
|
# add a forward rule for the traffic (same reason)
|
|
iptables -I FORWARD 4 -d 10.137.2.y -p tcp --dport 443 -m conntrack --ctstate NEW -j MY-HTTPS
|
|
|
|
fi
|
|
|
|
################
|
|
# In Qubes OS R4
|
|
|
|
# If not already present
|
|
if ! nft -nn list table ip qubes-firewall | grep "tcp dport 443 ct state new"; then
|
|
|
|
# Add a filtering rule
|
|
nft add rule ip qubes-firewall forward meta iifname eth0 ip saddr 192.168.x.0/24 ip daddr 10.137.0.y tcp dport 443 ct state new counter accept
|
|
|
|
fi
|
|
~~~
|
|
|
|
Finally make this file executable (so it runs at every Firewall VM update)
|
|
|
|
~~~
|
|
sudo chmod +x /rw/config/qubes-firewall-user-script
|
|
~~~
|
|
|
|
**3. Allow packets into the qube to reach the service**
|
|
|
|
Here no routing is required, only filtering. Proceed in the same way as above
|
|
but store the filtering rule in the `/rw/config/rc.local` script.
|
|
|
|
` sudo name /rw/config/rc.local `
|
|
|
|
~~~
|
|
######################
|
|
# My service filtering
|
|
|
|
# Create a new firewall filtering chain for my service
|
|
if iptables -N MY-HTTPS; then
|
|
|
|
# Add a filtering rule if it did not exit (to avoid cluter if script executed multiple times)
|
|
iptables -A MY-HTTPS -j ACCEPT
|
|
|
|
fi
|
|
|
|
# If no forward rule exist for my service
|
|
if ! iptables -n -L FORWARD | grep --quiet MY-HTTPS; then
|
|
|
|
# add a forward rule for the traffic (same reason)
|
|
iptables -I INPUT 5 -d 10.137.2.x -p tcp --dport 443 -m conntrack --ctstate NEW -j MY-HTTPS
|
|
|
|
fi
|
|
~~~
|
|
|
|
This time testing should allow connectivity to the service as long as the
|
|
service is up :-)
|
|
|
|
|
|
Where to put firewall rules
|
|
---------------------------
|
|
|
|
Implicit in the above example [scripts](/doc/config-files/), but worth
|
|
calling attention to: for all qubes *except* AppVMs supplying networking,
|
|
iptables commands should be added to the `/rw/config/rc.local` script. For
|
|
AppVMs supplying networking (`sys-firewall` inclusive),
|
|
iptables commands should be added to
|
|
`/rw/config/qubes-firewall-user-script`.
|
|
|