|
@@ -0,0 +1,95 @@
|
|
|
|
|
+# Deploying with systemd
|
|
|
|
|
+
|
|
|
|
|
+A minimal, single-instance internal deployment. Run once as root on the target VM.
|
|
|
|
|
+
|
|
|
|
|
+The app is a single process (waitress + an in-process worker thread + SQLite) — see
|
|
|
|
|
+AGENTS.md. Do **not** run multiple instances or web-server workers.
|
|
|
|
|
+
|
|
|
|
|
+## 1. Create the service user and lay down the code
|
|
|
|
|
+
|
|
|
|
|
+```bash
|
|
|
|
|
+sudo useradd --system --home /opt/pps-quarantine --shell /usr/sbin/nologin pps
|
|
|
|
|
+sudo mkdir -p /opt/pps-quarantine
|
|
|
|
|
+# copy this repo to /opt/pps-quarantine (git clone, rsync, scp — whatever you use), then:
|
|
|
|
|
+sudo chown -R pps:pps /opt/pps-quarantine
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+## 2. Install dependencies (as the service user)
|
|
|
|
|
+
|
|
|
|
|
+```bash
|
|
|
|
|
+cd /opt/pps-quarantine
|
|
|
|
|
+sudo -u pps python3 -m venv venv
|
|
|
|
|
+sudo -u pps venv/bin/pip install --upgrade pip # avoids "no matching distribution" on old pip
|
|
|
|
|
+sudo -u pps venv/bin/pip install -r requirements.txt # version ranges; pip picks compatible builds
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+Requires **Python 3.9+**. `requirements.lock` (exact pins from the dev machine) also exists,
|
|
|
|
|
+but only install from it if that exact Python/pip combination matches — otherwise use
|
|
|
|
|
+`requirements.txt` above.
|
|
|
|
|
+
|
|
|
|
|
+## 3. Configure
|
|
|
|
|
+
|
|
|
|
|
+```bash
|
|
|
|
|
+sudo -u pps cp config.example.toml config.toml
|
|
|
|
|
+sudo -u pps chmod 600 config.toml
|
|
|
|
|
+# generate a session key:
|
|
|
|
|
+python3 -c "import secrets; print(secrets.token_urlsafe(48))"
|
|
|
|
|
+sudoedit config.toml # (or edit as the pps user)
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+Fill in at least: `[okta]` (issuer/client_id/client_secret/redirect_uri), `[pps]` host +
|
|
|
|
|
+API credentials, `[[auth.users]]` (your admins), a real `[app] secret_key`, and set
|
|
|
|
|
+`[app] listen = "127.0.0.1"` + `cookie_secure = true` (you'll front it with TLS).
|
|
|
|
|
+`wsgi.py` refuses to boot with the placeholder or a `<32`-char secret_key.
|
|
|
|
|
+
|
|
|
|
|
+The `config.toml` and the `/opt/pps-quarantine` directory must stay **writable by `pps`** — the
|
|
|
|
|
+admin panel rewrites `config.toml`, and `jobs.db` / `*.log` are written there.
|
|
|
|
|
+
|
|
|
|
|
+## 4. Install and start the service
|
|
|
|
|
+
|
|
|
|
|
+```bash
|
|
|
|
|
+sudo cp /opt/pps-quarantine/deploy/pps.service /etc/systemd/system/pps.service
|
|
|
|
|
+# if you used a different path/user, edit the unit first
|
|
|
|
|
+sudo systemctl daemon-reload
|
|
|
|
|
+sudo systemctl enable --now pps
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+## 5. Verify
|
|
|
|
|
+
|
|
|
|
|
+```bash
|
|
|
|
|
+systemctl status pps
|
|
|
|
|
+journalctl -u pps -f # startup + request logs (also in /opt/pps-quarantine/app.log)
|
|
|
|
|
+curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080/login # -> 200
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+## Reverse proxy (TLS)
|
|
|
|
|
+
|
|
|
|
|
+Terminate TLS at nginx/Caddy and proxy to `127.0.0.1:8080`. Make sure the Okta
|
|
|
|
|
+`redirect_uri` matches the public HTTPS URL exactly (e.g.
|
|
|
|
|
+`https://pps.internal.example.com/authorize`). Minimal nginx:
|
|
|
|
|
+
|
|
|
|
|
+```nginx
|
|
|
|
|
+server {
|
|
|
|
|
+ listen 443 ssl;
|
|
|
|
|
+ server_name pps.internal.example.com;
|
|
|
|
|
+ ssl_certificate /etc/ssl/pps.crt;
|
|
|
|
|
+ ssl_certificate_key /etc/ssl/pps.key;
|
|
|
|
|
+ location / {
|
|
|
|
|
+ proxy_pass http://127.0.0.1:8080;
|
|
|
|
|
+ proxy_set_header Host $host;
|
|
|
|
|
+ proxy_set_header X-Forwarded-Proto https;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+## Day-to-day
|
|
|
|
|
+
|
|
|
|
|
+```bash
|
|
|
|
|
+sudo systemctl restart pps # after editing [app]/[okta] (restart-only keys)
|
|
|
|
|
+sudo systemctl stop pps
|
|
|
|
|
+journalctl -u pps --since '1 hour ago'
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+Most settings ([pps], [quarantine], users) are editable live in the admin panel and need
|
|
|
|
|
+no restart. See `docs/operations.md` for logs, backups, and credential rotation, and
|
|
|
|
|
+`docs/configuration.md` for which keys require a restart.
|