v0.13.0 web and terminal client enhance, replay api, linux server deploy, doc and dependency update
test / test (push) Canceled after 0s
test / test (push) Canceled after 0s
This commit is contained in:
1 parent
52ac0fe31a
commit
f1ac4cbbe7
71 files changed
+7677
-537
No files matched your search
@@ -0,0 +1,228 @@
|
||||
# Deployment: Linux with systemd and PostgreSQL
|
||||
|
||||
A native install on Debian, Ubuntu, Arch, or another systemd distribution
|
||||
that already runs PostgreSQL. The service is `chessd`, running as a system
|
||||
account `chess` without a login shell; it reaches PostgreSQL over the local
|
||||
Unix socket with peer authentication, so no database password exists. A
|
||||
reverse proxy (nginx) on the same host, if any, forwards `/chess/api/` to it.
|
||||
The FreeBSD jail layout is in [deployment.md](deployment.md); database
|
||||
maintenance is in [database.md](database.md).
|
||||
|
||||
```
|
||||
browser ──TLS──▶ nginx ──HTTP 127.0.0.1:8080──▶ chessd (chess-server, user chess)
|
||||
│ Unix socket, peer auth
|
||||
▼
|
||||
PostgreSQL 17+ (database chess)
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
PostgreSQL 17 or later (18 recommended), running, with its default local
|
||||
socket; Stockfish; systemd; Go 1.27 to build.
|
||||
|
||||
**Debian / Ubuntu.** Ubuntu 24.04 ships PostgreSQL 16, so 18 comes from the
|
||||
PostgreSQL project's repository (PGDG). With 18 already installed, check the
|
||||
cluster with `pg_lsclusters` and skip this.
|
||||
|
||||
```sh
|
||||
sudo apt install postgresql-common stockfish
|
||||
sudo /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh
|
||||
sudo apt install postgresql-18 # creates and starts cluster 18/main
|
||||
```
|
||||
|
||||
The default `pg_hba.conf` already maps local OS accounts to roles of the same
|
||||
name (`local all all peer`). Stockfish installs as `/usr/games/stockfish`;
|
||||
the service adds `/usr/games` to its `PATH`.
|
||||
|
||||
**Arch.** The package does not create a cluster; initialize one the first
|
||||
time only:
|
||||
|
||||
```sh
|
||||
sudo pacman -S postgresql stockfish
|
||||
sudo -u postgres initdb -D /var/lib/postgres/data --encoding=UTF8 \
|
||||
--locale-provider=builtin --builtin-locale=C.UTF-8 \
|
||||
--auth-local=peer --auth-host=scram-sha-256
|
||||
sudo systemctl enable --now postgresql
|
||||
```
|
||||
|
||||
A cluster made by a plain `initdb` trusts every local connection. The chess
|
||||
account connects either way, but `peer` is what you want: without it, any
|
||||
local account can connect as any role.
|
||||
|
||||
**Existing clusters.** Other databases, roles, and applications on the
|
||||
server are left alone: the setup creates one role and one database named
|
||||
`chess`, changes no server settings, and edits `pg_hba.conf` only when asked
|
||||
(`PG_HBA=yes`, below). It does not change `listen_addresses`; the service
|
||||
itself only uses the socket.
|
||||
|
||||
## Install
|
||||
|
||||
```sh
|
||||
make server # bin/chess-server, static
|
||||
sudo CHESS_BINARY=bin/chess-server TRUSTED_PROXIES=127.0.0.1 deploy/linux/setup.sh
|
||||
sudo chess-db user add -username <name> # prompts for the password
|
||||
```
|
||||
|
||||
The script is idempotent: rerun it with a new binary to upgrade. It stops
|
||||
`chessd`, installs everything, migrates the schema, starts the service, and
|
||||
fails unless `/health` reports `"storage":"ok"`.
|
||||
|
||||
| Input | Default | Meaning |
|
||||
|---|---|---|
|
||||
| `CHESS_BINARY` | required | `chess-server` binary to install |
|
||||
| `TRUSTED_PROXIES` | kept from `chessd.env` | Proxy address(es) whose `X-Real-IP` is trusted; `127.0.0.1` for nginx on this host |
|
||||
| `CHESSD_HOST`, `CHESSD_PORT` | `127.0.0.1`, `8080` | Listen address, first install only |
|
||||
| `SPLIT_PRIVILEGES` | `no` | `yes`: the service role only reads and writes rows ([database.md](database.md#typical-layout)) |
|
||||
| `ENABLE_BACKUP` | `no` | `yes`: nightly `pg_dump` through a systemd timer |
|
||||
| `PG_HBA` | `no` | `yes`: add `local chess chess peer` to `pg_hba.conf` if the chess account cannot connect |
|
||||
|
||||
What it installs:
|
||||
|
||||
| Path | Purpose |
|
||||
|---|---|
|
||||
| account `chess` | System account, no login shell, home `/var/lib/chessd` |
|
||||
| `/usr/local/bin/chess-server` | The server, `root:root 0755`; the previous copy kept as `.prev` |
|
||||
| `/usr/local/sbin/chess-db` | Runs `chess-server db ...` as `chess` against the database: `chess-db user list`, `chess-db verify` |
|
||||
| `/etc/chessd/chessd.env` | Settings, `root:chess 0640`; written on the first install only |
|
||||
| `/var/lib/chessd/jwt.key` | JWT signing key, `chess 0600`, generated once; sessions survive restarts |
|
||||
| `/etc/systemd/system/chessd.service` | The service unit, replaced on every run |
|
||||
| `chess-backup.service`, `.timer`, `/usr/local/sbin/chess-backup` | With `ENABLE_BACKUP=yes`: dumps to `backups/` in the postgres home, 14 days kept |
|
||||
|
||||
PostgreSQL steps, in order: check the server version (17+) and socket as
|
||||
`postgres`; create role, database, and schema with
|
||||
[`setup.sql`](../deploy/postgresql/setup.sql) when neither exists (a
|
||||
half-present pair stops the script); check that the `chess` account can
|
||||
connect.
|
||||
|
||||
### When the chess account cannot connect
|
||||
|
||||
The first matching line of `pg_hba.conf` decides. A cluster whose local
|
||||
lines require a password (`scram-sha-256`, `md5`) refuses the passwordless
|
||||
`chess` role, and the script stops with the file's path and this line to add
|
||||
above the other `local` lines:
|
||||
|
||||
```
|
||||
local chess chess peer
|
||||
```
|
||||
|
||||
Then reload (`sudo systemctl reload postgresql`) and rerun, or rerun with
|
||||
`PG_HBA=yes` to have the line inserted and PostgreSQL reloaded (the previous
|
||||
file is kept next to it with a timestamp).
|
||||
|
||||
## Settings
|
||||
|
||||
`/etc/chessd/chessd.env` is read by the unit; restart after a change
|
||||
(`sudo systemctl restart chessd`).
|
||||
|
||||
| Setting | First-install value | Purpose |
|
||||
|---|---|---|
|
||||
| `CHESSD_HOST`, `CHESSD_PORT` | `127.0.0.1`, `8080` | Listen address; keep loopback when nginx runs on the host |
|
||||
| `CHESSD_DSN` | `"host=<socket dir> dbname=chess"` | Database over the socket as the OS account |
|
||||
| `CHESSD_JWT_KEY` | `/var/lib/chessd/jwt.key` | Stable signing key |
|
||||
| `CHESSD_TRUSTED_PROXIES` | `TRUSTED_PROXIES` | Comma-separated proxy addresses |
|
||||
| `CHESSD_FLAGS` | empty | Other server flags, e.g. `-max-users 500 -db-cleanup report` |
|
||||
|
||||
Server flags worth knowing: `-max-users` (registration cap, default 100),
|
||||
`-anonymous-game-ttl` (default `24h`), `-finished-game-ttl` (default `1h`),
|
||||
`-db-cleanup off|report|delete` (the integrity sweep, see
|
||||
[database.md](database.md#integrity-sweep)), `-log-level`, `-log-http`.
|
||||
|
||||
Changes to the unit belong in a drop-in (`sudo systemctl edit chessd`), which
|
||||
the script never overwrites.
|
||||
|
||||
## Operation
|
||||
|
||||
```sh
|
||||
systemctl status chessd
|
||||
journalctl -u chessd -f # server log
|
||||
sudo chess-db user add -username <name> # accounts; see database.md
|
||||
sudo chess-db verify # check stored games; run after upgrades
|
||||
curl -s http://127.0.0.1:8080/health # "storage":"ok"
|
||||
systemctl list-timers chess-backup.timer # with ENABLE_BACKUP=yes
|
||||
```
|
||||
|
||||
**Upgrade:** build, then rerun `setup.sh` with the new `CHESS_BINARY`.
|
||||
Schema migrations run before the service starts.
|
||||
|
||||
**Rollback:** until you delete it, the previous binary is kept:
|
||||
|
||||
```sh
|
||||
sudo systemctl stop chessd
|
||||
sudo cp /usr/local/bin/chess-server.prev /usr/local/bin/chess-server
|
||||
sudo systemctl start chessd
|
||||
```
|
||||
|
||||
A newer schema is not downgraded: a binary older than the database refuses to
|
||||
start (see the log), so keep a dump from before the upgrade.
|
||||
|
||||
**Uninstall:**
|
||||
|
||||
```sh
|
||||
sudo systemctl disable --now chessd chess-backup.timer
|
||||
sudo rm -f /etc/systemd/system/chessd.service /etc/systemd/system/chess-backup.{service,timer} \
|
||||
/usr/local/bin/chess-server{,.prev} /usr/local/sbin/chess-{db,backup}
|
||||
sudo systemctl daemon-reload
|
||||
sudo rm -rf /etc/chessd /var/lib/chessd
|
||||
sudo -u postgres psql -X -c 'DROP DATABASE chess WITH (FORCE)' -c 'DROP ROLE IF EXISTS chess_owner' -c 'DROP ROLE chess'
|
||||
sudo userdel chess
|
||||
```
|
||||
|
||||
## nginx on the Same Host
|
||||
|
||||
Use [`deploy/nginx-chess.conf`](../deploy/nginx-chess.conf) with
|
||||
`<server-address>:<port>` = `127.0.0.1:8080`, and install with
|
||||
`TRUSTED_PROXIES=127.0.0.1`, so the server keys rate limits on the client
|
||||
address nginx sends in `X-Real-IP`. The browser clients and their
|
||||
publishing steps are the same as on FreeBSD
|
||||
([deployment.md](deployment.md#host-nginx-and-web-clients)).
|
||||
|
||||
## Service Sandbox
|
||||
|
||||
The unit runs the server with systemd's sandboxing: the whole file system is
|
||||
read-only except `/var/lib/chessd`, home directories and `/tmp` are hidden,
|
||||
no capabilities or privilege escalation, only Unix and IP sockets, and the
|
||||
`@system-service` system-call set. Stockfish runs inside the same sandbox.
|
||||
`systemd-analyze security chessd` summarizes the result.
|
||||
|
||||
## Running as Your Own Account (Development)
|
||||
|
||||
Without a `chess` account, the server runs as you, with a database owned by
|
||||
your PostgreSQL role; tables then live in that database's `public` schema,
|
||||
which is fine for development:
|
||||
|
||||
```sh
|
||||
sudo -u postgres createuser --createdb "$USER" # once, if you have no role yet
|
||||
createdb chess_dev
|
||||
bin/chess-server db init -dsn "dbname=chess_dev"
|
||||
bin/chess-server -dev -dsn "dbname=chess_dev" -serve # API :8080, web client :9090
|
||||
bin/chess-server db user add -username me -dsn "dbname=chess_dev"
|
||||
```
|
||||
|
||||
`-dev` relaxes rate limits and uses a fixed JWT key; never use it in
|
||||
production. The test suites need a separate, disposable database whose
|
||||
tables they drop ([test/README.md](../test/README.md)):
|
||||
|
||||
```sh
|
||||
createdb chess_test
|
||||
export CHESS_TEST_DSN="dbname=chess_test"
|
||||
go test ./...
|
||||
test/run-test-server.sh bin/chess-server # then test/test-*.sh in another terminal
|
||||
```
|
||||
|
||||
## Verification Checklist
|
||||
|
||||
```sh
|
||||
systemctl is-active chessd # active
|
||||
curl -s http://127.0.0.1:8080/health # "storage":"ok"
|
||||
ps -o user= -C chess-server # chess
|
||||
sudo -u postgres psql -X -d chess -c '\dt chess.*' # five tables
|
||||
sudo chess-db verify # 0 problem(s)
|
||||
sudo -u chess psql -X -d chess -Atc 'SHOW search_path' # chess
|
||||
```
|
||||
|
||||
The scripts were exercised on Ubuntu 24.04 with PostgreSQL 18.6 and
|
||||
`systemctl` replaced by a stand-in that starts the unit's `ExecStart` as its
|
||||
`User` with its `EnvironmentFile`: a first install that stops at
|
||||
`pg_hba.conf`, the same with `PG_HBA=yes`, an idempotent rerun, an upgrade,
|
||||
a trusted-proxy change, backups, and a fresh split-privilege install.
|
||||
`systemd-analyze verify` accepts the units.
|
||||
Reference in new issue
Block a user