Title: URL filtering HTTP(S) proxy on Qubes OS
       Author: Solène
       Date: 29 August 2025
       Tags: qubes qubesos security squid
       Description: In this article, you will learn how to configure a squid
       proxy on Qubes OS to filter outgoing http(s) queries.
       
       # Preamble
       
       This article was first published as a community guide on Qubes OS
       forum.  Both are kept in sync.
       
       => https://forum.qubes-os.org/t/url-filtering-https-proxy/35846
       
       # 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
       
       * an offline qube that need to reach a particular website
       * a web browsing qube restricted to a list of websites
       * mix multiple netvm / VPNs into a single qube
       
       # Setup the template
       
       * Install debian-12-xfce template
       * Make a clone of it, let's call it debian-12-xfce-squid
       * Start the qube and open a terminal
       * Type `sudo apt install -y squid`
       * Delete and replace `/etc/squid/squid.conf` with this content (the
       default file is not suitable at all)
       
       ```
       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.
       
       * Create a new qube, let's call it `sys-proxy-out`, based on the
       template you configured above (`debian-12-xfce-squid` in the example)
       * Configure its firewall to allow the destination `*` and port TCP 443,
       and also `*` and port TCP 80 (this covers basic needs for doing
       http/https). This is an extra safety to be sure the proxy will not use
       another port.
       * Start the qube
       * Configure the domain list in `/rw/config/domains.txt` with this
       format:
       
       ```
       # 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:
       
       * Start qube `MyQube`
       * Edit `/rw/config/rc.local` to add `qvm-connect-tcp ::3128`
       * Configure http(s) clients to use `localhost:3128` as a proxy
       
       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 <<EOF >/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:
       
       * mark sys-proxy-out as a disposable template in its settings
       * create a new disposable qube using sys-proxy-out as a template
       * adapt the dom0 rule to have the new disposable qube name in the
       target field
       
       ## 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.