Building an Ironclad Edge Gateway: Turn a Raspberry Pi 5 into a Zero-Trust Homelab Firewall and Micro-SOC

Modern homelabs and Internet of Things (IoT) ecosystems have evolved far beyond simple hobbyist setups. From smart switches, ESP32 microcontrollers, and IP surveillance cameras to self-hosted media servers and hypervisors, the sheer density of networked devices inside our homes now rivals small enterprise branch offices. However, consumer routers provided by Internet Service Providers (ISPs) offer practically zero visibility into lateral network movement, lack granular traffic inspection, and leave connected hardware vulnerable to botnet recruitment, malicious payload drops, and DNS spoofing.
Implementing a Zero-Trust Architecture (ZTA) at the local edge is the most effective strategy to safeguard your infrastructure. By assuming every device—especially consumer IoT hardware—is potentially compromised, we can isolate subnets, enforce strict cryptographic authentication, and deploy automated intrusion mitigation. In this comprehensive guide, we will transform a Raspberry Pi 5 into an autonomous, bulletproof edge security gateway and micro-Security Operations Center (Micro-SOC) using WireGuard, Pi-hole, Unbound, and CrowdSec.
Architectural Blueprint: The Edge Micro-SOC
Our security architecture relies on a multi-layered defense-in-depth model running seamlessly on top of a hardened 64-bit operating system:
- Hardware Base: Raspberry Pi 5 (4GB or 8GB) with PCIe NVMe SSD storage for high I/O throughput and endurance.
- DNS Isolation Layer: Pi-hole paired with an Unbound recursive resolver executing full DNSSEC validation, stripping third-party tracking and malicious domains before IP resolution.
- Encrypted Remote Ingress: Kernel-space WireGuard VPN, bypassing vulnerable port-forwarding schemes with cryptographic keypairs.
- Collaborative Intrusion Detection & Prevention (IDS/IPS): CrowdSec analyzes SSH, authentication, and packet logs in real time, leveraging global peer threat intelligence to auto-ban malicious IPs via
nftables. - Observability & Telemetry: Prometheus and Node Exporter streaming metrics to a lightweight Grafana instance for anomaly detection.
Step 1: Host OS Hardening & Kernel Tuning
Flash Raspberry Pi OS Lite (64-bit) or Debian 12 Bookworm to your NVMe drive. Before containerizing services, we must harden the host kernel parameters to optimize packet forwarding and protect against common network attack vectors like SYN floods and spoofing.
Open the sysctl configuration file:
sudo nano /etc/sysctl.d/99-security-hardening.conf
Append the following kernel security parameters:
# Enable IPv4 Packet Forwarding for Gateway Routing
net.ipv4.ip_forward = 1
# Protect against SYN flood attacks
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 2048
net.ipv4.tcp_synack_retries = 2
# Disable ICMP Redirect Acceptance (Prevents MITM Route Alterations)
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
# Enable Reverse Path Filtering (Mitigate IP Spoofing)
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
# Ignore ICMP Broadcast Requests (Smurf Attack Mitigation)
net.ipv4.icmp_echo_ignore_broadcasts = 1
# Disable Source Routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
Apply the parameters immediately without rebooting:
sudo sysctl --system
Next, enforce key-only SSH authentication and change the default port. Edit /etc/ssh/sshd_config.d/hardened.conf:
Port 2222
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
X11Forwarding no
MaxAuthTries 3
Restart the SSH daemon to apply changes:
sudo systemctl restart ssh
Step 2: Deploying Containerized Core Services
We deploy our core edge stack using Docker Compose. This ensures isolation, reproducible configuration, and rapid disaster recovery.
Create a dedicated directory structure for the gateway stack:
mkdir -p ~/edge-gateway/{pihole,unbound,wireguard,crowdsec,crowdsec/data}
cd ~/edge-gateway
Configuring the Recursive Unbound Resolver
Create the Unbound configuration file at ~/edge-gateway/unbound/unbound.conf:
server:
verbosity: 1
interface: 0.0.0.0@5335
do-ip4: yes
do-udp: yes
do-tcp: yes
do-ip6: no
prefer-ip6: no
# Access control
access-control: 127.0.0.1/32 allow
access-control: 172.16.0.0/12 allow
access-control: 192.168.0.0/16 allow
access-control: 10.0.0.0/8 allow
# Privacy & Security Hardening
hide-identity: yes
hide-version: yes
harden-glue: yes
harden-dnssec-stripped: yes
use-caps-for-id: no
edns-buffer-size: 1232
prefetch: yes
num-threads: 2
so-rcvbuf: 4m
so-sndbuf: 4m
The Orchestration: `docker-compose.yml`
Now, build the full multi-service composition file at ~/edge-gateway/docker-compose.yml:
version: "3.8"
networks:
gateway_net:
ipam:
driver: default
config:
- subnet: 172.28.0.0/16
services:
unbound:
image: mvance/unbound:latest
container_name: gateway_unbound
restart: always
volumes:
- ./unbound/unbound.conf:/opt/unbound/etc/unbound/unbound.conf:ro
networks:
gateway_net:
ipv4_address: 172.28.0.2
pihole:
image: pihole/pihole:latest
container_name: gateway_pihole
restart: always
depends_on:
- unbound
environment:
TZ: 'UTC'
WEBPASSWORD: 'SuperSecretAdminPasswordChangeMe'
PIHOLE_DNS_: '172.28.0.2#5335'
DNSSEC: "true"
DNS_BOGUS_PRIV: "true"
DNS_FQDN_REQUIRED: "true"
volumes:
- ./pihole/etc-pihole:/etc/pihole
- ./pihole/etc-dnsmasq.d:/etc/dnsmasq.d
ports:
- "53:53/tcp"
- "53:53/udp"
- "8080:80/tcp"
networks:
gateway_net:
ipv4_address: 172.28.0.3
wireguard:
image: lscr.io/linuxserver/wireguard:latest
container_name: gateway_wireguard
restart: always
cap_add:
- NET_ADMIN
- SYS_MODULE
environment:
- PUID=1000
- PGID=1000
- TZ=UTC
- SERVERURL=vpn.yourhomelab.net # Replace with your dynamic DNS
- SERVERPORT=51820
- PEERS=laptop,phone,tablet
- PEERDNS=172.28.0.3 # Route VPN client DNS directly through Pi-hole
- INTERNAL_SUBNET=10.13.13.0/24
- ALLOWEDIPS=0.0.0.0/0
volumes:
- ./wireguard/config:/config
- /lib/modules:/lib/modules:ro
ports:
- "51820:51820/udp"
sysctls:
- net.ipv4.conf.all.src_valid_mark=1
- net.ipv4.ip_forward=1
networks:
gateway_net:
ipv4_address: 172.28.0.4
crowdsec:
image: crowdsecurity/crowdsec:latest
container_name: gateway_crowdsec
restart: always
environment:
- GID=1000
- COLLECTIONS=crowdsecurity/linux crowdsecurity/sshd crowdsecurity/whitelist-good-actors crowdsecurity/wireguard
volumes:
- ./crowdsec/data:/var/lib/crowdsec/data
- ./crowdsec:/etc/crowdsec
- /var/log/auth.log:/var/log/auth.log:ro
- /var/log/syslog:/var/log/syslog:ro
networks:
gateway_net:
ipv4_address: 172.28.0.5
Start the gateway stack in detached mode:
docker compose up -d
Step 3: Implementing CrowdSec Host-Level Remediation
While the CrowdSec container analyzes logs and correlates events, we must install the CrowdSec Firewall Bouncer directly on the Raspberry Pi host. This bouncer hooks into nftables to drop packets at the kernel layer before they ever reach our containers or internal network.
Install the repository and the firewall bouncer on the host system:
curl -s https://packagecloud.io/install/repositories/crowdsec/crowdsec/script.deb.sh | sudo bash
sudo apt-get install -y crowdsec-firewall-bouncer-nftables
Generate a new API key inside the CrowdSec container for the bouncer:
docker exec -it gateway_crowdsec cscli bouncers add host-nftables-bouncer
Copy the generated API token, open the bouncer configuration on the host:
sudo nano /etc/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml
Configure the API URL and token:
api_url: http://127.0.0.1:8080/ # Or http://172.28.0.5:8080/
api_key: <YOUR_GENERATED_API_KEY>
mode: nftables
update_frequency: 10s
log_mode: file
log_level: info
Restart and verify the bouncer service:
sudo systemctl restart crowdsec-firewall-bouncer
sudo systemctl status crowdsec-firewall-bouncer
Test the dynamic ban mechanism using the command line interface:
# Simulate a manual IP ban
docker exec -it gateway_crowdsec cscli decisions add --ip 198.51.100.23 --duration 4h --reason "Suspicious Probe"
# Verify that nftables dropped the IP
sudo nft list set inet crowdsec crowdsec_blacklists
Step 4: Network Micro-Segmentation and Firewall Rules
To establish true Zero-Trust isolation, the Raspberry Pi gateway should dictate traffic rules between your primary LAN, your isolated IoT VLAN, and external remote tunnels. Using nftables, we reject unauthenticated lateral movement from IoT devices to critical servers.
Create an nftables ruleset at /etc/nftables.conf:
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
# Accept loopback traffic
iif "lo" accept
# Accept established and related connections
ct state established,related accept
# Drop invalid connections
ct state invalid drop
# Allow Custom SSH Port
tcp dport 2222 accept
# Allow DNS queries from internal subnets
ip saddr { 192.168.1.0/24, 10.13.13.0/24 } udp dport 53 accept
ip saddr { 192.168.1.0/24, 10.13.13.0/24 } tcp dport 53 accept
# Allow WireGuard VPN Ingress
udp dport 51820 accept
# Allow ICMP (Ping) with Rate Limiting
ip protocol icmp icmp type echo-request limit rate 5/second accept
}
chain forward {
type filter hook forward priority 0; policy drop;
# Allow established and related forwarded traffic
ct state established,related accept
# Allow WireGuard clients (10.13.13.0/24) to reach Internet and Internal LAN
ip saddr 10.13.13.0/24 oifname "eth0" accept
# ISOLATION: Prevent IoT Subnet (VLAN 30) from accessing Management LAN (VLAN 10)
ip saddr 192.168.30.0/24 ip daddr 192.168.10.0/24 drop
# Allow IoT Subnet to access only WAN through Gateway
ip saddr 192.168.30.0/24 oifname "eth0" accept
}
}
Load and enable the ruleset permanently:
sudo nft -f /etc/nftables.conf
sudo systemctl enable nftables
Step 5: Telemetry & Real-Time Threat Auditing
Visibility is the core pillar of Zero-Trust. Without active telemetry, stealthy beaconing from compromised IoT appliances can go unnoticed for months. To monitor gateway health and dropped traffic:
- Expose CrowdSec Metrics: CrowdSec natively exposes a Prometheus metrics endpoint on port
6060. Query metrics usingcurl http://localhost:6060/metrics. - Deploy Node Exporter: Monitor Raspberry Pi 5 CPU throttling, temperature under load, and I/O bottlenecks.
- Grafana Dashboard Integration: Import Grafana dashboard ID
13332for CrowdSec to visualize active global bans, brute-force attempts, and top offending autonomous system numbers (ASNs).
Verification and Penetration Testing
Once deployed, validate that your Zero-Trust gateway performs as expected:
# 1. Verify Unbound DNSSEC validation
dig sigfail.verteiltesysteme.net @127.0.0.1 -p 53
# The query MUST return SERVFAIL, demonstrating bogus signature rejection.
# 2. Test WireGuard Endpoint Handshake from an external cellular network
wg show
# 3. Verify Active CrowdSec Log Stream Processing
docker exec -it gateway_crowdsec cscli metrics
Conclusion
By transforming a low-power single-board computer like the Raspberry Pi 5 into a hardened edge gateway, you achieve enterprise-grade network security without recurring SaaS subscriptions or closed-source proprietary appliances. With full recursive DNSSEC resolution, adaptive intrusion mitigation powered by global threat intelligence, and zero-trust micro-segmentation, your homelab is thoroughly fortified against modern automated exploits and lateral intrusions.
Comments
Post a Comment