# 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` | **nicht versioniert**, Portainer-Stack in `/data/compose/8` | | gitea | `gitea/gitea:latest` | `thread-net-git` | dito | | cadvisor | `gcr.io/cadvisor/cadvisor:latest` | `thread-net-git` | dito | | 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 und cAdvisor sind nicht versioniert **Status:** offen Die drei laufen im Compose-Projekt `thread-net-git` aus `/data/compose/8`, einem **von Portainer verwalteten Stack**. Es gibt kein Repo dazu. Betroffen ist damit ausgerechnet die Infrastruktur, an der alles andere hängt: - **Traefik** — die einzige Stelle, an der Entrypoints, ACME-Resolver und Zertifikatsspeicher definiert sind. Die Resolver-Namen aus dieser Datei sind der Grund, warum ein Label `certresolver=le` monatelang ins Leere zeigte. - **Gitea** — hostet die Repos, in denen die Infrastruktur beschrieben wird. Das ist ein Bootstrapping-Problem: die Definition von Gitea könnte in Gitea liegen, wäre bei einem Ausfall aber genau dann nicht erreichbar, wenn man sie braucht. - **cAdvisor** — wird von Prometheus als `operating_cadvisor` gescrapt, ist also eine Abhängigkeit des Monitoring-Stacks, die selbst außerhalb der IaC liegt. **Nächster Schritt:** Stack-Definition aus Portainer exportieren (`/data/compose/8/docker-compose.yml` plus zugehörige Files), in ein Repo überführen und dabei die `:latest`-Tags pinnen — `gitea/gitea:latest` und `cadvisor:latest` machen jeden Pull zu einem unkontrollierten Update. Beim Übertragen denselben Projektnamen `thread-net-git` beibehalten, sonst legt Compose neue leere Volumes an. Für Gitea zusätzlich klären, wo die Definition liegen soll, damit sie bei einem Gitea-Ausfall erreichbar bleibt. --- ## 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. Für den Matrix-Server auf `49.13.132.245` prüfen, ob er in dasselbe Netz aufgenommen werden kann — siehe [matrix](matrix.md). --- ## 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. --- ## 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.