Files
management/hosts/cfgmon.md
T
Thore CimbalandClaude Sonnet 5 02946481ae cfgmon: Runner-Praemisse korrigiert (CFGMON-02/-08/-10)
Frueherer Stand ging von "kein Runner registriert, Standort offen" aus
(CFGMON-08). Beides falsch: builder-1 laeuft bereits auf CFGMON, als Teil
von thread-net-git's rework/stack-Branch (noch nicht in main gemergt, aber
produktiv aktiv, mit gezielt fuer Electron-Builds eingerichteten Labels).
CFGMON-02 entsprechend aktualisiert (in Arbeit statt offen, Merge-Rueckstand
als eigener Punkt benannt), CFGMON-08 nach Erledigt verschoben mit klarer
Korrektur-Notiz statt geloescht. Neuer Punkt CFGMON-10 fuer die dabei
entdeckten threadnet-call-CI-Fehlschlaege (Artifact-Schritt), verlinkt zum
neuen Issue threadnet-call#1.

Cross-referenziert in gitops#33 (korrigiert und geschlossen), gitops#44
(geschlossen als Duplikat), ThreadNet-Web#2 (praezisiert).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 12:00:00 +00:00

290 lines
15 KiB
Markdown

# CFGMON
Monitoring-Stack, Gitea und der Reverse Proxy für alles Öffentliche.
| | |
|---|---|
| **Hostname** | `CFGMON` |
| **OS** | Ubuntu 24.04.4 LTS |
| **IPv4** | `188.245.193.243` |
| **IPv6** | `2a01:4f8:c17:93eb::1` |
| **Privat** | `10.0.0.3` (`enp7s0`, Hetzner-Netz — dort liegt auch k3s auf `10.0.0.2`) |
| **DNS** | `rohana.axion1337.de` → Gitea, `selendis.axion1337.de` → Grafana |
| **Stand** | 2026-07-30 |
## Dienste
| Container | Image | Compose-Projekt | Definition |
|---|---|---|---|
| prometheus | `prom/prometheus:v3.3.1` | `monitoring` | `sorb/threadnet-operating`, `monitoring/` |
| loki | `grafana/loki:3.7.1` | `monitoring` | dito |
| grafana | `grafana/grafana:12.0.0` | `monitoring` | dito |
| alloy | `grafana/alloy:v1.16.0` | `monitoring` | dito |
| node-exporter | `prom/node-exporter:v1.9.1` | `monitoring` | dito |
| traefik | `traefik:v3.7.9` | `thread-net-git` | `sorb/thread-net-git`, Branch `rework/stack` (**nicht in `main` gemergt**, aber produktiv aktiv — siehe [CFGMON-02](#cfgmon-02--traefik-gitea-cadvisor-und-runner-werden-unter-iac-gebracht)) |
| gitea | `gitea/gitea:1.27.0` | `thread-net-git` | dito, gepinnt (war `:latest`) |
| cadvisor | `gcr.io/cadvisor/cadvisor:v0.49.1` | `thread-net-git` | dito, gepinnt (war `:latest`) |
| runner | `gitea/act_runner:0.6.1` | `thread-net-git` | dito, Container `gitea-runner`, siehe CFGMON-02 |
| portainer_agent | `portainer/agent:2.27.5` | — | standalone, kein Compose |
Prometheus-Jobs: `operating_prometheus`, `operating_node-exporter`,
`operating_cadvisor`, `operating_traefik` (alle lokal), `k3s_host_node`
(`10.0.0.2:9100`), `pterodactyl_host_node` und `gameserver_cadvisor`
(beide `157.90.155.206`, siehe [game](game.md)).
---
## CFGMON-01 — Zertifikatserneuerung braucht offene Ports (IPv4 **und** IPv6)
**Status:** offen — zeitkritisch ab **2026-09-28**
Das Cert für `selendis.axion1337.de` wurde am 2026-07-30 ausgestellt und läuft am
**2026-10-28** ab. Traefik erneuert ca. 30 Tage vorher, also ab Ende September.
Gitea (`rohana.axion1337.de`) hängt am selben Resolver und ist damit genauso
betroffen — inklusive der Erreichbarkeit dieses Repos per HTTPS.
Traefik nutzt TLS-ALPN-01
(`--certificatesresolvers.letsencrypt.acme.tlschallenge=true`). Die Challenge läuft
ausschließlich über **Port 443, erreichbar aus dem offenen Internet**. Bei
gesperrtem Port scheitert sie mit:
```
urn:ietf:params:acme:error:connection :: Timeout during connect (likely firewall problem)
```
Traefik liefert dann sein Default-Self-Signed-Cert aus. Das fällt niemandem auf, bis
jemand die Seite aufruft — genau so ist es am 2026-07-30 passiert.
Eine IP-Allowlist ist **keine** Option: Let's Encrypt veröffentlicht seine
Validierungs-IPs nicht und prüft seit Einführung der Multi-Perspective Validation
aus mehreren Regionen gleichzeitig.
**IPv6 war der vermutete Fallstrick — geprüft und in Ordnung.** `rohana`, `selendis`
(und noch `www.*`, siehe [ZONE-01](../shared/zone-axion1337.md)) haben AAAA-Records
auf `2a01:4f8:c17:93eb::1`. Damit validiert LE **bevorzugt über IPv6**. Die
Hetzner-Cloud-Firewall kennt kein familienübergreifendes „any": das Feld *Source IPs*
ist eine Liste, `0.0.0.0/0` deckt IPv6 **nicht** mit ab, `::/0` muss separat drinstehen.
Stand 2026-07-30 enthält die Inbound-Regel für TCP 443 **beide** Einträge — vom User
in der Hetzner-Console nachgesehen. Damit ist der IPv6-Pfad frei.
Diese Angabe stammt **nicht** aus einer Messung von diesem Host aus, sondern aus der
Console. Vom Host ist es nicht verifizierbar: die Cloud-Firewall liegt außerhalb des
Systems, `iptables` und `ufw` sagen nichts darüber (und Root gibt es hier nicht), eine
Verbindung auf die eigene öffentliche Adresse läuft lokal, es gibt keinen zweiten Host
für eine Rückverbindung, und Traefik läuft ohne Access-Log — es existieren also auch
keine historischen Client-IPs. Für eine Prüfung von hier aus wäre ein
Hetzner-API-Token mit Read-Berechtigung nötig.
Auch die erfolgreiche Ausstellung vom 2026-07-30 um 12:00 UTC belegt IPv6 **nicht**:
zu dem Zeitpunkt existierte für `selendis` noch kein AAAA-Record, LE hat also über
IPv4 validiert. Die Erneuerung Ende September ist der erste Lauf, der IPv6 überhaupt
versucht.
**Damit bleibt als Risiko nur, ob die Ports offen bleiben.** Sie wurden am
2026-07-30 für die Ausstellung geöffnet; der Normalzustand dieser Umgebung ist, 80
und 443 aus Sicherheitsvorsicht auf die eigene IP zu beschränken. Werden sie wieder
zugezogen, scheitert die Erneuerung Ende September genau wie heute Mittag — und zwar
still.
**Nächster Schritt:** Entscheidung zwischen
- **A — Ports offen lassen** bzw. zur Erneuerung wieder öffnen. Nichts umzubauen,
aber alle ~60 Tage daran zu denken, und ein Fehlschlag fällt erst auf, wenn jemand
die Seite aufruft. Wenn A, dann mit einem Kalendereintrag auf **Mitte September**,
nicht auf das Ablaufdatum.
- **B — auf DNS-01 umstellen** (empfohlen). Validierung über einen TXT-Record,
**kein offener Port nötig**, läuft bei dichter Firewall durch. Braucht ein
API-Token für die Zone bei IONOS, als Secret für Traefik hinterlegt. Ermöglicht
zusätzlich Wildcard-Certs (`*.axion1337.de`), also künftige Subdomains ohne
Portöffnung. Setzt voraus, dass der Traefik-Stack anfassbar ist — siehe
[CFGMON-02](#cfgmon-02--traefik-gitea-und-cadvisor-sind-nicht-versioniert).
---
## CFGMON-02 — Traefik, Gitea, cAdvisor und Runner werden unter IaC gebracht
**Status:** in Arbeit (Stand 2026-07-30)
Liefen ursprünglich im Compose-Projekt `thread-net-git` aus `/data/compose/8`, einem
**von Portainer verwalteten Stack** ohne Repo dazu. Betroffen war damit ausgerechnet die
Infrastruktur, an der alles andere hängt (Traefik = einzige Stelle für Entrypoints/ACME/
Zertifikatsspeicher, Gitea = hostet die Repos, die die Infrastruktur beschreiben,
cAdvisor = Abhängigkeit des Monitoring-Stacks).
**Export existiert bereits**: `sorb/thread-net-git`, Branch `rework/stack` (6 Commits) —
`:latest`-Tags gepinnt (Gitea `1.27.0`, cAdvisor `v0.49.1`), Projektname `thread-net-git`
beibehalten (Volume-Kontinuität), README mit Betriebsregeln ("nie wieder über Portainer
anfassen", Volume-Namen, Downgrade-Verbot für Gitea), nächtliches nicht-rotierendes
Backup-Script. **Zusätzlich neu**: ein `runner`-Service
(`gitea/act_runner:0.6.1`, Container `gitea-runner`, Labels `ubuntu-latest`/`linux-build`/
`win-wine` — die letzten beiden gezielt für Electron-Builds, `electronuserland/builder`-
Images) — das war ursprünglich [CFGMON-08](#cfgmon-08) als offene Frage gelistet, ist aber
längst umgesetzt, siehe dort.
**Offen bleibt**: der Branch ist **nicht in `main` gemergt**, obwohl er nachweislich
produktiv läuft (der Runner darauf verarbeitet bereits echte Jobs, siehe CFGMON-10). Damit
ist der aktuelle Live-Stand nur über den Branch nachvollziehbar, nicht über die
Standardansicht des Repos — sollte gemergt werden, sobald das Backup-Script (siehe README
auf dem Branch) einmal verifiziert lief.
**Nächster Schritt:** `rework/stack` nach `main` mergen (Entscheidung/Ausführung beim
Nutzer, nicht automatisiert), danach diesen Punkt auf `erledigt` setzen.
---
## CFGMON-03 — Prometheus-Remote-Write und Loki sind öffentlich ohne Auth
**Status:** offen
Prometheus läuft mit `--web.enable-remote-write-receiver` und Port `9090` ist
veröffentlicht, weil der k3s-Cluster und der Matrix-Server dorthin pushen. Loki ist
auf `3100` ebenfalls veröffentlicht.
Ein offener Remote-Write-Receiver ohne Auth erlaubt Fremden, **beliebige Metriken
einzuspeisen** — damit lassen sich Dashboards und Alerts unbrauchbar machen — und
über die Query-API alle Daten auszulesen. Bei Loki auf `3100` gilt dasselbe für
Logs, die typischerweise mehr Interna enthalten als Metriken.
In `monitoring/docker-compose.yml` steht dazu bereits ein Warnhinweis als Kommentar,
umgesetzt ist er nicht.
**Nächster Schritt:** Beide Ports per Hetzner-Cloud-Firewall auf die Absender
einschränken. Sauberer wäre, sie gar nicht öffentlich zu binden: k3s liegt mit
`10.0.0.2` im selben privaten Netz wie CFGMON (`10.0.0.3`), der Push könnte also
über das private Netz laufen.
**Update 2026-07-30**: der k3s/Matrix-Host (`49.13.132.245`, privat `10.0.0.2` — ist
derselbe Host, siehe [MATRIX-02](matrix.md#matrix-02--pusht-per-remote-write-auf-einen-offenen-prometheus--erledigt-2026-07-30))
nutzt für seinen eigenen Remote-Write/Loki-Push bereits die private IP
(`http://10.0.0.3:9090`/`:3100`, verifiziert in `apps/monitoring/alloy-config.yaml`), nicht
`188.245.193.243`. Der öffentlich erreichbare, unauthentifizierte Port bleibt trotzdem ein
CFGMON-seitiges Risiko (jeder im Internet könnte ihn ansprechen, nicht nur der k3s-Host) -
dieser Teil des Punkts ist also weiterhin offen, nur nicht mehr durch fehlende
Netz-Erreichbarkeit des Matrix-Hosts blockiert.
---
## CFGMON-04 — Grafana-Admin-Credentials aus `.env` gelten nicht für die HTTP-API
**Status:** offen, geringe Priorität
`GF_SECURITY_ADMIN_USER` und `GF_SECURITY_ADMIN_PASSWORD` greifen nur beim
allerersten Start mit leerem Volume. Der Live-Admin wurde offenbar später in der UI
geändert, die Werte aus der `.env` sind damit funktionslos. Verifikation von
Datasources und Dashboards läuft deshalb über `grafana.db` statt über die API.
Kein Defekt, aber eine Falle: die `.env` sieht aus, als wäre sie die Quelle der
Wahrheit, ist es aber nicht.
**Nächster Schritt:** entweder das echte Admin-Passwort in die `.env` nachziehen und
dokumentieren, dass es nur den Ersteinrichtungsfall abdeckt, oder einen
Service-Account mit API-Token für Verifikationszwecke anlegen. Letzteres ist
sauberer, weil dafür kein Admin-Passwort nötig ist.
---
## CFGMON-09 — Gitea-Backups off-host in die Storage Box (eigenes Borg-Repo)
**Status:** offen — bewusst zurückgestellt am 2026-07-30
**Achtung: Der nächtliche Backup-Cron ist seit dem 2026-07-30 deaktiviert**
(auskommentiert in der Crontab von `rantanplan`). Bis zur Umsetzung dieses Punkts
laufen **keine Gitea-Backups** — der letzte Stand ist
`/opt/backup/gitea-dump-2026-07-30.tar.gz`. Beim Erledigen (oder Verwerfen) dieses
Punkts den Cron wieder aktivieren.
Ausgangslage: Die Platte (38G) ist zu 73 % voll, der einzelne lokale Dump belegt
4,3 GB (beides `df`/`du` vom 2026-07-30). Das Backup-Script
(`/opt/thread-net-git/backup/gitea-backup.sh`) rotiert deshalb auf genau einen
Stand; Historie existiert nur, wenn jemand manuell per scp abzieht.
Off-host-Kopien fehlen komplett.
Plan: **Eigenes Borg-Repo auf der Hetzner Storage Box.** Die Storage Box spricht
Borg nativ über SSH (Port 23, laut Hetzner-Doku — nicht von hier verifiziert,
es existiert noch keine Storage Box dafür), z. B.
`ssh://uXXXXXX@uXXXXXX.your-storagebox.de:23/./backups/gitea`.
- **Dedup:** Die nächtlichen Dumps sind fast identisch — Borg überträgt nur
geänderte Blöcke. Dafür den Dump **unkomprimiert** an Borg geben (das `gzip` im
Script entfällt, Borg komprimiert selbst), sonst greift die Deduplizierung nicht.
- **Retention** über `borg prune` (z. B. 7 täglich, 4 wöchentlich, 6 monatlich)
statt nur eines Standes.
- Optional ein **Sub-Account** der Storage Box mit eigenem Verzeichnis, damit das
Gitea-Repo von anderen Backups getrennt bleibt.
Voraussetzungen, beide beim User:
1. Storage-Box-Username (`uXXXXXX`) bzw. Sub-Account (im Hetzner-Robot anlegen).
2. Der Host hat **keinen SSH-Key** (`~/.ssh/` enthält nur `authorized_keys`,
geprüft 2026-07-30) — Key generieren und den Public Key in der Storage Box
hinterlegen (Robot-Webinterface oder einmalig per Passwort-Login).
## CFGMON-10 — threadnet-call-CI schlägt am Artifact-Schritt fehl
**Status:** offen
Ausgelöst durch einen Push nach `threadnet-call` am 2026-07-30: der Runner (`builder-1`,
siehe CFGMON-02) verarbeitet mehrere geerbte Upstream-Workflows (`build.yaml`,
`publish-embedded-packages.yaml`, u. a.), die meisten scheitern am selben Punkt —
Checkout/Dependencies/teils sogar der Build-Schritt selbst laufen durch, aber
**"📥 Download built element-call artifact"** bzw. **"Upload Artifact"** schlagen fehl.
**Nicht verifizierte Hypothese**: das Job-Container-Limit in `runner/config.yaml`
(2,2 GiB / 1,5 CPU, Kommentar dort: "2 parallele Electron-Builds ... enden im OOM — bei
Host-Upgrade hochsetzen") ist für einen Element-Call-Build knapp. Kein OOM-Log direkt
eingesehen — reine Vermutung aus dem Fehlerbild, nicht bestätigt.
**Nächster Schritt:** Job-Logs des Runners (nicht nur die Gitea-Actions-UI) auf
OOM-Kill-Meldungen prüfen, bevor das Limit angehoben wird. Falls bestätigt: Kapazität/Limit
in `thread-net-git`s `runner/config.yaml` anpassen (Trade-off gegen den knappen
Host-Speicher, siehe CFGMON-02).
Getrackt (inkl. der separaten npm-Registry-Fehlkonfiguration) als
[threadnet-call#1](https://rohana.axion1337.de/sorb/threadnet-call/issues/1) - dort die
eigentliche Arbeit, hier nur der Host-seitige Aspekt (Runner-Ressourcenlimit).
---
## Erledigt
### CFGMON-05 — Monitoring-Stack unter IaC bringen · erledigt 2026-07-30
Der Stack lief aus `/opt/monitoring` ohne Versionierung und mit `:latest`-Tags. Jetzt
in `sorb/threadnet-operating` unter `monitoring/`, Images gepinnt,
Grafana-Datasources und 9 Dashboards provisioniert. Projektname `monitoring`
beibehalten, dadurch blieben die Volumes erhalten.
### CFGMON-06 — Grafana-Certresolver zeigte ins Leere · erledigt 2026-07-30
Das Label sagte `certresolver=le`, Traefik kennt den Resolver aber als
`letsencrypt`. Traefik protokollierte `Router uses a nonexistent certificate
resolver` und lieferte für `selendis.axion1337.de` sein Default-Self-Signed-Cert
aus. Aus dem Altbestand in `/opt/monitoring` unverändert übernommen und dort
mindestens seit dem 2026-07-27 vorhanden.
Behoben in `threadnet-operating`, Commit `a400f8a`. Cert von Let's Encrypt (YR2)
ausgestellt, gültig bis 2026-10-28 — die Nachfolge davon ist
[CFGMON-01](#cfgmon-01--zertifikatserneuerung-braucht-offene-ports-ipv4-und-ipv6).
### CFGMON-07 — Alloy verlor seine Positions-Datei bei jedem Deploy · erledigt 2026-07-30
`--storage.path=/var/lib/alloy/data` war gesetzt, aber ohne Volume: die
Positions-Datei lag im Container-Layer. Nach jedem Recreate las Alloy alle
Docker-Logdateien von vorn, worauf Loki alles älter als 7 Tage mit HTTP 400 abwies
(`timestamp too old`, `reject_old_samples`). Betroffen waren nur Alt-Logzeilen bis
zurück zu 2025, keine aktuellen Daten.
Behoben durch ein `alloy_data`-Volume, Commit `edac97e`. Verifiziert: Positions
überleben `--force-recreate`, zweiter Recreate erzeugt 0 Fehler.
### CFGMON-08 — Kein Gitea-Actions-Runner registriert, Standort noch offen · erledigt 2026-07-30
**Korrektur einer falschen Prämisse**: der Eintrag ging davon aus, dass gar kein Runner
existiert und wo einer laufen sollte, noch offen sei. Beides falsch — ein Runner
(`builder-1`) läuft bereits, auf CFGMON, als Teil von `thread-net-git`s `rework/stack`-
Branch, mit gezielt für Electron-Builds eingerichteten Labels. Details siehe
[CFGMON-02](#cfgmon-02--traefik-gitea-cadvisor-und-runner-werden-unter-iac-gebracht) — hier
nicht dupliziert. [gitops#33](https://rohana.axion1337.de/sorb/axion1337.chat-gitops/issues/33)
(dieselbe falsche Prämisse) entsprechend korrigiert/geschlossen.