Kaynağa Gözat

adding deployment options

Josef Straßl 2 hafta önce
ebeveyn
işleme
f52e7470dc
4 değiştirilmiş dosya ile 148 ekleme ve 12 silme
  1. 3 2
      config.example.toml
  2. 95 0
      deploy/README.md
  3. 41 0
      deploy/ppsq.service
  4. 9 10
      docs/operations.md

+ 3 - 2
config.example.toml

@@ -15,8 +15,9 @@ password = "secret"                            # (W) plaintext on disk (0600); r
 # to a CA bundle to verify against it.
 verify_tls = false
 timeout = 120                                  # seconds per PPS call (5..600)
-# Mutual TLS client cert, if PPS/nginx requires one ("400 No required SSL certificate").
-client_cert = "certs/client.pem"               # combined cert+key PEM, or a cert with client_key
+# Mutual TLS client cert — only if PPS/nginx requires one ("400 No required SSL
+# certificate"). Leave BOTH empty otherwise (the file must exist if set).
+client_cert = ""                               # path to a combined cert+key PEM, or a cert with client_key
 client_key = ""
 
 [quarantine]                                   # (A)

+ 95 - 0
deploy/README.md

@@ -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.

+ 41 - 0
deploy/ppsq.service

@@ -0,0 +1,41 @@
+# systemd unit for the PPS Quarantine Manager.
+#
+# Install (as root):
+#   see deploy/README.md for the full walk-through. In short:
+#   1. put the app in /opt/ppsq, create a `ppsq` user that owns it
+#   2. edit the paths/user below if you used different ones
+#   3. cp deploy/ppsq.service /etc/systemd/system/ppsq.service
+#   4. systemctl daemon-reload && systemctl enable --now ppsq
+#
+# IMPORTANT: single process only. Do NOT template this into multiple instances or add
+# web-server workers — the background job queue and SQLite live in-process (see AGENTS.md).
+
+[Unit]
+Description=PPS Quarantine Manager
+After=network-online.target
+Wants=network-online.target
+
+[Service]
+Type=simple
+User=ppsq
+Group=ppsq
+WorkingDirectory=/opt/ppsq
+ExecStart=/opt/ppsq/venv/bin/python wsgi.py
+Restart=on-failure
+RestartSec=5
+
+# Config path is optional: the app defaults to ./config.toml in WorkingDirectory.
+# Uncomment to point elsewhere:
+# Environment=PPSQ_CONFIG=/etc/ppsq/config.toml
+
+# --- modest hardening (safe defaults; remove any that get in your way) ---
+NoNewPrivileges=true
+PrivateTmp=true
+ProtectSystem=full
+ProtectHome=true
+# The app rewrites config.toml + writes jobs.db/*.log in WorkingDirectory, so it must be
+# writable. If ProtectSystem hides it, grant it explicitly:
+ReadWritePaths=/opt/ppsq
+
+[Install]
+WantedBy=multi-user.target

+ 9 - 10
docs/operations.md

@@ -6,7 +6,8 @@ Single process (mandatory — see AGENTS.md #1). On the VM, as a dedicated servi
 
 ```bash
 python3 -m venv venv
-venv/bin/pip install -r requirements.lock     # pinned; or requirements.txt for ranges
+venv/bin/pip install --upgrade pip
+venv/bin/pip install -r requirements.txt      # ranges (recommended); requirements.lock = exact pins
 cp config.example.toml config.toml
 # edit config.toml: [okta], [pps] creds, [[auth.users]], and a real secret_key:
 python -c "import secrets; print(secrets.token_urlsafe(48))"
@@ -18,18 +19,16 @@ The config file must be **writable by the service user** (the admin panel rewrit
 temp-file + atomic rename). A read-only mount breaks admin saves. `wsgi.py` refuses to boot
 with the placeholder or a `<32` char `secret_key`.
 
-Run under a supervisor (systemd) that restarts on exit and keeps it a single instance:
+Run under systemd — a ready-to-import unit and step-by-step instructions live in
+[`deploy/`](../deploy/README.md):
 
-```ini
-[Service]
-User=ppsq
-WorkingDirectory=/opt/ppsq
-ExecStart=/opt/ppsq/venv/bin/python wsgi.py
-Restart=on-failure
-# do NOT add multiple instances / workers
+```bash
+sudo cp deploy/ppsq.service /etc/systemd/system/ppsq.service
+sudo systemctl daemon-reload && sudo systemctl enable --now ppsq
 ```
 
-Put a TLS-terminating reverse proxy (nginx) in front; set `[app] cookie_secure = true` and
+Keep it a **single instance** — do not template it or add web-server workers. Put a
+TLS-terminating reverse proxy (nginx) in front; set `[app] cookie_secure = true` and
 `listen = "127.0.0.1"`.
 
 ## Logs