
Networking
- 3 installs
- 19 repo stars
- Updated August 1, 2026
- xobotyi/cc-foundry
Helps with ai & agent building tasks.
About
networking is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- networking
- AI & Agent Building
- AI-coding skill
Networking by the numbers
- 3 all-time installs (skills.sh)
- Ranked #13,657 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/xobotyi/cc-foundry --skill networkingAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 3 |
|---|---|
| repo stars | ★ 19 |
| Last updated | August 1, 2026 |
| Repository | xobotyi/cc-foundry ↗ |
What it does
Helps with ai & agent building tasks.
Files
Networking
Security is a non-negotiable default, not an optional add-on. Every network design decision must account for trust boundaries.
References
Extended configuration examples, comparison tables, and detailed patterns for the rules below live in ${CLAUDE_SKILL_DIR}/references/.
vlan-segmentation.md— VLAN design, trunk/access ports, inter-VLAN policy, Layer 2 security: segment table, firewall
rule matrix, hardware requirements, DHCP snooping, DAI, port security, L2 attack mitigation
firewall-rules.md— nftables syntax, OPNsense/pfSense hardening, IPv6 firewall rules: chain types/hooks/priorities,
connection tracking, NAT, rate limiting, sets/maps, ICMPv6 policy, dual-stack rules
dns-architecture.md— Pi-hole, AdGuard Home, split-horizon, mDNS, Unbound, DoH/DoT: tool comparison, deployment
patterns, Avahi reflector config, recursive vs authoritative, encrypted DNS, IPv6 DNS
reverse-proxy.md— Caddy, Traefik, Nginx Proxy Manager, Cloudflare tunnels: Caddyfile examples, Traefik Docker
labels, decision matrix, snippet patterns, tunnel patterns, auth proxy integration
vpn-tunnels.md— WireGuard, Tailscale, Headscale, site-to-site, HA with OSPF: config examples, topology comparison,
subnet router, hybrid WG+TS, HA failover with BIRD/OSPF
tls-certificates.md— Let's Encrypt, ACME, wildcard certs, acme.sh: challenge types, ACME client comparison,
certificate storage patterns, TLS config
security-hardening.md— SSH, fail2ban, CrowdSec, IDS/IPS, monitoring, hardening: sshd_config, SSH CA, fail2ban vs
CrowdSec, Suricata IDS, Prometheus stack, monitoring metrics, IPv6 hardening
auth-proxies.md— Authelia, Authentik, forward auth, SSO patterns: Authelia vs Authentik comparison, ForwardAuth
with Traefik/Caddy, SSO/MFA patterns, deployment guidance
VLAN Segmentation
Segment by Trust Level
Separate traffic into functional zones based on trust, not device count:
- Management (VLAN 10): Hypervisors, switches, routers, IPMI/iLO -- highest trust
- Trusted/Lab (VLAN 20): VMs, containers, workstation -- high trust
- IoT (VLAN 30): Smart devices, cameras, sensors -- low trust, restricted
- Guest (VLAN 40): Visitor devices -- zero trust, internet only
- Storage (VLAN 50): NAS, iSCSI, backup targets -- high trust, limited access
- DMZ (VLAN 99): Publicly exposed services -- medium trust, no inward access
Start with 3-4 VLANs. Add more only with a clear security or performance reason. Over-segmentation adds complexity without proportional benefit. Separate production self-hosted services from experimental lab services -- prevent experimentation from causing downtime for household-facing apps.
Inter-VLAN Policy
VLANs without firewall rules provide zero security benefit. Every VLAN boundary needs explicit allow/deny policy. Default deny between all VLANs, then explicitly allow required flows. Always permit established/related return traffic.
Infrastructure Requirements
- Managed (VLAN-aware) switches with 802.1Q support
- Router/firewall capable of VLAN termination and inter-VLAN routing
- Access points with per-SSID VLAN tagging
- Set native VLAN on trunks to an unused VLAN (not VLAN 1)
Firewalls
nftables
Modern Linux firewall replacing iptables. Use inet family for dual-stack rules.
Core structure: tables contain chains, chains contain rules. Base chains attach to Netfilter hooks (input, forward, output, prerouting, postrouting).
Minimal host firewall:
table inet filter {
chain input {
type filter hook input priority filter; policy drop;
ct state established,related accept
ct state invalid drop
iifname "lo" accept
icmp type echo-request accept
icmpv6 type { echo-request, nd-neighbor-solicit, nd-router-advert } accept
tcp dport { ssh } accept
}
chain forward {
type filter hook forward priority filter; policy drop;
}
chain output {
type filter hook output priority filter; policy accept;
}
}Key rules:
- Place
ct state established,related acceptearly in input/forward chains -- handles bulk of traffic efficiently - Drop
ct state invalidpackets explicitly - Use
policy dropon input and forward chains (default deny) - Use
policy accepton output chains (restrict outbound only when needed) acceptis not final across chains -- later chains at the same hook still evaluate.dropis always final.- Use
counteron rules during development to verify traffic is hitting them - Persist rules:
nft list ruleset > /etc/nftables.conf, enablenftables.service
OPNsense / pfSense
GUI-managed firewalls. Rules evaluate top-to-bottom, first match wins. Place more specific rules (e.g., block-LAN) above general rules (e.g., allow-internet) -- rule ordering mistakes are the most common cause of VLAN isolation failures.
Post-install hardening (first 30 minutes):
1. Change default admin password 2. Enable 2FA (OPNsense: built-in; pfSense: package) 3. Disable web UI access from WAN 4. Configure DNS over TLS upstream 5. Enable automatic config backups 6. Restrict RFC1918 on WAN interface 7. Restrict DNS resolver to internal interfaces only (default allows queries from all interfaces -- open resolvers get abuse complaints)
OPNsense has faster security patches and built-in 2FA. pfSense has a larger community knowledge base. Security posture depends more on configuration than platform choice.
Throughput problems after install: disable hardware offloading (CRC, TSO, LRO) first -- this is the most common culprit in virtualized environments. If still slow, check IDS rulesets -- too many active rules kill performance. Start with 2-3 rulesets, add more only as needed.
IPv6
Dual-Stack Configuration
Run IPv4 and IPv6 concurrently. Dual-stack doubles the attack surface -- maintain identical security policies for both protocols. Use inet family in nftables for rules that apply to both stacks; use ip6 only for IPv6-specific rules (ICMPv6, neighbor discovery).
ICMPv6 Firewall Policy
ICMPv6 is essential for IPv6 operation -- blocking all ICMPv6 breaks the network. Apply granular filtering:
- Must allow transit: Destination Unreachable (Type 1), Packet Too Big (Type 2), Time Exceeded (Type 3) -- required
for PMTU discovery and communication
- Link-local only: Router/Neighbor Solicitation and Advertisement (Types 133-136) -- critical for local discovery,
must never cross network boundaries
- Drop invalid: Drop ICMPv6 from unexpected sources or with malformed headers
Address Assignment
- SLAAC: Stateless, no server needed. Devices auto-configure from router advertisements. Simple but less control.
- DHCPv6: Stateful, centralized address management. Provides DNS server addresses. Use for servers requiring fixed
addresses.
- Privacy extensions: Randomize interface identifiers to prevent tracking. Enable for external communications,
disable internally (rotating addresses break logging and service correlation).
DNS and IPv6
Add AAAA records only after IPv6 connectivity is verified and working. Premature AAAA records cause timeouts when IPv6 is not properly configured. In dual-stack environments, test both A and AAAA resolution paths.
IDS/IPS
Suricata
Network threat detection engine. Performs deep packet inspection and generates alerts based on rulesets. OPNsense includes Suricata built-in; pfSense requires a package.
Performance impact: enabling 3 rulesets causes ~27% throughput drop. Start with 2-3 essential rulesets, add more only as needed. Disable hardware offloading (CRC, TSO, LRO) first if throughput is poor -- offloading conflicts with packet inspection.
CrowdSec
Collaborative security engine that replaces or augments fail2ban. Key differences from fail2ban:
fail2ban: Detection via regex on local logs; local-only intelligence; iptables/nftables ban remediation; configured via jail.conf.
CrowdSec: Detection via YAML scenarios with leakspeed/capacity; community-shared threat data; Bouncers for remediation (firewall, Nginx, Traefik); configured via acquis.yaml + Hub collections.
Install CrowdSec collections from the Hub for specific services (SSH, Nginx, Suricata). Configure whitelists immediately to prevent banning your own IPs.
Suricata + CrowdSec Integration
Suricata detects threats via packet inspection and logs to fast.log. CrowdSec parses these logs via acquis.yaml and makes blocking decisions. This separates detection (Suricata) from remediation (CrowdSec bouncers) -- each tool does what it does best.
DNS
DNS Filtering
Pi-hole and AdGuard Home are DNS sinkholes that block unwanted domains at the network level. Point DHCP-advertised DNS at the sinkhole.
- Pi-hole: Lighter resource usage, larger community/blocklist ecosystem
- AdGuard Home: Built-in encrypted DNS (DoH/DoT/DoQ), per-client rules, YAML config
Deploy redundant pairs on separate hosts for resilience. Sync blocklists between instances. Avoid using firewall-integrated DNS blocking plugins (e.g., pfBlockerNG) -- a core firewall update can silently break the plugin and kill all DNS resolution. Run DNS filtering as a separate service.
Split-Horizon DNS
Return different DNS answers based on client network. Internal clients resolve to private IPs; external clients resolve to public IPs. Implementation:
- Wildcard DNS rewrite in Pi-hole/AdGuard (
*.example.com -> 192.168.1.100) for internal resolution to reverse proxy - Separate DNS instances for internal vs external (cleanest separation -- avoids AdGuard Home's global rewrite problem
where a self-hosted DoH instance rewrites external queries with internal IPs)
- Conditional forwarding for local domains to internal authoritative server
mDNS Across VLANs
mDNS (.local) is link-local and does not cross routed boundaries. Use Avahi with enable-reflector=yes to bridge mDNS between specific VLANs (e.g., trusted LAN discovering IoT devices). Restrict interfaces -- never reflect to guest or WAN.
Upstream Encryption
Always encrypt DNS queries upstream (DNS over TLS or HTTPS) to prevent ISP snooping. Configure the recursive resolver to use encrypted upstream; serve plain DNS internally.
Reverse Proxy
Single entry point for all HTTP/HTTPS services. Terminates TLS, routes by hostname, eliminates per-service port exposure.
Tool Selection
| Factor | Caddy | Traefik | Nginx Proxy Manager |
|---|---|---|---|
| Config style | Caddyfile | YAML + Docker labels | Web UI |
| Auto HTTPS | Default behavior | Via cert resolvers | One-click |
| Docker-aware | Plugin | Native | No |
| Learning curve | Low | Medium | Lowest |
| Best for | Simple setups | Docker/K8s stacks | GUI preference |
- Docker-heavy: Traefik -- discovers containers automatically
- Simplicity-first: Caddy -- automatic HTTPS, minimal config
- GUI management: Nginx Proxy Manager -- point-and-click
Architecture Pattern
Client --[HTTPS]--> Reverse Proxy --[HTTP]--> Backend ServicesOnly the reverse proxy needs TLS certificates. Backend services run plain HTTP on internal networks. One wildcard certificate covers all services.
Wildcard Certificates
Use DNS-01 ACME challenge for wildcard certs (*.example.com). Required for internal services not reachable from the internet. Caddy and Traefik handle this natively with DNS provider plugins.
Authentication Proxies
Authelia vs Authentik
Both provide SSO and MFA for self-hosted services. Choose based on resource constraints and protocol needs:
- Authelia: Lightweight (~20 MB image, ~30 MB RAM). YAML-configured, no GUI. Supports OIDC, TOTP, WebAuthn,
Passkeys. Best for homelabs with limited resources needing web SSO.
- Authentik: Full identity provider (~690 MB, requires PostgreSQL). Web GUI with user dashboard. Supports OIDC,
SAML2, LDAP, RADIUS, SCIM. Best when SAML support or a user portal is needed.
Forward Auth Pattern
Reverse proxies intercept requests and forward headers to the auth server before reaching the backend. Traefik uses ForwardAuth middleware; Caddy uses similar mechanisms. The auth server validates the session and returns 200 (allow) or 401 (redirect to login).
Deployment
Gate user-facing services behind auth proxies. Keep admin panels (router, Proxmox, NAS) on VPN or management VLAN only -- do not expose through public auth proxies. Prefer phishing-resistant MFA (FIDO2/WebAuthn) over TOTP where possible.
Cloudflare Tunnels
When to Use
Cloudflare Tunnels expose internal services without opening inbound ports. Useful behind CGNAT or when port forwarding is impossible. The cloudflared daemon initiates outbound connections to Cloudflare's edge.
Architecture Pattern
Internet -> Cloudflare Edge -> cloudflared -> Reverse Proxy -> BackendRoute tunnels into a local reverse proxy (Traefik/Caddy) for SSL termination and routing. Layer Authelia/Authentik or Cloudflare Access for identity gating.
When NOT to Use
- Admin panels: Router, hypervisor, NAS UIs -- keep on VPN/LAN only
- Sensitive data services: Password managers, private document portals
- LAN-only tools: Printer dashboards, IoT hubs with weak/no auth
- High-bandwidth media: Jellyfin/Plex through CDN may violate ToS -- disable caching for media subdomains if used
A tunnel makes services accessible, not secure. Weak credentials, missing MFA, or unpatched software remain exploitable regardless of the tunnel.
VPN
WireGuard
Kernel-level VPN protocol. ~4,000 lines of code. Fast, simple, no cipher negotiation.
- Hub-and-spoke topology for remote access VPN
- Site-to-site between fixed endpoints with static IPs
- Requires manual port forwarding (UDP 51820) and key management
- ~5 MB RAM (kernel module)
- Use when: zero external dependencies required, resource-constrained devices, traditional VPN gateway needed
Tailscale
Mesh VPN built on WireGuard. Automatic key management, NAT traversal, DNS.
- Full mesh topology -- devices connect directly peer-to-peer
- Zero-config: SSO login, no port forwarding, no config files
- MagicDNS for automatic device name resolution
- Subnet routers expose local networks without per-device installation
- ~30-50 MB RAM (userspace daemon)
- Use when: multiple devices across networks, NAT traversal needed, want managed ACLs and DNS
Topology Resilience
Hub-and-spoke has a single point of failure: if the hub goes down, the entire VPN is unreachable. Mesh (Tailscale) is resilient -- nodes remain connected to each other even if one fails. Choose topology based on failure tolerance, not just convenience.
Hybrid Pattern
Run both: Tailscale for day-to-day remote access (mesh, zero-config), WireGuard as backup VPN gateway for full-tunnel routing or when Tailscale's coordination server is unreachable.
OpenVPN
Legacy. Slower, larger attack surface. Choose only when TCP-based tunneling is required to bypass UDP-blocking firewalls. For all new deployments, use WireGuard or Tailscale.
TLS/SSL Certificates
ACME and Let's Encrypt
Free, automated certificate issuance. Certificates valid 90 days, renew at 60.
Challenge types:
- HTTP-01: CA fetches token from port 80. For public-facing servers.
- DNS-01: Client creates TXT record. Required for wildcards and internal services.
- TLS-ALPN-01: CA connects to port 443. When only 443 is available.
Recommended Approach
Let the reverse proxy handle all certificate management. Caddy and Traefik do this automatically. For standalone cert management, use acme.sh (shell-only, no dependencies, many DNS providers).
TLS Configuration
- Minimum TLS 1.2, prefer TLS 1.3
- Enable HSTS:
Strict-Transport-Security: max-age=31536000; includeSubDomains - Enable OCSP stapling when supported
- Don't manually configure cipher suites unless you have a specific reason -- modern defaults are optimal
- Monitor certificate expiry (Prometheus blackbox_exporter, Uptime Kuma)
Security Hardening
SSH
- Disable password authentication -- key-based only (Ed25519)
- Disable root login -- authenticate as user, then
sudo - Restrict agent/X11 forwarding
- Use CrowdSec or fail2ban as a mitigation layer (not a substitute for key-based auth)
- For fleet management: use SSH Certificate Authority (step-ca) instead of distributing authorized_keys. SSH CA issues
short-lived certificates (~16h), eliminating stale key accumulation. Configure TrustUserCAKeys in sshd_config to point at the CA public key.
Network Hardening Checklist
- Default deny inbound on all firewall interfaces
- Filter outbound traffic -- a compromised service with unrestricted outbound can exfiltrate data or contact C2 servers.
At minimum: allow DNS only to designated DNS servers, allow HTTP/HTTPS for updates, block all other outbound from IoT and untrusted VLANs
- Restrict management interfaces to management VLAN only
- Enable DHCP snooping on managed switches -- prevents rogue DHCP servers
- Enable Dynamic ARP Inspection (DAI) -- prevents ARP poisoning (requires DHCP snooping binding table)
- Enable port security -- limits MAC addresses per port, prevents CAM table overflow attacks
- Disable DTP on all user-facing ports -- prevents VLAN hopping
- Enable BPDU Guard on access ports -- prevents STP manipulation
- Prefer DHCP reservations over static IPs for centralized management
- Change all default credentials (every device, no exceptions)
- Update firmware on all network devices regularly
- Backup configurations before any change, store off-device
- Disable unused services (UPnP, WPS, SNMP v1/v2 on WAN)
- For IPv6: maintain identical firewall policies for both stacks, allow required ICMPv6 types, disable IPv6 if not
actively used
- Document network topology and keep diagrams current
Monitoring
Deploy Prometheus + Grafana for network observability:
- snmp_exporter: Switch/router metrics via SNMP
- blackbox_exporter: Endpoint probing, certificate monitoring
- node_exporter: Host-level metrics
- Centralize logs with syslog or Loki+Promtail
- Use Monit or systemd watchdog to auto-restart critical services (DNS resolvers, reverse proxies) -- silent failures
cause household-wide outages
- Review firewall deny logs weekly during initial setup
Application
When designing network architecture: propose segmented designs with explicit trust boundaries. Default to security-first configurations. Present trade-offs between complexity and security.
When implementing configurations: produce production-ready config files, not toy examples. Include comments explaining non-obvious choices. Test connectivity after changes.
When debugging network issues: start at layer 2 (link/VLAN), work up through layer 3 (routing/firewall), then layer 7 (application/proxy). Check firewall deny logs first -- they reveal 80% of connectivity issues. Key tools:
ping/traceroute-- reachability and path verificationdig/nslookup-- DNS resolution from specific resolversss -tlnp/netstat-- listening ports and socket statetcpdump/tshark-- packet captures for traffic flow analysisnft list ruleset-- verify active firewall rules match intentavahi-browse -a-- verify mDNS discovery across VLANscurl -v-- HTTP/HTTPS connectivity with TLS details
When reviewing network configurations: flag missing inter-VLAN firewall rules, default credentials, disabled encryption, overly permissive access, and missing monitoring. State what's wrong and how to fix it.
Integration
The containers skill covers Docker/Podman networking (bridge, host, macvlan). This skill covers the network infrastructure those containers sit on. When a task involves both container networking and VLAN/firewall design, both skills apply.
The ansible skill handles automation of network configuration deployment. Use this skill for what to configure, ansible for how to deploy it.
Security is not optional. Every default must be secure. Convenience follows.
{
"sources": {
"nftables Wiki - Quick Reference": "https://wiki.nftables.org/wiki-nftables/index.php/Quick_reference-nftables_in_10_minutes",
"nftables Wiki - Configuring Tables": "https://wiki.nftables.org/wiki-nftables/index.php/Configuring_tables",
"nftables Wiki - Configuring Chains": "https://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains",
"nftables Wiki - Configuring Rules": "https://wiki.nftables.org/wiki-nftables/index.php/Configuring_rules",
"WireGuard Conceptual Overview": "https://www.wireguard.com/quickstart/",
"Traefik Documentation - Routing": "https://doc.traefik.io/traefik/routing/overview/",
"Traefik Documentation - HTTPS and TLS": "https://doc.traefik.io/traefik/https/overview/",
"Caddy Documentation - Caddyfile Concepts": "https://caddyserver.com/docs/caddyfile/concepts",
"Caddy Documentation - Reverse Proxy": "https://caddyserver.com/docs/caddyfile/directives/reverse_proxy",
"Let's Encrypt - How It Works": "https://letsencrypt.org/how-it-works/",
"ACME Protocol RFC 8555": "https://www.rfc-editor.org/rfc/rfc8555",
"Pi-hole Documentation - Overview": "https://docs.pi-hole.net/",
"AdGuard Home Wiki": "https://github.com/AdguardTeam/AdGuardHome/wiki/Getting-Started",
"Tailscale Documentation - How It Works": "https://tailscale.com/kb/1151/what-is-tailscale"
},
"lastFetched": "2026-03-13T19:26:57.765Z"
}
Authentication Proxies
Authelia vs Authentik
Both are open-source identity providers for self-hosted environments, providing SSO and MFA to protect web applications from unauthorized access.
Authelia
- Lightweight: ~20 MB Docker image, ~30 MB RAM
- Apache 2.0 license, no paid edition
- Supports OIDC (certified), TOTP, Duo, WebAuthn, Passkeys
- Configuration via YAML only, no admin GUI
- Access rules by user, group, network, or path
- Ideal for homelabs with limited resources
Authentik
- Full identity provider: ~690 MB container, requires PostgreSQL
- MIT license with paid Pro/Enterprise editions
- Supports OIDC, OAuth2, SAML2, LDAP, RADIUS, SCIM, Kerberos
- Web GUI with admin portal and user application dashboard
- Visual flow editor for custom login sequences
- Can replace commercial solutions (Okta, Auth0)
Feature Comparison
Authelia:
- Resource usage: ~30 MB RAM
- Config style: YAML files
- Protocol support: OIDC
- MFA options: TOTP, WebAuthn, Passkeys, Duo
- User portal: Login page only
- Best for: Lightweight SSO for web apps
Authentik:
- Resource usage: ~690 MB + PostgreSQL
- Config style: Web GUI + YAML
- Protocol support: OIDC, SAML2, LDAP, RADIUS, SCIM
- MFA options: TOTP, WebAuthn + visual flow editor
- User portal: Full app dashboard
- Best for: Full IdP with SAML/LDAP needs
Decision
- Choose Authelia for lightweight, config-as-code SSO with minimal overhead
- Choose Authentik when SAML support, LDAP, or a user dashboard is required
Forward Auth Integration
How Forward Auth Works
1. Client sends request to reverse proxy 2. Reverse proxy forwards request headers to auth server 3. Auth server checks session/cookies:
- Authenticated: Returns 200, proxy passes request to backend
- Not authenticated: Returns 401/302, proxy redirects to login page
4. After login, auth server sets session cookie, redirects back to original URL
Traefik ForwardAuth
Traefik uses ForwardAuth middleware to integrate with Authelia or Authentik:
# Docker Compose labels for protected service
labels:
- "traefik.http.routers.app.middlewares=authelia@docker"
# Authelia middleware definition
labels:
- "traefik.http.middlewares.authelia.forwardauth.address=http://authelia:9091/api/authz/forward-auth"
- "traefik.http.middlewares.authelia.forwardauth.trustForwardHeader=true"
- "traefik.http.middlewares.authelia.forwardauth.authResponseHeaders=Remote-User,Remote-Groups"For enhanced security, connect Traefik to Authelia over TLS with client certificates to ensure only authorized proxies communicate with the auth server.
Caddy Integration
Caddy can integrate via forward_auth directive or through Tailscale's certificate provisioning module for internal services with browser-valid certificates.
SSO and MFA Patterns
Single Sign-On
- Authelia: Minimalist -- login portal and password reset only
- Authentik: Full app dashboard where users see all authorized services
- Both support cookie-based SSO across subdomains on the same domain
Multi-Factor Authentication
Modern standards (2025+) emphasize phishing-resistant MFA:
- FIDO2/WebAuthn: Hardware tokens or biometric -- resistant to phishing because verification is bound to the domain.
Preferred over TOTP.
- TOTP: Time-based one-time passwords (Google Authenticator, Authy). Widely supported but vulnerable to phishing
(user can be tricked into entering code on a fake site).
- Passkeys: Device-bound credentials. Authelia supports these natively.
Prefer FIDO2/WebAuthn for high-value services. TOTP is acceptable for lower-risk services where hardware tokens are impractical.
Deployment Patterns
Service Classification
Classify services by exposure level:
- Public with own auth: Services with strong built-in authentication (Nextcloud, Gitea) -- can be exposed directly
with their own login
- Public via auth proxy: Services with weak/no auth -- gate behind Authelia/Authentik before exposing
- VPN/LAN only: Admin panels (router, Proxmox, NAS), password managers, sensitive data -- never expose publicly,
access via VPN or management VLAN
Cloudflare Access Alternative
Cloudflare Access provides a zero-trust layer at the edge:
- Stops traffic at Cloudflare until identity verification passes
- Supports one-time codes, OAuth2 (GitHub, Google), and custom IdPs
- Does not replace local auth proxies -- it adds an additional layer
Integration with CrowdSec
Layer CrowdSec behind the auth proxy to monitor authentication logs and automatically block IPs exhibiting brute-force patterns. CrowdSec's community blocklists provide preemptive blocking of known malicious sources.
DNS Architecture
DNS Filtering: Pi-hole and AdGuard Home
Both are DNS sinkholes that block unwanted domains (ads, trackers, malware) at the network level by intercepting DNS queries and returning NXDOMAIN for blocked domains.
Pi-hole
- DNS sinkhole with web dashboard for monitoring and management
- Blocks ads in non-browser contexts (smart TVs, mobile apps, IoT devices)
- Can function as DHCP server (ensures all devices use Pi-hole for DNS)
- Lightweight: runs on minimal hardware (Raspberry Pi, LXC container, Docker)
- Uses blocklists (gravity) updated periodically
- FTL (Faster Than Light) engine handles DNS and DHCP with caching
AdGuard Home
- Similar DNS sinkhole with additional features over Pi-hole:
- Built-in DNS-over-HTTPS / DNS-over-TLS / DNS-over-QUIC server
- Per-client settings and filtering rules
- Built-in DHCP server
- Parental controls and safe search enforcement
- Runs as single binary, supports Docker and Snap deployment
- Ports: 53/UDP (DNS), 80/TCP (web UI), 3000/TCP (initial setup)
- Configuration via YAML (
AdGuardHome.yaml)
Decision Criteria
Pi-hole:
- Encrypted DNS (DoH/DoT): Requires separate setup
- Per-client rules: Limited
- DHCP server: Yes
- Community/ecosystem: Larger, more blocklists
- Resource usage: Lower
- Upstream DNS encryption: Requires Unbound/dnscrypt
AdGuard Home:
- Encrypted DNS (DoH/DoT): Built-in
- Per-client rules: Full support
- DHCP server: Yes
- Community/ecosystem: Growing
- Resource usage: Slightly higher
- Upstream DNS encryption: Native
Deployment Patterns
- Single instance: Simplest. Point router DHCP to Pi-hole/AdGuard IP as DNS.
- Redundant pair: Two instances on separate hosts. Both configured identically. Router DHCP advertises both as DNS
servers. Gravity sync (Pi-hole) or config replication keeps lists consistent.
- Per-VLAN filtering: Different DNS servers per VLAN with different blocklists. IoT VLAN gets aggressive blocking;
trusted VLAN gets lighter filtering.
Split-Horizon DNS
Split-horizon (split-brain) DNS returns different answers for the same domain depending on the source network. Internal clients resolve app.example.com to an internal IP; external clients resolve it to a public IP.
Implementation Approaches
1. Firewall DNS overrides: OPNsense/pfSense can override specific domains for internal clients. Simple but limited to IP-level overrides.
2. Separate DNS instances: Run two DNS resolvers -- one for internal, one for external. Internal instance has local zone overrides. Router directs internal clients to the internal resolver.
3. Conditional forwarding: Configure Pi-hole/AdGuard to forward queries for your domain to an authoritative internal DNS server, while forwarding everything else upstream.
4. CoreDNS with conditional zones: CoreDNS can serve different zone files based on source network using the view plugin.
Common Pattern
Internet -> Public DNS (Cloudflare/Route53) -> Public IP -> Reverse Proxy
LAN -> Local DNS (Pi-hole/AdGuard) -> Private IP -> Reverse ProxyBoth paths reach the same reverse proxy, but internal traffic stays local instead of hairpinning through the WAN.
Gotchas
- AdGuard Home's DNS rewrites apply globally -- you cannot easily make them network-dependent with a single instance. If
you run a self-hosted DoH instance, external queries will be rewritten with internal IPs, making services inaccessible from outside. Use separate instances for true split behavior.
- Wildcard DNS rewrites (
*.example.com -> 192.168.1.100) simplify configuration when all services share a reverse
proxy.
- Test from both internal and external perspectives after changes.
- Avoid firewall-integrated DNS blocking plugins (e.g., pfBlockerNG on pfSense). A core firewall update can silently
break the plugin and kill all DNS resolution network-wide. Run DNS filtering as a standalone service (Pi-hole, AdGuard Home) on separate infrastructure.
mDNS Across VLANs
Multicast DNS (mDNS, .local domains) is link-local -- it does not cross routed network boundaries. Devices on different VLANs cannot discover each other via mDNS without a reflector/proxy.
Avahi Reflector
Avahi is the standard mDNS/DNS-SD implementation on Linux. Enable the reflector to bridge mDNS across VLANs:
# /etc/avahi/avahi-daemon.conf
[server]
allow-interfaces=br-lan,eth1.2 # Limit to specific interfaces
[reflector]
enable-reflector=yesOn OpenWrt: opkg install avahi-daemon, then configure interfaces.
Restrict interfaces: By default Avahi listens on all interfaces including WAN. Always limit to the specific internal interfaces that need mDNS bridging.
When to Use
- IoT device discovery from trusted LAN (Chromecast, AirPlay, printers)
- Home automation controllers discovering devices across VLANs
- Do NOT reflect mDNS to/from guest or untrusted networks
Recursive vs Authoritative DNS
- Recursive resolver (Unbound, Pi-hole's FTL, AdGuard Home): Takes a client query, walks the DNS hierarchy from root
servers to authoritative servers, caches the result. This is what clients talk to.
- Authoritative server (PowerDNS, CoreDNS, BIND): Holds zone data and answers queries for domains it owns. Does not
recurse.
Homelab Pattern
Run a recursive resolver (Unbound or AdGuard Home) that:
1. Serves as the network's DNS resolver with ad blocking 2. Forwards your local domain (e.g., home.example.com) to a local authoritative server or uses local zone overrides 3. Resolves everything else via upstream DNS (Cloudflare 1.1.1.1, Quad9 9.9.9.9) or by doing full recursion from root servers
DNS over TLS / HTTPS
Always encrypt upstream DNS queries to prevent ISP snooping:
- DNS over TLS (DoT): Port 853. Supported by Unbound, AdGuard Home natively.
- DNS over HTTPS (DoH): Port 443. Harder to block, looks like regular HTTPS.
- DNS over QUIC (DoQ): UDP-based. Supported by AdGuard Home.
Configure your recursive resolver to use encrypted upstream, then serve plain DNS internally (encryption between resolver and clients on the LAN is usually unnecessary).
Firewall Rules
nftables
nftables is the modern Linux firewall framework, replacing iptables. It uses a unified syntax for IPv4, IPv6, ARP, and bridge filtering.
Core Concepts
- Table: Container for chains. Has a family:
ip,ip6,inet(both),arp,bridge,netdev. - Chain: Container for rules. Base chains attach to Netfilter hooks; regular chains are called via
jump/goto. - Rule: Match expressions + verdict statement.
Families
Use inet for dual-stack rules that apply to both IPv4 and IPv6. Use ip or ip6 only when rules are protocol-specific (e.g., ICMPv6 neighbor discovery).
Chain Types and Hooks
filter— Packet filtering: prerouting, input, forward, output, postroutingnat— Address translation: prerouting, input, output, postroutingroute— Rerouting (mangle equivalent): output only
Hooks determine where in the packet path the chain fires:
- input: Packets destined for the local machine
- forward: Packets routed through the machine
- output: Packets originating from the local machine
- prerouting/postrouting: Before/after routing decisions
Priority
Chain priority determines evaluation order. Lower numbers run first.
-400(conntrack defrag) — Defragmentation-300(raw) — Pre-conntrack filtering-200(conntrack) — Connection tracking-100(dstnat) — DNAT (port forwarding)0(filter) — Standard filtering100(srcnat) — SNAT/masquerade
Verdict Statements
accept: Accept packet, stop evaluating current chain (but later chains at same hook still run)drop: Drop packet immediately, no further evaluation anywherereject: Drop with ICMP error responsejump <chain>: Evaluate rules in target chain, then returngoto <chain>: Evaluate rules in target chain, don't return
Critical: accept in one chain does NOT prevent evaluation by later chains at the same hook with higher priority numbers. drop is always final.
Connection Tracking
Stateful filtering uses ct state:
ct state established,related accept # Allow return traffic
ct state invalid drop # Drop malformed packets
ct state new tcp dport { 22, 80, 443 } accept # Allow new connections to servicesAlways place conntrack rules early -- they handle the bulk of traffic and are fast.
Minimal Host Firewall
table inet filter {
chain input {
type filter hook input priority filter; policy drop;
ct state established,related accept
ct state invalid drop
iifname "lo" accept
icmp type echo-request accept
icmpv6 type { echo-request, nd-neighbor-solicit, nd-router-advert } accept
tcp dport { ssh } accept
}
chain forward {
type filter hook forward priority filter; policy drop;
}
chain output {
type filter hook output priority filter; policy accept;
}
}Rate Limiting
# Limit SSH connection attempts
tcp dport 22 ct state new limit rate 3/minute burst 5 packets accept
# Log and drop excess
tcp dport 22 ct state new log prefix "SSH-excess: " dropNAT / Masquerade
table ip nat {
chain postrouting {
type nat hook postrouting priority srcnat;
oifname "eth0" masquerade
}
chain prerouting {
type nat hook prerouting priority dstnat;
tcp dport 8080 dnat to 192.168.1.100:80
}
}Sets and Maps
# Named set for allowed IPs
define ADMIN_IPS = { 192.168.1.10, 192.168.1.11 }
tcp dport 22 ip saddr $ADMIN_IPS accept
# Verdict map for port-based routing
tcp dport vmap { 80 : accept, 443 : accept, 22 : jump ssh_filter }Persistence
Save and load rulesets:
nft list ruleset > /etc/nftables.conf
nft -f /etc/nftables.confEnable nftables.service for persistence across reboots.
IPv6 Firewall Rules
Dual-Stack with nftables
Use inet family for rules that apply to both IPv4 and IPv6. Use ip6 only for protocol-specific rules:
table inet filter {
chain input {
type filter hook input priority filter; policy drop;
ct state established,related accept
ct state invalid drop
iifname "lo" accept
# IPv4 ICMP
icmp type echo-request accept
# ICMPv6 -- essential for IPv6 operation
icmpv6 type { echo-request, echo-reply } accept
icmpv6 type { nd-neighbor-solicit, nd-neighbor-advert,
nd-router-solicit, nd-router-advert } accept
# Services (dual-stack)
tcp dport { ssh } accept
}
}ICMPv6 Filtering Policy
ICMPv6 is not optional -- blocking it breaks IPv6 networking entirely.
- Destination Unreachable (1) — Allow transit: communication maintenance
- Packet Too Big (2) — Allow transit: PMTU discovery, blocking breaks large packets
- Time Exceeded (3) — Allow transit: traceroute, path diagnostics
- Echo Request/Reply (128/129) — Allow (policy): connectivity testing
- Router Solicitation (133) — Link-local only: router discovery, never route
- Router Advertisement (134) — Link-local only: prefix advertisement, never route
- Neighbor Solicitation (135) — Link-local only: ARP equivalent for IPv6
- Neighbor Advertisement (136) — Link-local only: ARP reply equivalent
Use port-based ACLs to prevent Router Advertisement messages from entering the network from end-user ports (RA Guard).
Dual-Stack Security Considerations
- Maintain identical firewall policies for IPv4 and IPv6
- When updating rules, apply changes to both stacks as a single coordinated process
- If IPv6 is not actively used, disable it at the interface level to prevent unintended IPv6 traffic from bypassing
IPv4-only firewalls
- Monitor for unauthorized IPv6 tunnel traffic (6to4, Teredo) that can bypass IPv4 firewall rules
OPNsense / pfSense
For GUI-managed firewalls (OPNsense, pfSense), the same principles apply:
- Default deny inbound on all interfaces
- Explicit allow rules for each permitted flow
- Rules evaluate top-to-bottom, first match wins
- Floating rules apply across interfaces (use sparingly)
- Place more specific rules before general ones
- Log denied traffic initially to catch legitimate traffic being blocked
- Review and prune rules quarterly -- unused rules are attack surface
Post-Install Hardening
First 30 minutes after installation:
1. Change default admin password 2. Enable 2FA (OPNsense: built-in; pfSense: requires package) 3. Disable web UI access from WAN 4. Configure DNS over TLS upstream 5. Enable automatic config backups 6. Restrict RFC1918 traffic on WAN interface 7. Restrict DNS resolver to internal interfaces only -- default configurations often allow queries from all interfaces, turning the firewall into an open resolver (ISP abuse complaints follow)
Troubleshooting
Poor throughput despite fast connection: Disable hardware offloading first -- CRC, TSO, LRO. This is the most common cause in virtualized environments.
- OPNsense: Interfaces > Settings > disable Hardware CRC/TSO/LRO
- pfSense: System > Advanced > Networking > disable all hardware checksum offloading
Reboot and retest. If still slow, check IDS rule count -- too many active rulesets kill performance. Start with 2-3 recommended rulesets.
Rule ordering mistakes: In top-to-bottom first-match evaluation, a common error is placing allow-internet above block-LAN when isolating a VLAN. The allow rule matches first, and the block never fires. Always place deny rules above allow rules for the same traffic path.
Reverse Proxy
Role in Self-Hosted Architecture
A reverse proxy is the single entry point for all HTTP/HTTPS services. It terminates TLS, routes requests by hostname to backend services, and eliminates the need to expose multiple ports to the network.
Client -> Reverse Proxy (443) -> service-a:8080
-> service-b:3000
-> service-c:8443Caddy
Automatic HTTPS by default. Minimal configuration. Ideal when you want TLS to "just work" without thinking about certificate management.
Key Characteristics
- Automatic HTTPS: Obtains and renews certificates from Let's Encrypt/ZeroSSL automatically. No configuration needed
for publicly-accessible domains.
- Caddyfile syntax: Declarative, minimal. Site address implies HTTPS.
- Wildcard certificates: Supported via DNS challenge with provider plugins (e.g.,
caddy-dns/cloudflare). - Automatic HTTP->HTTPS redirect: Enabled by default.
- On-Demand TLS: Obtain certificates at request time for unknown domains.
Caddyfile Structure
{
email admin@example.com # Global options block
acme_dns cloudflare {env.CF_TOKEN} # DNS challenge for wildcards
}
# Reverse proxy with automatic HTTPS
app.example.com {
reverse_proxy localhost:8080
}
# Multiple upstreams with load balancing
api.example.com {
reverse_proxy app-01:8080 app-02:8080 {
lb_policy round_robin
health_uri /health
health_interval 30s
}
}
# Wildcard with host matching
*.example.com {
tls {
dns cloudflare {env.CF_TOKEN}
}
@grafana host grafana.example.com
handle @grafana {
reverse_proxy grafana:3000
}
@prom host prometheus.example.com
handle @prom {
reverse_proxy prometheus:9090
}
handle {
respond "Unknown service" 404
}
}Key Directives
reverse_proxy: Proxy requests to upstreams. Supports load balancing, health checks, header manipulation, buffering.file_server: Serve static files.handle/handle_path: Group directives by path or matcher.import: Reuse snippets or include external config files.tls: Override TLS settings (internal CA, DNS challenge, client certs).encode: Enable gzip/zstd compression.
Upstream Syntax
reverse_proxy localhost:8080 # Single upstream
reverse_proxy app:8080 app2:8080 # Multiple upstreams
reverse_proxy unix//run/app.sock # Unix socket
reverse_proxy srv+https://my.service # SRV record lookupSnippets for Reuse
(security-headers) {
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains"
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
Referrer-Policy "strict-origin-when-cross-origin"
}
}
app.example.com {
import security-headers
reverse_proxy localhost:8080
}Traefik
Dynamic, container-aware reverse proxy. Discovers services automatically via Docker labels, Kubernetes ingress, or file-based configuration.
Key Characteristics
- Dynamic configuration: Detects Docker containers, Kubernetes services, and file changes without restart.
- Docker integration: Labels on containers define routing rules.
- Middleware chain: Composable middleware for auth, rate limiting, headers, etc.
- Dashboard: Built-in monitoring dashboard.
- Let's Encrypt integration: Automatic certificate management with HTTP and DNS challenges.
Docker Labels Pattern
services:
app:
image: myapp:latest
labels:
- "traefik.enable=true"
- "traefik.http.routers.app.rule=Host(`app.example.com`)"
- "traefik.http.routers.app.entrypoints=websecure"
- "traefik.http.routers.app.tls.certresolver=letsencrypt"
- "traefik.http.services.app.loadbalancer.server.port=8080"Static Configuration (traefik.yml)
entryPoints:
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
scheme: https
websecure:
address: ":443"
certificatesResolvers:
letsencrypt:
acme:
email: admin@example.com
storage: /etc/traefik/acme.json
dnsChallenge:
provider: cloudflare
providers:
docker:
exposedByDefault: false
file:
directory: /etc/traefik/dynamic/Nginx Proxy Manager
GUI-based reverse proxy management built on Nginx. Lowest learning curve.
Key Characteristics
- Web UI for everything: Add proxy hosts, SSL certificates, access lists, and redirections via browser.
- Let's Encrypt integration: One-click certificate provisioning and renewal.
- Access lists: IP-based allow/deny lists via UI.
- Custom Nginx config: Advanced users can inject raw Nginx configuration.
- Best for: Users who prefer GUI management and don't need container-aware dynamic routing.
Decision Criteria
| Factor | Caddy | Traefik | Nginx Proxy Manager |
|---|---|---|---|
| Config style | Caddyfile (declarative) | YAML + Docker labels | Web UI |
| Auto HTTPS | Default behavior | Via cert resolvers | One-click |
| Docker-aware | Plugin | Native | No |
| Learning curve | Low | Medium | Lowest |
| Wildcard certs | DNS challenge plugin | DNS challenge | DNS challenge |
| Performance | Excellent | Excellent | Excellent (Nginx) |
| Middleware/plugins | Moderate ecosystem | Rich middleware | Limited (raw Nginx) |
| Best for | Simple setups, auto-TLS | Docker/K8s environments | GUI preference |
Recommendations
- Docker-heavy environment: Traefik. Dynamic discovery eliminates config management.
- Simplicity-first: Caddy. Automatic HTTPS, minimal config, easy to reason about.
- GUI management: Nginx Proxy Manager. Point-and-click for non-CLI users.
- Maximum control: Raw Nginx or Caddy with JSON config for complex routing needs.
Cloudflare Tunnels
Cloudflare Tunnels expose internal services to the internet without opening inbound firewall ports. The cloudflared daemon initiates outbound connections to Cloudflare's edge network, which routes incoming traffic back through the tunnel.
Architecture
Internet -> Cloudflare Edge -> cloudflared (outbound conn) -> Local ServiceCommon Patterns
- Direct exposure:
cloudflaredroutes directly to a local service port. Simplest, but each service needs separate
tunnel configuration.
- Tunnel to reverse proxy: Route the tunnel into Traefik/Caddy/NPM. The local proxy handles routing, SSL
termination, and auth middleware. More flexible -- one tunnel serves all services.
- With auth layer: Layer Authelia/Authentik behind the tunnel for SSO/MFA, or use Cloudflare Access at the edge for
zero-trust gating.
Cloudflare Access (Zero Trust)
Cloudflare Access adds identity verification at the edge before traffic reaches the tunnel:
- One-time codes via email
- OAuth2 (GitHub, Google, Microsoft)
- Custom IdP integration
- Stops all traffic until identity is verified -- service is invisible to unauthenticated scanners
Limitations and Risks
- Not a security layer: A tunnel makes services accessible, not secure. Weak credentials and unpatched software
remain exploitable.
- ToS for media: Streaming services (Jellyfin, Plex) through Cloudflare CDN may violate Terms of Service if caching
is enabled. Disable caching for media subdomains.
- Non-HTTP edge cases: Proxying non-HTTP protocols (SSH, databases) through tunnels can introduce unexpected
behavior. Use VPN for non-web protocols.
- Management complexity: Automating tunnel creation via API or Docker container requires custom scripting for
updates and migrations.
What NOT to Expose via Tunnels
- Admin panels: Router, Proxmox, NAS management -- keep on VPN/LAN
- Password managers: Vaultwarden and similar -- too sensitive for public access
- LAN-only tools: IoT hubs, printer dashboards -- assume trusted network
- Sensitive data portals: Private document systems, personal databases
For these services, use Tailscale/WireGuard VPN for remote access instead.
Auth Proxy Integration
When using a reverse proxy with authentication middleware:
Traefik + Authelia
# Protected service with ForwardAuth middleware
services:
app:
labels:
- "traefik.http.routers.app.middlewares=authelia@docker"
- "traefik.http.routers.app.rule=Host(`app.example.com`)"
- "traefik.http.routers.app.tls.certresolver=letsencrypt"Service Classification
- No auth needed: Public content (blogs, documentation, status pages)
- Own auth sufficient: Services with strong built-in auth (Nextcloud, Gitea)
- Auth proxy required: Services with weak/no auth (dashboards, internal tools)
- VPN only: Admin interfaces, sensitive data -- never proxy publicly
Network Security Hardening
Principles
Security is a non-negotiable default, not an optional add-on. Every recommendation here is a baseline, not an aspiration.
SSH Hardening
Configuration (/etc/ssh/sshd_config)
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
MaxAuthTries 3
MaxSessions 3
AllowAgentForwarding no
X11Forwarding no
PermitEmptyPasswords no
ClientAliveInterval 300
ClientAliveCountMax 2Key changes from defaults:
- Disable password auth: Key-based only. Eliminates brute-force attacks entirely.
- Disable root login: Force users to authenticate as themselves, then
sudo. - Restrict agent/X11 forwarding: Reduces attack surface from compromised clients.
- Client keepalive: Drop idle sessions after 10 minutes (300s \* 2).
Key Management
- Use Ed25519 keys:
ssh-keygen -t ed25519 -C "user@host" - Protect private keys with passphrases and use ssh-agent
- Rotate keys annually or when personnel changes
- Remove stale authorized_keys entries
SSH Certificate Authority
For fleets larger than a handful of hosts, SSH CA eliminates authorized_keys management. Instead of distributing individual public keys, a central CA signs short-lived certificates.
Setup with step-ca:
1. Initialize CA: step ca init (generates SSH host and user CA keys) 2. Configure hosts: add TrustUserCAKeys /path/to/ssh_user_key.pub to sshd_config 3. Configure clients: append SSH host CA key to ~/.ssh/known_hosts 4. Issue certificates: step ssh certificate user@host (default: 16h validity)
Key benefits over static keys:
- Provisioning — Static Keys: update authorized_keys on every host; SSH Certificates: one-time CA key distribution
- Scalability — Static Keys: O(users × hosts); SSH Certificates: O(1), CA key on each host
- Revocation — Static Keys: manual removal from every server; SSH Certificates: natural expiry (short-lived)
- Identity — Static Keys: bound to key file; SSH Certificates: bound to identity (principals)
Provisioners control who can request certificates:
- JWK: Password-based (simple, for personal homelabs)
- OIDC: SSO via Google Workspace, Okta -- enables Single Sign-On SSH
Short-lived certificates (~16h) minimize the window of exposure if a certificate is compromised. Users authenticate with the CA at the start of their session.
Fail2ban
Intrusion prevention that monitors logs and bans IPs exhibiting malicious behavior.
Configuration
# /etc/fail2ban/jail.local
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5
banaction = nftables-multiport # Use nftables backend
[sshd]
enabled = true
port = ssh
logpath = %(sshd_log)s
maxretry = 3
bantime = 24hCustom Filters
Create filters for self-hosted services (Nextcloud, Vaultwarden, etc.) that expose login pages. Monitor their auth logs and ban repeated failures.
Gotchas
- Whitelist your own IPs: Add management IPs to
ignoreipto avoid locking yourself out. - Don't rely solely on fail2ban: It's a mitigation, not a solution. Disable password auth for SSH entirely.
- Ban action must match your firewall: Use
nftables-multiportfor nftables, not the default iptables actions.
IDS/IPS
Suricata
High-performance network threat detection engine. Performs deep packet inspection using signature-based rulesets.
Deployment:
- OPNsense: Built-in, manageable via UI
- pfSense: Requires package installation
- Linux: Standalone installation, configure via
suricata.yaml
Log configuration:
fast.log: Human-readable alert format, lightweighteve.json: Rich JSON metadata, but very noisy -- enable only specific app-layer protocols to maintain readability- Use
fast.logfor CrowdSec integration (simpler parsing)
Performance tuning:
- Start with 2-3 essential rulesets -- each ruleset adds CPU overhead
- 3 rulesets typically cause ~27% throughput drop on 1Gbps links
- Disable hardware offloading (CRC, TSO, LRO) if throughput is poor -- offloading conflicts with packet inspection
engines
- Monitor false positive rate for the first week before adding more rules
CrowdSec
Collaborative security engine. Detects threats using YAML-based scenarios, shares intelligence with a global community, and remediates via "Bouncers."
Architecture:
- Security Engine: Parses logs, matches scenarios, makes decisions
- Hub: Repository of community-maintained parsers, scenarios, collections
- Bouncers: Enforce decisions (nftables ban, Nginx block, Traefik middleware)
- Console: Web dashboard for visualizing alerts and decisions
Configuration:
# /etc/crowdsec/acquis.d/suricata.yaml
filenames:
- /var/log/suricata/fast.log
labels:
type: suricata-fastEssential post-install steps:
1. Install collections from Hub for your services: cscli collections install crowdsecurity/suricata 2. Configure whitelists immediately -- add management IPs to prevent self-banning 3. Set up a Bouncer for your firewall (nftables) or reverse proxy
CrowdSec vs fail2ban:
fail2ban:
- Detection: Regex patterns on logs
- Intelligence: Local only
- Remediation: iptables/nftables actions
- Configuration: jail.conf + filter.d
- Scaling: Single host
- Resource usage: Low RAM, struggles with high log volume
CrowdSec:
- Detection: YAML scenarios (leakspeed/capacity/blackhole)
- Intelligence: Community-shared threat data
- Remediation: Pluggable Bouncers (firewall, proxy, CDN)
- Configuration: acquis.yaml + Hub collections
- Scaling: Multi-host with shared decisions
- Resource usage: Configurable cache_size for memory control
Both can run simultaneously. CrowdSec is the stronger choice for new deployments due to community intelligence and the separation of detection from remediation.
Suricata + CrowdSec Integration
The recommended pattern: Suricata detects, CrowdSec decides and remediates.
1. Suricata inspects packets, generates alerts in fast.log 2. CrowdSec Security Engine reads fast.log via acquis.yaml 3. CrowdSec matches alerts against installed scenarios 4. On match, CrowdSec creates a "decision" (ban, captcha, throttle) 5. Bouncer enforces the decision at the firewall or proxy level
Notifications: Integrate with Pushover for mobile alerts on CrowdSec decisions. Configure in /etc/crowdsec/profiles.yaml with application and user tokens.
Firewall Hardening
Default Deny
Every interface starts with policy drop for inbound traffic. Explicitly allow only what's needed. This applies to both the host firewall (nftables) and the network firewall (OPNsense/pfSense).
Outbound Filtering
Most homelabbers allow all outbound traffic. This is convenient but dangerous -- a compromised service can exfiltrate data or contact C2 servers.
Minimum outbound policy:
- Allow DNS only to your designated DNS servers (prevents DNS exfiltration and ensures all queries go through your
filtering resolver)
- Allow HTTP/HTTPS outbound (for updates and legitimate traffic)
- Block all other outbound from IoT and untrusted VLANs
- Log denied outbound traffic to detect compromise indicators
Per-VLAN outbound rules:
- Management: Full outbound (admin access for updates and management)
- Trusted/Lab: HTTP/HTTPS + DNS to internal resolver + specific ports as needed
- IoT: HTTP/HTTPS + DNS to internal resolver only -- block everything else. IoT devices have no legitimate reason
for arbitrary outbound
- Guest: HTTP/HTTPS + DNS only -- complete isolation from internal networks
- DMZ: Restrict to required service ports -- a compromised DMZ service with full outbound access can be used as a
pivot point
Restrict Management Access
- Firewall web UIs: Accessible only from management VLAN
- Switch management interfaces: Management VLAN only
- IPMI/iLO/iDRAC: Isolated management VLAN, never on production network
- SSH: Key-based, from management VLAN or via VPN only
Network Monitoring
SNMP
Simple Network Management Protocol for monitoring network devices (switches, routers, APs). Most managed switches support SNMPv2c or SNMPv3.
- SNMPv2c: Community-string authentication (essentially a password in plaintext). Acceptable on isolated management
VLANs only.
- SNMPv3: Authentication and encryption. Use when SNMP traffic crosses network boundaries.
Prometheus + Grafana Stack
- snmp_exporter: Scrapes SNMP data from network devices, exposes as Prometheus metrics. Use generator to build
config from MIBs.
- blackbox_exporter: Probes endpoints (ICMP ping, HTTP, DNS, TCP) to monitor reachability and certificate validity.
- node_exporter: Host-level metrics (CPU, memory, disk, network interfaces).
- Grafana dashboards: Visualize network metrics. Community dashboards exist for common setups.
What to Monitor
- Interface traffic (bytes/packets) — SNMP / node_exporter: capacity planning, anomaly detection
- Error/discard counters — SNMP: failing cables, duplex mismatch
- DNS query rate and latency — Pi-hole/AdGuard metrics: detect DNS issues or abuse
- Certificate expiry — blackbox_exporter: prevent outages from expired certs
- Firewall rule hit counts — nftables counters: identify unused rules, detect scans
- VPN peer status — WireGuard/Tailscale metrics: detect tunnel failures
- Uptime/reachability — blackbox_exporter ICMP: service availability
Service Health and Auto-Restart
Critical network services (DNS resolvers, reverse proxies, VPN endpoints) can die silently. Without auto-restart, a failed DNS resolver causes household-wide internet outages until manually noticed.
- Monit: Lightweight process monitor. Checks service health and restarts automatically. OPNsense includes Monit
built-in.
- systemd watchdog: For services managed by systemd, configure
WatchdogSec=andRestart=on-failurein the unit
file.
- Docker restart policies: Use
restart: unless-stoppedfor containerized network services.
Monitor auto-restart events -- frequent restarts indicate an underlying issue, not a healthy system.
Log Aggregation
Centralize logs from all network devices and services:
- Syslog: Most network devices support syslog output. Point them at a central collector.
- Loki + Promtail: Lightweight log aggregation that integrates with Grafana.
- Graylog: Full-featured log management with alerting.
Review firewall deny logs weekly during initial setup to catch legitimate traffic being blocked. Reduce review frequency once rules stabilize.
DHCP Security
Reservations Over Static IPs
Prefer DHCP reservations over static IP configuration on devices:
- Centralized management: All IP assignments visible in one place (DHCP server)
- Easier changes: Update the reservation, renew the lease -- no SSH to the device
- Consistency: Devices get the same IP every time via MAC binding
- DNS integration: DHCP servers can auto-register hostnames in local DNS
Static IPs are appropriate for: the DHCP server itself, the DNS server, the default gateway, and infrastructure that must function before DHCP is available.
DHCP Snooping
On managed switches, enable DHCP snooping to prevent rogue DHCP servers from hijacking client configurations. Mark switch uplink ports as trusted; all other ports as untrusted.
General Practices
- Update firmware: Switches, routers, APs, firewalls. Unpatched network devices are a common entry point.
- Backup configurations: Before any change, backup. Store backups off-device (NAS, git repo, cloud storage).
- Change default credentials: Every device, every service. No exceptions.
- Disable unused services: SNMP v1/v2 on WAN, UPnP, WPS, remote management on consumer devices.
- Document your network: Maintain a network diagram. Update it when changes are made. Future-you will thank
present-you.
TLS/SSL Certificate Management
Let's Encrypt and ACME
Let's Encrypt is a free, automated Certificate Authority. The ACME protocol (RFC 8555) automates domain validation and certificate issuance.
How ACME Works
1. Account creation: ACME client generates a key pair and registers with the CA. 2. Domain validation: CA issues challenges to prove domain ownership. 3. Certificate issuance: Client sends a CSR, CA verifies authorization, issues cert. 4. Renewal: Same process, automated. Certificates are valid for 90 days; renew at 60 days. 5. Revocation: Client signs revocation request with account key.
Challenge Types
- HTTP-01: CA fetches
http://<domain>/.well-known/acme-challenge/<token>— public-facing servers with port 80 open - DNS-01: Client creates
_acme-challenge.<domain>TXT record — wildcard certs, internal servers, no port 80 - TLS-ALPN-01: CA connects to port 443 with special ALPN protocol — when only port 443 is available
DNS Challenge for Internal Services
Internal/homelab services typically use DNS-01 challenges because:
- Services may not be publicly accessible (no port 80/443 from internet)
- Wildcard certificates require DNS-01 (no alternative)
- One wildcard cert (
*.home.example.com) covers all services
Wildcard Certificates
A wildcard certificate (*.example.com) covers all single-level subdomains:
- Matches:
app.example.com,grafana.example.com - Does NOT match:
app.sub.example.com(two levels deep) - Does NOT match:
example.com(bare domain -- get a separate cert or SAN)
ACME Clients
- Caddy — Built-in: zero-config HTTPS by default
- Traefik — Built-in: via
certificatesResolversconfig - certbot — Standalone: original LE client, many plugins
- acme.sh — Standalone: shell-only, no dependencies, many DNS providers
- lego — Standalone: Go binary, CLI and library
acme.sh with DNS Provider
# Install
curl https://get.acme.sh | sh
# Set DNS provider credentials (Cloudflare example)
export CF_Email="you@example.com"
export CF_Key="your-global-api-key"
# Issue wildcard certificate
acme.sh --issue -d "*.example.com" --dns dns_cf
# Auto-renewal is configured via cron by the installerCertificate Storage Patterns
- Reverse proxy manages certs: Caddy/Traefik handle issuance and renewal internally. Simplest approach -- the proxy
is the single point of TLS termination.
- Centralized cert management: acme.sh or certbot obtains certs, stores them on shared storage or distributes via
automation (Ansible). Multiple services reference the same certificate files.
- Per-service certs: Each service manages its own certificate. Avoid this in homelabs -- it's error-prone and hard
to monitor.
Recommended Approach
Use the reverse proxy as the sole TLS termination point:
Client --[HTTPS]--> Reverse Proxy --[HTTP]--> Backend Services- Only the reverse proxy needs certificates
- Backend services run plain HTTP on internal networks
- One place to manage TLS configuration, ciphers, and renewals
- Wildcard cert on the reverse proxy covers everything
Certificate Monitoring
Monitor certificate expiry. If auto-renewal fails silently, services break when certs expire. Options:
- Prometheus
ssl_exporterorblackbox_exporter(probe HTTPS endpoints) certbot renew --deploy-hookto trigger alerts on failure- Uptime monitoring tools (Uptime Kuma) that check certificate validity
TLS Configuration
When configuring TLS manually:
- Minimum TLS version: 1.2 (disable TLS 1.0 and 1.1)
- Prefer TLS 1.3: Better performance (fewer round trips) and stronger security
- HSTS header:
Strict-Transport-Security: max-age=31536000; includeSubDomains - OCSP stapling: Enable when supported (reduces client-side certificate checks)
- Don't configure cipher suites manually unless you have a specific reason -- modern defaults (Go's crypto/tls, OpenSSL
3.x) are already optimal
VLAN Segmentation
Design Principles
VLANs logically separate traffic on shared physical infrastructure. Segment by function and trust level, not by device type.
Standard Homelab Segments
- VLAN 10 — Management (High trust): hypervisors, switches, routers, IPMI/iLO
- VLAN 20 — Trusted / Lab (High trust): VMs, Docker hosts, Kubernetes nodes
- VLAN 30 — IoT (Low trust): smart home, cameras, sensors, media players
- VLAN 40 — Guest (No trust): guest Wi-Fi, visitor devices
- VLAN 50 — Storage (High trust): NAS, iSCSI targets, backup servers
- VLAN 99 — DMZ (Medium trust): publicly exposed services
Adapt to your environment. The specific VLAN IDs don't matter -- consistency does.
Inter-VLAN Firewall Rules
Creating VLANs without firewall rules is security theater. Each VLAN boundary needs explicit policy:
- Management -> All: Allow (admin access to all segments)
- Trusted -> IoT: Allow (control smart devices from trusted network)
- IoT -> Trusted: Deny except established/related (IoT initiates nothing upstream)
- IoT -> Internet: Allow with DNS filtering (devices phone home; block known bad)
- Guest -> Internet: Allow (internet only, no local access)
- Guest -> Any local: Deny (complete isolation from internal networks)
- Storage -> Trusted: Allow established/related only (responds to requests)
- DMZ -> Internal: Deny (compromised DMZ cannot pivot inward)
Hardware Requirements
VLAN implementation requires VLAN-aware (managed) networking equipment:
- Router/Firewall: Must support VLAN tagging -- OPNsense, pfSense, OpenWrt, or enterprise gear. The router
terminates VLANs and enforces inter-VLAN policy.
- Managed switches: Must support 802.1Q VLAN tagging. Consumer "unmanaged" switches pass all traffic without VLAN
awareness. TP-Link Omada, UniFi, MikroTik are common homelab choices.
- Access points: Must support VLAN tagging per SSID to map wireless networks to their respective VLANs (e.g.,
"Guest" SSID -> VLAN 40, "IoT" SSID -> VLAN 30).
Trunk vs Access Ports
- Trunk ports: Carry multiple VLANs tagged with 802.1Q headers. Used between switches, between switch and router,
and between switch and hypervisor.
- Access ports: Carry a single untagged VLAN. Used for end devices that don't understand VLAN tags (most clients,
printers, IoT devices).
- Native VLAN: The untagged VLAN on a trunk port. Set this to an unused VLAN (not VLAN 1) to prevent VLAN hopping
attacks.
Common Mistakes
- VLAN 1 as production: VLAN 1 is the default on most switches and carries untagged traffic. Use it only for switch
management or not at all.
- No inter-VLAN firewall rules: VLANs without firewall rules provide zero security benefit -- they only separate
broadcast domains.
- Flat management access: Management interfaces (switch web UIs, IPMI, iLO) should live on a dedicated management
VLAN, not on the same network as user devices.
- Over-segmentation: Every VLAN adds complexity. Start with 3-4 VLANs and add more only when you have a clear
security or performance reason.
Layer 2 Security
VLANs provide logical separation but are vulnerable to Layer 2 attacks. Managed switches must be configured with these protections:
DHCP Snooping
Prevents rogue DHCP servers from hijacking client configurations:
- Trusted ports: Uplinks and ports connected to legitimate DHCP servers
- Untrusted ports: All user-facing ports (default)
- Switch builds a binding table mapping MAC -> IP -> port from observed DHCP exchanges
- Drops unauthorized DHCP offer/ack messages on untrusted ports
- Rate-limit DHCP requests to prevent starvation attacks
DHCP snooping is a prerequisite for Dynamic ARP Inspection and IP Source Guard.
Dynamic ARP Inspection (DAI)
Prevents ARP poisoning / Man-in-the-Middle attacks:
- Intercepts all ARP requests and responses
- Validates IP-to-MAC pairings against the DHCP snooping binding table
- Drops ARP packets that don't match -- prevents an attacker from claiming another device's IP address
- Requires DHCP snooping enabled first
- For devices with static IPs (servers, gateways): add static entries to the binding table
Port Security
Limits MAC addresses per port to prevent CAM table overflow attacks:
- Set maximum MAC addresses per port (typically 1-3 for end devices)
- Violation actions: Shutdown (disable port), Restrict (drop + log), Protect (drop silently)
- Sticky MACs: Dynamically learned MACs saved to config, persist across reboots
- Prevents attackers from flooding the switch with random MACs, which forces it into hub mode (forwarding all traffic to
all ports)
VLAN Hopping Prevention
- Disable DTP: Set all user-facing ports to
switchport mode access-- prevents devices from negotiating a trunk - Unused ports: Shut down or assign to an isolated dead VLAN
- Native VLAN: Use a dedicated unused VLAN for native VLAN on trunks, never VLAN 1
- Explicit trunking: Configure trunk ports manually, never auto-negotiate
STP Protections
- BPDU Guard: Enable on access ports -- disables the port if it receives STP BPDU frames (prevents rogue switches
from manipulating spanning tree)
- Root Guard: Prevents unauthorized switches from becoming the STP root bridge
- Loop Guard: Detects unidirectional link failures that could cause loops
VPN Tunnels
WireGuard
Modern VPN protocol. ~4,000 lines of code in the Linux kernel module. Fast, simple, cryptographically opinionated (no cipher negotiation).
Core Concepts
- Interface: A virtual network interface (
wg0) with a private key and listen port. - Peer: A public key + allowed IPs + optional endpoint. Each peer is identified solely by its public key.
- Allowed IPs: Acts as both routing table and ACL. Only traffic matching a peer's allowed IPs is sent to/accepted
from that peer.
- Cryptokey routing: Packets are routed based on peer public keys, not IP addresses.
Key Generation
umask 077
wg genkey | tee privatekey | wg pubkey > publickeyOr generate a preshared key for additional post-quantum security:
wg genpsk > presharedkeyConfiguration
# /etc/wireguard/wg0.conf
[Interface]
PrivateKey = <server-private-key>
Address = 10.0.0.1/24
ListenPort = 51820
# Optional: run commands on up/down
PostUp = nft add rule inet filter input udp dport 51820 accept
PostDown = nft delete rule inet filter input udp dport 51820 accept
[Peer]
PublicKey = <client-public-key>
PresharedKey = <optional-preshared-key>
AllowedIPs = 10.0.0.2/32
# For site-to-site, include remote subnets:
# AllowedIPs = 10.0.0.2/32, 192.168.2.0/24Management
wg-quick up wg0 # Bring interface up
wg-quick down wg0 # Bring interface down
wg show # Show current peers and stats
systemctl enable wg-quick@wg0 # Persist across rebootsPersistent Keepalive
When a peer is behind NAT, set PersistentKeepalive = 25 to keep the NAT mapping alive. Without it, incoming packets may not reach the peer after idle periods. Only enable when needed -- it makes WireGuard slightly more chatty.
Topologies
- Hub-and-spoke (star): All clients connect to a central server. Simple, but all traffic routes through the hub. Use
for remote access VPN.
- Site-to-site: Two servers with static IPs connect networks. AllowedIPs includes the remote subnet. Requires port
forwarding (UDP 51820) on both sides.
- Full mesh: Every peer connects to every other peer. Complex to manage manually -- this is where Tailscale excels.
Use When
- Site-to-site tunnels between fixed endpoints with static IPs
- Traditional VPN gateway (full-tunnel routing through home network)
- Zero dependency on external services required
- Resource-constrained devices (routers, embedded systems) -- kernel module uses ~5 MB
- Maximum control over every aspect of the tunnel
Tailscale
Mesh VPN built on WireGuard. Adds automatic key management, NAT traversal, ACLs, and DNS. The coordination server handles the control plane; data flows peer-to-peer.
Architecture
- Coordination server: Exchanges public keys and network state between devices. Run by Tailscale Inc., or self-host
with Headscale.
- DERP relays: Fallback when direct connections fail. Encrypted end-to-end -- relays cannot read traffic.
- Direct connections: Tailscale uses STUN and other NAT traversal techniques to establish direct WireGuard tunnels
between peers whenever possible.
Key Features
- MagicDNS: automatic DNS names for all devices (
hostname.tailnet-name.ts.net) - ACLs: policy engine for access control, defined in JSON/HuJSON
- Exit nodes: route all traffic through a specific device (one-click toggle)
- Subnet routers: advertise local subnets to the tailnet without installing Tailscale on every device
- Taildrop: peer-to-peer file transfer between tailnet devices
- SSH: Tailscale SSH eliminates SSH key management
- Funnel: expose services to the public internet via Tailscale's infrastructure
Subnet Router Pattern
Instead of installing Tailscale on every device in a network, run a subnet router:
# On the subnet router device:
tailscale up --advertise-routes=192.168.1.0/24,192.168.2.0/24
# Enable IP forwarding:
echo 'net.ipv4.ip_forward = 1' | sudo tee /etc/sysctl.d/99-tailscale.conf
sudo sysctl -p /etc/sysctl.d/99-tailscale.confThen approve the routes in the Tailscale admin console.
Headscale (Self-Hosted)
Open-source reimplementation of the Tailscale coordination server. Full control over the control plane, but you manage the infrastructure. Tailscale clients connect to your Headscale instance instead of Tailscale's servers.
Use When
- Multiple devices across different networks need mesh connectivity
- NAT traversal required (CGNAT, hotel WiFi, mobile networks)
- Want DNS-based service discovery without manual configuration
- Need access controls without writing iptables rules
- Sharing access with other users via SSO
WireGuard vs Tailscale Decision
WireGuard:
- Topology: Hub-and-spoke
- NAT traversal: Manual port forwarding
- Key management: Manual
- Config complexity: High (per-peer config files)
- External dependency: None
- RAM overhead: ~5 MB (kernel module)
- DNS: Manual setup
- ACLs: iptables / nftables
- Cost: Free (GPLv2)
Tailscale:
- Topology: Full mesh
- NAT traversal: Automatic
- Key management: Automatic
- Config complexity: Zero-config (SSO login)
- External dependency: Coordination server
- RAM overhead: ~30-50 MB (userspace daemon)
- DNS: MagicDNS (automatic)
- ACLs: Built-in policy engine
- Cost: Free up to 100 devices
Topology Resilience
Hub-and-spoke has a single point of failure: if the hub goes down, the entire VPN is unreachable. Mesh (Tailscale) is resilient -- if one node fails, all other nodes remain connected to each other. Mesh can also be faster for device-to-device traffic because it avoids routing through a central bottleneck.
Choose topology based on failure tolerance requirements, not just convenience.
Hybrid Pattern
Common to run both:
- Tailscale for day-to-day remote access to services (mesh, zero-config)
- WireGuard as backup VPN gateway for full-tunnel routing or when Tailscale's coordination server is unreachable
High Availability Site-to-Site
For multi-location homelabs requiring redundancy, deploy parallel WireGuard routers with dynamic routing for automatic failover.
Architecture
Site A Site B
Router 1 ---[WG tunnel]--- Router 3
Router 2 ---[WG tunnel]--- Router 4
| |
OSPF OSPF
| |
LAN Router LAN RouterTwo independent WireGuard tunnels provide redundancy. OSPF tracks link state and reroutes traffic within seconds if one tunnel fails.
WireGuard Configuration for OSPF
Key differences from standard WireGuard config:
- `AllowedIPs = 0.0.0.0/0, ::/0`: Pass all packets to the peer, letting the routing daemon decide where traffic goes
- `Table = off`: Prevent
wg-quickfrom adding static routes that conflict with OSPF's dynamic routing - Each router pair shares a private transit subnet (e.g.,
10.99.13.0/24)
BIRD Routing Daemon
Use BIRD 2.x for OSPF between WireGuard routers:
- Assign unique Router IDs (by convention, matching the router's IP)
- Define OSPF areas for each tunnel pair
- Export site subnets to share with the remote site
- Filter out transit subnets (WireGuard link IPs) from propagation to LAN
- Alternative: use iBGP instead of OSPF for LAN router integration
Failover Behavior
When a WireGuard link fails:
1. OSPF detects link-state change (dead interval, typically 40s) 2. OSPF updates routing table on all routers 3. LAN routers reroute traffic through the surviving tunnel 4. Convergence within seconds of detection
DNS Across Sites
- Use conditional forwarding: Site A's DNS forwards
*.site-b.lanqueries to Site B's DNS server - Use a real domain (not
.lanor.local) for wildcard SSL certificate support - Tailscale alternative: MagicDNS automatically resolves hostnames across the entire tailnet without manual DNS
configuration
OpenVPN
Legacy. Slower, more complex, larger attack surface than WireGuard. Only choose OpenVPN when you specifically need TCP-based tunneling to bypass restrictive firewalls that block UDP. For all new deployments, use WireGuard or Tailscale.