E2guardian Filtering Proxy 5.5.9r
Overview
E2guardian is an open-source web content filter. Your clients use it as their HTTP and HTTPS proxy. It checks every request against site, URL, file-type and phrase lists, fetches what is allowed and answers a refused request with a clear block page.
This guide covers launching and verifying E2guardian Filtering Proxy 5.5.9r on Ubuntu 24.04 from SecureAnyCloud on Amazon EC2. This version runs E2guardian on its own: it fetches permitted pages itself, with no separate cache in front of the internet. The listing also has an E2guardian 5.5.9r with Squid version, which adds a Squid cache behind the filter.
The image is one version of the SecureAnyCloud Hardened WireGuard VPN & VPN, Proxy and Remote Access AMIs listing on AWS Marketplace, and you choose it as the software version when you subscribe and launch. The listing's guide hub describes the other VPN, proxy and remote access servers.
E2guardian listens on tcp/8080 and answers only clients inside your VPC's address range, which it reads from the instance metadata on first boot, so it can never be used as an open proxy. Because the proxy makes outbound connections on behalf of your clients, the image also limits where it may connect: never to the instance metadata service, loopback, link-local addresses or the instance's own addresses, and only to TCP ports 80 and 443.
SecureAnyCloud compiles E2guardian 5.5.9r from the checksum-pinned upstream release, because the Ubuntu archive carries the much older 5.3 branch. The compiler is removed again before the image is captured.
What you get:
- E2guardian 5.5.9r (
e2guardian.service) on tcp/8080, running as the unprivilegede2guardianuser - tcp/8080 for your VPC only: an nftables allow-list (
e2guardian-firewall.service) written on first boot from your VPC's CIDR blocks, with every other source dropped - A destination wall: E2guardian refuses metadata, loopback, link-local and own-address destinations with its block page, and refuses
CONNECTto any port but 443. An nftables egress rule for thee2guardianuser enforces the same addresses even for host names that resolve to them, and allows only TCP 80 and 443 - No TLS interception: no CA certificate or private key exists on the image or your instance, and HTTPS is filtered by host name
- A demonstration policy: one blocked domain,
example.org(IANA's reserved documentation domain), so you can prove filtering in one command - Helpers:
e2guardian-roundtrip.sh(the self-test) ande2guardian-acl(the client allow-list and egress rules) - The firmware-refresh timer (
fwupd-refresh), masked because EC2 has no firmware that LVFS can update - The complete E2guardian source and a GPL written offer in
/usr/share/doc/sac/
Default user: ubuntu | Architecture: x86_64 | Root volume: 8 GiB gp3
Security posture
- Not an open proxy. E2guardian listens on tcp/8080 on every interface, but an nftables table drops every packet to port 8080 whose source is not on
/etc/e2guardian/allowed-clients.txt. First boot fills that file with your VPC's IPv4 (and IPv6, if any) CIDR blocks; loopback is always allowed. The listing's default security group opens SSH only, so the proxy is closed until you open tcp/8080 to your own networks. - No server-side request forgery through the proxy. A forward proxy fetches whatever its clients ask for, so this image decides where it may connect. E2guardian first checks every request against a SecureAnyCloud destination list, before any of your exception lists. IP addresses in 127.0.0.0/8, 0.0.0.0/8, 169.254.0.0/16 (including the metadata service), 168.63.129.16 and the instance's own private address, and the names localhost and instance-data, get the block page. E2guardian's lists see only addresses written into the URL, so a host name that resolves to one of those addresses would slip past them. An nftables egress rule for the e2guardian user therefore rejects every new connection to loopback, link-local and metadata addresses and to any address of the instance itself, and allows only TCP ports 80 and 443 (DNS goes to the local resolver only).
- HTTPS tunnels to port 443 only.
CONNECTto any other port is refused, so the proxy cannot carry SSH, mail or unfiltered plain HTTP inside a tunnel. - No TLS interception, no CA key. E2guardian can decrypt HTTPS if you give it a certificate authority, but this image does not.
/etc/e2guardian/privateis empty, TLS interception stays off, and HTTPS requests are allowed or refused by host name. - No SSRF to instance credentials.
e2guardian.servicealso carries anIPAddressDenyrule for the metadata addresses (169.254.169.254, fd00:ec2::254, 168.63.129.16). The AMI is registered IMDSv2 only. - Least privilege. E2guardian runs as the
e2guardiansystem user with a read-only system and a private/tmp. It can write only its log directory. - Fail-closed start. E2guardian starts only after first boot has written this instance's allow-list and a ready marker, and only while the allow-list and the egress rules are loaded. A start-up check also refuses to run it with anything in /etc/e2guardian/private, with TLS interception switched on, with the SecureAnyCloud destination list or its check removed, or with an upstream proxy (proxyip) on a loopback, link-local or metadata address.
- No default credential. E2guardian has no login on this image, and clients are allowed by source address. Administration is over SSH with your key pair (password and root logins are off). Upstream's example secrets for two optional, switched-off features (the
testsecretof the BearerBasic proxy-authentication plugin and the example CGI bypass key) are removed. The start-up check refuses to run E2guardian with either feature switched on until you set a strong secret of your own (24 or more characters). - Per-instance identity. SSH host keys and the machine ID are generated fresh on every instance.
- Known issue. E2guardian 5.5.9r has no published security advisory. An upstream fix for a crash in the optional
headerauthentication plugin (issue #897) is not yet in a release. The plugin is off on this image, so do not enable it before E2guardian 5.5.10.
Prerequisites
- An AWS account and permission to launch EC2 instances
- An EC2 key pair in the Region you launch in
- A subnet with outbound HTTP and HTTPS access to the internet (a public subnet, or a private subnet with a NAT gateway), because E2guardian fetches pages for your clients
Recommended instance type: m5.large.
Connecting to your instance
E2guardian Filtering Proxy 5.5.9r on Ubuntu 24.04 runs Ubuntu 24.04 LTS. Connect with the SSH user ubuntu.
Launch the instance
Option A: AWS Marketplace console
- Open the SecureAnyCloud Hardened WireGuard VPN & VPN, Proxy and Remote Access AMIs listing on AWS Marketplace, choose View purchase options, and then choose Subscribe. One subscription covers every version in the listing.
- When the subscription is active, choose Launch your software.
- Choose One-click launch from AWS Marketplace, or choose Launch from EC2 Console to use the full launch wizard. In Version, select E2guardian Filtering Proxy 5.5.9r on Ubuntu 24.04, and then select your Region.
- Pick an instance type (
m5.largeis recommended; the software charge is $0.04 per vCPU per hour) and your key pair. - Check the security group. The listing's default group allows SSH (22) only, so restrict SSH to your own IP address. You open the proxy port to your VPC in Step 7.
- Select the VPC whose clients will use the proxy and a subnet with outbound internet access. Leave the metadata defaults, because the image already requires IMDSv2, and then choose Launch.
Option B: AWS CLI
Run these commands on your own workstation. They create a security group that allows SSH from your management address and the proxy port from the VPC, and then launch the instance. Replace the AMI ID with the one that the Marketplace launch page shows for your Region, and replace the VPC, subnet, key pair and CIDR ranges with your own:
SG=$(aws ec2 create-security-group --group-name e2guardian-proxy \
--description "E2guardian filtering proxy" --vpc-id vpc-0123456789abcdef0 \
--query GroupId --output text)
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 22 --cidr <your-mgmt-cidr>
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 8080 --cidr <your-vpc-cidr>
aws ec2 run-instances \
--image-id ami-0123456789abcdef0 \
--instance-type m5.large \
--key-name my-key-pair \
--security-group-ids "$SG" \
--subnet-id subnet-0123456789abcdef0 \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=e2guardian-proxy}]'You do not need --metadata-options: the AMI already makes IMDSv2 required.
Connect and read the instance details
Connect as ubuntu with the key pair you chose:
ssh -i ~/.ssh/my-key-pair.pem ubuntu@<public-ip>First boot finishes within a minute of launch. It writes the client allow-list from your VPC's CIDR blocks, adds the instance's private address to E2guardian's loop protection and destination list, loads the firewall rules, starts E2guardian and checks that it answers. The details are in /root/e2guardian-info.txt, which only root can read. Nothing in it is secret, because the proxy has no password:
sudo cat /root/e2guardian-info.txt# E2guardian Filtering Proxy 5.5.9r on Ubuntu 24.04 - generated on first boot by e2guardian-firstboot.service.
# This proxy has NO login and NO password: clients are allowed by source address only (allowed-clients.txt).
# Nothing here is secret; the file is root-only because it describes this instance's network layout. PROXY_URL=http://172.31.89.75:8080 PROXY_PRIVATE_IP=172.31.89.75 PROXY_PORT=8080 ALLOWED_CLIENTS=172.31.0.0/16 (+ loopback; /etc/e2guardian/allowed-clients.txt) FILTER=e2guardian 5.5.9r (tcp/8080; /etc/e2guardian/e2guardian.conf; fetches directly, no upstream proxy) DESTINATIONS=TCP 80/443 only; never loopback, link-local, the metadata service or this instance (e2guardian-acl show) TLS_INTERCEPTION=off (no CA certificate or key exists; HTTPS is filtered by host name) SELF_TEST=sudo /usr/local/sbin/e2guardian-roundtrip.sh GUIDE=https://secureanycloud.com/stacks/e2guardian-filtering-proxy-5-5-9r/PROXY_URL is the address your clients use. Your values differ.
Confirm the proxy is running
The allow-list unit and E2guardian are active, and E2guardian owns tcp/8080:
systemctl is-active e2guardian-firewall.service e2guardian.service e2guardian -v | head -1 sudo ss -Htlnp 'sport = :8080' | awk '{print $4}'active active e2guardian 5.5.9r 0.0.0.0:8080Run the self-test
The shipped self-test checks the whole proxy from the instance itself: the services and the listener, the allow-list and egress rules, a permitted page served, the blocked domain refused over HTTP and HTTPS, CONNECT refused to ports other than 443, the metadata service refused through the proxy, a test page on the instance's own port 80 never returned through the proxy (by address, short and numeric forms, private address or localhost), no CA material, the access log, and no metadata access from inside the service:
sudo /usr/local/sbin/e2guardian-roundtrip.shok: e2guardian (user e2guardian) on tcp/8080 ok: tcp/8080 allow-list loaded: 127.0.0.0/8 172.31.0.0/16 (every other source dropped); egress wall loaded ok: permitted URL served through e2guardian (control: the blocked domain is reachable directly) ok: example.org blocked over HTTP (block page, real content withheld) and HTTPS CONNECT (control: https://example.com/ tunnels) ok: CONNECT refused to :22 (302) and :80 (302); control: :443 tunnels (200) ok: metadata service refused through the proxy (GET and IMDSv2 token PUT get the block page; the name instance-data is refused by the egress wall) ok: loopback and own-address destinations refused through the proxy (127.0.0.1, 127.1, 2130706433, 0x7f000001, 172.31.89.75, localhost; control: the probe page is served directly) ok: no TLS interception and no CA material ok: the blocked request is recorded in /var/log/e2guardian/access.log ok: metadata: 169.254.169.254 is unreachable from inside e2guardian ROUNDTRIP_OKSee filtering work
A permitted page comes back through the proxy; the demonstration domain example.org gets E2guardian's block page instead; an HTTPS page tunnels (200) while HTTPS to the blocked domain is refused (000: the tunnel is never opened):
curl -s -x http://127.0.0.1:8080 http://example.com/ | grep -o '<title>.*</title>'
curl -s -x http://127.0.0.1:8080 http://example.org/ | grep -o '<title>.*</title>'
curl -s -o /dev/null -w '%{http_code}\n' -x http://127.0.0.1:8080 https://example.com/
curl -s -o /dev/null -w '%{http_code}\n' -x http://127.0.0.1:8080 https://example.org/ || true<title>Example Domain</title> <title>E2Guardian - Access Denied</title> 200 000E2guardian records every request, allowed or denied, with the client address:
sudo grep -ac 'DENIED' /var/log/e2guardian/access.log25See the destination wall work
The proxy refuses to fetch the metadata service, loopback addresses or the instance itself for a client, whatever form the address takes, and opens HTTPS tunnels to port 443 only. CONNECT to port 22 is answered with 302 (a redirect to E2guardian's block page) instead of a tunnel, while port 443 tunnels (200):
curl -s -x http://127.0.0.1:8080 http://169.254.169.254/latest/meta-data/ | grep -o '<title>.*</title>'
curl -s -x http://127.0.0.1:8080 http://127.0.0.1:22/ | grep -o '<title>.*</title>'
curl -s -o /dev/null -w '%{http_connect}\n' -x http://127.0.0.1:8080 https://example.com:22/ || true
curl -s -o /dev/null -w '%{http_connect}\n' -x http://127.0.0.1:8080 https://example.com/<title>E2Guardian - Access Denied</title> <title>E2Guardian - Access Denied</title> 302 200The SecureAnyCloud destination list sits next to your own lists and is checked first:
sudo grep -v '^#' /etc/e2guardian/lists/example.group/sacdenysiteiplist
sudo grep -v '^#' /etc/e2guardian/lists/example.group/sacdenysitelist
sudo grep -A2 '^function(hook_checkrequest)' /etc/e2guardian/site.story0.0.0.0/8 127.0.0.0/8 169.254.0.0/16 168.63.129.16 172.31.89.75 localhost localhost.localdomain ip6-localhost ip6-loopback instance-data instance-data.ec2.internal function(hook_checkrequest) if(sitein, sacdeny) return setblock if(headerin, sacconnectport) return setblockE2guardian's lists only see an address written in the URL. A host name that resolves to an internal address is stopped by the egress rules for the e2guardian user instead, each with its own counter:
sudo nft list chain inet sac_e2g e2g_egresstable inet sac_e2g { chain e2g_egress { ip daddr { 127.0.0.53, 127.0.0.54 } meta l4proto { tcp, udp } th dport 53 accept comment "dns-stub" ip daddr @deny4 counter packets 0 bytes 0 reject with icmp port-unreachable comment "deny-internal-v4" ip6 daddr @deny6 counter packets 0 bytes 0 reject with icmpv6 port-unreachable comment "deny-internal-v6" fib daddr type local counter packets 0 bytes 0 reject comment "deny-own-address" tcp dport @dports accept comment "allowed-port" counter packets 0 bytes 0 reject comment "deny-port" } }Serve your VPC
The allow-list already contains your VPC's CIDR blocks; the security group decides whether proxy traffic reaches the instance at all. Open tcp/8080 to your VPC (or to the subnets of your clients) in the instance's security group, never to 0.0.0.0/0:
aws ec2 authorize-security-group-ingress --group-id <your-sg> --protocol tcp --port 8080 --cidr <your-vpc-cidr>Then point clients at PROXY_URL from Step 2. For a Linux client, set the standard proxy variables (most command-line tools and package managers honour them); browsers and desktops take the same address in their proxy settings, or through a PAC file or group policy:
export http_proxy=http://<instance-private-ip>:8080 https_proxy=http://<instance-private-ip>:8080
export no_proxy=169.254.169.254,localhost,127.0.0.1 curl -I https://example.com/Keep the metadata address in no_proxy: the proxy refuses to fetch it for a client, so a client's own metadata requests must go direct.
A client outside the allow-list cannot even open a connection, even if the security group lets its packets in: its packets are dropped and counted. The current list:
sudo grep -v '^#' /etc/e2guardian/allowed-clients.txt
sudo nft list chain inet sac_e2g input172.31.0.0/16 table inet sac_e2g { chain input { type filter hook input priority filter - 10; policy accept; iifname "lo" accept tcp dport 8080 ip saddr @allow4 accept tcp dport 8080 ip6 saddr @allow6 accept tcp dport 8080 counter packets 0 bytes 0 drop } }Allow more client networks or destination ports
Clients outside the VPC range (a VPN pool, a peered VPC, an on-premises network over Direct Connect) need their range on the allow-list. Add one CIDR per line and apply it; the change is atomic and needs no restart:
echo '10.8.0.0/24' | sudo tee -a /etc/e2guardian/allowed-clients.txt
sudo e2guardian-acl applye2guardian-acl refuses 0.0.0.0/0 and ::/0, which would turn the instance into an open proxy. Remember to allow the same range in the security group. If you start or reload nftables.service (which flushes every firewall table), the allow-list and egress rules are applied again automatically.
E2guardian may connect to TCP ports 80 and 443 only. To let clients fetch plain-HTTP sites on another port (for example an internal web server on 8000), add the port to /etc/e2guardian/allowed-dest-ports.txt and apply it. CONNECT tunnels stay limited to port 443 whatever this file says:
echo 8000 | sudo tee -a /etc/e2guardian/allowed-dest-ports.txt
sudo e2guardian-acl applysudo grep -v '^#' /etc/e2guardian/allowed-dest-ports.txt80 443Write your filtering policy
E2guardian's policy lives in /etc/e2guardian/: the main settings in e2guardian.conf, the filter group in e2guardianf1.conf, the decision logic in *.story files and the lists in lists/example.group/. Your own blocked domains go in localbannedsitelist, one per line (a domain also blocks its subdomains). The image ships one entry:
sudo grep -v '^#' /etc/e2guardian/lists/example.group/localbannedsitelist | grep .example.orgReplace it with your own domains and restart E2guardian:
sudo nano /etc/e2guardian/lists/example.group/localbannedsitelist
sudo systemctl restart e2guardianThe same directory holds the other upstream lists (bannedsitelist, exceptionsitelist, bannedurllist, bannedextensionlist, phrase lists and more); E2guardian's documentation at https://github.com/e2guardian/e2guardian/wiki describes each. Leave the three SecureAnyCloud* lists and the hook_checkrequest function at the end of site.story in place: the start-up check refuses to run E2guardian without them.
To chain E2guardian to an upstream proxy you already run (an existing Squid, for example), set proxyip and proxyport in e2guardian.conf to its private address and port, add that port to allowed-dest-ports.txt, apply it, and restart E2guardian. The start-up check refuses a loopback, link-local or metadata address for proxyip.
HTTPS without interception
E2guardian decides HTTPS requests by host name from the CONNECT request, so blocking a domain also blocks it over HTTPS, without decrypting anything. No certificate authority exists on the image, and TLS interception is off:
sudo ls -A /etc/e2guardian/private | wc -l grep -cE '^[[:space:]]*enablessl' /etc/e2guardian/e2guardian.conf || true0 0Page-level filtering of HTTPS (phrases, file types inside encrypted pages) needs TLS interception with your own certificate authority, installed as trusted on every client. This image deliberately does not ship that: its start-up check refuses to run E2guardian with anything in /etc/e2guardian/private or with enablessl = on. If you build that yourself, generate the authority on your own instance, never copy one between instances, and adjust the start-up check knowingly.
How the fail-closed start works
e2guardian.service runs only when first boot's ready marker exists, only together with the allow-list unit, and every start runs a check as root first. The metadata addresses are also denied to the service itself:
systemctl cat e2guardian.service | grep -E '^(ConditionPathExists|Requires|ExecStartPre|ExecStart)='
systemctl show -p IPAddressDeny e2guardian.service
sudo stat -c '%a %U:%G %n' /root/e2guardian-info.txt /etc/e2guardian/allowed-clients.txt /etc/e2guardian/privateRequires=e2guardian-firewall.service ConditionPathExists=/var/lib/sac/data/e2guardian-ready ExecStartPre=+/usr/local/sbin/e2guardian-preflight ExecStart=/usr/local/sbin/e2guardian -N -c /etc/e2guardian/e2guardian.conf IPAddressDeny=fd00:ec2::254/128 169.254.169.254/32 168.63.129.16/32 600 root:root /root/e2guardian-info.txt 644 root:root /etc/e2guardian/allowed-clients.txt 700 root:root /etc/e2guardian/privateLogs and rotation
E2guardian logs every request (time, client address, URL, action, filter group) to /var/log/e2guardian/access.log, which is rotated daily and kept for 14 days. Treat the log as sensitive: it shows which client visited which site.
cat /etc/logrotate.d/e2guardian/var/log/e2guardian/*.log { su e2guardian e2guardian daily rotate 14 compress delaycompress missingok notifempty copytruncate create 0640 e2guardian e2guardian }Security recommendations
- Open SSH only to your management addresses. Open tcp/8080 only to the networks that the proxy serves, never to
0.0.0.0/0. - Limit
/etc/e2guardian/allowed-clients.txtto the ranges that need the proxy, and limitallowed-dest-ports.txtto the ports they need. - Leave TLS interception off unless you run your own certificate authority and manage client trust deliberately.
- Do not enable the
headerauthentication plugin before E2guardian 5.5.10 (see the known issue under Security posture). - Leave unattended security upgrades on. The operating system updates from Ubuntu. E2guardian is compiled into /usr/local, and SecureAnyCloud ships updated versions as new versions of this listing.
Support and licensing
E2guardian is open-source software by Frederic Bourgeois, Philip Pearce and contributors, distributed under the GNU General Public License version 2 with an exception permitting linking with OpenSSL. As required by the GPL, the complete source code of the E2guardian binaries on this image, the exact build options and a written offer valid for three years are in /usr/share/doc/sac/. SecureAnyCloud provides the packaging, the secure defaults, the VPC allow-list, the destination wall and egress rules, the metadata block, the fail-closed start, the self-test and helpers, and 24/7 support.
SecureAnyCloud is not affiliated with or endorsed by the E2guardian project. E2guardian is used here only to identify the software.
For help with deployment or configuration, email sales@secureanycloud.com or visit SecureAnyCloud support.
Deploy on AWS
Launch E2guardian Filtering Proxy 5.5.9r on Ubuntu 24.04 on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com