Skip to main content
Back to Journal
user@argobox:~/journal/2026-02-08-the-security-overhaul-and-the-hardcoded-ip
$ cat entry.md

The Security Overhaul and the Hardcoded IP

○ NOT REVIEWED

The Security Overhaul and the Hardcoded IP

Duration: Full day across 4 parallel sessions Components: ArgoBox network page, lab engine security, admin editor UX, apkg v0.6.0 Files Changed: 15+ Bugs Found: 1 production security issue


The Discovery That Made Me Wince

Started the day with a network page overhaul. Took a deep look at /command/network/ and realized the entire thing was a sparse placeholder with no real data. But before I could dig into that, I found something worse.

The /projects/build-swarm.astro page had this hardcoded in the frontend:

http://10.0.0.199:8090/api/v1

A real internal IP. Unreachable from Cloudflare Pages. Dead code that would break in production if anyone actually hit that page.

That's the kind of bug that lives in production for months before someone notices. "Why is the build swarm dashboard offline?" "Oh, the code is trying to talk to a private IP from a CDN."

Changed it to /api/gateway (the Cloudflare Function proxy) and moved on, annoyed at myself.


The Network Page Rebuild

The /command/network/ page needed structure. Created a new centralized data module: src/config/network-data.ts. Single source of truth for everything network-related.

MILKY_WAY_HOSTS: 7 systems
ANDROMEDA_HOSTS: 5 systems
BUILD_SWARM_NODES: 8 nodes
CLOUDFLARE_TUNNEL_SERVICES: 8 services
TAILSCALE_NODES: 8 VPN nodes

All sanitized per the galactic identity system. All typed. All in one place instead of sprinkled across 20 pages.

Rewrote the page with 6 new sections:

  • Stats bar (gateway status, tunnel count, VPN mesh health)
  • Galaxy cards with CIDRs, gateways, host lists
  • Filterable host inventory grid
  • Network topology diagram
  • Cloudflare tunnel service cards
  • VPN mesh node cards

Built clean, no errors.


The Lab Engine Gets Five Layers of Security

The lab engine was exposed via Cloudflare with zero authentication. Anyone finding port 8094 could create containers, destroy them, escape isolation. The fix went deep.

Layer 1 — HMAC-Signed Requests Frontend signs every API call with HMAC-SHA256(secret, "{timestamp}:{METHOD}:{path}"). Backend rejects unsigned or expired (>60s) requests. Stops curl scripts and random attackers cold.

Layer 2 — Cryptographic Session Tokens Each session gets an IP-bound HMAC token: HMAC(session_id:client_ip:expiry). Required for all session operations. Can't hijack a session from a different IP.

Layer 3 — Random Container Passwords Each container gets secrets.token_urlsafe(12) instead of the hardcoded string "argobox". Password returned in the API response for the terminal UI. Never travels over a network.

Layer 4 — Proxmox Network Firewall ebtables drops inter-container traffic on the lab bridge. iptables blocks containers from reaching the internet, private networks, or the Proxmox management interface (ports 8006, 22). Each lab is actually isolated.

Layer 5 — Enhanced Rate Limiting Sliding window rate limiter that survives engine restarts. Max 2 concurrent sessions per IP. Global 30 creations per minute cap. 128-bit session IDs. Max 1 WebSocket per session.

Removed the ability to enumerate active sessions (GET /api/labs/active is gone). Removed the ability to spoof client IP in the request body.

Five layers. Not perfect, but honest.


Admin Editor Gets Some Love

The editor modals were rendering off-screen. Turned out position: fixed doesn't center things the way <dialog> does natively. Switched everything over.

Float menu now clamps to viewport boundaries. Inline chat popover is responsive: min(360px, calc(100vw - 24px)). Small fixes, but they stack up.

Also added a post browser. Instead of navigating back to /admin/ to find an existing post, you can now search through all posts and journals from the editor itself. Injected the full post/journal collection into the page via getCollection() and a bit of JavaScript tab switching.


apkg v0.6.0 in the Same Breath

Finished the v0.6.0 release notes for the Gentoo package manager. Rewrote the CLI from argparse (1209 lines, 46 custom wrappers) to Click (1018 lines, zero wrappers). Added Rich for better terminal output.

Found 7 bugs during testing:

  • USE flags regex was looking for + flag (with space) but equery outputs +flag (no space)
  • Emerge's dependency tree flag is --deep N, not --depth=N
  • qsize scanning all 1678 packages takes 120+ seconds; rewrote to read /var/db/pkg/*/SIZE directly (0.23 seconds)
  • qsize -m rounds small packages to 0; switched to -k
  • Click's __version__ doesn't exist on the module; used importlib.metadata instead
  • ValidationError defined in two places

Fixed them all. Added a test suite: apkg dev test runs 29 automated tests across core, Gentoo tools, and command functions.


The Thread

The day had a theme: exposure. The hardcoded IP exposed the build swarm dashboard. The lab engine was exposed with no defenses. The admin editor was exposing a poor user experience. Each one got fixed in the same session.

Feels good when security, UX, and code quality all move at once.


The hardcoded IP still bugs me. How long was it live? How many things silently failed because of it?