diff --git a/.gitattributes b/.gitattributes index e231bc8..8c151b6 100644 --- a/.gitattributes +++ b/.gitattributes @@ -8,4 +8,3 @@ docs export-ignore .claude export-ignore .gitignore export-ignore .gitattributes export-ignore -CONTRIBUTING.md export-ignore diff --git a/FOOTPRINT.md b/FOOTPRINT.md deleted file mode 100644 index da41065..0000000 --- a/FOOTPRINT.md +++ /dev/null @@ -1,59 +0,0 @@ -# LibrePortal system footprint (outside `/docker`) - -Everything LibrePortal *is* lives under `/docker` (app data, configs, the install -tree, the database). This file catalogues the few things it must place **outside** -`/docker` to integrate with the host. The OS dictates where most of these live — -sudoers, systemd units, sysctl, and `$PATH` entries can only be read from their -fixed locations, so they can't all sit in one folder. What we control, we keep -together in **`/usr/local/lib/libreportal/`**; everything else is named -`libreportal*` so the whole footprint is greppable and removable. - -## Executables — `/usr/local/lib/libreportal/` (root:root, our own dir) - -| File | Owner | Purpose | -|------|-------|---------| -| `libreportal` | root | the CLI wrapper (symlinked onto `$PATH`, see below) | -| `libreportal-ownership` | root | reconcile the `/docker` ownership model | -| `libreportal-dns` | root | edit `/etc/resolv.conf` (nameservers) | -| `libreportal-ssh-access` | root | manage admin `authorized_keys` + sshd `PasswordAuthentication` | -| `libreportal-socket` | root | docker-socket read perms (type switcher) | -| `libreportal-svc` | root | generate/install the task-processor systemd unit | -| `libreportal-bininstall` | root | install the restic/kopia backup-engine binaries | -| `libreportal-appcfg` | root | rewrite AdGuard/CrowdSec/ownCloud config files | - -These are the scoped-sudoers trust boundary: root-owned in a root-owned dir, so the -manager can `sudo` them but can't modify them. Source of truth: `scripts/system/` -in the repo; installed by `init.sh` → `initRootHelpers` (re-installed only on a -reinstall, not by the quick-deploy). - -## OS-mandated locations (must live where the OS reads them) - -| Path | Owner | Purpose | -|------|-------|---------| -| `/usr/local/bin/libreportal` | root | **symlink** → `/usr/local/lib/libreportal/libreportal` (puts the CLI on `$PATH`) | -| `/etc/sudoers.d/libreportal` | root | scoped least-privilege grant for the manager | -| `/etc/systemd/system/libreportal.service` | root | the task-processor service (`User=libreportal`) | -| `/etc/sysctl.d/99-libreportal-hardening.conf` | root | kernel LPE-surface hardening | -| `/etc/sysctl.d/99-libreportal-rootless.conf` | root | rootless sysctl settings + "rootless configured" marker | - -## Third-party tools we install (not ours, conventional home) - -`/usr/local/bin/{restic,kopia,ufw-docker,docker-compose}` — installed on demand -(restic/kopia via the `libreportal-bininstall` helper). `/usr/local/bin` is the -correct home for these; left under their own names. - -## System users - -`libreportal` (the manager) and `dockerinstall` (the rootless docker user), each -with a home under `/home/`. The rootless daemon config lives at -`~dockerinstall/.config/docker/daemon.json`. - -## Uninstall sketch - -``` -sudo systemctl disable --now libreportal.service -sudo rm -f /etc/systemd/system/libreportal.service /etc/sudoers.d/libreportal -sudo rm -f /etc/sysctl.d/99-libreportal-*.conf -sudo rm -rf /usr/local/lib/libreportal /usr/local/bin/libreportal -# optional: the backup-engine binaries, the users, and /docker itself -``` diff --git a/README.md b/README.md index e18aa06..a43ccf5 100755 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ toggle — it's the whole point. The entire platform is **free software under the [GNU AGPLv3](LICENSE)**. Self-host it and you get **everything** — every feature, no paywalls, no -telemetry. See [our Promise](PROMISE.md) for exactly what that means. +telemetry. See [our Promise](docs/PROMISE.md) for exactly what that means. ## What you get @@ -59,11 +59,11 @@ different: we work like a **courier carrying a sealed box.** We move your data between your devices and store backup copies, but it stays locked and *you* hold the only key — we can't open it, and we never run your apps for you. **Everything we offer, you can also set up yourself for free.** -[Our Promise](PROMISE.md) spells out exactly where that line sits. +[Our Promise](docs/PROMISE.md) spells out exactly where that line sits. ## Contributing -PRs welcome — see [CONTRIBUTING.md](CONTRIBUTING.md). We use a lightweight +PRs welcome — see [CONTRIBUTING.md](docs/CONTRIBUTING.md). We use a lightweight DCO sign-off (`git commit -s`), no CLA. ## Acknowledgments diff --git a/CONTRIBUTING.md b/docs/CONTRIBUTING.md similarity index 100% rename from CONTRIBUTING.md rename to docs/CONTRIBUTING.md diff --git a/docs/FOOTPRINT.md b/docs/FOOTPRINT.md new file mode 100644 index 0000000..b734157 --- /dev/null +++ b/docs/FOOTPRINT.md @@ -0,0 +1,79 @@ +# LibrePortal system footprint (outside the data roots) + +LibrePortal's own data lives in **three independently-relocatable roots**, chosen +at install (defaults shown) and owned by exactly one principal each: + +| Root (default) | Owner | Holds | +|---|---|---| +| `/libreportal-system` | the manager user | configs, logs, the install tree, the database, ssl/ssh | +| `/libreportal-containers` | the container user | live app data (one dir per app) | +| `/libreportal-backups` | the container user | backup repositories | + +This file catalogues the few things LibrePortal must place **outside** those roots +to integrate with the host. The OS dictates where most of these live — sudoers, +systemd units, sysctl, and `$PATH` entries can only be read from their fixed +locations, so they can't sit inside a relocatable root. What we control, we keep +together in **`/usr/local/lib/libreportal/`**; everything else is named +`libreportal*` so the whole footprint is greppable and removable. + +> The three roots **and** the manager username are chosen at install and **baked** +> into the helpers / systemd unit / CLI wrapper below (sed placeholders, like the +> manager name has always been) — never read at runtime from a manager-writable +> config. That immutability is the privilege boundary: a root helper can't be +> redirected at, say, `/etc` by editing config. This footprint itself stays at +> **fixed** paths by design; only the data roots relocate. + +## Executables — `/usr/local/lib/libreportal/` (root:root, our own dir) + +| File | Owner | Purpose | +|------|-------|---------| +| `libreportal` | root | the CLI wrapper (symlinked onto `$PATH`, see below) | +| `libreportal-ownership` | root | reconcile the three-root ownership model | +| `libreportal-dns` | root | edit `/etc/resolv.conf` (nameservers) | +| `libreportal-ssh-access` | root | manage admin `authorized_keys` + sshd `PasswordAuthentication` | +| `libreportal-socket` | root | docker-socket read perms (type switcher) | +| `libreportal-svc` | root | generate/install the task-processor systemd unit | +| `libreportal-bininstall` | root | install the restic/kopia backup-engine binaries | +| `libreportal-appcfg` | root | rewrite AdGuard/CrowdSec/ownCloud config files | + +These are the scoped-sudoers trust boundary: root-owned in a root-owned dir, so the +manager can `sudo` them but can't modify them. Each bakes the three roots + the +manager name at install. Source of truth: `scripts/system/` in the repo; installed +by `init.sh` → `initRootHelpers` (re-installed only on a reinstall, not by the +quick-deploy). + +## OS-mandated locations (must live where the OS reads them) + +| Path | Owner | Purpose | +|------|-------|---------| +| `/usr/local/bin/libreportal` | root | **symlink** → `/usr/local/lib/libreportal/libreportal` (puts the CLI on `$PATH`) | +| `/etc/sudoers.d/` | root | scoped least-privilege grant for the manager (drop-in named after the manager user) | +| `/etc/systemd/system/libreportal.service` | root | the task-processor service (`User=`; bakes the roots as `Environment=LP_*_DIR`) | +| `/etc/sysctl.d/99-libreportal-hardening.conf` | root | kernel LPE-surface hardening | +| `/etc/sysctl.d/99-libreportal-rootless.conf` | root | rootless sysctl settings + "rootless configured" marker | + +## Third-party tools we install (not ours, conventional home) + +`/usr/local/bin/{restic,kopia,ufw-docker,docker-compose}` — installed on demand +(restic/kopia via the `libreportal-bininstall` helper). `/usr/local/bin` is the +correct home for these; left under their own names. + +## System users + +The **manager** (default `libreportal`, configurable via `--manager-user=` / +`LP_MANAGER_USER`) and the **container user** (default `dockerinstall`, from +`CFG_DOCKER_INSTALL_USER`) — each with a home under `/home/`. The rootless daemon +config lives at `~/.config/docker/daemon.json`. + +## Uninstall sketch + +`init.sh uninstall` does all of this; the sketch (with default roots/manager): + +``` +sudo systemctl disable --now libreportal.service +sudo rm -f /etc/systemd/system/libreportal.service /etc/sudoers.d/libreportal +sudo rm -f /etc/sysctl.d/99-libreportal-*.conf +sudo rm -rf /usr/local/lib/libreportal /usr/local/bin/libreportal +sudo rm -rf /libreportal-system /libreportal-containers /libreportal-backups +# optional: the backup-engine binaries and the two users +``` diff --git a/PROMISE.md b/docs/PROMISE.md similarity index 100% rename from PROMISE.md rename to docs/PROMISE.md diff --git a/init.sh b/init.sh index dae5b9a..7e75d7f 100755 --- a/init.sh +++ b/init.sh @@ -858,7 +858,7 @@ initUsers() # op the manager can't modify), and a fixed system-binary set. Excluded: # bash/su + tee/cp/chmod/chown/sed/mv/rm/install (each root-equivalent). Also # clears legacy broad grants (a NOPASSWD: ALL in the main /etc/sudoers, sudo-group -# membership). See FOOTPRINT.md. +# membership). See docs/FOOTPRINT.md. initScopedSudoers() { local sudoers_dropin="/etc/sudoers.d/${sudo_user_name}" @@ -1544,7 +1544,7 @@ completeInitMessage() # containers run AS the docker-install user and the rootless daemon is that user's # systemd --user service, so stop those BEFORE removing the users. Self-contained: # uses only init.sh's inline helpers, so it still works as it deletes /docker. -# Keep in sync with FOOTPRINT.md. +# Keep in sync with docs/FOOTPRINT.md. runFullUninstall() { local mgr="${sudo_user_name:-libreportal}" @@ -1618,7 +1618,7 @@ runFullUninstall() fi fi - # 3. Remove the out-of-/docker footprint (see FOOTPRINT.md). + # 3. Remove the out-of-/docker footprint (see docs/FOOTPRINT.md). rm -f /usr/local/bin/libreportal rm -rf /usr/local/lib/libreportal rm -f "/etc/sudoers.d/$mgr" diff --git a/site/src/_data/site.json b/site/src/_data/site.json index 315890e..b248c66 100644 --- a/site/src/_data/site.json +++ b/site/src/_data/site.json @@ -5,8 +5,8 @@ "installUrl": "https://get.libreportal.org", "installCmd": "bash <(curl -fsSL https://get.libreportal.org) init", "repo": "https://gitea.scottwebstar.co.uk/Webstar/LibrePortal", - "promiseUrl": "https://gitea.scottwebstar.co.uk/Webstar/LibrePortal/raw/branch/main/PROMISE.md", - "contributingUrl": "https://gitea.scottwebstar.co.uk/Webstar/LibrePortal/raw/branch/main/CONTRIBUTING.md", + "promiseUrl": "https://gitea.scottwebstar.co.uk/Webstar/LibrePortal/raw/branch/main/docs/PROMISE.md", + "contributingUrl": "https://gitea.scottwebstar.co.uk/Webstar/LibrePortal/raw/branch/main/docs/CONTRIBUTING.md", "initUrl": "https://gitea.scottwebstar.co.uk/Webstar/LibrePortal/raw/branch/main/init.sh", "inspiration": "https://gitlab.com/bmcgonag/docker_installs" }