Coffee and Nicotine
From "Picture Phixer" to Full RV Security Stack: A 12-Hour AI-Assisted Deep Dive
What started as a quick image editing tool turned into one of the most educational — and humbling — nights I've had in tech.
How It Started
I sat down to build a simple image processing tool I was calling "Picture Phixer." Clean idea, quick project. Then, as often happens when you're deep in a Linux terminal with an AI co-pilot and a cellular router, one thing led to another.
Six hours later, I had a full enterprise-grade security stack running on a Dell OptiPlex 5050 bolted into my RV.
This is that story.
The Stack We Built
My rig is a Dell OptiPlex 5050 (i7-7700, 32GB RAM) running Debian 12, connected to a GL-iNet GL-X3000 Spitz AX cellular router via an ALFA AWUS1900 WiFi adapter. Not exactly a data center — but with Docker, it doesn't have to be.
By the end of the night, the security stack included:
DNS & Ad Blocking: AdGuard Home + Unbound (recursive DNS resolver pointing directly to root servers — no third-party DNS middlemen)
Intrusion Detection: Suricata (7 active rules, live packet inspection) + Zeek (deep network analysis and logging)
Traffic Analysis: ntopng (real-time flow visualization)
Threat Intelligence: CrowdSec (collaborative threat blocking) + CrowdSec Bouncer
Host Security: Wazuh (SIEM + host intrusion detection) + OpenSnitch (application-level firewall)
Network Privacy: WireGuard VPN + Tor (for when you need it)
Remote Access: Tailscale (zero-config mesh VPN — genuinely magical)
Firewall: UFW with LAN-only rules + fail2ban (SSH, Node-RED, and Grafana jails)
Telemetry Pipeline: InfluxDB + Grafana + Mosquitto (MQTT) + Node-RED — all feeding into a live security dashboard
All containerized. All networked together. All running on a machine that fits under a passenger seat.
The DNS Rabbit Hole
Here's where the night got interesting — and educational.
Everything was deployed. Containers were green. And then... no internet. On the console, anyway. My laptop was fine.
What followed was three hours of methodical elimination:
- Router DNS? Responding to Docker containers but not to the host.
- Cellular carrier DNS (10.177.0.34)? Reachable through the router but not directly.
- Pi-hole? Port conflicts. Replaced with AdGuard.
- AdGuard? Running, listening on port 53, not resolving. Config file was empty.
- Tailscale? Kept overwriting /etc/resolv.conf every time it tried to connect.
The root cause turned out to be elegant in a frustrating way: the GL-iNet router accepts DNS queries from Docker container IPs (172.19.0.x) but silently blocks them from the host IP (10.10.30.240). We verified this by running nslookup from inside a Node-RED container — it resolved instantly. From the host? Timeout.
The fix: AdGuard Home on the host network, pointed directly at the cellular carrier's DNS servers, with /etc/resolv.conf locked via chattr +i to prevent Tailscale from overwriting it.
One config file. Four hours of detective work to find it.
What Suricata Found
While we were fighting DNS, Suricata was quietly doing its job. Within the first five minutes of running, it had processed over 7,000 packets and generated 144 alerts. The most interesting findings:
Port 6464 traffic to M247 Ltd IPs — traced back to Windscribe VPN client running in the background. Removed.
DNS bypass alerts — the host was querying the router directly instead of going through Pi-hole/AdGuard. This was us, but it's exactly the kind of lateral movement a compromised device would make.
Tailscale infrastructure IPs (192.200.0.x) — confirmed legitimate after cross-referencing with Tailscale's published IP ranges.
This is the value of running IDS on your own network. You find out real fast what's actually talking to what.
Lessons Learned
1. Cellular networks are not your friend for DNS.
Carrier-grade NAT, private DNS servers only reachable through the carrier's own infrastructure, and firewalls you can't control. Plan for this from day one. Run your own resolver.
2. YAML cares about spaces. A lot.
When you're typing commands on a TTY at midnight, every character counts. One three-space indent instead of four cost us 45 minutes. Python one-liners that output YAML programmatically are your friend.
3. AI as a pair programmer is genuinely useful — but it needs context.
Working with Claude over a multi-hour session, I built a living JSON document we called the "brain" — a snapshot of the full system state, all configs, all IPs, all action items. When context started to drift, we'd reload it. This is the right pattern for long technical sessions with an LLM.
4. OpenSnitch will humble you.
An application-level firewall that prompts for every new outbound connection is incredibly powerful. It's also going to wake you up to how chatty modern software is. Set your rules carefully before you enable it — or your Cinnamon desktop session will die on login because a system process can't get a response through.
5. Know your display manager.
Grey screen on login after a crash. Right click does nothing. Cursor works. Spent an hour troubleshooting what turned out to be OpenSnitch blocking the Cinnamon session manager's D-Bus response. ~/.xsession-errors told us immediately once we thought to look.
The Bigger Picture
This stack didn't start as a security project. It started as a home automation platform for full-time RV living — Node-RED for automation, InfluxDB for telemetry, Grafana for dashboards, MQTT for device communication. The security layer came after because when your "home network" is a cellular connection in a parking lot somewhere in America, the threat model is very different from a house with a cable modem.
You don't control the upstream. You don't control who else is on the tower. You can't call your ISP when something looks weird. You have to be your own SOC.
Is this overkill for an RV? Maybe. But it's also the best hands-on security education I've ever had — and it runs on hardware that cost less than a month of cloud security tooling.
What's Next
The stack is running. The immediate priorities are:
- Migrate all service accounts to a dedicated MPDC email (no personal accounts on infrastructure)
- Deploy Vaultwarden for self-hosted password management (KeePass-compatible, zero cloud)
- Build the Grafana security dashboards to surface Suricata + Zeek + CrowdSec data
- Complete the Node-RED automation flows for network monitoring alerts
- Run a full Lynis security audit
And yes, eventually, finish Picture Phixer.
Building something similar or have questions about any part of this stack? Drop a comment — happy to share configs and lessons learned.