Skip to content

Project Structure

Repository Layout

control-center/
├── .gitignore
├── Dockerfile                    # Python 3.12 + iptables + wireguard-tools
├── docker-compose.yml            # Host network, NET_ADMIN
├── requirements.txt              # fastapi, uvicorn, aiohttp, pyyaml
├── data/
│   ├── .gitkeep
│   ├── rules.json                # Firewall config (runtime, gitignored)
│   └── audit.jsonl               # Audit events (runtime, gitignored)
├── app/
│   ├── __init__.py
│   ├── main.py                   # FastAPI app, lifespan, router mounts
│   ├── config.py                 # Environment variables, paths, constants
│   ├── models.py                 # Pydantic models
│   ├── static/
│   │   └── index.html            # Single-page frontend
│   ├── services/
│   │   ├── __init__.py
│   │   ├── iptables.py           # iptables operations, rule application
│   │   ├── pihole.py             # Pi-hole v5/v6 API client
│   │   ├── traefik.py            # Traefik route management (YAML)
│   │   ├── vpn.py                # WireGuard monitoring
│   │   ├── audit.py              # Structured audit logging
│   │   └── config_store.py       # Config persistence (JSON)
│   └── routers/
│       ├── __init__.py
│       ├── status.py             # GET /api/status
│       ├── firewall.py           # Rules CRUD, profiles, killswitch
│       ├── dns.py                # Pi-hole endpoints
│       ├── routes.py             # Traefik route management
│       ├── vpn.py                # GET /api/vpn/status
│       ├── audit.py              # GET /api/audit/events
│       └── debug.py              # iptables dump, blocked traffic

Architecture

main.py (FastAPI app + lifespan)
  ├── routers/ (API endpoints)
  │   ├── status.py    → services/iptables, pihole, vpn, traefik, config_store
  │   ├── firewall.py  → services/iptables, audit, config_store
  │   ├── dns.py       → services/pihole, audit
  │   ├── routes.py    → services/traefik, audit
  │   ├── vpn.py       → services/vpn
  │   ├── secrets.py   → services/secrets
  │   ├── audit.py     → services/audit
  │   └── debug.py     → services/iptables
  └── services/ (business logic)
      ├── iptables.py     (subprocess: iptables, ping)
      ├── pihole.py       (aiohttp: Pi-hole API)
      ├── traefik.py      (file I/O: vm-services.yml)
      ├── vpn.py          (subprocess: wg show all dump)
      ├── secrets.py      (file I/O: secrets.yml + SSH deploy)
      ├── audit.py        (file I/O: audit.jsonl)
      └── config_store.py (file I/O: rules.json)

Key Design Patterns

Service Layer

All business logic lives in services/. Routers are thin — they validate input, call services, and return responses. This makes services testable independently of HTTP.

Config as Code

All configuration is in config.py using environment variables with sensible defaults. No hardcoded values in service files.

Audit Integration

Every router that modifies state calls audit.log_event():

audit.log_event("firewall.rule_added", {
    "rule_id": r.id, "description": r.description,
    "protocol": r.protocol, "port": r.port,
}, severity="info")

Legacy Compatibility

The config loader handles old config files:

data.pop("ssh_forward", None)  # Strip removed field