Most home networks are flat: every device — your laptop, your phone, your smart fridge, your IP camera — lives on the same subnet. If any one of them is compromised, the attacker can pivot to everything else. This post documents how I redesigned mine from scratch.
Why segment at all?
A flat /24 gives you 254 usable addresses and zero trust boundaries. The moment an IoT device with a backdoor hits your network, it can talk freely to your NAS, your workstation, your Raspberry Pis. Segmentation with VLANs enforces isolation at layer 2 — no routing between zones unless you explicitly allow it.
Hardware I used
- pfSense (running on a Protectli VP2420 mini-PC) as the router/firewall
- TP-Link TL-SG108E managed switch — 8-port, supports 802.1Q VLANs, costs ~$30
- Ubiquiti UAP-AC-Lite access point (supports multiple SSIDs per VLAN)
VLAN layout
| VLAN | Name | Subnet | Purpose |
|---|---|---|---|
| 10 | TRUSTED | 10.0.10.0/24 | Laptops, phones |
| 20 | LAB | 10.0.20.0/24 | Servers, Raspberry Pis |
| 30 | IOT | 10.0.30.0/24 | Smart plugs, cameras |
| 99 | MGMT | 10.0.99.0/24 | Switch, AP management |
pfSense firewall rules
The key rule set: IOT can never initiate connections to TRUSTED or LAB. Only return traffic is allowed (stateful firewall handles this automatically). LAB can reach the internet but not TRUSTED.
# IOT outbound — internet only
pass out on VLAN30 all
block in on VLAN30 destination <trusted_net>
block in on VLAN30 destination <lab_net>
Switch configuration
On the TL-SG108E: set each port as an access port for a single VLAN (untagged) or a trunk port carrying multiple VLANs (tagged) toward the pfSense uplink and the AP.
Port 1 → pfSense (tagged: VLAN10, 20, 30, 99)
Port 2 → AP (tagged: VLAN10, 30)
Port 3 → NAS (untagged: VLAN20)
Port 4-6 → Lab servers (untagged: VLAN20)
What I learned
- Start with the firewall rules before plugging anything in. It is much easier to open holes selectively than to close them retroactively.
- VLAN 1 is the default VLAN on most switches — never put production traffic on it. Use it for nothing, or disable it.
- DNS matters. Run a separate Pi-hole per VLAN or use pfSense’s DNS resolver with VLAN-aware overrides so IoT devices can not query your internal hostnames.
The result: a network where my IP cameras literally cannot reach my laptop, even if I try.
A Raspberry Pi 4 (4 GB) costs $55. For that price you get a always-on Linux box that can block ads network-wide, collect metrics from every device on your network, and serve a Grafana dashboard — all simultaneously.
What we are building
- Pi-hole — DNS-level ad and tracker blocker for the entire network
- Prometheus — time-series metrics collector
- Grafana — dashboard to visualise everything
- pihole-exporter — bridge between Pi-hole stats and Prometheus
Install Pi-hole
curl -sSL https://install.pi-hole.net | bash
Set a static IP on the Pi first. Then point your router’s DHCP to hand out the Pi’s IP as the DNS server for all clients.
# /etc/dhcpcd.conf
interface eth0
static ip_address=10.0.20.10/24
static routers=10.0.20.1
static domain_name_servers=127.0.0.1
Install Prometheus
sudo apt install prometheus
Default config scrapes itself every 15s. We will add pihole-exporter as a target.
Install pihole-exporter
wget https://github.com/eko/pihole-exporter/releases/latest/download/pihole_exporter-linux-arm
chmod +x pihole_exporter-linux-arm
./pihole_exporter-linux-arm -pihole_hostname 127.0.0.1
Add to Prometheus scrape config:
scrape_configs:
- job_name: pihole
static_configs:
- targets: ['localhost:9617']
Install Grafana
sudo apt install grafana
sudo systemctl enable --now grafana-server
Import dashboard ID 10176 (Pi-hole Exporter) from grafana.com — it gives you blocked queries %, top blocked domains, query rate, and client breakdown out of the box.
Metrics worth watching
- Blocked % — healthy range is 15–30%. Much lower means blocklists are stale. Much higher might mean false positives.
- Query rate by client — spot anomalous devices making thousands of queries per minute (IoT malware signature).
- Top permitted domains — tells you what your devices actually phone home to.
Performance on Pi 4
After 30 days of continuous operation: CPU never exceeded 12%, RAM usage settled at ~380 MB. The SD card is the bottleneck — use a good A2-rated card or boot from USB SSD.
Every time I expose a Linux machine to the internet I run through the same checklist. These are not theoretical controls — they are the minimum I apply before any service goes live.
1. Disable root login and password auth
# /etc/ssh/sshd_config
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
Generate a key pair locally, copy the public key, then restart sshd:
ssh-keygen -t ed25519 -C "homelab"
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server
sudo systemctl restart sshd
2. UFW — allow only what you need
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp # SSH — change to your port
sudo ufw enable
Never open port ranges. Never use ufw allow from any. Be explicit.
3. fail2ban
Blocks IPs after repeated failed auth attempts:
sudo apt install fail2ban
# /etc/fail2ban/jail.local
[sshd]
enabled = true
port = 22
maxretry = 3
bantime = 1h
findtime = 10m
sudo systemctl enable --now fail2ban
4. Unattended security upgrades
sudo apt install unattended-upgrades
sudo dpkg-reconfigure --priority=low unattended-upgrades
This auto-applies security patches daily without touching non-security packages.
5. Audit with Lynis
Lynis is an open-source security auditing tool that scores your system against CIS benchmarks:
sudo apt install lynis
sudo lynis audit system
Aim for a hardening index above 70. Common quick wins: disable unused kernel modules, restrict /proc mount options, enable auditd.
6. Change the SSH port
Not real security, but it eliminates 99% of automated scan noise in your logs:
# /etc/ssh/sshd_config
Port 2222
Update UFW to allow the new port before restarting sshd — or you will lock yourself out.
What I skip (and why)
- SELinux/AppArmor profiles for every service — useful but high maintenance. I use AppArmor only for internet-facing services.
- Port knocking — adds friction without meaningful security if you already have key-only auth + fail2ban.
Security is a process, not a checklist. Run Lynis monthly and re-evaluate after any major config change.