Deployment guide

Squid E2guardian

Overview

This image pairs two mature open-source proxies on a single VM to give you a filtering and caching forward web proxy that works the moment it boots. E2guardian is the content-filtering engine. Squid sits behind it as the caching upstream. Every transaction is written to a structured access log, so outbound browsing is both governed and auditable.

E2guardian sits between your users and the internet and applies policy to every request. It filters by domain, URL, file extension, MIME type and weighted phrase content. When it refuses a request, it returns a clear block page instead of failing silently. Squid stores frequently requested content to cut bandwidth and latency.

This SecureAnyCloud image builds E2guardian 5.5.9r from upstream source and runs it as the e2guardian.service systemd unit, listening on port 8080. It installs Squid 6.x from the Ubuntu archive as squid.service, listening on 127.0.0.1:3128. E2guardian is configured with proxyip = 127.0.0.1 and proxyport = 3128, so it forwards every permitted request to the local Squid, which fetches from origin or serves from its cache.

E2guardian is the only client-facing listener. Clients point at port 8080. Squid is bound to the loopback interface only, so it is never reachable off the machine, and it exists solely as E2guardian's caching upstream. One appliance both governs and accelerates outbound browsing, with no second proxy for you to deploy or expose.

It is not an open relay, and that is enforced, not just documented. An exposed proxy is one of the most abused assets on the internet. E2guardian has no source-address allowlist of its own and binds all interfaces by default. This image therefore ships e2guardian-firewall.service, an nftables allowlist that accepts port 8080 only from loopback and RFC1918 client networks and drops every other source.

e2guardian.service declares Requires=e2guardian-firewall.service and Requires=squid.service. If the allowlist cannot be applied, or the caching upstream is down, the proxy does not start at all. Scope your Azure network security group to the same client range as a second layer.

No TLS interception, so no certificate authority key exists. E2guardian can run as a man in the middle to inspect HTTPS payloads, which requires a certificate authority certificate and private key. A CA key baked into a marketplace image would be shared by every customer of that image. This appliance therefore ships with interception off and no CA material at all, at build time or first boot. HTTPS is still filtered, by matching the hostname in the CONNECT request against your site lists.

No login, no bootstrap password. E2guardian is a headless daemon with no web console, and Squid is controlled by source address rather than a proxy password. There is nothing to sign in to and no default credential to find. You administer the VM over your own key-based SSH.

What you get:

  • E2guardian 5.5.9r (GPL-2.0) built from a sha256-pinned upstream source tarball
  • Squid 6.x (GPL-2.0) from the Ubuntu 24.04 archive, as the caching upstream
  • e2guardian.service running the filtering proxy on port 8080
  • squid.service running the caching upstream on 127.0.0.1:3128 (loopback only)
  • e2guardian-firewall.service, a fail-closed nftables source allowlist
  • A working, self-demonstrating filter policy, so you can prove filtering on first boot
  • Structured access logs at /var/log/e2guardian/access.log and /var/log/squid/access.log
  • squid-e2guardian-firstboot.service for per-VM proxy facts and request-loop prevention
  • The complete corresponding source and GPL-2 written offer, shipped on the image
  • An Ubuntu 24.04 LTS base, patched at build time, with unattended security upgrades enabled
  • 24/7 SecureAnyCloud support, 24h response SLA

Prerequisites

You need an active Azure subscription, an SSH key pair, and a VNet with a subnet that your client machines sit on.

Standard_B2s (2 vCPU, 4 GB RAM) comfortably handles a small to medium user population.

The Squid cache and both access logs live on the OS disk. Expand the disk if you plan to run a large cache, or to keep a long browsing history locally instead of forwarding it to a SIEM.

1

Deploy the virtual machine

Option A: Azure Portal

Search the Marketplace for Squid + E2guardian Filtering and Caching Proxy on Ubuntu 24.04, choose your VM size, and provide your SSH public key. Attach a network security group that allows:

  • TCP 22 (admin SSH) from your management network only.
  • TCP 8080 (the proxy) from your client subnet only. Never allow it from Internet or Any. An open proxy is found and abused within hours.

Do not add an NSG rule for port 3128. Squid listens on the loopback interface only and is never reached from off the machine.

Option B: Azure CLI

The rule that matters here is allow-proxy-clients. It is scoped to a client CIDR, never to Internet.

RG="squid-e2guardian-prod"; LOCATION="eastus"; VM_NAME="filterproxy-01" IMAGE_URN="sac1647283583153:squid-e2guardian-ubuntu-24-04:default:latest" SSH_KEY="$(cat ~/.ssh/id_rsa.pub)" az group create --name "$RG" --location "$LOCATION" az network vnet create -g "$RG" --name fp-vnet --address-prefix 10.60.0.0/16 --subnet-name fp-subnet --subnet-prefix 10.60.1.0/24 az network nsg create -g "$RG" --name fp-nsg az network nsg rule create -g "$RG" --nsg-name fp-nsg --name allow-admin-ssh --priority 100 \
  --source-address-prefixes "<your-mgmt-cidr>" --destination-port-ranges 22 --access Allow --protocol Tcp az network nsg rule create -g "$RG" --nsg-name fp-nsg --name allow-proxy-clients --priority 110 \
  --source-address-prefixes "10.60.1.0/24" --destination-port-ranges 8080 --access Allow --protocol Tcp
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:squid-e2guardian-ubuntu-24-04:default:latest
az vm create -g "$RG" --name "$VM_NAME" --image "$IMAGE_URN" \
  --size Standard_B2s --storage-sku StandardSSD_LRS \
  --admin-username azureuser --ssh-key-values "$SSH_KEY" \
  --vnet-name fp-vnet --subnet fp-subnet --nsg fp-nsg --public-ip-sku Standard
2

Connect over SSH

Connect over SSH as azureuser:

ssh azureuser@<vm-ip>

Find the public IP with az vm show -d -g squid-e2guardian-prod -n filterproxy-01 --query publicIps -o tsv.

3

Verify the proxy, its cache and its access control

Three units must be active. The filtering proxy is coupled to both the allowlist and the caching upstream, so a healthy e2guardian implies a healthy squid and a healthy allowlist.

sudo systemctl is-active squid e2guardian e2guardian-firewall ss -tln | grep -E ':8080 |:3128 |:22 ' || true e2guardian -v | head -1 squid -v | head -1 echo "stack verified"

The output shows all three units reporting active. It also shows e2guardian listening on 0.0.0.0:8080, Squid listening on 127.0.0.1:3128 (loopback only), your admin SSH on 0.0.0.0:22, and the two version banners.

active active active LISTEN 0 4096 0.0.0.0:22 0.0.0.0:* LISTEN 0 256 127.0.0.1:3128 0.0.0.0:* LISTEN 0 256 0.0.0.0:8080 0.0.0.0:* e2guardian 5.5.9r Squid Cache: Version 6.14
4

Prove that filtering works

This is the key step. A running service does not prove that a filtering proxy filters. A status code does not prove it either, because a block page and a real page can both return successfully. The check below compares the content returned for a permitted site against the content returned for a blocked one, both fetched through the proxy.

The image ships exactly one demo block entry: example.org, IANA's reserved documentation domain, so blocking it affects no real service. example.com is not blocked, and both domains serve the same page when unfiltered, which makes them a clean pair for the test.

printf 'ALLOWED example.com -> ' curl -s -m 20 -x http://127.0.0.1:8080 http://example.com/ | grep -o '<h1>[^<]*</h1>' printf 'BLOCKED example.org -> ' curl -s -m 20 -x http://127.0.0.1:8080 http://example.org/ | grep -o '<title>[^<]*</title>' echo "filtering verified"

The permitted request returns the real page (<h1>Example Domain</h1>). The blocked request returns E2guardian's own block page (<title>E2Guardian - Access Denied</title>), and the real page content is absent. That difference shows the filter doing its job.

ALLOWED example.com -> <h1>Example Domain</h1> BLOCKED example.org -> <title>E2Guardian - Access Denied</title>
5

See the caching upstream at work

In this image, E2guardian does not fetch from the internet itself. It forwards permitted requests to the local Squid, which fetches them or serves them from its cache. You can see both halves of that.

First, the request you just allowed appears in Squid's access log. That proves E2guardian forwarded it to the caching upstream instead of going straight to origin:

grep -m1 'example.com' /var/log/squid/access.log
1786236560.021 14 <client> TCP_MISS/200 929 GET http://example.com/ - HIER_DIRECT/<origin> text/html

Then, to see a real cache hit, fetch a cacheable object through Squid twice. TCP_HIT or TCP_MEM_HIT in the log means Squid served the repeat from cache instead of refetching it:

D=$(mktemp -d); echo "cache probe" > "$D/probe.txt" ( cd "$D" && python3 -m http.server 18080 >/dev/null 2>&1 & echo $! > /tmp/origpid ) sleep 1 curl -s -o /dev/null -x http://127.0.0.1:3128 http://127.0.0.1:18080/probe.txt curl -s -o /dev/null -x http://127.0.0.1:3128 http://127.0.0.1:18080/probe.txt sleep 1 grep 'probe.txt' /var/log/squid/access.log | awk '{print $4, $7}' | tail -2 kill "$(cat /tmp/origpid)" 2>/dev/null echo "caching verified"
TCP_HIT/200 http://127.0.0.1:18080/probe.txt TCP_MEM_HIT/200 http://127.0.0.1:18080/probe.txt

You now have filtering and caching in one appliance.

6

Confirm the appliance is not an open relay

The allowlist accepts loopback and RFC1918 sources and drops everything else. You can exercise the real rule with a foreign source address. 203.0.113.7 is from TEST-NET-3, a range reserved for documentation, so it can never collide with your own network.

The first request is the positive control. If a permitted source could not relay either, a refusal would prove nothing.

sudo nft list table inet e2guardian
H=$(hostname -I | awk '{print $1}')
sudo ip addr add 203.0.113.7/32 dev lo 2>/dev/null || true printf 'permitted source %s -> ' "$H" curl -s -m 15 --interface "$H" -x http://$H:8080 http://example.com/ | grep -o '<h1>[^<]*</h1>' printf 'unauthorised source 203.0.113.7 -> ' curl -s -m 8 --interface 203.0.113.7 -x http://$H:8080 http://example.com/ >/dev/null 2>&1 && echo "RELAYED" || echo "refused by the source allowlist"
sudo ip addr del 203.0.113.7/32 dev lo 2>/dev/null || true echo "relay refusal verified"

The permitted private address relays successfully, and the unauthorised address is dropped.

To serve clients from a range outside RFC1918, add it to the e2g_allowed set in /etc/e2guardian/sac-firewall.nft and run sudo systemctl restart e2guardian-firewall. Then widen your NSG rule to match.

7

Read the access logs

Every request, allowed or denied, is recorded. E2guardian's log carries the filtering decision, and Squid's log carries the cache decision. Denied entries carry the reason and the list that matched, which makes the appliance auditable.

sudo tail -n 3 /var/log/e2guardian/access.log echo "access log verified"

Each e2guardian line records the timestamp, client address, URL, method and status. For a denial, it also records the reason, such as *DENIED* Blocked site (local): example.org. Forward both logs to your SIEM with any standard log shipper.

8

Read this VM's instance note

The first-boot service wrote a root-only note that describes this VM's proxy endpoint, the access control in force and the TLS posture. Read it:

sudo cat /root/squid-e2guardian-info.txt echo "instance note verified"

The note is owned by root:root with mode 0600. It states that the appliance has no login and no bootstrap credential, and it records the private proxy endpoint. No public proxy endpoint is advertised, because clients should always reach the proxy over your own network.

9

Set your own filtering policy

The block list is a plain-text file with one domain per line. A bare domain also covers its subdomains. Locate the file:

E2G_LISTS=$(sed -nE 's/^[[:space:]]*\.Define[[:space:]]+LISTDIR[[:space:]]*<([^>]+)>.*/\1/p' /etc/e2guardian/e2guardianf1.conf | head -1)
echo "your block list is at $E2G_LISTS/localbannedsitelist"
sudo grep -v '^#' "$E2G_LISTS/localbannedsitelist" | grep -v '^$' echo "block list located"

Add your own domains to that file, and remove the example.org demo line when you no longer need it. Then apply the change:

sudo systemctl reload e2guardian echo "policy reloaded"

E2guardian also ships upstream's category and weighted-phrase lists in the same directory (bannedsitelist, bannedurllist, weightedphraselist and others). You can populate them with a commercial or community blocklist feed.

The localbannedsitelist used above is upstream's dedicated file for local administrator additions, so your entries stay separate from any feed you import.

10

Point a client at the proxy

From a machine on your client subnet, set the standard proxy environment variables, or configure the proxy in your browser or operating system network settings. Point them at the e2guardian port 8080, never at Squid. For example:

PROXY=$(sudo sed -n 's/^PROXY_URL=//p' /root/squid-e2guardian-info.txt)
echo "using proxy $PROXY" export http_proxy="$PROXY" export https_proxy="$PROXY" curl -s -o /dev/null -w 'allowed site -> HTTP %{http_code}\n' http://example.com/ curl -s http://example.org/ | grep -o '<title>[^<]*</title>'
echo "client proxy configuration verified"

Run these commands from a client machine, using the private address from the instance note. The allowed site returns HTTP 200 and the blocked site returns the E2Guardian block page. That confirms the client goes through the filter and its cache instead of straight out to the internet.

HTTPS requests are filtered by the hostname in the CONNECT request, so domain policy applies to HTTPS as well as HTTP, without any interception of the encrypted payload.

Tuning the cache

Squid's cache size and memory are set in /etc/squid/squid.conf (cache_dir and cache_mem). After you edit the file, validate and apply the change without dropping connections:

sudo squid -k parse
sudo systemctl reload squid echo "squid config applied"

Squid is bound to 127.0.0.1:3128 by design. Do not change it to a routable address. It is an internal component reached only by e2guardian, and exposing it would bypass the content filter entirely. The only client-facing surface is e2guardian on port 8080.

Managing the services

Check the status, reload, restart and read the logs of the services:

sudo systemctl status e2guardian squid e2guardian-firewall
sudo systemctl reload e2guardian
sudo systemctl restart e2guardian
sudo journalctl -u e2guardian -n 50 --no-pager

reload re-reads the e2guardian lists without dropping connections, which is what you want after a policy edit. Stopping e2guardian-firewall or squid also stops the filtering proxy, because it requires both.

Server components

  • E2guardian 5.5.9r: web content-filtering proxy (client-facing, port 8080)
  • Squid 6.x: caching upstream (loopback only, 127.0.0.1:3128)
  • e2guardian.service (systemd): runs the filtering proxy in the foreground under supervision
  • squid.service (systemd): runs the caching upstream, with the cache initialised on first start
  • e2guardian-firewall.service (systemd): applies the nftables source allowlist, fail-closed
  • squid-e2guardian-firstboot.service (systemd): sets per-VM proxy facts and the loop-prevention address
  • nftables (Ubuntu 24.04): enforces the proxy source allowlist
  • Ubuntu 24.04 LTS: base operating system

Filesystem layout

  • /etc/e2guardian/e2guardian.conf: main e2guardian configuration (including proxyip to Squid)
  • /etc/e2guardian/e2guardianf1.conf: filter group configuration and list directory definition
  • /etc/e2guardian/lists/example.group/: the filter group's lists, including localbannedsitelist
  • /etc/e2guardian/sac-firewall.nft: the nftables source allowlist
  • /etc/e2guardian/private/: empty by design, because no CA certificate or key is ever created
  • /etc/squid/squid.conf: Squid configuration (loopback only, secure by default)
  • /var/spool/squid/: Squid on-disk cache
  • /var/log/e2guardian/access.log: structured filtering access log
  • /var/log/squid/access.log: structured cache access log
  • /root/squid-e2guardian-info.txt: root-only per-VM instance note
  • /usr/share/doc/sac/: GPL-2 licences, written offer and corresponding source

Persistence, first boot and updates

On each VM's first boot, squid-e2guardian-firstboot.service does the following:

  • Resolves this machine's own address and records it as a checkip entry, so a client that points the proxy back at itself cannot start a request loop.
  • Writes fresh, empty access logs, so no build-time browsing history is ever present.
  • Writes the instance note and MOTD.
  • Re-asserts that no CA material exists.

The service then marks itself complete and does not run again. Squid initialises its cache directory automatically on first start.

Unattended upgrades apply Ubuntu security updates automatically. E2guardian was built from source at image build time and Squid is the archive package, so new upstream releases arrive through a refreshed SecureAnyCloud image and need no manual work.

Security recommendations

  • Never expose port 8080 to the internet. Keep the NSG rule scoped to your client subnet. The on-host allowlist is a safety net, not a substitute for the NSG.
  • Leave Squid on loopback. It is an internal component, and a routable Squid would let clients bypass the content filter entirely.
  • Keep the two client-facing layers in agreement. If you widen the e2g_allowed nftables set, widen the NSG rule to match, and no further.
  • Leave TLS interception off unless you have a specific mandate for it. Enabling it means generating and distributing a CA private key, and every client that trusts the CA is exposed if that key leaks. Hostname filtering covers most policy needs without that risk.
  • Restrict admin SSH to your management network, and keep it key-based.
  • Forward the access logs to a SIEM if browsing records are subject to retention requirements.
  • Review your block lists on a schedule, and remove the shipped example.org demo entry once your own policy is in place.

Support

24/7 SecureAnyCloud support with a 24h response SLA is included with this image. Contact sales@secureanycloud.com.

E2guardian and Squid are each distributed under the GNU General Public License version 2. The complete corresponding source for the e2guardian binaries in this image ships on the image at /usr/share/doc/sac/e2guardian-5.5.9r-complete-source.tar.gz, alongside the licence text and a written offer valid for three years. Squid is the unmodified Ubuntu archive package, and its source is available from the Ubuntu archive.

SecureAnyCloud is not affiliated with or endorsed by the E2guardian project, the Squid project or the Squid Software Foundation. "E2guardian" and "Squid" are used nominatively to identify the software. All product and company names are trademarks of their respective holders.

Deploy on Azure

Launch Squid + E2guardian Filtering Proxy on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure