diff --git a/README.md b/README.md index 6f2dcdc..946f617 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,17 @@ It can also set up [Tailscale](phones.md#tailscale-recommended), so your phones over HTTPS, which they need to install an app and get notifications. You only open a link to sign in; there are no keys or settings to copy. Then [put the apps on your phones](phones.md). +Prefer to read a script before you run it? Download it, read it, then run it: + +```sh +curl -fsSLO https://raw.githubusercontent.com/selfhostit/docs/main/install.sh +less install.sh +sh install.sh +``` + +[Security and privacy](security.md) says step by step what it does, what the apps protect +and what you are trusting. + ## Guides - [Raspberry Pi and Linux](linux.md): a Pi, a mini PC, an old laptop or a VPS. @@ -36,6 +47,7 @@ sign in; there are no keys or settings to copy. Then [put the apps on your phone CasaOS and anything else that takes a Compose file. - [Use them on your phones](phones.md): Tailscale, or a reverse proxy with your own domain. - [Updates and backups](updates-and-backups.md) +- [Security and privacy](security.md): how your data is protected, and how to report a problem. ## What you need diff --git a/install.sh b/install.sh index f377f06..ca013d2 100644 --- a/install.sh +++ b/install.sh @@ -4,6 +4,9 @@ # # curl -fsSL https://raw.githubusercontent.com/selfhostit/docs/main/install.sh | sh # +# To read it before running it, download it first (curl -fsSLO ), +# then run `sh install.sh`. security.md says what it does, step by step. +# # It asks which apps you want, gives each one its own folder and port, and runs # that app's own installer (or its compose file). Then it can put them all on # your Tailscale network with https:// addresses, which phones need: you sign in diff --git a/security.md b/security.md new file mode 100644 index 0000000..09cdd5f --- /dev/null +++ b/security.md @@ -0,0 +1,130 @@ +# Security and privacy + +This page says how the selfhostit apps protect your data, what you are trusting when you +install them, and what is not done yet. Each point links to the code or the setting, so you +can check it rather than take it on trust. + +Found a security problem? Please don't open a public issue. Write to **kalfasyan@pm.me** +with what you found and how to reproduce it; see [SECURITY.md](https://github.com/selfhostit/.github/blob/main/SECURITY.md). + +## In short + +- **Your data stays on your machine.** Each app keeps everything in one folder (`pb_data`, + a SQLite database and its files). There is no selfhostit cloud, account or server, and the + apps have no analytics or tracking. +- **The apps are not on the internet.** Every app listens on `127.0.0.1` only. You reach + them through [Tailscale](phones.md#tailscale-recommended), a private network of your own + devices, or through your own reverse proxy. The guides say not to forward ports. +- **Nobody can claim a new install before you.** A fresh install prints a one-time setup + code, and the first-run screen asks for it. +- **The code is open** (MIT) and small enough to read: one Go backend on + [PocketBase](https://pocketbase.io) and one Svelte web app per app. + +## What protects an install + +| What | Where | +|---|---| +| Apps listen on `127.0.0.1` only, so only this machine (and Tailscale or a reverse proxy on it) can connect | `ports:` in each app's compose file, for example [Flowy](https://github.com/selfhostit/flowy/blob/main/deploy/docker-compose.yml) | +| Containers drop every Linux capability and can't gain new privileges; Flowy and Troye also run as a normal user (Splitwiz and should-we not yet) | same compose files: `cap_drop: [ALL]`, `security_opt`, `user:` | +| One-time setup code: the first account (the owner) can only be made with the code printed at install, which is deleted after setup | `backend/app/setup.go` in [plasto](https://github.com/selfhostit/plasto/blob/main/backend/app/setup.go), Flowy, Troye and Splitwiz | +| Sign-in rate limits: 10 password attempts a minute for people and for the admin UI, 5 a minute for setup | `backend/migrations/*_rate_limits.go` in each app | +| Access rules on every collection: people only see their own records or their group's | the collection API rules in `backend/migrations` | +| Content Security Policy, `nosniff` and a same-origin referrer on the web app, so injected scripts and outside resources don't load | `backend/app/headers.go` in each app | +| Discreet reminders (Flowy, off by default): notifications only say "Reminder from Flowy", for a lock screen others can see | Flowy settings, `discreet_reminders` | +| Nightly database backups, the last 7 kept | [Updates and backups](updates-and-backups.md#backups) | + +## How the code and images are built + +- Every app is built and tested in GitHub Actions on each pull request and push: Go and + web tests, type checks, and (for the plasto apps) an end-to-end run of the published + compose file. +- Workflows run with a read-only token by default (`permissions: contents: read`); only the + job that publishes images gets `packages: write`. +- Every third-party Action is pinned to a full commit hash, not a movable tag. +- [Dependabot](https://docs.github.com/code-security/dependabot) keeps the Actions up to date + in every repository, and in plasto, Flowy and Troye also the Go, npm, Python and Docker + dependencies. +- should-we also scans every push for committed secrets with gitleaks. +- Images are published to [ghcr.io/selfhostit](https://github.com/orgs/selfhostit/packages) + for amd64, arm64 and armv7, tagged `latest` and with the commit they were built from. + +## What you are trusting + +Being honest about this matters more than sounding safe. + +- **The installer and the images come from this GitHub organization.** The installer + downloads each app's installer or compose file from that app's `main` branch, and the apps + run the `latest` image. Someone who took over the maintainer's GitHub account could ship + a bad update to every install that updates. Releases are not signed yet; see + [Not done yet](#not-done-yet). +- **Docker and the host.** The containers are locked down, but Docker itself runs as root, + and anyone with access to the machine can read `pb_data`. The data on disk is not + encrypted, so encrypt backups that leave the machine and wipe the disk before you sell it. +- **Tailscale**, if you use it, to keep your private network private. Turn on two-factor + sign-in for your Tailscale account and only share the machine with people you trust. +- **What the apps send out:** push notifications go through your phone's browser push + service (Apple, Google or Mozilla), encrypted end to end so the service can't read them. + Troye looks up scanned barcodes on Open Food Facts and downloads its food table once a + month. Nothing else leaves your server. + +## Read the installer before you run it + +`curl ... | sh` runs a script straight from the internet. To read it first: + +```sh +curl -fsSLO https://raw.githubusercontent.com/selfhostit/docs/main/install.sh +less install.sh # read it +sh install.sh +``` + +It is one plain shell script with comments. What it does: + +1. Installs Docker with Docker's official script, only if Docker is missing and you say yes. +2. Makes one folder per app (in `~/selfhostit`, or `/volume1/docker` on a Synology). +3. For each app you pick, downloads and runs that app's own installer or compose file from + GitHub ([Flowy](https://github.com/selfhostit/flowy/blob/main/deploy/install.sh), + [Troye](https://github.com/selfhostit/troye/blob/main/deploy/install.sh), + [Splitwiz](https://github.com/selfhostit/splitwiz/blob/main/scripts/install.sh), + [should-we](https://github.com/selfhostit/should-we/blob/main/docker-compose.yml)). + Read those too if you want to see every step. +4. If you say yes, starts one Tailscale container and gives each app an `https://` address. + +It uses `sudo` only to run Docker when your user can't, and changes nothing outside those +folders and Docker. Rather not run any script? Set the apps up from their compose files +yourself: see [Synology without SSH](synology.md#without-ssh) and +[other NAS and home servers](other-nas.md). + +### Pin a version + +To decide yourself when to update, pin an image to the commit it was built from instead of +`latest`, in the app's `.env`, for example +`FLOWY_IMAGE=ghcr.io/selfhostit/flowy:` or `SPLITWIZ_VERSION=`. The commit +hashes are in each repository's history; should-we already pins its major version (`:2`). + +## Project status + +selfhostit is young and run by one person, [Yannis Kalfas](https://github.com/kalfasyan). +There has been no independent security audit. The history of every change is public in +each repository, and the hardening above was added in public pull requests. + +If the project ever stops, your install keeps working: the images stay on ghcr.io, the code +is MIT licensed so anyone can keep building it, and your data is a SQLite database in +`pb_data` that you own. Splitwiz and Flowy also export everything as CSV. + +## Not done yet + +These are planned, in roughly this order: + +- Versioned releases with signed images, so an install updates to a release you can verify, + not to whatever is on `main`. +- Splitwiz and should-we running as a normal user inside their containers, like Flowy and Troye. +- A "sign out everywhere" button in every app (Flowy has it), for a lost phone. +- An OpenSSF Scorecard run on each repository. + +## Your part + +- Keep the apps updated: `selfhostit update all` or the installer again + ([Updates and backups](updates-and-backups.md)). +- Keep a backup somewhere else, encrypted. +- Don't forward ports to the apps; use Tailscale or your own reverse proxy with HTTPS. +- Use a strong password for the owner account; it is also the admin UI (`/_/`) password.