URL filtering HTTP(S) proxy on Qubes OS

Comment on Mastodon

Preamble

This article was first published as a community guide on Qubes OS forum. Both are kept in sync.

Introduction

This guide is meant to users who want to allow a qube to reach some websites but not all the Internet, but facing the issue that using the firewall does not work well for DNS names using often changing IPs.

⚠️ This guide is for advanced users who understand what a HTTP(s) proxy is, and how to type commands or edit files in a terminal.

The setup will create a `sys-proxy-out` qube that will define a list of allowed domains, and use qvm-connect-tcp to allow client qubes to use it as a proxy. Those qubes could have no netvm, but still reach the filtered websites.

I based it on debian 12 xfce, so it's easy to set up and will be supported long term.

Use case

Setup the template

acl localnet src 127.0.0.1/32

acl SSL_ports port 443
acl Safe_ports port 80
acl Safe_ports port 443

http_access deny !Safe_ports
http_access deny CONNECT !SSL_ports

acl permit_list dstdomain '/rw/config/domains.txt'
http_access allow localnet permit_list

http_port 3128

cache deny all
logfile_rotate 0
coredump_dir /var/spool/squid

The configuration file only allows the proxy to be used for ports 80 and 443, and disables cache (which would only apply to port 80).

Close the template, you are done with it.

Setup an out proxy qube

This step could be repeated multiple times, if you want to have multiple proxies with different lists of domains.

# for a single domain
domain.example

# for all direct subdomains of qubes.org including qubes.org
# this work for doc.qubes-os.org for instance, but not foo.doc.qubes-os.org
.qubes-os.org

ℹ️ If you change the file, reload with `sudo systemctl reload squid`.

ℹ️ If you want to check squid started correctly, type `systemctl status squid`. You should read that it's active, and that there are no error in the log lines.

⚠️ If you have a line with a domain included by another line, squid will not start as it considers it an error! For instance `.qubes.org` includes `doc.qubes-os.org`.

⚠️ As far as I know, it is only possible to allow a hostname or a wildcard of this hostname, so you at least need to know the depth of the hostname. If you want to allow `anything.anylevel.domain.com`, you could use `dstdom_regex` instead of `dstdomain`, but it seems a regular source of configuration problems, and should not be useful for most users.

In dom0, using the "Qubes Policy Editor" GUI, create a new file named 50-squid (or edit the file `/etc/qubes/policy.d/50-squid.policy`) and append the configuration lines that you need to adapt from the following example:

qubes.ConnectTCP +3128 MyQube @default allow target=sys-proxy-out
qubes.ConnectTCP +3128 MyQube2 @default allow target=sys-proxy-out

This will allow qubes `MyQube` and `MyQube2` to use the proxy from `sys-proxy-out`. Adapt to your needs here.

How to use the proxy

Now the proxy is set up, and `MyQube` is allowed to use it, a few more things are required:

It's possible to define the proxy user wide, this should be picked by all running programs, using this:

mkdir -p /home/user/.config/environment.d/
cat </home/user/.config/environment.d/proxy.conf
all_proxy=http://127.0.0.1:3128/
EOF

Going further

Using a disposable qube for the proxy

The sys-proxy-out could be a disposable. In order to proceed:

Checking logs

In the proxy qube, you can check all requests done in `/var/log/squid/access.log`, you can filter with `grep TCP_DENIED` to see denied requests, this can be useful to adapt the domain list.

Test the proxy

Check allowed domains are reachable

From the http(s) client qube, you can try this command to see if the proxy is working:

curl -x http://localhost:3128 https://a_domain_you_allowed/

If the output is not `curl: (56) CONNECT tunnel failed, response 403` then it's working.

Check non-allowed domains are denied

Use the same command as above, but with a domain you did not allow

curl -x http://localhost:3128 https://a_domain_you_allowed/

The output should be `curl: (56) CONNECT tunnel failed, response 403`.

Verify nothing is getting cached

In the qube `sys-proxy-out`, inspect `/var/spool/squid/`, it should be empty. If not, please report here, this should not happen.

Some logs file exist in `/var/log/squid/`, if you don't want any hints about queried domains, configure squid accordingly. Privacy-specific tweaks are beyond the scope of this guide.