AWS lab — from scratch to live traffic
A complete, click-by-click lab guide. It takes you from nothing in AWS to a working Enforza firewall that is enrolled in the Cloud Controller (CCX) console, enforcing a policy, and showing you live traffic.
Every step has an [Explanation] of what you’re doing and why, written for semi-technical people — you don’t need to be a network engineer.
What you’ll build
Section titled “What you’ll build”An Internet Gateway connects the VPC to the internet; the public subnet
holds the Enforza firewall EC2 (source/dest check off, public IP), which enrols
with the Enforza Cloud Controller; the private subnet holds a workload
EC2 with no public IP, whose route table sends 0.0.0.0/0 to the firewall’s
ENI — so all its traffic is forced through the firewall for inspection.
Editable topology diagram (open in draw.io / diagrams.net):
aws-lab-topology.drawio.
Before you start
- An AWS account and permission to create VPC/EC2 resources.
- An Enforza console login (CCX) with at least 1 free engine licence.
- An SSH key pair (you’ll create or reuse one at launch).
- ~20 minutes. Cost: a
t3.smallis a few pence/hour — terminate it when done. - Region: this guide uses eu-west-2 (London); any region works, just stay consistent.
Part 1 — Build the network foundation
Section titled “Part 1 — Build the network foundation”Step 1 — Create a VPC
Section titled “Step 1 — Create a VPC”Console: VPC → Your VPCs → Create VPC → choose VPC only →
- Name:
enforza-lab - IPv4 CIDR:
10.10.0.0/16 - Leave IPv6 off, tenancy default → Create VPC.
[Explanation] A VPC is your own private, isolated network inside AWS — think of it as the “building” all your lab machines live in. The CIDR
10.10.0.0/16is the pool of private IP addresses available inside it (65k addresses). Nothing here is reachable from the internet yet; we add that next.
Step 2 — Create a public subnet
Section titled “Step 2 — Create a public subnet”Console: VPC → Subnets → Create subnet →
- VPC:
enforza-lab - Name:
enforza-lab-public - Availability Zone: pick one, e.g.
eu-west-2a - IPv4 CIDR:
10.10.1.0/24→ Create subnet.
[Explanation] A subnet is a slice of the VPC’s addresses pinned to one Availability Zone (one physical data-centre area). We’ll put the firewall here. It’s called “public” only because in Step 4 we give it a route to the internet — a subnet isn’t public until its route table says so.
Step 3 — Create and attach an Internet Gateway
Section titled “Step 3 — Create and attach an Internet Gateway”Console: VPC → Internet gateways → Create internet gateway → name
enforza-lab-igw → Create. Then Actions → Attach to VPC → select
enforza-lab → Attach.
[Explanation] An Internet Gateway (IGW) is the door between your VPC and the public internet. Creating it isn’t enough — you must attach it to the VPC, and then (next step) tell the subnet’s route table to use it. The firewall needs internet access so it can reach the Enforza control plane to enrol.
Step 4 — Route the subnet to the internet
Section titled “Step 4 — Route the subnet to the internet”Console: VPC → Route tables → Create route table →
- Name:
enforza-lab-public-rt, VPC:enforza-lab→ Create.
Then on that route table:
- Routes tab → Edit routes → Add route:
- Destination
0.0.0.0/0, Target Internet Gateway →enforza-lab-igw→ Save changes.
- Destination
- Subnet associations tab → Edit subnet associations → tick
enforza-lab-public→ Save.
[Explanation] A route table is the signpost that tells traffic where to go. The
0.0.0.0/0route means “anything not local to the VPC → send to the internet gateway”. Associating it with the public subnet is what actually makes that subnet “public”. Without this, your firewall would launch but never be able to phone home to Enforza.
Step 5 — Create a wide-open security group (lab only)
Section titled “Step 5 — Create a wide-open security group (lab only)”Console: VPC (or EC2) → Security groups → Create security group →
- Name:
enforza-lab-any, VPC:enforza-lab - Inbound rules → Add rule: Type
All traffic, SourceAnywhere-IPv4(0.0.0.0/0) - Outbound rules: leave the default
All traffic → 0.0.0.0/0 - Create security group.
[Explanation] A security group (AWS’s equivalent of an NSG) is a stateful firewall around the instance’s network card. We open it any/any so it never gets in the way while you’re learning — that way, the only thing filtering traffic is Enforza itself, which is the whole point of the lab. Never do this in production — there you’d lock inbound down to just your admin IP. Note that even wide-open, AWS security groups only allow; the Enforza engine is what gives you deny and hostname rules.
Part 2 — Launch the firewall EC2 instance
Section titled “Part 2 — Launch the firewall EC2 instance”Step 6 — Launch the instance
Section titled “Step 6 — Launch the instance”Console: EC2 → Instances → Launch instances →
- Name:
enforza-fw-lab - AMI: Ubuntu Server 22.04 LTS (what Enforza tests against; Debian/RHEL/Rocky/ Amazon Linux 2023 also work)
- Instance type:
t3.small(fine for a lab — 2 vCPU / 2 GiB) - Key pair: select an existing one, or Create new key pair (download the
.pem— you’ll need it to SSH) - Network settings → Edit:
- VPC:
enforza-lab - Subnet:
enforza-lab-public - Auto-assign public IP: Enable
- Firewall (security groups): Select existing →
enforza-lab-any
- VPC:
- Launch instance. Note the Instance ID and, once running, its Public IPv4 address.
[Explanation] This EC2 instance is the Enforza firewall — the engine is a single Linux daemon we’ll install on it in Part 3. Auto-assign public IP is essential: it’s how the box reaches the internet (via the IGW + route table you just built) to enrol and stream logs. The key pair is your SSH login. We picked a small size because a lab pushes tiny traffic; the engine auto-tunes to bigger instances when you need throughput.
Step 7 — Turn off the source/destination check ⚠️ critical
Section titled “Step 7 — Turn off the source/destination check ⚠️ critical”Console: EC2 → Instances → select enforza-fw-lab → Actions →
Networking → Change source/destination check → tick Stop (uncheck the
enabled box) → Save.
(CLI equivalent, region eu-west-2:)
aws ec2 modify-network-interface-attribute --region eu-west-2 \ --network-interface-id <eni-of-the-instance> --no-source-dest-check[Explanation] By default AWS drops any packet whose destination isn’t the instance’s own IP address. But a firewall’s entire job is to handle packets meant for other machines. So this AWS safety check must be off, or the firewall will silently blackhole every forwarded packet. This is the single most common “it’s installed but nothing works” mistake. In a lab where the box only filters its own traffic you can get away without it, but turn it off now so Part 7 (routing a workload through it) just works.
Part 3 — Enrol the engine with the console
Section titled “Part 3 — Enrol the engine with the console”Step 8 — Get a deployment key
Section titled “Step 8 — Get a deployment key”Console (CCX): sidebar → Onboard Firewall → Deployment Keys tab → Generate key. Choose:
- Single-use — binds exactly one firewall (use this for the lab), or
- Provisioning — reusable across a fleet (Terraform/Ansible/CI).
Copy the install command it shows you — it looks like:
curl -fsSL https://dl.neon.efz.io/install.sh \ | sudo bash -s -- --regkey=YOUR-KEY-HERE[Explanation] The deployment key is a one-time token that lets this firewall prove to the Enforza cloud “I’m allowed to join your account.” The install command downloads the engine installer and passes the key to it. A single-use key is consumed by the first engine that uses it — so mint one key per firewall. (There’s also a Cloud Claim tab for AWS Marketplace instances that self-register — ignore it for a manual lab install.)
Step 9 — SSH in and run the install command
Section titled “Step 9 — SSH in and run the install command”From your laptop (use the .pem from Step 6 and the instance’s public IP):
chmod 400 enforza-lab.pem # first time onlyssh -i enforza-lab.pem ubuntu@<PUBLIC-IP>Then paste the copied install command and press Enter:
curl -fsSL https://dl.neon.efz.io/install.sh \ | sudo bash -s -- --regkey=YOUR-KEY-HERE[Explanation] SSH is a secure remote terminal into the box. The installer (~30–60 s) detects your Linux distro, installs prerequisites, fetches the engine binary, swaps your one-time key for a long-lived licence token, and starts the
enforza-engineservice. The username isubuntufor the Ubuntu AMI (adminfor Debian,ec2-userfor Amazon Linux/RHEL).
Step 10 — Verify it’s online
Section titled “Step 10 — Verify it’s online”On the VM:
systemctl status enforza-engine # should say: active (running)In the console: sidebar → Firewalls — your new engine appears with status Online within ~10 seconds of the install finishing.
[Explanation] “Online” means the engine has enrolled and opened its always-on WebSocket back to the Enforza cloud. That control channel is how the cloud pushes policy and how the engine streams logs — and it bypasses policy by design, so you can never accidentally lock the engine away from its own management plane. If it never shows up, the box has no internet path — re-check the IGW, route table, and public IP (Part 1 + Step 6).
Part 4 — Build a basic policy
Section titled “Part 4 — Build a basic policy”Enforza policies have three sections, which map exactly onto the classic firewall chains you may know as input / forward / output:
| Enforza section | Classic name | What it controls |
|---|---|---|
| to-firewall | INPUT | Traffic addressed to the firewall box itself (e.g. SSH to it) |
| through-firewall | FORWARD | Traffic passing through the firewall between other hosts — the main event |
| from-firewall | OUTPUT | Traffic the firewall box originates itself (DNS, NTP, updates) |
[Explanation] Every policy is built in the console — no YAML editing. Each section has its own default action; we’ll set them to drop (fail-closed: if nothing explicitly allows a packet, it’s denied). Nothing reaches the firewall until you Push — saving a draft is safe.
Step 11 — Create the policy shell
Section titled “Step 11 — Create the policy shell”Console: sidebar → Policies → New policy →
- Name:
lab-policy - Default action: drop → Create.
[Explanation] This creates an empty policy with a deny-by-default posture. From here we add just enough “allow” rules to keep things working, then one “deny” rule to prove filtering works.
Step 12 — from-firewall (OUTPUT): let the engine’s OS reach out
Section titled “Step 12 — from-firewall (OUTPUT): let the engine’s OS reach out”Open the Local tab (from-firewall) → Add rule for each:
| Comment | Destination | Protocol | Port | Action |
|---|---|---|---|---|
dns | any | udp/tcp | 53 | Accept |
ntp | any | udp | 123 | Accept |
https | any | tcp | 443 | Accept |
[Explanation] These let the firewall’s own operating system do housekeeping — resolve names (DNS), keep its clock right (NTP), and fetch updates (HTTPS). Everything the engine needs to reach the Enforza cloud already bypasses policy, so the box won’t brick without these — but OS services start failing, so it’s good hygiene to allow them.
Step 13 — to-firewall (INPUT): keep your SSH, drop the rest
Section titled “Step 13 — to-firewall (INPUT): keep your SSH, drop the rest”Open the Management tab (to-firewall) → Add rule:
| Comment | Source | Protocol | Port | Action |
|---|---|---|---|---|
allow my ssh | <your-laptop-public-IP>/32 | tcp | 22 | Accept |
Leave the section default action = drop.
[Explanation] This section guards the firewall box itself. We explicitly allow SSH only from your IP so you keep your terminal, and drop everything else aimed at the box. ⚠️ If you skip the SSH allow and set default drop, your next SSH attempt is blocked (the engine’s own cloud control channel keeps working — only your SSH is affected). Find your public IP at
https://ifconfig.me. Note: hostname/URL rules are not allowed in this section — inbound-to-the-box traffic is matched on IP/port only.
Step 14 — through-firewall (FORWARD): the actual traffic inspection
Section titled “Step 14 — through-firewall (FORWARD): the actual traffic inspection”Open the Network tab (through-firewall) → add two rules:
Rule 1 — allow web egress (start permissive):
| Comment | Source | Destination | Protocol | Port | Action |
|---|---|---|---|---|---|
web egress | any | any | tcp | 443 | Accept |
Rule 2 — block SSH crossing the firewall, and log it:
| Comment | Source | Destination | Protocol | Port | Action | Flags |
|---|---|---|---|---|---|---|
block fwd ssh | any | any | tcp | 22 | Drop | tick Log |
Then Save.
[Explanation] This is the section that inspects traffic passing through the firewall — the real product. We allow HTTPS so normal web traffic flows, and we drop + log SSH so you have something visibly blocked to watch in the live logs later. Rules are first-match-wins within a section — drag the row handle to reorder. Ticking Log is what makes a matched flow show up in Live Traffic. Want hostname filtering (like
github.comsubdomains) instead of IP/port? Switch the rule type to URL Filtering and match on the SNI hostname.
Step 15 — Push the policy live
Section titled “Step 15 — Push the policy live”From the policy editor toolbar → Push. The publish dialog runs preflight checks (schema + any guardrails), then publishes.
[Explanation] Push is the moment your draft becomes a real, versioned policy in the cloud — but it’s still not enforcing on any firewall yet. Publishing and binding (next) are two separate steps on purpose: you can prepare a policy safely without touching a live box. Tip: Download policy first if you want to eyeball the generated YAML.
Part 5 — Bind the policy to your firewall
Section titled “Part 5 — Bind the policy to your firewall”Step 16 — Apply the policy
Section titled “Step 16 — Apply the policy”Console: sidebar → Bindings → find your enforza-fw-lab row (it shows
Unbound) → Apply policy → pick lab-policy → Confirm.
[Explanation] Publishing made the policy available; the binding is what actually wires this firewall to this policy. A brand-new engine is Unbound — it enforces nothing and drops nothing until you bind. The engine notices the change on its next config poll (within ~5 seconds) and applies it.
Step 17 — Confirm it applied
Section titled “Step 17 — Confirm it applied”Console: sidebar → Firewalls. Your firewall row now shows the policy name and a green Synced chip with the active policy’s SHA.
(Optional, on the VM:)
sudo nft list ruleset | head -40 # your rules appear in the enforza-fwd table[Explanation] The green Synced chip means the engine successfully downloaded, compiled, and loaded your policy into the Linux kernel. If instead you see a red Parse / Compile / Apply error chip, it carries the engine’s own error message — fix the rule and Push again. The
nftcommand is bare-metal proof the rules are live in the kernel.
Part 6 — View the traffic
Section titled “Part 6 — View the traffic”Step 18 — Watch live logs
Section titled “Step 18 — Watch live logs”Console: Firewalls → click your firewall → Live logs tab. (Or sidebar → Live Traffic to stream one or several firewalls together.)
Generate some traffic to see it: SSH into the box and run
curl -sI https://example.com # should succeed → accept on TCP/443[Explanation] Every accept / drop / flow-close on the engine streams to the console over the WebSocket only while a viewer is open (to keep costs bounded; each session caps at 15 minutes — just reload to continue). New events appear at the top. Pause to inspect, Resume to catch up, Clear to wipe the on-screen buffer.
Step 19 — Read a row
Section titled “Step 19 — Read a row”Columns: Time · Flow · Action · Proto · Source · Destination · SNAT · Hostname · Rule. Click any row for the full JSON detail (Event / Network / GeoIP / Packet / TLS / Flow / Engine).
- Action — accept (green) / drop (red)
- Proto — e.g.
TCP/443 - Hostname — the TLS SNI / HTTP Host the engine extracted (URL rules only)
- Rule — the comment of the rule that matched (that’s why good comments matter)
- Filter box — matches across IP, port, hostname, rule name, country, ASN.
[Explanation] This closes the whole loop: you can literally watch your
web egressrule turn a request green and — if you try to SSH through the box — yourblock fwd sshrule turn it red. The Rule column tells you exactly which line of policy decided each packet, which is how you debug a policy in seconds instead of guessing.
Step 20 — Search historical traffic
Section titled “Step 20 — Search historical traffic”For “what happened earlier”, use the firewall’s Search logs tab: pick a time range (up to 7 days) and filter by action, protocol, IP, port, Hostname / SNI, or free text.
[Explanation] Live logs only show new events. Search logs queries the engine’s own on-disk history — nothing is stored in the cloud by default, so your traffic data stays on your VM unless you set up Log Export (S3 / Splunk / Sentinel) under Configure → Log Export.
Part 7 — Route a real workload through the firewall
Section titled “Part 7 — Route a real workload through the firewall”Parts 1–6 filter the firewall box’s own traffic. To see the through-firewall section really work, add a private subnet with a second “workload” EC2 whose only way out is through the firewall. Then you watch the workload’s real internet traffic appear in the firewall’s live logs.
Step 21 — Create the private subnet
Section titled “Step 21 — Create the private subnet”Console: VPC → Subnets → Create subnet →
- VPC:
enforza-lab, Name:enforza-lab-private - Same AZ as the firewall (e.g.
eu-west-2a) - IPv4 CIDR:
10.10.2.0/24→ Create subnet.
[Explanation] This subnet has no route to the Internet Gateway — that’s deliberate. A machine here can’t reach the internet on its own; we’ll force its traffic through the firewall instead. That’s what makes the inspection real: the workload literally cannot bypass the firewall.
Step 22 — Find the firewall’s network interface (ENI)
Section titled “Step 22 — Find the firewall’s network interface (ENI)”Console: EC2 → Instances → enforza-fw-lab → Networking tab →
Network interfaces → note the Interface ID (eni-…).
[Explanation] The ENI is the firewall’s virtual network card. We’ll point the workload’s default route at this specific card so all its traffic lands on the firewall.
Step 23 — Route the private subnet through the firewall ENI
Section titled “Step 23 — Route the private subnet through the firewall ENI”Console: VPC → Route tables → Create route table →
- Name:
enforza-lab-private-rt, VPC:enforza-lab→ Create.
Then:
- Routes tab → Edit routes → Add route: Destination
0.0.0.0/0, Target Network Interface → select the firewall’seni-…→ Save changes. - Subnet associations tab → Edit → tick
enforza-lab-private→ Save.
[Explanation] This is the heart of the inspection pattern. The private subnet’s default route now says “anything bound for the internet → hand it to the firewall’s ENI.” The firewall inspects it against your
through-firewallrules, then source-NATs (SNAT) it out through its own public path. Remember Step 7: if the firewall’s source/dest check were still on, AWS would drop these forwarded packets — that’s why we turned it off up front.
Step 24 — Launch the workload EC2 in the private subnet
Section titled “Step 24 — Launch the workload EC2 in the private subnet”Console: EC2 → Launch instances →
- Name:
enforza-workload-lab, same Ubuntu AMI,t3.small - Network settings → Edit: VPC
enforza-lab, Subnetenforza-lab-private, Auto-assign public IP: Disable, Security groupenforza-lab-any - Same key pair → Launch.
[Explanation] This is the “customer” machine sitting behind the firewall. It has no public IP, so its only path to the internet is the route you just made — through the firewall. To get a terminal on it (it’s not directly reachable), either use it as-is via AWS Systems Manager → Session Manager, or SSH to it from the firewall box as a jump host (
ssh ubuntu@10.10.2.xfrom inside the firewall). Session Manager works because the workload’s outbound 443 now flows out through the firewall, which yourweb egressrule allows.
Step 25 — Generate traffic and watch it on the firewall
Section titled “Step 25 — Generate traffic and watch it on the firewall”- In the console, open Firewalls →
enforza-fw-lab→ Live logs (leave it running). - On the workload box, generate some traffic:
Terminal window curl -sI https://example.com # allowed → accept on TCP/443nc -vz somehost 22 # crosses the firewall → your block-fwd-ssh Drop fires - Watch the rows appear on the firewall’s live log — the workload’s private IP
(
10.10.2.x) as Source, the Rule column naming which policy line decided each flow, and the SNAT column green as the firewall translates the traffic on the way out.
[Explanation] This closes the loop end-to-end: a real machine’s traffic is now being forced through your firewall, inspected against the
through-firewallrules, and shown to you live with the deciding rule named. The workload has no direct internet path, so all its outbound traffic must cross the firewall — this is the real inspection pattern. Change a rule, Push, re-run thecurl, and watch the verdict flip: the full authoring-to-observation cycle in seconds.
Troubleshooting
Section titled “Troubleshooting”| Symptom | Most likely cause |
|---|---|
Install exits registration key not found | Key already consumed or expired — mint a fresh one (Onboard Firewall → Deployment Keys). |
| Engine installs but never appears in console | No outbound internet — check IGW attached, route table 0.0.0.0/0 → IGW, subnet association, and Auto-assign public IP. |
| Firewall shows red Apply/Compile error chip | Policy rejected by the engine — read the inline error, fix the rule, Push again. |
| Traffic through the box is dropped unexpectedly | Source/dest check still on (Step 7), or a section default-drop with no matching allow. |
| You got locked out of SSH | to-firewall default-drop with no allow my ssh rule — fix via AWS Session Manager or relax the rule and re-push. |
| Nothing in Live logs | No viewer was open when traffic flowed, the flow didn’t match a Log-ticked rule, or no traffic was generated. |
Clean up (stop the meter)
Section titled “Clean up (stop the meter)”When you’re done: EC2 → Instances → select enforza-fw-lab → Terminate.
Then delete the workload instance (if any), the security group, subnets, route
tables, IGW, and the VPC. In the console, the firewall drops to Offline;
remove it from Firewalls if you won’t reuse it.
[Explanation] A running
t3.smallcosts a few pence per hour; terminating stops all charges. Tearing the VPC down keeps your account tidy. Your deployment key was single-use and is already spent — mint a new one next time.
One-glance recap
Section titled “One-glance recap”- Network: VPC → public subnet → IGW → route
0.0.0.0/0 → IGW→ any/any SG. - Instance: launch in public subnet, public IP on, source/dest check OFF.
- Enrol: Onboard Firewall → Deployment Key → SSH → paste install command → Online.
- Policy:
dropdefault, allow OS egress (from-firewall), guard SSH (to-firewall), inspect + block (through-firewall) → Push. - Bind: Bindings → Apply policy → Synced.
- Observe: Firewalls → Live logs (and Search logs for history).