roof 8650704316 feat: add authentication and authorization system
Backend:
- AuthManager (api/auth_manager.py): server-side user store with bcrypt
  password hashing, account lockout after 5 failed attempts (15 min),
  and atomic file writes
- AuthRoutes (api/auth_routes.py): Blueprint at /api/auth/* — login,
  logout, me, change-password, admin reset-password, list-users
- app.py: register auth_bp blueprint; add enforce_auth before_request
  hook (401 for unauthenticated, 403 for wrong role; only active when
  auth store has users so pre-auth tests remain green); instantiate
  AuthManager; update POST /api/peers to require password >= 10 chars
  and auto-provision email + calendar + files + auth accounts with full
  rollback on any failure; extend DELETE /api/peers to tear down all
  four service accounts; add /api/peer/dashboard and /api/peer/services
  peer-scoped routes; fix is_local_request to also trust the last
  X-Forwarded-For entry appended by the reverse proxy (Caddy)
- Role-based access: admin for /api/* (except /api/auth/* which is
  public and /api/peer/* which is peer-only)
- setup_cell.py: generate and print initial admin password, store in
  .admin_initial_password with 0600 permissions; cleaned up on first
  admin login

Frontend:
- AuthContext.jsx: React context with login/logout/me state and Axios
  interceptor for automatic 401 redirect
- PrivateRoute.jsx: route guard component
- Login.jsx: login page with error handling and must-change-password
  redirect
- AccountSettings.jsx: change-password form for any authenticated user
- PeerDashboard.jsx: peer-role landing page (IP, service list)
- MyServices.jsx: peer service links page
- App.jsx, Sidebar.jsx: AuthContext integration, logout button,
  PrivateRoute wrappers, peer-role routing
- Peers.jsx, WireGuard.jsx, api.js: auth-aware API calls

Tests: 100 new auth tests all pass (test_auth_manager, test_auth_routes,
test_route_protection, test_peer_provisioning). Fix pre-existing test
failures: update WireGuard test keys to valid 44-char base64 format
(test_wireguard_manager, test_peer_wg_integration), add password field
and service manager mocks to test_api_endpoints peer tests, add auth
helpers to conftest.py. Full suite: 845 passed, 0 failures.

Fixed: .admin_initial_password security cleanup on bootstrap, username
minimum length (3 chars enforced by USERNAME_RE regex)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 15:00:06 -04:00
2025-09-14 03:31:14 -05:00
2025-09-12 23:04:52 +03:00
2025-09-12 23:04:52 +03:00
2025-09-12 23:04:52 +03:00
2025-09-12 23:04:52 +03:00
2025-09-12 23:04:52 +03:00
2025-09-12 23:04:52 +03:00

Personal Internet Cell (PIC)

PIC is a self-hosted digital infrastructure platform. It manages DNS, DHCP, NTP, WireGuard VPN, email, calendar/contacts (CalDAV), file storage (WebDAV), a reverse proxy, and a certificate authority — all controlled from a single REST API and React web UI. No manual config file editing is required for normal operations.


Architecture

Browser
  └── React SPA (cell-webui :8081)
        └── Flask REST API (cell-api :3000, bound to 127.0.0.1)
              └── Docker SDK / config files
                    ├── cell-caddy        :80/:443      reverse proxy
                    ├── cell-dns          :53           CoreDNS
                    ├── cell-dhcp         :67/udp       dnsmasq
                    ├── cell-ntp          :123/udp      chrony
                    ├── cell-wireguard    :51820/udp    WireGuard VPN
                    ├── cell-mail         :25/:587/:993 Postfix + Dovecot
                    ├── cell-radicale     127.0.0.1:5232  CalDAV/CardDAV
                    ├── cell-webdav       127.0.0.1:8080  WebDAV
                    ├── cell-rainloop     :8888         webmail (RainLoop)
                    ├── cell-filegator    :8082         file manager UI
                    └── cell-webui        :8081         React UI (Nginx)

All containers run on a custom Docker bridge network (cell-network, default 172.20.0.0/16). Static IPs per container are set in docker-compose.yml and overridden via .env.

The Flask API (api/app.py, ~2800 lines) contains all REST endpoints, runs a background health-monitoring thread, and manages the entire lifecycle of generated config artefacts: Caddyfile, Corefile, wg0.conf, and cell_config.json (the single source of truth at config/api/cell_config.json).

The React frontend (webui/) is built with Vite + Tailwind CSS. All API calls go through src/services/api.js (Axios). Pages: Dashboard, Peers, Network Services, WireGuard, Email, Calendar, Files, Routing, Vault, Containers, Cell Network, Logs, Settings.


Requirements

  • Linux host with the WireGuard kernel module loaded
  • Docker Engine and Docker Compose (v2 plugin or v1 standalone)
  • Python 3.10+ (for make setup and local dev only; not needed at runtime)
  • 2 GB+ RAM, 10 GB+ disk
  • Ports available: 53, 67/udp, 80, 443, 51820/udp, 25, 587, 993

Quick Start

See QUICKSTART.md for step-by-step setup.


Configuration

Runtime configuration is controlled by .env in the project root. Copy .env.example to .env before first run.

Variable Default Description
CELL_NETWORK 172.20.0.0/16 Docker bridge subnet for all containers
CADDY_IP through FILEGATOR_IP 172.20.0.2.13 Static IP for each container
DNS_PORT 53 DNS (UDP+TCP)
DHCP_PORT 67 DHCP (UDP)
NTP_PORT 123 NTP (UDP)
WG_PORT 51820 WireGuard listen port (UDP)
API_PORT 3000 Flask API (bound to 127.0.0.1)
WEBUI_PORT 8081 React UI
MAIL_SMTP_PORT 25 SMTP
MAIL_SUBMISSION_PORT 587 SMTP submission
MAIL_IMAP_PORT 993 IMAP
RADICALE_PORT 5232 CalDAV (bound to 127.0.0.1)
WEBDAV_PORT 8080 WebDAV (bound to 127.0.0.1)
RAINLOOP_PORT 8888 Webmail
FILEGATOR_PORT 8082 File manager UI
WEBDAV_USER admin WebDAV basic-auth username
WEBDAV_PASS (required) WebDAV basic-auth password — must be set before make start
FLASK_DEBUG (unset) Set to 1 to enable Flask debug mode; do not use in production
PUID / PGID current user UID/GID passed to the WireGuard container

Cell identity (cell name, domain, VPN IP range) is configured via make setup or the Settings → Identity page in the UI after startup. The VPN IP range must be an RFC-1918 CIDR (10.0.0.0/8, 172.16.0.0/12, or 192.168.0.0/16); the API and UI both enforce this.


Security Notes

Ports exposed to the network:

  • 80 / 443 — Caddy (HTTP/HTTPS reverse proxy)
  • 51820/udp — WireGuard
  • 25 / 587 / 993 — Mail (SMTP, submission, IMAP)
  • 53 — DNS (UDP + TCP)
  • 67/udp — DHCP
  • 8081 — Web UI
  • 8888 — Webmail (RainLoop)
  • 8082 — File manager (Filegator)

Ports bound to 127.0.0.1 only (not directly reachable from the network):

  • 3000 — Flask API
  • 5232 — Radicale (CalDAV)
  • 8080 — WebDAV

The API has no authentication layer. It relies on is_local_request() to restrict sensitive endpoints (containers, vault) to requests originating from loopback or the cell's Docker network. The Docker socket is mounted into cell-api; treat access to port 3000 as equivalent to root access on the host.

For internet-facing deployments, place the host behind a firewall or VPN and restrict access to the API and UI ports.


Development

# Start the full stack (builds api and webui images)
make start

# Rebuild a single image after code changes
make build-api
make build-webui

# Run Flask API locally without Docker (port 3000)
pip install -r api/requirements.txt
python api/app.py

# Run React UI dev server locally (port 5173, proxies /api to :3000)
cd webui && npm install && npm run dev

# Follow all container logs
make logs

# Follow logs for one service (e.g. api, dns, caddy, wireguard, mail)
make logs-api

# Open a shell inside a container
make shell-api

Testing

make test            # run the full pytest suite
make test-coverage   # run with coverage; HTML report in htmlcov/

Tests live in tests/ (34 files, 642 test functions). Coverage includes:

  • All service managers (network, WireGuard, email, calendar, file, routing, vault, container)
  • API endpoint tests for each service area
  • Config manager (CRUD, validation, backup/restore)
  • IP utilities and Caddyfile generation
  • Peer registry and WireGuard peer lifecycle
  • Service bus pub/sub
  • Firewall manager
  • Pending-restart logic

Integration tests (tests/integration/) require a running PIC stack:

make test-integration             # full suite (creates peers)
make test-integration-readonly    # read-only checks, safe to run anytime

Management Commands

make setup           # generate WireGuard keys, write configs, create data dirs
make start           # docker compose up -d --build
make stop            # docker compose down
make restart         # docker compose restart
make status          # container status + API health check
make logs            # follow all service logs
make logs-<svc>      # follow logs for one service
make shell-<svc>     # shell inside a container

make update          # git pull + rebuild + restart
make reinstall       # full wipe of config/ and data/, then setup + start
make uninstall       # stop containers; prompts whether to also delete config/ and data/

make backup          # tar config/ + data/ into backups/
make restore         # list available backups

make list-peers      # show WireGuard peers via API
make show-routes     # wg show inside the wireguard container
make add-peer PEER_NAME=foo PEER_IP=10.0.0.5 PEER_KEY=<pubkey>

License

MIT — see LICENSE.

S
Description
No description provided
Readme 1.6 MiB
Languages
Python 79.6%
JavaScript 18.7%
Shell 0.9%
Makefile 0.6%