diff --git a/docs/index.md b/docs/index.md index 1615876..e347d01 100644 --- a/docs/index.md +++ b/docs/index.md @@ -10,8 +10,9 @@ Theta Suite is your one-line solution to replacing fragmented, hard-to-wire authentication setups with a unified security stack. It wires together OIDC authentication, LDAP user directories, automated host enrollment, and centralized secret management in a single command. It eliminates the manual -configuration friction so you get secure access, auditability, and multi-site -replication running in seconds. +configuration friction so you get secure access, auditability, and +[multi-site](sso/multi-site.html) replication when you need more than one +location. ## Who This Is For * **Self-Hosters & Homelab Engineers:** Anyone running local bare metal, @@ -24,8 +25,8 @@ replication running in seconds. lock-in. * **DevOps & Systems Operators:** Engineers who value idempotent, single-command deployments (`./setup.sh`) and need a production-grade baseline supporting - zero-trust proxying, SSH jump-host access control, and multi-site replication - out of the box. + zero-trust proxying, SSH jump-host access control, and + [multi-site](sso/multi-site.html) replication out of the box. ## Screenshots diff --git a/docs/jump-host/index.md b/docs/jump-host/index.md index 5d73302..537d645 100644 --- a/docs/jump-host/index.md +++ b/docs/jump-host/index.md @@ -84,7 +84,7 @@ Theta Gateway answers both from your directory: inventory, not a static list - **Per-user key injection** — no downstream changes, no key distribution - **Shell, exec, and SFTP** bridging -- **WireGuard mesh routing** — cross-site network access alongside SSH +- **[WireGuard mesh routing](mesh.html)** — cross-site network access alongside SSH - **Web UI + HTTP API** for auditing and metrics — active sessions, a searchable audit log, per-user/per-host counters - **Full audit trail** — who, target, method, result, bytes, duration, and the diff --git a/docs/jump-host/mesh.md b/docs/jump-host/mesh.md new file mode 100644 index 0000000..14249ae --- /dev/null +++ b/docs/jump-host/mesh.md @@ -0,0 +1,60 @@ +--- +layout: default +title: Gateway Mesh +--- + +# Gateway Mesh + +Theta Gateway can mesh with other Theta Gateway instances over real +site-to-site WireGuard tunnels — separate from its [SSH jump +host](connecting.html) role, and separate from the roaming-client/exit-node +WireGuard feature (individual peer configs for laptops/phones). This is +gateway-to-gateway: two sites' networks reaching each other directly. + +## Why and when to use this + +- **Direct site-to-site networking**, not just SSH. Once two gateways are + meshed, hosts behind each can reach each other over the tunnel using the + mesh addressing scheme below — not limited to jumping through SSH. +- **No manual WireGuard config.** Meshing is a join-token exchange; both + sides come out with a live, working peer entry for each other + automatically. +- **Works without a kernel WireGuard module.** Prefers in-kernel WireGuard, + falls back to the userspace `wireguard-go` implementation automatically — + useful for older kernels, some container/cloud images, or hosts where the + kernel module isn't available. + +## How it works + +1. On the gateway you want others to join, mint a join token: **Mesh** page + → **Mint a Join Token**. It's single-use and expires in 15 minutes. +2. On the new gateway, use **Join a Remote Gateway's Mesh**: paste the other + gateway's URL and the token. +3. Both sides now have a live WireGuard peer for each other. The **Meshed + Gateways** table shows every peer, its assigned mesh subnet, and when it + was last seen. + +Each gateway is assigned a **mesh index** (an integer 1–254) the first time +it either mints a token or is registered by another gateway. That index +determines its subnet: `172.24..0/24` for the mesh tunnel itself, plus +`10..0.0/16` reserved for that site's own local network — 254 sites is +the hard ceiling this addressing scheme supports. + +## Requirements + +- Both gateways need a reachable endpoint (host:port) for the WireGuard + handshake — typically the same public host the SSH/web ports are already + on, with UDP 51820 reachable. +- `NET_ADMIN` capability (or equivalent) on the container/host running the + gateway, to create the WireGuard interface. + +## Not yet connected to directory sync + +This mesh is a networking layer on its own. [Theta Directory's multi-site +join](../sso/multi-site.html) (catalog + LDAP replication between a master +and its spokes) does not currently route its traffic over this mesh — the +two features work independently today. Routing directory sync over the mesh, +and using the mesh to reach a spoke site with no inbound access of its own, +are both designed but not yet automated — see the [architecture +spec](https://github.com/theta42/theta-suite/blob/master/docs/MULTI_SITE_SPEC.md) +for current status. diff --git a/docs/sso/index.md b/docs/sso/index.md index cd83bd3..54c972e 100644 --- a/docs/sso/index.md +++ b/docs/sso/index.md @@ -45,7 +45,8 @@ stack with one command. - **Direct LDAP binds** — anything that binds LDAP directly (Linux hosts via PAM/SSSD, Gitea, Emby, …) uses LDAPS/StartTLS against the same directory. -- **Geo-Location Scaling** — built-in support for N-Way Multi-Master OpenLDAP [replication](replication.html) across physical sites. +- **[Multi-Site](multi-site.html)** — one master site, any number of read-only spokes that join with a single key and stay live-synced, with god_admin-gated promotion if the master goes down for good. +- **Geo-Location Scaling** — built-in support for N-Way Multi-Master OpenLDAP [replication](replication.html) across physical sites (a different, lower-level mechanism — see [Multi-Site](multi-site.html) for how the two compare). - **[Directory & Inventory](directory.html)** — map sites, hosts, and services as a graph with rich metadata (IP/MAC, OS/kernel, ports, git repos), auto-provisioned access groups, and automatic registration from theta-suite's agents and discovery plugins. Drives directory-aware tools like the [SSH jump host](../jump-host/). - **Subtype metrics & lifecycle drivers** — telemetry, log streaming, and remote control for resources tagged with a `subType` (`systemd`, `docker`, `proxmox`, `wireguard`, `postgresql`, `redis`, `k8s`, …). - **OpenBao-backed secrets** — per-resource and per-user secrets with explicit upward inheritance (`Resource → Host → Cluster → Site`). diff --git a/docs/sso/multi-site.md b/docs/sso/multi-site.md new file mode 100644 index 0000000..5492ac3 --- /dev/null +++ b/docs/sso/multi-site.md @@ -0,0 +1,109 @@ +--- +layout: default +title: Multi-Site (Master/Spoke Join) +--- + +# Multi-Site (Master/Spoke Join) + +If you run more than one physical site, Theta Directory can run one site as +the **master** (single write authority for the shared catalog) and any +number of **spokes** — read-only replicas that stay in sync automatically and +run local authentication with zero WAN dependency. + +This is a different, higher-level mechanism than [raw LDAP N-way +replication](replication.html) — see [How this relates to LDAP +replication](#how-this-relates-to-ldap-replication) below if you're deciding +between the two. + +## Why and when to use this + +- **Zero-touch spoke setup.** One join key, one URL, and a spoke adopts the + whole directory (users, groups, resource catalog) in one step — no manual + `syncrepl` configuration. +- **Single write authority, no split-brain.** Only the master accepts + directory writes. A spoke that loses WAN connectivity keeps working for + local reads/auth and unconditionally stays read-only — it never silently + promotes itself. Changing which site is master always requires an explicit, + authenticated action by a `god_admin`. +- **Stays in sync, not just a one-time copy.** Once joined, a spoke keeps + receiving live updates whenever the master's catalog changes — you don't + re-run the join to pick up new hosts/apps/users. + +## How it works + +1. **On the master**, an admin mints a **site join key** (Directory → the + Master Site modal → **Site Join Keys** → Mint key). It's shown once, + stored hashed, and revocable. +2. **On the spoke** (must be a fresh install — no users beyond the bootstrap + admin, no enrolled agents), either: + - Paste the master's URL and the join key into the Master Site modal's + **Join an Existing Site** form, or + - Set `CFG_MASTER_DIRECTORY_URL` / `CFG_MASTER_DIRECTORY_JOIN_KEY` in + `setup.env` before the first `./setup.sh` run. +3. The spoke pulls the master's full export (LDAP tree, resource catalog, + agent-signing key) and adopts it, then registers its own reachable URL + with the master so it can receive live updates going forward. +4. From then on, every change to the master's catalog pushes to every + registered spoke automatically. A spoke's own directory-write requests are + rejected with a `403` pointing at the master — writes always go there. + +### Promoting a spoke to master + +If the master site goes down for good (or you're relocating write +authority), a `god_admin` can promote any spoke from its own Master Site +modal. Promotion is one coordinated action: it demotes the previous master as +part of the same request (best-effort — an unreachable old master never +blocks the promotion, since that's exactly the scenario this exists for), and +every other spoke gets pointed at the new master automatically. + +## What replicates + +| Data | How | +|---|---| +| LDAP (users, groups) | Full export on join; live push on every master change | +| Resource catalog (hosts, apps, sites) | Same | +| Agent-signing key | Same — every site can validly sign a command for any agent enrolled at *any* site | + +The agent-signing key being identical everywhere is a deliberate tradeoff for +small, trusted deployments (a handful of sites, not hundreds) — it means +compromising the least-secured spoke has the same agent-command blast radius +as compromising the master. If that tradeoff doesn't fit your deployment, +don't rely on this mechanism as-is. + +Secrets *beyond* the agent-signing key (LDAP admin password, JWT secret, and +so on) are **not** currently synced — each site still generates its own. + +## Requirements and current limits + +- Both sites need a network path to each other's HTTP(S) API — the master to + pull an export from, the spoke to push replication updates back to. A site + with no inbound path at all (e.g. behind CGNAT) can't join yet on its own; + a relay mechanism for that case is designed but not automated (see the + [architecture spec](https://github.com/theta42/theta-suite/blob/master/docs/MULTI_SITE_SPEC.md) + for the current status). +- Joining only ever happens on a **fresh install**. There's no way to merge + an already-populated directory into a master's — re-provision the host + first. + +## How this relates to LDAP replication + +[N-way LDAP replication](replication.html) is a *lower-level*, different +mechanism: every site runs a fully independent, fully writable `slapd`, wired +together with raw `syncrepl` environment variables, and there's no concept of +a master or a managed join. It predates this feature and is still there for +deployments that specifically want every site independently writable. + +Multi-site join (this page) is the opposite design: one write authority, a +managed onboarding flow, and automatic ongoing sync — closer to what most +"add a second office" or "add a home-lab spoke" setups actually want. **Don't +combine the two** — pick one per deployment. + +## See also + +- Full architecture and current implementation status: + [`MULTI_SITE_SPEC.md`](https://github.com/theta42/theta-suite/blob/master/docs/MULTI_SITE_SPEC.md) + in the `theta-suite` repo. +- Endpoint-level detail: [`docs/site-join.md`](https://github.com/theta42/theta-directory/blob/master/docs/site-join.md) + in the `theta-directory` repo. +- Site-to-site networking (WireGuard mesh between gateways, independent of + directory sync): [Theta Gateway → Mesh](../jump-host/mesh.html).