Deployment-Härtung + Podman-Container
- http.Server mit Timeouts (Slowloris) und Graceful Shutdown (SIGTERM) - Adresse und DB-Pfad per Env (KVER_ADDR, KVER_DB) - Security-Header global: nosniff, X-Frame-Options, Referrer-Policy, CSP - Upload-Requests hart auf 17 MB gedeckelt (MaxBytesReader statt nur Multipart-Speichergrenze) - Indizes für Feed-, Thread- und Vote-Queries - middleware.RealIP für Logging/Rate-Limit hinter dem Reverse-Proxy - Containerfile (Multi-Stage, Alpine, non-root) + Deploy-Doku in notes/ Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+111
@@ -0,0 +1,111 @@
|
||||
# Deployment mit Podman
|
||||
|
||||
## Bauen
|
||||
|
||||
```sh
|
||||
podman build -t kver .
|
||||
```
|
||||
|
||||
Multi-Stage-Build (`Containerfile`): statisches Go-Binary (CGO aus, modernc-
|
||||
SQLite ist pures Go), Laufzeit-Image ist Alpine mit unprivilegiertem Nutzer.
|
||||
|
||||
## Starten
|
||||
|
||||
```sh
|
||||
podman run -d --name kver \
|
||||
-p 127.0.0.1:8080:8080 \
|
||||
-v kver-data:/app/data \
|
||||
-v kver-media:/app/static/media \
|
||||
kver
|
||||
```
|
||||
|
||||
- `kver-data` hält die SQLite-DB (`/app/data/kver.db`), `kver-media` die
|
||||
hochgeladenen Bilder. Beides Named Volumes -> überleben Image-Updates.
|
||||
- Port nur an localhost binden; nach außen geht es über den Reverse-Proxy.
|
||||
|
||||
Konfiguration über Umgebungsvariablen:
|
||||
|
||||
| Variable | Default | Bedeutung |
|
||||
|-------------|---------------------|----------------------|
|
||||
| `KVER_ADDR` | `:8080` | Listen-Adresse |
|
||||
| `KVER_DB` | `/app/data/kver.db` | Pfad zur SQLite-Datei |
|
||||
|
||||
## Bestehende Daten übernehmen
|
||||
|
||||
DB und Medien einmalig in die Volumes kopieren (Container muss dafür kurz
|
||||
gelaufen sein bzw. die Volumes existieren):
|
||||
|
||||
```sh
|
||||
podman volume create kver-data
|
||||
podman volume create kver-media
|
||||
podman cp kver.db kver:/app/data/kver.db # bei laufendem Container, oder:
|
||||
cp kver.db "$(podman volume inspect kver-data -f '{{.Mountpoint}}')/"
|
||||
cp static/media/* "$(podman volume inspect kver-media -f '{{.Mountpoint}}')/"
|
||||
```
|
||||
|
||||
Danach Container neu starten. (Rootless: `podman unshare cp ...`, damit die
|
||||
UID-Zuordnung ins User-Namespace-Mapping passt.)
|
||||
|
||||
## Reverse-Proxy (TLS)
|
||||
|
||||
Der Container spricht nur HTTP. Davor gehört ein TLS-terminierender Proxy
|
||||
(Caddy/nginx), der `X-Forwarded-Proto: https` setzt — daran erkennt die App
|
||||
HTTPS und setzt das `Secure`-Flag auf den Session-Cookie (siehe `isHTTPS`).
|
||||
|
||||
nginx-Beispiel:
|
||||
|
||||
```nginx
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8080;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
client_max_body_size 20m; # Uploads bis 17 MB durchlassen
|
||||
}
|
||||
```
|
||||
|
||||
`client_max_body_size` muss über dem App-Limit (17 MB, `maxUploadBytes`)
|
||||
liegen, sonst kappt nginx vor der App.
|
||||
|
||||
Hinweis Rate-Limiting: `httprate.LimitByIP` auf den Auth-Routen sieht hinter
|
||||
einem Proxy die Proxy-IP. Entweder den Proxy die echte Client-IP als
|
||||
`X-Real-IP`/`X-Forwarded-For` setzen lassen und chi's `middleware.RealIP`
|
||||
vorschalten — oder damit leben, dass das Limit global statt pro IP wirkt.
|
||||
|
||||
## Autostart mit systemd (Quadlet)
|
||||
|
||||
`~/.config/containers/systemd/kver.container` (rootless):
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=kver (Kontrollverlust)
|
||||
|
||||
[Container]
|
||||
Image=localhost/kver:latest
|
||||
PublishPort=127.0.0.1:8080:8080
|
||||
Volume=kver-data:/app/data
|
||||
Volume=kver-media:/app/static/media
|
||||
|
||||
[Service]
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
```
|
||||
|
||||
Aktivieren:
|
||||
|
||||
```sh
|
||||
systemctl --user daemon-reload
|
||||
systemctl --user start kver
|
||||
loginctl enable-linger "$USER" # damit der Dienst ohne Login läuft
|
||||
```
|
||||
|
||||
## Update einspielen
|
||||
|
||||
```sh
|
||||
podman build -t kver .
|
||||
systemctl --user restart kver # bzw. podman stop kver && podman run ...
|
||||
```
|
||||
|
||||
Die App fährt bei SIGTERM sauber herunter (laufende Requests werden zu Ende
|
||||
bedient, WAL-Checkpoint beim DB-Close).
|
||||
Reference in New Issue
Block a user