diff --git a/CLAUDE.md b/CLAUDE.md index 2904ed5..fa46f4e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -8,7 +8,7 @@ This file is the primary context source for Claude Code in this repository. Read **Personal Internet Cell (PIC)** is a self-hosted digital infrastructure platform for individuals who want full ownership of their core internet services without relying on cloud providers. -A PIC instance runs DNS, DHCP, NTP, WireGuard VPN, email (SMTP/IMAP), calendar/contacts (CalDAV/CardDAV), file storage (WebDAV), HTTPS reverse proxy (Caddy), an internal certificate authority, and optional third-party services — all managed from a single REST API and a React web UI. No manual config-file editing is required for normal operations. +A PIC instance runs DNS, NTP, WireGuard VPN, an HTTPS reverse proxy (Caddy), an internal certificate authority, and — as optional store services — email (SMTP/IMAP), calendar/contacts (CalDAV/CardDAV), file storage (WebDAV), and extended-connectivity exits (WireGuard-ext, OpenVPN, Tor, sshuttle, proxy) — all managed from a single REST API and a React web UI. No manual config-file editing is required for normal operations. **Primary users:** technically capable individuals, homelab operators, small families or teams. @@ -45,8 +45,7 @@ A PIC instance runs DNS, DHCP, NTP, WireGuard VPN, email (SMTP/IMAP), calendar/c ### Infrastructure - **Docker Compose** — all 12+ service containers - **Caddy** — reverse proxy, TLS termination (Let's Encrypt DNS-01 or HTTP-01 or internal CA) -- **CoreDNS** — `.cell` TLD authoritative DNS -- **dnsmasq** — DHCP +- **CoreDNS** — `.cell` TLD authoritative DNS + split-horizon for the effective domain - **chrony** — NTP - **WireGuard** — VPN (kernel module, not userspace) - **Postfix + Dovecot** — email via `docker-mailserver` @@ -69,7 +68,7 @@ Browser / WireGuard peer └── Caddy (:80/:443) TLS termination, reverse proxy └── React SPA (:8081) Vite + Tailwind (Nginx in container) └── Flask API (:3000) REST API, bound to 127.0.0.1 only - ├── NetworkManager CoreDNS, dnsmasq, chrony + ├── NetworkManager CoreDNS, chrony ├── WireGuardManager WireGuard peer lifecycle ├── PeerRegistry peer registration and trust ├── EmailManager Postfix + Dovecot diff --git a/Personal Internet Cell – Project Wiki.md b/Personal Internet Cell – Project Wiki.md index 8bf160d..8988382 100644 --- a/Personal Internet Cell – Project Wiki.md +++ b/Personal Internet Cell – Project Wiki.md @@ -2,7 +2,7 @@ ## Overview -Personal Internet Cell (PIC) is a self-hosted digital infrastructure platform. It runs DNS, DHCP, NTP, WireGuard VPN, email, calendar/contacts, file storage, HTTPS reverse proxy, a certificate authority, and optional services — all managed from a single REST API and React web UI. +Personal Internet Cell (PIC) is a self-hosted digital infrastructure platform. It runs DNS, NTP, WireGuard VPN, HTTPS reverse proxy, a certificate authority, and optional services (email, calendar/contacts, file storage, connectivity exits, and more) — all managed from a single REST API and React web UI. The goal is to give a person full ownership of their core internet services on their own hardware, without relying on cloud providers. @@ -16,13 +16,15 @@ The goal is to give a person full ownership of their core internet services on t 4. [Authentication](#authentication) 5. [API Reference](#api-reference) 6. [DDNS](#ddns) -7. [Services UI](#services-ui) -8. [Service Store (Add-ons)](#service-store-add-ons) -9. [Cell-to-Cell Networking](#cell-to-cell-networking) -10. [Extended Connectivity](#extended-connectivity) -11. [Security Model](#security-model) -12. [Testing](#testing) -13. [Development](#development) +7. [Network Services and DNS](#network-services-and-dns) +8. [Services UI](#services-ui) +9. [Service Store (Add-ons)](#service-store-add-ons) +10. [Backup and Restore](#backup-and-restore) +11. [Cell-to-Cell Networking](#cell-to-cell-networking) +12. [Extended Connectivity](#extended-connectivity) +13. [Security Model](#security-model) +14. [Testing](#testing) +15. [Development](#development) --- @@ -31,20 +33,20 @@ The goal is to give a person full ownership of their core internet services on t ``` Browser / WireGuard peer └── Caddy (:80/:443) reverse proxy, TLS termination - └── React SPA (:8081) Vite + Tailwind (Nginx in container) + └── React SPA (:8081→8080) Vite + Tailwind (Nginx in container) └── Flask API (:3000) REST API, bound to 127.0.0.1 - ├── NetworkManager CoreDNS, dnsmasq, chrony + ├── NetworkManager CoreDNS, chrony (split-horizon DNS) ├── WireGuardManager WireGuard VPN peer lifecycle ├── PeerRegistry peer registration and trust - ├── EmailManager Postfix + Dovecot - ├── CalendarManager Radicale CalDAV/CardDAV - ├── FileManager WebDAV + Filegator + ├── EmailManager Postfix + Dovecot (optional service) + ├── CalendarManager Radicale CalDAV/CardDAV (optional service) + ├── FileManager WebDAV + Filegator (optional service) ├── RoutingManager iptables NAT and routing ├── FirewallManager iptables firewall rules - ├── VaultManager internal CA, cert lifecycle, Age encryption + ├── VaultManager internal CA, cert lifecycle, Fernet encryption ├── ContainerManager Docker SDK ├── CellLinkManager cell-to-cell WireGuard links - ├── ConnectivityManager exit routing (WG ext, OpenVPN, Tor) + ├── ConnectivityManager exit routing (WG ext, OpenVPN, Tor, sshuttle, proxy) ├── DDNSManager dynamic DNS heartbeat ├── ServiceStoreManager optional service install/remove ├── CaddyManager Caddyfile generation and reload @@ -52,7 +54,9 @@ Browser / WireGuard peer └── SetupManager first-run wizard state ``` -The 7 core containers run on a Docker bridge network (`cell-network`, `172.20.0.0/16` default). Static IPs per container are defined in `docker-compose.yml`. Installed optional services join the same network via their own compose projects, managed by `ServiceComposer`. +Six core containers run on a Docker bridge network (`cell-network`, `172.20.0.0/16` default): `cell-caddy`, `cell-dns`, `cell-ntp`, `cell-wireguard`, `cell-api`, `cell-webui`. Static IPs per container are defined in `docker-compose.yml`. Installed optional services join the same network via their own compose projects, managed by `ServiceComposer`. + +The WireGuard container runs unprivileged — it uses `NET_ADMIN` only and requires the WireGuard module on the host kernel (Linux 5.6+ or a loadable module). The `cell-api` and `cell-webui` images are slim builds. The CoreDNS image is pinned to a specific digest. Runtime configuration lives in `config/api/cell_config.json`, managed by `ConfigManager`. All service managers read and write through `ConfigManager`, which validates and backs up automatically. @@ -73,7 +77,7 @@ The `ServiceBus` (`api/service_bus.py`) handles pub/sub events between managers | Manager | Responsibilities | |---|---| -| `NetworkManager` | CoreDNS zone files, dnsmasq DHCP config and lease monitoring, chrony NTP | +| `NetworkManager` | CoreDNS zone files and split-horizon DNS, chrony NTP | | `WireGuardManager` | Key generation, `wg0.conf` generation, peer add/remove, route sync | | `PeerRegistry` | Peer registration, trust tracking, peer statistics | | `EmailManager` | docker-mailserver accounts, mailbox config, alias management | @@ -84,7 +88,7 @@ The `ServiceBus` (`api/service_bus.py`) handles pub/sub events between managers | `VaultManager` | Internal CA (self-signed root), TLS cert issue/revoke, Age public key | | `ContainerManager` | Docker container/image/volume management via SDK | | `CellLinkManager` | Site-to-site WireGuard links to other PIC cells, peer-sync protocol | -| `ConnectivityManager` | Per-peer exit routing via WireGuard external, OpenVPN, or Tor | +| `ConnectivityManager` | Per-peer exit routing via WireGuard external, OpenVPN, Tor, sshuttle, or proxy (redsocks) | | `DDNSManager` | Public IP heartbeat, provider abstraction (pic_ngo, cloudflare, duckdns, noip, freedns) | | `ServiceStoreManager` | Fetch manifest index, install/remove optional services | | `CaddyManager` | Caddyfile generation, reload-on-change | @@ -104,6 +108,16 @@ The wizard collects: - **Services to install** — optional services (email, calendar, files) to install after setup; each starts a background install via `ServiceStoreManager` - **Admin password** — minimum 12 characters +Domain modes and their TLS behavior: + +| Mode | Certificate | +|---|---| +| `pic_ngo` | Wildcard Let's Encrypt cert via DNS-01 (requires accurate host clock for ACME + DDNS token) | +| `cloudflare` | Wildcard Let's Encrypt cert via Cloudflare DNS-01 | +| `duckdns` | Let's Encrypt via DuckDNS DNS-01 | +| `http01` | Let's Encrypt per-subdomain cert via HTTP-01 (no wildcard; port 80 must be reachable) | +| `lan` | Internal CA only; no internet required | + On completion: 1. Admin account is created in `data/auth_users.json` 2. Cell identity is written to `config/api/cell_config.json` @@ -175,9 +189,9 @@ Auth enforcement is active once any user exists in the store. If the store is em | POST | `/api/setup/step` | Submit current step | | POST | `/api/setup/complete` | Finalize setup | -### Network Services (`/api/dns/`, `/api/dhcp/`, `/api/ntp/`, `/api/network/`) +### Network Services (`/api/dns/`, `/api/ntp/`, `/api/network/`) -DNS records, DHCP leases and reservations, NTP status, network connectivity test. +DNS overview (effective domain, public records, internal records, per-mode actions), NTP status, network connectivity test. ### WireGuard (`/api/wireguard/`, `/api/peers/`) @@ -213,7 +227,7 @@ List connected cells, add/remove cell links, peer-sync. ### Connectivity (`/api/connectivity/`) -List exit nodes, configure WireGuard external / OpenVPN / Tor exits, assign per-peer exit policy. +List exit nodes, configure WireGuard external / OpenVPN / Tor / sshuttle / proxy exits, assign per-peer exit policy, assign per-service egress. ### Service Store (`/api/store/`) @@ -249,6 +263,90 @@ Supported providers: `pic_ngo`, `cloudflare`, `duckdns`, `noip`, `freedns`. --- +## Network Services and DNS + +### Split-horizon DNS + +PIC operates a split-horizon DNS configuration for the cell domain. + +- **Outside the VPN** — the cell domain (e.g. `myhome.pic.ngo`) resolves to the public IP via the DDNS provider. +- **Inside the VPN** — CoreDNS answers the same cell domain with the WireGuard internal IP of `cell-caddy`. Traffic therefore flows through the WireGuard tunnel and Caddy serves it on both the public and WireGuard interface. + +`NetworkManager.update_split_horizon_zone()` writes a zone file for the effective domain and reloads CoreDNS via SIGUSR1 whenever the cell name or domain mode changes. + +### Network Services page + +The **Network Services** page (`/network`) shows a provider-aware DNS overview: +- Current domain mode label and effective domain +- Public DNS records (A records registered with the DDNS provider) +- Service subdomains (shown for `pic_ngo` and `duckdns` modes) +- Internal records served by CoreDNS on the WireGuard network +- Per-mode action buttons (e.g. force-refresh DDNS, reload CoreDNS) +- NTP status (running/stopped, current time source) + +DHCP has been removed from PIC. There is no `cell-dhcp` container and no DHCP configuration in the UI. The `cell-dns` container runs CoreDNS only. + +### NTP + +The `cell-ntp` container runs chrony in a pinned Alpine image. It provides NTP to WireGuard peers that configure the cell as their NTP server. The installer also enables host-level chrony to keep the host clock accurate for ACME certificate issuance and DDNS TOTP tokens. + +--- + +## Backup and Restore + +### What `make backup` captures + +`make backup` creates `backups/cell-backup-.tar.gz` containing: +- `config/` — all service configuration including `cell_config.json` +- `data/` excluding logs (`data/logs/`) and internal config-backup snapshots (`data/api/config_backups/`) +- `docker-compose.yml` and `Makefile` + +The archive is written mode `0600`. It contains key material (WireGuard keys, internal CA, vault fernet.key, admin credentials, DDNS token, cell links, Caddy ACME certs). Store it securely. + +Data volumes of installed store services (email mailboxes, calendar collections, file trees) are **not** included in `make backup`. They are captured by the API-driven backup described below. + +### API-driven backup (`POST /api/config/backup`) + +The API backup captures everything `make backup` does, plus: +- `.env` +- The Caddyfile and Corefile (runtime-generated) +- DNS zone files +- WireGuard key material and live peer configs +- Vault directory (CA, certificates, fernet.key, trust store) +- Per-service connectivity configs (sshuttle keys, redsocks config, OpenVPN configs, WireGuard external config) +- Auth users, Flask secret key, DDNS token, cell links, peer service credentials +- Caddy issued ACME certificates and ACME state +- Live service data volumes (streamed via `docker exec tar`) + +**Passphrase encryption**: pass `{"passphrase": "..."}` in the request body to encrypt the archive. The encrypted file is named `.tar.gz.age` and uses Fernet with an scrypt-derived key. The plaintext staging directory is removed immediately after encryption. Supply the same passphrase when calling `POST /api/config/restore/`. + +Both backup and encrypted archive files are written mode `0600`. + +### Restore + +**From `make backup`:** + +```bash +tar -xzf backups/cell-backup-YYYYMMDD-HHMMSS.tar.gz +make restart +``` + +**From API backup:** + +Call `POST /api/config/restore/` (with `{"passphrase": "..."}` for encrypted archives). The restore process: +1. Restores the vault first (fernet.key must be present before any encrypted secrets are read) +2. Restores identity, `.env`, WireGuard key material, cell links +3. Restores Caddy ACME certs, Caddyfile, Corefile, DNS zones +4. Restores connectivity configs, auth users, DDNS token +5. Restores service user account files +6. Reloads `cell_config.json` into memory +7. Restores live service data volumes (if service containers are running) +8. Calls `_reapply_runtime_state()` — regenerates Caddyfile and Corefile from the restored config and re-applies routing rules + +After an API restore, run `make restart` to ensure all containers pick up the restored configuration. + +--- + ## Services UI ### Navigation @@ -298,7 +396,7 @@ The config form and users list are not shown to peers. ### Settings page -The Email, Calendar, and Files configuration forms have been removed from the Settings page. Settings now covers: Identity, DDNS, Network (DNS/DHCP/NTP), WireGuard, Routing & Firewall, Vault & Trust, and Backup & Restore. +The Email, Calendar, and Files configuration forms have been removed from the Settings page. Settings now covers: Identity, DDNS, Network (DNS, NTP), WireGuard, Routing & Firewall, Vault & Trust, and Backup & Restore. ### Relevant API endpoints @@ -345,14 +443,21 @@ Access control is per-service (calendar, files, mail, WebDAV) and enforced at th ## Extended Connectivity -`ConnectivityManager` provides per-peer exit routing: traffic from a specific WireGuard peer can be routed through an alternate exit instead of the cell's default gateway. +`ConnectivityManager` provides per-peer and per-service exit routing: traffic from a specific WireGuard peer (or a specific installed service) can be routed through an alternate exit instead of the cell's default gateway. -Supported exits: -- **WireGuard external** — another WireGuard endpoint (e.g. a VPS) -- **OpenVPN** — OpenVPN client running in a container -- **Tor** — Tor SOCKS proxy with transparent redirection +All exit types are optional store services installed from the Services catalog. Each exit type corresponds to a store service ID: -Routing uses fwmark and `ip rule` / `ip route` in separate routing tables. Configuration is via `PUT /api/connectivity/peers//exit`. +| Exit type | Store service | Mechanism | +|---|---|---| +| `wireguard_ext` | `wireguard-ext` | WireGuard client tunnel to an external server; iface `wg_ext0`; fwmark `0x10`, table 110 | +| `openvpn` | `openvpn-client` | OpenVPN client tunnel; iface `tun0`; fwmark `0x20`, table 120 | +| `tor` | `tor` | Transparent proxy → Tor SOCKS on port 9040; fwmark `0x30`, table 130 | +| `sshuttle` | `sshuttle` | SSH tunnel via sshuttle to any SSH server; transparent proxy on port 12300; fwmark `0x40`, table 140 | +| `proxy` | `proxy` | HTTP or SOCKS5 upstream proxy via redsocks transparent redirection on port 12345; fwmark `0x50`, table 150 | + +Routing uses fwmark and `ip rule` / `ip route` in separate routing tables inside the `cell-wireguard` container. A kill-switch FORWARD rule drops traffic for configured exits if the exit container is not active. + +Configuration is via `PUT /api/connectivity/peers//exit`. Service-level egress is declared in the service manifest's `egress` block and configured via the Connectivity page in the UI. --- diff --git a/QUICKSTART.md b/QUICKSTART.md index a1eb1d1..7d43067 100644 --- a/QUICKSTART.md +++ b/QUICKSTART.md @@ -10,6 +10,7 @@ This guide walks through a first-time PIC installation on a clean Linux host. - 2 GB+ RAM, 10 GB+ disk - Always-required ports: 53, 80, 443, 51820/udp - Email service only (when installed): 25, 587, 993 +- WireGuard kernel module available on the host (`modprobe wireguard`); required — userspace WireGuard is not supported The installer handles all software dependencies (git, docker, make, etc.) automatically. @@ -27,13 +28,16 @@ Always review the script before running it: curl -fsSL https://install.pic.ngo | less ``` +The installer runs 7 steps and prints clean one-line progress for each. Run with `PIC_DEBUG=1` or `--debug` for full verbose output. A complete log is always written to `/var/log/pic-install.log`. + The installer: 1. Detects your OS and installs Docker, git, make via the system package manager -2. Creates a `pic` system user and adds it to the `docker` group -3. Clones the repository to `/opt/pic` -4. Runs `make install` (generates keys and config, writes a systemd unit) -5. Runs `make start-core` to bring up the core containers -6. Waits for the API to respond, then prints the wizard URL +2. Installs and starts host NTP (chrony) — required for ACME certificate issuance and DDNS token registration +3. Creates a `pic` system user and adds it to the `docker` group +4. Clones the repository to `/opt/pic` +5. Runs `make install` — generates keys and config, writes a systemd unit. The admin password is printed once here; it does not appear again. +6. Runs `make start-core` to bring up the six core containers +7. Enables the `pic` systemd unit so the stack starts on reboot, then waits for the API health check When it finishes, open the URL it prints: @@ -54,7 +58,13 @@ sudo make install make start-core ``` -Then open `http://:8081` in a browser. +Then open `http://:8081/setup` in a browser. + +Note: install host NTP before running `make install` if you plan to use `pic_ngo` domain mode. The installer does this automatically in Option A. + +```bash +sudo apt-get install -y chrony && sudo systemctl enable --now chrony +``` --- @@ -66,11 +76,11 @@ The wizard asks for: - **Cell name** — used for hostnames and DDNS subdomain. Lowercase letters, digits, hyphens, 2–31 characters. Example: `myhome`. - **Domain mode** — how HTTPS certificates are issued: - - `pic_ngo` — automatic `.pic.ngo` subdomain with Let's Encrypt via DNS-01 (recommended for internet-facing cells) - - `cloudflare` — Let's Encrypt via Cloudflare DNS-01 (bring your own domain) + - `pic_ngo` — automatic `.pic.ngo` subdomain with a wildcard Let's Encrypt cert via DNS-01 (recommended for internet-facing cells; requires accurate host clock) + - `cloudflare` — wildcard Let's Encrypt cert via Cloudflare DNS-01 (bring your own domain) - `duckdns` — Let's Encrypt via DuckDNS DNS-01 - `http01` — Let's Encrypt via HTTP-01 (no wildcard; cell must be reachable on port 80) - - `lan` — internal CA, no internet required (for LAN-only installs) + - `lan` — internal CA only, no internet required (for LAN-only installs) - **Timezone** - **Services to install** — email, calendar, files (optional; installed in the background after setup completes; can be added later via the Services store page) - **Admin password** — minimum 12 characters, must contain uppercase, lowercase, and a digit @@ -155,13 +165,19 @@ make backup # archives config/ and data/ into backups/cell-backup-.tar.gz.age`. + +To restore from a `make backup` archive: ```bash tar -xzf backups/cell-backup-YYYYMMDD-HHMMSS.tar.gz -make start +make restart ``` +After restore, the API re-generates the Caddyfile and Corefile from the restored config and re-applies routing rules automatically. + --- ## Updating PIC @@ -203,13 +219,15 @@ echo "nameserver 8.8.8.8" | sudo tee /etc/resolv.conf Then run `make start` again. -### WireGuard container fails to load the kernel module +### WireGuard container fails to start + +The WireGuard container runs unprivileged (NET_ADMIN only, no privileged mode). It requires the host kernel's WireGuard module — either compiled in (Linux 5.6+) or loadable. ```bash sudo modprobe wireguard ``` -On minimal installs you may need `wireguard-tools` and the kernel headers for the running kernel. +On minimal installs you may need `wireguard-tools` and the kernel headers for the running kernel. On kernels that lack a builtin WireGuard module, check your distro's `wireguard-dkms` or `wireguard-linux-compat` package. ### API returns 428 and redirects to /setup diff --git a/README.md b/README.md index c0dadd1..7321ea3 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Personal Internet Cell (PIC) -PIC is a self-hosted digital infrastructure platform. It packages DNS, DHCP, NTP, WireGuard VPN, email, calendar/contacts (CalDAV), file storage (WebDAV), a reverse proxy, a certificate authority, and optional third-party services — all managed through a single REST API and a React web UI. No manual config file editing is required for normal operations. +PIC is a self-hosted digital infrastructure platform. It packages DNS, NTP, WireGuard VPN, a reverse proxy, a certificate authority, and optional third-party services (email, calendar/contacts, file storage, and more) — all managed through a single REST API and a React web UI. No manual config file editing is required for normal operations. --- @@ -8,19 +8,18 @@ PIC is a self-hosted digital infrastructure platform. It packages DNS, DHCP, NTP ``` Browser - └── React SPA (cell-webui :8081) + └── React SPA (cell-webui :8081, container port 8080) └── Flask REST API (cell-api :3000, bound to 127.0.0.1) └── Service managers + Docker SDK ├── cell-caddy :80/:443 Caddy reverse proxy (HTTPS/TLS) ├── cell-dns :53 CoreDNS - ├── cell-dhcp :67/udp dnsmasq ├── cell-ntp :123/udp chrony - ├── cell-wireguard :51820/udp WireGuard VPN - └── cell-webui :8081 React UI (Nginx) + ├── cell-wireguard :51820/udp WireGuard VPN (NET_ADMIN only, not privileged) + └── cell-webui :8081→8080 React UI (Nginx) (+ per-service containers, started when a service is installed) ``` -Core containers run on a Docker bridge network (`cell-network`, default subnet `172.20.0.0/16`). Static IPs per container are set in `docker-compose.yml` and can be overridden via `.env`. Installed service containers join the same network with their own compose projects managed by `ServiceComposer`. +Six core containers run on a Docker bridge network (`cell-network`, default subnet `172.20.0.0/16`). Static IPs per container are set in `docker-compose.yml` and can be overridden via `.env`. Installed service containers join the same network with their own compose projects managed by `ServiceComposer`. The Flask API (`api/app.py`) contains REST endpoints and a background health-monitoring thread. Service managers are instantiated as singletons in `api/managers.py`. The single source of truth for runtime configuration is `config/api/cell_config.json`, managed by `ConfigManager`. @@ -38,10 +37,11 @@ The React frontend (`webui/`) is built with Vite + Tailwind CSS. All API calls g - **Caddy HTTPS** — automatic TLS via Let's Encrypt (DNS-01 or HTTP-01) or an internal CA, depending on domain mode. - **DDNS (pic.ngo)** — registers a `.pic.ngo` subdomain. Supported providers: `pic_ngo`, `cloudflare`, `duckdns`, `noip`, `freedns`. A background thread re-publishes the public IP every 5 minutes. - **Service store** — install/remove optional third-party services from the `pic-services` index at `git.pic.ngo`. Manifests declare container images, Caddy routes, and iptables rules. -- **Extended connectivity** — per-peer egress routing through alternate exits: WireGuard external, OpenVPN, or Tor. Configured via policy routing (fwmark + ip rule) in the WireGuard container. +- **Extended connectivity** — per-peer egress routing through alternate exits: WireGuard external, OpenVPN, Tor, sshuttle (SSH tunnel), or proxy (HTTP/SOCKS5 via redsocks). Exit nodes are optional store services. Per-service egress policy is also supported. Routing uses fwmark and `ip rule` in the WireGuard container. - **Cell-to-cell networking** — WireGuard-based site-to-site links between PIC cells with service-level access control (calendar, files, mail, WebDAV) and a peer-sync protocol. - **Certificate authority** — `vault_manager` issues and revokes TLS certificates for internal services. -- **Network services** — CoreDNS (`.cell` TLD), dnsmasq DHCP, chrony NTP. +- **Network services** — CoreDNS (`.cell` TLD and split-horizon DNS for the cell domain), chrony NTP. +- **Split-horizon DNS** — from outside the VPN, the cell domain resolves to the public IP. Inside the VPN, CoreDNS resolves it to the WireGuard IP so traffic stays in the tunnel. Caddy serves on both interfaces. - **Email** _(optional, install via Service Store)_ — Postfix + Dovecot via `docker-mailserver`. - **Calendar/contacts** _(optional, install via Service Store)_ — Radicale CalDAV/CardDAV. - **File storage** _(optional, install via Service Store)_ — WebDAV with per-user accounts; Filegator for browser-based file management. @@ -53,11 +53,11 @@ The React frontend (`webui/`) is built with Vite + Tailwind CSS. All API calls g ## Requirements -- Linux host with the WireGuard kernel module loaded (`modprobe wireguard` to verify) +- Linux host with the WireGuard kernel module loaded (`modprobe wireguard` to verify; required — userspace WireGuard is not supported) - Docker Engine and Docker Compose (v2 plugin or v1 standalone) - Python 3.10+ (for `make setup` and local development; not needed at runtime) - 2 GB+ RAM, 10 GB+ disk -- Ports available: 53, 67/udp, 80, 443, 51820/udp (plus 25, 587, 993 when the email service is installed) +- Ports available: 53, 80, 443, 51820/udp (plus 25, 587, 993 only when the email service is installed) --- @@ -65,10 +65,17 @@ The React frontend (`webui/`) is built with Vite + Tailwind CSS. All API calls g See [QUICKSTART.md](QUICKSTART.md) for step-by-step instructions. -The short version: +The short version — one-line installer (recommended): ```bash -git clone gitea@192.168.31.50:roof/pic.git pic +curl -fsSL https://install.pic.ngo | sudo bash +# open http://:8081/setup — the setup wizard appears automatically +``` + +Or clone manually for development: + +```bash +git clone https://git.pic.ngo/roof/pic.git pic cd pic make start # open http://:8081 — the setup wizard appears automatically @@ -83,13 +90,12 @@ Port assignments and container IPs are configured in `.env` in the project root. | Variable | Default | Description | |---|---|---| | `CELL_NETWORK` | `172.20.0.0/16` | Docker bridge subnet | -| `CADDY_IP` through `WG_IP` | `172.20.0.2`–`.9` | Static IP per core container | +| `CADDY_IP` through `WG_IP` | `172.20.0.2`–`.11` | Static IP per core 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 (127.0.0.1 only) | -| `WEBUI_PORT` | `8081` | React UI | +| `WEBUI_PORT` | `8081` | Host port mapped to container port 8080 | | `FLASK_DEBUG` | _(unset)_ | Set to `1` for Flask debug mode; do not use in production | | `PUID` / `PGID` | current user | UID/GID passed to the WireGuard container | @@ -104,7 +110,6 @@ Cell identity (cell name, domain mode, timezone) is set through the first-run wi - `80` / `443` — Caddy (HTTP/HTTPS reverse proxy) - `51820/udp` — WireGuard - `53` — DNS -- `67/udp` — DHCP - `8081` — Web UI - `25` / `587` / `993` — mail _(only when the email service is installed)_ diff --git a/docs/service-developer-guide.md b/docs/service-developer-guide.md index 683bba4..0647c05 100644 --- a/docs/service-developer-guide.md +++ b/docs/service-developer-guide.md @@ -30,7 +30,7 @@ A PIC service is a Docker container (or a set of containers) that plugs into the - Which paths to include in automated backups - Which outbound network interfaces the service is allowed to use -All PIC services are **store services** — optional packages installed by the cell admin from the `pic-services` catalog. PIC downloads the manifest, renders a per-service Docker Compose file, and starts the containers. The core PIC stack (DNS, DHCP, NTP, WireGuard, Caddy, API, WebUI) runs independently of any installed services. +All PIC services are **store services** — optional packages installed by the cell admin from the `pic-services` catalog. PIC downloads the manifest, renders a per-service Docker Compose file, and starts the containers. The core PIC stack (DNS, NTP, WireGuard, Caddy, API, WebUI) runs independently of any installed services. The email, calendar, and files services (in `pic-services/services/`) are the reference implementations and show the full feature set. The `ServiceRegistry` in `api/service_registry.py` is the single source of truth for all installed services. `CaddyManager`, the backup system, and the peer services endpoint all read from it rather than from hardcoded lists. @@ -218,12 +218,12 @@ Required when `has_egress` is `true`. Declares which outbound network interfaces | `default` | string | The interface selected when the admin has not changed anything. | | `allowed` | array of strings | The complete set of interfaces the admin can choose from. | -Valid interface identifiers: `default`, `wireguard_ext`, `openvpn`, `tor`. +Valid interface identifiers: `default`, `wireguard_ext`, `openvpn`, `tor`, `sshuttle`, `proxy`. ```json "egress": { "default": "default", - "allowed": ["default", "wireguard_ext", "openvpn", "tor"] + "allowed": ["default", "wireguard_ext", "openvpn", "tor", "sshuttle", "proxy"] } ``` @@ -549,9 +549,11 @@ The valid values for `egress.allowed` and what they mean: | Value | Path | |---|---| | `default` | Default route through the cell's WAN interface (no VPN). | -| `wireguard_ext` | Traffic leaves through `wg_ext0` (fwmark `0x10`, table 110). | -| `openvpn` | Traffic leaves through `tun0` (fwmark `0x20`, table 120). | -| `tor` | Traffic is redirected to the Tor transparent proxy on port 9040 (fwmark `0x30`, table 130). | +| `wireguard_ext` | Traffic leaves through `wg_ext0` (fwmark `0x10`, table 110). Requires the `wireguard-ext` store service. | +| `openvpn` | Traffic leaves through `tun0` (fwmark `0x20`, table 120). Requires the `openvpn-client` store service. | +| `tor` | Traffic is redirected to the Tor transparent proxy on port 9040 (fwmark `0x30`, table 130). Requires the `tor` store service. | +| `sshuttle` | Traffic is redirected to the sshuttle transparent proxy on port 12300 (fwmark `0x40`, table 140). Requires the `sshuttle` store service. | +| `proxy` | Traffic is redirected to the redsocks transparent proxy on port 12345 (fwmark `0x50`, table 150). Requires the `proxy` store service. | List only the interfaces that make sense for your service in `allowed`. The `default` value is used when the admin has not changed anything. Always include `default` in `allowed` so the admin has a way to use the normal path.