E2guardian Filtering Proxy 5.5.9r on AWS User Guide
Overview
This guide covers launching and verifying E2guardian Filtering Proxy 5.5.9r on Ubuntu 24.04 from cloudimg on Amazon EC2. It is one version of the cloudimg Hardened WireGuard VPN & VPN, Proxy and Remote Access AMIs listing on AWS Marketplace; you pick it as the software version when you subscribe and launch. The other VPN, proxy and remote access servers in the listing are described on the listing's guide hub.
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 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.)
On this image 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.
E2guardian 5.5.9r is compiled by cloudimg from the checksum-pinned upstream release (the Ubuntu archive carries the much older 5.3 branch); the compiler is removed again before the image is captured.
What is on the image:
- 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; every other source is 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; 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) is masked: EC2 has no firmware that LVFS can update - The complete E2guardian source and a GPL written offer in
/usr/share/doc/cloudimg/
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 checks every request against a cloudimg destination list first, 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.16and the instance's own private address, and the nameslocalhostandinstance-data, get the block page. E2guardian's lists only see 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 thee2guardianuser 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; 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. - Fails closed. 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 cloudimg 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: 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.
- Security notes. 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 to the internet (a public subnet, or a private subnet with a NAT gateway): E2guardian fetches pages for your clients
Recommended instance type: m5.large.
Connecting to your instance
| Version | Operating system | SSH user |
|---|---|---|
| E2guardian Filtering Proxy 5.5.9r on Ubuntu 24.04 | Ubuntu 24.04 LTS | ubuntu |
Step 1: Launch the instance
Option A: AWS Marketplace console
- Open the cloudimg Hardened WireGuard VPN & VPN, Proxy and Remote Access AMIs listing on AWS Marketplace and choose View purchase options, then Subscribe. One subscription covers every version in the listing.
- Once the subscription is active, choose Launch your software.
- Choose One-click launch from AWS Marketplace (or Launch from EC2 Console for the full launch wizard). In Version, choose E2guardian Filtering Proxy 5.5.9r on Ubuntu 24.04, then choose your Region.
- Pick an instance type (
m5.largerecommended; the software charge is $0.04 per vCPU per hour) and your key pair. - Security group: the listing's default group allows SSH (22) only. Restrict SSH to your own IP address. You open the proxy port to your VPC in Step 7.
- Choose the VPC whose clients will use the proxy and a subnet with outbound internet access, leave the metadata defaults (the image already requires IMDSv2), then choose Launch.
Option B: AWS CLI
Run this on your own workstation. It creates a security group that allows SSH from your management address and the proxy port from the VPC, then launches the instance. Replace the AMI ID with the one shown for your Region on the Marketplace launch page, and 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.
Step 2: 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 to its destination list, loads the firewall rules, starts E2guardian and checks that it answers. The details are in /root/e2guardian-info.txt, readable only by root. 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://www.cloudimg.co.uk/guides/e2guardian-filtering-proxy-5-5-9r-on-ubuntu-24-04-aws/
PROXY_URL is the address your clients use. Your values differ.
Step 3: 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:8080
Step 4: Run 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.sh
ok: 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_OK
Step 5: See 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
000
E2guardian records every request, allowed or denied, with the client address:
sudo grep -ac 'DENIED' /var/log/e2guardian/access.log
25
Step 6: See 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
200
The cloudimg destination list sits next to your own lists and is checked first:
sudo grep -v '^#' /etc/e2guardian/lists/example.group/cloudimgdenysiteiplist
sudo grep -v '^#' /etc/e2guardian/lists/example.group/cloudimgdenysitelist
sudo grep -A2 '^function(hook_checkrequest)' /etc/e2guardian/site.story
0.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, cloudimgdeny) return setblock
if(headerin, cloudimgconnectport) return setblock
E2guardian'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 cloudimg_e2g e2g_egress
table inet cloudimg_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"
}
}
Step 7: 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 cloudimg_e2g input
172.31.0.0/16
table inet cloudimg_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
}
}
Step 8: 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 apply
e2guardian-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 apply
sudo grep -v '^#' /etc/e2guardian/allowed-dest-ports.txt
80
443
Step 9: Write 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.org
Replace it with your own domains and restart E2guardian:
sudo nano /etc/e2guardian/lists/example.group/localbannedsitelist
sudo systemctl restart e2guardian
The 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 cloudimg* 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.
Step 10: 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 || true
0
0
Page-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.
Step 11: 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/private
Requires=e2guardian-firewall.service
ConditionPathExists=/var/lib/cloudimg/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/private
Step 12: Logs 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
}
Step 13: Security recommendations
- Keep SSH open only to your management addresses. Open tcp/8080 only to the networks the proxy serves, never to
0.0.0.0/0. - Keep
/etc/e2guardian/allowed-clients.txtto the ranges that really need the proxy, andallowed-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 security notes). - Leave unattended security upgrades on: the operating system updates from Ubuntu. E2guardian is compiled into
/usr/local; cloudimg 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/cloudimg/. cloudimg 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.
cloudimg 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 support@cloudimg.co.uk or visit cloudimg support.