Zum Inhalt springen
Selbst hosten

Selbst hosten

samudo ist ein Binary, samu-server, mit SQLite-Dateien in einem Datenverzeichnis. Es liefert API, Live-Ereignisse und Web-Oberfläche auf einem Port aus. Diese Schritte nehmen Docker Compose; das Binary läuft genauso gut unter systemd.

Bevor es losgeht

  • Privat halten. Die Grenze ist die Anmeldung: Ohne aktives Konto ist alles außer /health zu, Passwörter werden mit Argon2 gehasht, Sitzungen sind Tokens. Mehr nicht. Es ist nicht darauf geprüft, offen im Internet zu stehen. Am besten in einem VPN oder im Heimnetz betreiben, hinter einem Reverse-Proxy, der begrenzt, wer verbinden darf.
  • HTTPS. Das Sitzungs-Cookie ist Secure; Browser nehmen es über einfaches HTTP nur auf localhost an. Einen Reverse-Proxy mit TLS davorsetzen (Caddy, nginx, Traefik).
  • Ressourcen sind klein: Ein paar tausend Aufgaben werden im Speicher berechnet; eine halbe CPU und 256 MB reichen reichlich.

Das Image bauen

Ein veröffentlichtes Image gibt es noch nicht. Eines aus dem Repository bauen:

git clone <repository> samu && cd samu
docker build -f deploy/Dockerfile -t samu-server .

Das Image ist Debian slim mit samu-server und samu, läuft als Benutzer 10010, mit seinen Daten in /data.

Compose

# /srv/samu/docker-compose.yml
name: samu
services:
  server:
    image: samu-server
    restart: unless-stopped
    volumes:
      - data:/data
      - ./export:/export
    ports:
      - "127.0.0.1:8490:8490"     # hierhin verbindet der Reverse-Proxy
    environment:
      SAMU_DATA: /data
      SAMU_LISTEN: 0.0.0.0:8490
      SAMU_SECRET_KEY: ${SAMU_SECRET_KEY:-}
    healthcheck:
      test: ["CMD", "samu", "--server", "http://127.0.0.1:8490", "health"]
      interval: 30s
volumes:
  data:
cd /srv/samu
(umask 077; echo "SAMU_SECRET_KEY=$(openssl rand -base64 32)" > .env)
docker compose up -d
curl -s http://127.0.0.1:8490/health        # {"status":"ok",...}

Ein Eintrag im Reverse-Proxy, zum Beispiel mit Caddy:

samu.example.org {
	reverse_proxy 127.0.0.1:8490
}

Live-Aktualisierungen nutzen Server-Sent Events auf /events; der Proxy darf sie nicht puffern (Caddy, und nginx mit proxy_buffering off, sind in Ordnung).

Optionen des Servers

OptionUmgebungVorgabe
--dataSAMU_DATA/dataserver.db (Konten, Sitzungen) und accounts/<name>.db
--listenSAMU_LISTEN0.0.0.0:8490Adresse und Port
--secret-keySAMU_SECRET_KEYkeinerversiegelt die Kalenderquellen; siehe unten
--blob-max-bytesSAMU_BLOB_MAX_BYTES50 MBgrößter Anhang; nie mehr als 50 MB
--blob-quota-bytesSAMU_BLOB_QUOTA_BYTESkeinealle Anhänge eines Kontos zusammen
--calendar-hostsSAMU_CALENDAR_HOSTSalleHosts, von denen Kalender abgerufen werden, durch Kommas getrennt

Konten

Konten werden nur auf dem Server verwaltet, nie über HTTP:

docker compose exec -it server samu-server account add privat
docker compose exec -it server samu-server account list
docker compose exec -it server samu-server account passwd privat   # beendet seine Sitzungen
docker compose exec server samu-server account disable privat      # beendet seine Sitzungen

Jedes Konto ist eine eigene Datenbank. Lieber eins fürs Private und eins für die Arbeit, statt beides zu mischen.

Dann von einem Client aus anmelden: die Web-Oberfläche unter der eigenen Adresse, die Kommandozeile oder die Android-App koppeln.

Der geheime Schlüssel

Kalenderquellen (Abo-Adressen, CalDAV-Passwörter) liegen verschlüsselt in der Datenbank des Kontos. Der Schlüssel ist SAMU_SECRET_KEY, 32 Bytes in Base64. Ohne ihn wird kein Kalender abgerufen. Ihn getrennt von den Datenbanken sichern: Wer beides hat, hat die Quellen; wer den Schlüssel verliert, setzt jede Quelle neu (samu calendar source).

Eine öffentliche Instanz

Ein Server, den jeder erreichen kann, sollte begrenzen, was ein Fremder damit anstellen kann:

  • Anmeldungen werden immer gebremst: Nach fünf gescheiterten Anmeldungen eines Kontonamens in 15 Minuten wartet dieser Name, bis die 15 Minuten um sind (HTTP 429); eine gelungene Anmeldung vergisst die Fehlschläge. Höchstens zwei Passwörter werden gleichzeitig geprüft. Zwanzig falsche Kopplungscodes in 15 Minuten halten das Koppeln genauso an. Ein Neustart vergisst all das.
  • Anhänge: SAMU_BLOB_MAX_BYTES und SAMU_BLOB_QUOTA_BYTES setzen, damit die Platte nicht vollläuft.
  • Kalender: Mit SAMU_SECRET_KEY ruft der Server jede Adresse ab, die ein Benutzer ihm als Kalender gibt, auch im eigenen Netz. SAMU_CALENDAR_HOSTS=cal.example.org begrenzt das auf die genannten Hosts; Weiterleitungen auf andere Hosts werden nicht verfolgt.

Sicherung

Alles liegt im Volume: server.db und accounts/<name>.db, dazu die Dateien der Anhänge. Bei angehaltenem Server kopieren, oder im laufenden Betrieb mit dem Backup von SQLite:

sqlite3 /pfad/zum/volume/accounts/privat.db ".backup '/backup/privat.db'"

Vor jedem Update sichern, das eine Datenbank-Migration mitbringt.

Export und Wiederherstellung

Ein Export ist ein einfaches Verzeichnis, das man mit einem Texteditor, jq oder git diff lesen kann, ohne samudo:

samu export ~/samu-export --org                                  # von einem Client, angemeldet
docker compose exec server samu-server export --account privat /export/privat   # auf dem Server, auch im laufenden Betrieb

Es enthält manifest.json, eine JSON-Datei je Art von Objekt (tasks.json, projects.json, comments.json, …), logbook.json, die Dateien der Anhänge unter files/<sha256>, eine README.md, die alles beschreibt, und mit --org eine Gliederung samu.org. Derselbe Stand ergibt dieselben Bytes, also taugt das Verzeichnis gut für git: zeitgesteuert exportieren und committen, und man hat einen lesbaren Verlauf. deploy/export-git.sh mit den systemd-Units deploy/samu-export.service und .timer ist ein Weg dahin (Pfade und Konten oben anpassen).

Nicht im Export: gelöschte Objekte, Sitzungen und Passwörter, Hooks und die Kalenderquellen.

Zum Wiederherstellen den Server anhalten, ein leeres Konto anlegen und den Export hineinlesen:

docker compose stop server
docker compose run --rm server account add privat      # nur, wenn es noch nicht existiert
docker compose run --rm server restore --account privat /export/privat
docker compose start server

Danach jede Kalenderquelle neu setzen.

Aktualisieren

Das neue Image bauen, sichern, dann docker compose up -d. Der Server migriert die Datenbanken beim Start; eine Datenbank, die eine neuere Version migriert hat, lässt sich mit einer älteren nicht öffnen, also nicht ohne die Sicherung zurückgehen. /health meldet die laufende Version.

Webhooks

Der Server nimmt signierte Zustellungen unter /hooks/<name> an, etwa von einem smarten Ring. Siehe Erfassen vom Index-Ring.