Deployment guide

E2guardian

Overview

E2guardian is a mature, open source web content filtering proxy. It sits between your users and the internet and applies policy to every request, filtering by domain, URL, file extension, MIME type and weighted phrase content. A refused request returns a clear block page, and every transaction is written to a structured access log, so outbound browsing is both governed and auditable.

This SecureAnyCloud image builds E2guardian 5.5.9r from upstream source and runs it as the e2guardian.service systemd unit on port 8080. Ubuntu's own archive still carries the 5.3.5 branch, so building from upstream keeps the appliance on a current filtering engine.

What you get:

  • E2guardian 5.5.9r (GPL-2.0) built from a sha256-pinned upstream source tarball
  • e2guardian.service running the filtering proxy on port 8080
  • e2guardian-firewall.service, a fail-closed nftables source allowlist
  • A working, self-demonstrating filter policy, so you can prove filtering on first boot
  • A structured access log at /var/log/e2guardian/access.log
  • 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
  • Ubuntu 24.04 LTS base, patched at build time, with unattended security upgrades enabled
  • 24/7 SecureAnyCloud support with a 24h response SLA

Standalone, with no Squid to deploy. E2guardian 5.x connects directly to origin sites when no upstream proxy is configured, so this image is a complete filtering proxy on its own: one daemon, one set of access rules. If you already run Squid, you can chain to it with two configuration lines, shown later in this guide.

Never an open relay. 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, so this image 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, so if the allowlist cannot be applied, 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 act as a man in the middle to inspect HTTPS payloads, but that 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 build time or first boot. HTTPS is still filtered by the hostname in the CONNECT request, checked against your site lists.

No login and no bootstrap password. E2guardian is a headless daemon with no web console and no product account, so there is nothing to sign in to and no default credential to find. You administer it over your own key-based SSH.

Prerequisites

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

E2guardian is efficient. Standard_B2s (2 vCPU, 4 GB RAM) comfortably handles a small to medium user population. Access logs live on the OS disk, so expand the disk if you want to retain a long browsing history locally rather than forward it to a SIEM.

1

Deploy the Virtual Machine

Option A: Azure Portal

Search the Marketplace for E2guardian Filtering 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 from Internet or Any. An open proxy is found and abused within hours.

Option B: Azure CLI

Run the commands below to create the network and the VM. The rule that matters is allow-proxy-clients, which is scoped to a client CIDR and never to Internet.

RG="e2guardian-prod"; LOCATION="eastus"; VM_NAME="e2guardian-01" IMAGE_URN="sac1647283583153: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 e2g-vnet --address-prefix 10.60.0.0/16 --subnet-name e2g-subnet --subnet-prefix 10.60.1.0/24 az network nsg create -g "$RG" --name e2g-nsg az network nsg rule create -g "$RG" --nsg-name e2g-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 e2g-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: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 e2g-vnet --subnet e2g-subnet --nsg e2g-nsg --public-ip-sku Standard
2

Connect via SSH

ssh azureuser@<vm-ip>

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

3

Verify the proxy and its access control are running

Confirm that both units are active. The proxy is deliberately coupled to the allowlist, so a healthy proxy implies a healthy allowlist.

sudo systemctl start e2guardian-firewall e2guardian 2>/dev/null || true sleep 3
sudo systemctl is-active e2guardian
sudo systemctl is-active e2guardian-firewall ss -tln | grep -E ':8080 |:22 ' || true e2guardian -v | head -1 echo "e2guardian verified"

Expect both units to report active, a listening socket on 0.0.0.0:8080 for the proxy, admin SSH on 0.0.0.0:22, and the version e2guardian 5.5.9r.

4

Prove that filtering actually works

A running service does not prove that a filtering proxy filters, and neither does a status code, because a block page and a real page can both return successfully. Instead, compare the content returned for a permitted site against the content returned for a blocked one.

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 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 '<title>[^<]*</title>' 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 (<title>Example Domain</title>). The blocked request returns E2guardian's own block page (<title>E2Guardian - Access Denied</title>), and the real page content is absent entirely. That difference is the filter doing its job. Expected output:

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

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 '<title>[^<]*</title>' 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, and widen your NSG rule to match.

6

Read the access log

Review the access log. It records every request, allowed or denied, and denied entries carry the reason and the list that matched.

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

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

7

Read this VM's instance note

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

sudo cat /root/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.

8

Set your own filtering policy

Locate the block list. It is a plain text file with one domain per line, and a bare domain also covers its subdomains.

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.

9

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.

PROXY=$(sudo sed -n 's/^E2GUARDIAN_PROXY_URL=//p' /root/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"

When you run this from a client machine, replace the first line with your proxy's private address. The allowed site returns HTTP 200 and the blocked site returns the E2Guardian block page, which confirms the client goes through the filter rather than straight out to the internet.

Use the private address from the instance note. 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.

Optional: chain to an existing Squid deployment

If you already run Squid and want E2guardian to filter in front of it, add the upstream proxy directives to /etc/e2guardian/e2guardian.conf and restart. E2guardian then forwards permitted requests to Squid instead of fetching them directly.

Edit the file with sudo nano /etc/e2guardian/e2guardian.conf and add these two lines, substituting your Squid host's address:

proxyip = 10.60.1.20 proxyport = 3128

Then apply and confirm the proxy still serves traffic:

sudo systemctl restart e2guardian sleep 3 curl -s -m 20 -x http://127.0.0.1:8080 http://example.com/ | grep -o '<title>[^<]*</title>'

Do not skip that last check. If the upstream address is wrong or unreachable, systemctl still reports e2guardian as active while every request through it returns nothing. Always confirm that a real page comes back after you change proxyip. If it does not, remove the two lines and restart to return to standalone mode.

Restrict Squid's own ACLs to the same client range. Two proxies mean two access control lists, and the weaker one sets your real exposure.

Managing the service

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

Use reload after a policy edit: it re-reads the lists without dropping connections. Stopping e2guardian-firewall also stops the proxy, because the proxy requires it.

Server Components

Component Version Purpose

E2guardian 5.5.9r Web content filtering proxy

e2guardian.service systemd Runs the proxy in the foreground under supervision

e2guardian-firewall.service systemd Applies the nftables source allowlist, fail-closed

e2guardian-firstboot.service systemd Per-VM proxy facts and loop prevention address

nftables Ubuntu 24.04 Enforces the proxy source allowlist

Ubuntu 24.04 LTS Base operating system

Filesystem Layout

Path Purpose

/etc/e2guardian/e2guardian.conf Main configuration, including the SecureAnyCloud policy block

/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; no CA certificate or key is ever created

/var/log/e2guardian/access.log Structured access log

/root/e2guardian-info.txt Root-only per-VM instance note

/usr/share/doc/sac/ GPL-2 license, written offer and corresponding source

Persistence, first boot and updates

On each VM's first boot, 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 spin a request loop.
  • Writes a fresh, empty access log, so no build-time browsing history is ever present.
  • Writes the instance note and MOTD.
  • Re-asserts that no CA material exists.

It then marks itself complete and does not run again.

Ubuntu security updates are applied automatically by unattended upgrades. E2guardian itself was built from source at image build time, so new upstream releases arrive through a refreshed SecureAnyCloud image rather than through apt.

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.
  • Keep the two 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 it 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 log 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 is distributed under the GNU General Public License version 2. The complete corresponding source for the binaries in this image ships on the image at /usr/share/doc/sac/e2guardian-5.5.9r-complete-source.tar.gz, alongside the license text and a written offer valid for three years. "E2guardian" is used nominatively to identify the software; all product and company names are trademarks of their respective holders.

Deploy on Azure

Launch E2guardian 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