From 8e98a394471c19b8c85808986b5d4a7dd2300926 Mon Sep 17 00:00:00 2001 From: Thore Cimbal Date: Thu, 30 Jul 2026 12:00:00 +0000 Subject: [PATCH] Backlogs zentral strukturieren Bisher lag der Backlog als BACKLOG.md in threadnet-operating und damit in einem Repo, das nur einen Stack auf einem Host beschreibt. Da die Arbeit inzwischen mehrere Hosts umfasst, hier ein File pro Host plus shared/ fuer Themen, die sich nicht pro Host trennen lassen. Uebernommen und auf die Hosts verteilt: - CFGMON: Cert-Erneuerung inkl. IPv6-Firewall, nicht versionierter Portainer-Stack (traefik/gitea/cadvisor), offener Remote-Write- Receiver, Grafana-API-Credentials; dazu drei erledigte Punkte von heute als Historie - game: Host von CFGMON aus nicht erreichbar, 2 Targets down - matrix: Mailversand klaeren, bevor die Zone gehaertet wird - k3s: pusht auf den offenen Receiver, liegt aber schon im privaten Netz - shared/zone-axion1337: DNS-Bereinigung und Apex-DMARC-Policy Konventionen in README.md, Vorlage in TEMPLATE.md. IDs sind pro File fortlaufend und werden nicht wiederverwendet. Praefix fuer DNS-Themen ist ZONE-, nicht DNS-, weil DNS-01 der Name eines ACME-Challenge-Typs ist und hier laufend als Fachbegriff vorkommt. Co-Authored-By: Claude Opus 5 --- .gitignore | 6 ++ README.md | 72 +++++++++++++++ TEMPLATE.md | 58 ++++++++++++ hosts/cfgmon.md | 187 +++++++++++++++++++++++++++++++++++++++ hosts/game.md | 74 ++++++++++++++++ hosts/k3s.md | 46 ++++++++++ hosts/matrix.md | 83 +++++++++++++++++ shared/zone-axion1337.md | 161 +++++++++++++++++++++++++++++++++ 8 files changed, 687 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 TEMPLATE.md create mode 100644 hosts/cfgmon.md create mode 100644 hosts/game.md create mode 100644 hosts/k3s.md create mode 100644 hosts/matrix.md create mode 100644 shared/zone-axion1337.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8b85335 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +# Nichts Betriebliches hier ablegen -- dieses Repo enthaelt nur Text. +*.env +.env +*.token +*.key +*.pem diff --git a/README.md b/README.md new file mode 100644 index 0000000..3542168 --- /dev/null +++ b/README.md @@ -0,0 +1,72 @@ +# Backlogs + +Zentrale Sammlung offener Punkte über alle Hosts und Dienste hinweg. Ein Eintrag +landet hier, wenn er bewusst nicht sofort erledigt wurde — damit er nicht in einer +Chat-Historie oder einem Kopf verschwindet. + +## Aufbau + +``` +hosts/ ein File pro Host, benannt nach dem Hostnamen +shared/ Themen, die mehrere Hosts betreffen (DNS-Zone, Mail-Policy) +TEMPLATE.md Vorlage für einen neuen Host +``` + +Faustregel für die Einordnung: **Der Eintrag gehört dorthin, wo die Arbeit +stattfindet, nicht dorthin, wo das Symptom auftritt.** Ein down-Target in Prometheus +auf CFGMON, dessen Ursache ein blockierter Port auf dem Gameserver ist, gehört nach +`hosts/game.md` — mit Querverweis von CFGMON. + +## Hosts + +| Host | Adressen | Rolle | Backlog | +|---|---|---|---| +| **CFGMON** | `188.245.193.243`, `2a01:4f8:c17:93eb::1`, privat `10.0.0.3` | Monitoring-Stack, Gitea, Traefik | [hosts/cfgmon.md](hosts/cfgmon.md) | +| **game** | `157.90.155.206` (`game.axion1337.de`) | Pterodactyl / Gameserver | [hosts/game.md](hosts/game.md) | +| **matrix** | `49.13.132.245` (`matrix.axion1337.de`) | Matrix-Homeserver | [hosts/matrix.md](hosts/matrix.md) | +| **k3s** | privat `10.0.0.2` | k3s-Cluster | [hosts/k3s.md](hosts/k3s.md) | + +## Übergreifend + +| Thema | Backlog | +|---|---| +| DNS-Zone `axion1337.de` und Mail-Policy (IONOS) | [shared/zone-axion1337.md](shared/zone-axion1337.md) | + +## Konventionen + +**IDs** sind pro Datei fortlaufend mit einem Präfix, das den Ort benennt: +`CFGMON-01`, `GAME-01`, `MATRIX-01`, `K3S-01`, `ZONE-01`. IDs werden **nicht +wiederverwendet** — ein erledigter Punkt behält seine Nummer, damit Querverweise und +Commit-Messages dauerhaft stimmen. + +Das Präfix für DNS-Themen ist bewusst `ZONE-` und nicht `DNS-`: `DNS-01` ist der +Name eines ACME-Challenge-Typs und käme in genau diesen Backlogs ständig als +Fachbegriff vor. + +**Status:** + +| Wert | Bedeutung | +|---|---| +| `offen` | noch nicht angefasst | +| `in Arbeit` | läuft gerade | +| `wartet auf …` | blockiert, mit Angabe worauf | +| `erledigt` | fertig, mit Datum — Eintrag bleibt stehen, siehe unten | + +Erledigte Punkte werden **nicht gelöscht**, sondern auf `erledigt` gesetzt und nach +unten in den Abschnitt „Erledigt" verschoben. Der Grund: bei mehreren Hosts ist die +Frage „haben wir das damals eigentlich gemacht, und warum so?" häufiger als die +Frage „was ist noch offen". + +**Jeder Eintrag braucht** einen Status, eine Beschreibung des tatsächlichen +Zustands, und einen konkreten nächsten Schritt. Wenn eine Aussage nicht verifiziert +ist, wird das dazugeschrieben — eine unbestätigte Vermutung, die wie ein Befund +aussieht, kostet beim nächsten Mal mehr Zeit als sie spart. + +**Zeitkritisches** bekommt ein Datum in der Statuszeile, nicht nur „bald". + +## Verhältnis zu den Projekt-Repos + +Dieses Repo hält die offenen Punkte. Die **Konfiguration** liegt weiter in den +jeweiligen Projekt-Repos, z. B. `sorb/threadnet-operating` für den +Monitoring-Stack auf CFGMON. Damit nichts doppelt geführt wird, verweisen die +Projekt-Repos hierher statt eigene Backlog-Dateien zu pflegen. diff --git a/TEMPLATE.md b/TEMPLATE.md new file mode 100644 index 0000000..332fde3 --- /dev/null +++ b/TEMPLATE.md @@ -0,0 +1,58 @@ +# + + + +| | | +|---|---| +| **Hostname** | | +| **OS** | | +| **IPv4** | | +| **IPv6** | | +| **Privat** | | +| **DNS** | | +| **Stand** | JJJJ-MM-TT | + +> Solange der Host nicht direkt inventarisiert wurde: diesen Hinweis stehen lassen +> und dazuschreiben, woher die Angaben stammen. Eine unbestätigte Vermutung, die wie +> ein Befund aussieht, kostet beim nächsten Mal mehr Zeit als sie spart. + +## Dienste + +| Container / Dienst | Version | Compose-Projekt | Definition | +|---|---|---|---| +| | | | | + +Wichtig in der Spalte „Definition": ob die Konfiguration versioniert ist und wo. Ein +Dienst, der nur in einer Portainer- oder händischen Installation existiert, gehört als +eigener Backlog-Eintrag erfasst. + +--- + +## -01 — + +**Status:** offen | in Arbeit | wartet auf … | erledigt JJJJ-MM-TT + + + + + +**Nächster Schritt:** + + + +--- + +## Erledigt + +### -NN — · erledigt JJJJ-MM-TT + + + + diff --git a/hosts/cfgmon.md b/hosts/cfgmon.md new file mode 100644 index 0000000..c132ba0 --- /dev/null +++ b/hosts/cfgmon.md @@ -0,0 +1,187 @@ +# 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 ist der Fallstrick.** `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 braucht für 443 deshalb eine Regel mit Quelle `::/0` — +`0.0.0.0/0` deckt IPv6 **nicht** mit ab. Fehlt sie, scheitert die Erneuerung, obwohl +IPv4 offen ist. + +Vom Host aus ist das **nicht verifizierbar**: eine Verbindung auf die eigene +öffentliche Adresse läuft lokal und sagt nichts über den Weg von außen. Das +Ausliefern über IPv6 funktioniert (`rohana` und `selendis` antworten mit 200, +Traefik lauscht auf `[::]:443`) — das ist aber nicht dasselbe wie von außen +erreichbar. + +**Nächster Schritt:** Entscheidung zwischen + +- **A — Port 443 zur Erneuerung öffnen**, für IPv4 *und* IPv6. Nichts umbauen, aber + alle ~60 Tage daran denken, und das Scheitern fällt nicht auf. +- **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. diff --git a/hosts/game.md b/hosts/game.md new file mode 100644 index 0000000..0c6ab6f --- /dev/null +++ b/hosts/game.md @@ -0,0 +1,74 @@ +# game + +Pterodactyl- / Gameserver-Host. + +| | | +|---|---| +| **IPv4** | `157.90.155.206` | +| **IPv6** | kein AAAA-Record | +| **DNS** | `game.axion1337.de` | +| **Privat** | unbekannt — siehe GAME-01 | +| **Stand** | 2026-07-30 | + +> **Nicht inventarisiert.** Alle Angaben hier stammen aus Beobachtungen von CFGMON +> aus. Auf dem Host selbst wurde nichts geprüft. Beim ersten direkten Zugriff +> sollten OS, Dienste und Compose-Projekte hier nachgetragen werden. + +--- + +## GAME-01 — Host von CFGMON aus nicht erreichbar, 2 Prometheus-Targets down + +**Status:** offen — Ursache liegt auf diesem Host oder in dessen Firewall + +Zwei Scrape-Targets sind down. Das bestand **schon vor** dem Monitoring-Rework vom +2026-07-30, ist also kein Regressionsschaden aus der Umstellung. + +| Job | Target | Fehler | +|---|---|---| +| `gameserver_cadvisor` | `157.90.155.206:8080` | `context deadline exceeded` | +| `pterodactyl_host_node` | `157.90.155.206:9100` | `context deadline exceeded` | + +Von CFGMON aus geprüft: der Host antwortet **nicht auf Ping** (100 % packet loss) und +auf keinem der beiden Ports. Das Problem ist also nicht die Prometheus-Konfiguration, +sondern die Erreichbarkeit. Mögliche Ursachen, von wahrscheinlich nach unwahrscheinlich: + +1. Die Hetzner-Cloud-Firewall dieses Hosts lässt `188.245.193.243` nicht auf + 8080/9100 — passend zum restriktiven Firewall-Setup der Umgebung. +2. cAdvisor und node-exporter laufen nicht (mehr) oder binden nur auf `127.0.0.1`. +3. Der Host ist abgeschaltet oder umgezogen und die IP ist veraltet. Dagegen spricht, + dass `game.axion1337.de` weiter auf sie zeigt. + +**Nächster Schritt:** auf dem Host prüfen, ob die beiden Exporter laufen und worauf +sie binden, dann die Firewall entsprechend öffnen. + +**Empfehlung: nicht über die öffentliche IP.** CFGMON scrapt den k3s-Host bereits +über das private Netz (`10.0.0.2:9100`) und der ist up — das ist das Modell, das +hier funktioniert. Wenn dieser Host in denselben Hetzner-vSwitch aufgenommen wird +(CFGMON hat `10.0.0.3`), braucht es für das Monitoring **gar keine im Internet +offenen Ports**. Zwei Exporter öffentlich erreichbar zu machen wäre der schlechtere +Weg: node-exporter gibt detaillierte Host-Interna preis, cAdvisor die komplette +Container-Landschaft. + +Danach in `threadnet-operating` die Targets in `monitoring/prometheus/prometheus.yml` +von der rohen IP auf die private Adresse bzw. den DNS-Namen umstellen — aktuell +stehen dort öffentliche IPs, obwohl mit `game.axion1337.de` ein Name existiert. + +**Solange offen:** die beiden Targets in Grafana als *known down* behandeln, damit +sie kein Dauer-Alerting erzeugen. + +--- + +## GAME-02 — `www.game.axion1337.de` ist überflüssig + +**Status:** offen, geringe Priorität + +Es existiert ein A-Record `www.game.axion1337.de` → `157.90.155.206`, angelegt nach +IONOS-Default-Muster. Bei einer Subdomain ist ein `www.`-Präfix überflüssig, siehe +die Begründung in [ZONE-01](../shared/zone-axion1337.md). + +**Nicht verifiziert**, ob auf diesem Host etwas auf den Namen hört — von CFGMON aus +ist er nicht erreichbar. Bei `rohana` und `selendis` liefert das äquivalente +Konstrukt einen Zertifikatsfehler. + +**Nächster Schritt:** prüfen, ob der Name irgendwo verlinkt oder konfiguriert ist, +sonst A-Record löschen. diff --git a/hosts/k3s.md b/hosts/k3s.md new file mode 100644 index 0000000..04698df --- /dev/null +++ b/hosts/k3s.md @@ -0,0 +1,46 @@ +# k3s + +k3s-Cluster. + +| | | +|---|---| +| **Privat** | `10.0.0.2` (Hetzner-Netz, zusammen mit CFGMON auf `10.0.0.3`) | +| **Öffentlich** | unbekannt — kein DNS-Name zugeordnet | +| **Stand** | 2026-07-30 | + +> **Nicht inventarisiert.** Bekannt ist nur, was von CFGMON aus sichtbar ist: die +> private Adresse, ein laufender node-exporter auf `9100`, und dass der Cluster per +> Remote-Write an Prometheus pusht. Beim ersten direkten Zugriff Distribution, +> k3s-Version, Nodes und Workloads hier nachtragen. Möglicherweise zugehörig: +> `sorb/axion1337.chat-gitops`. + +**Bemerkenswert:** Dieser Host ist der einzige, der von CFGMON aus **über das private +Netz** gescrapt wird (`k3s_host_node` → `10.0.0.2:9100`) — und der einzige +Remote-Host, dessen Target `up` ist. Das ist das Modell, das für +[game](game.md#game-01--host-von-cfgmon-aus-nicht-erreichbar-2-prometheus-targets-down) +und [matrix](matrix.md#matrix-02--pusht-per-remote-write-auf-einen-offenen-prometheus) +angestrebt werden sollte. + +--- + +## K3S-01 — Pusht per Remote-Write auf einen offenen Prometheus + +**Status:** offen — Gegenstück zu [CFGMON-03](cfgmon.md#cfgmon-03--prometheus-remote-write-und-loki-sind-öffentlich-ohne-auth) + +Der Cluster schreibt Metriken per Remote-Write an Prometheus auf CFGMON. Der Receiver +dort ist auf `188.245.193.243:9090` öffentlich und **ohne Authentifizierung** +erreichbar — jeder im Internet kann dort beliebige Metriken einspeisen und alle Daten +auslesen. + +Anders als bei [matrix](matrix.md) ist die Lösung hier ohne Vorbedingungen +erreichbar: der Cluster liegt mit `10.0.0.2` **schon** im privaten Netz mit CFGMON +(`10.0.0.3`), das Scraping in die Gegenrichtung läuft bereits darüber. + +**Nächster Schritt:** die Remote-Write-URL in der Cluster-Konfiguration von der +öffentlichen Adresse auf `10.0.0.3:9090` umstellen. Wenn danach kein Absender mehr +über die öffentliche Adresse pusht — Matrix ist der verbleibende, siehe +[MATRIX-02](matrix.md#matrix-02--pusht-per-remote-write-auf-einen-offenen-prometheus) +— kann Port 9090 auf CFGMON zugemacht werden. + +Reihenfolge beachten: erst umstellen und verifizieren, dass Metriken weiter ankommen, +dann die Firewall zuziehen. Andernfalls fehlen Daten, ohne dass es auffällt. diff --git a/hosts/matrix.md b/hosts/matrix.md new file mode 100644 index 0000000..9efff99 --- /dev/null +++ b/hosts/matrix.md @@ -0,0 +1,83 @@ +# matrix + +Matrix-Homeserver. + +| | | +|---|---| +| **IPv4** | `49.13.132.245` | +| **IPv6** | kein AAAA-Record | +| **DNS** | `matrix.axion1337.de` | +| **Stand** | 2026-07-30 | + +> **Nicht inventarisiert.** Alle Angaben stammen aus DNS-Abfragen und der +> Prometheus-Konfiguration auf CFGMON. Auf dem Host selbst wurde nichts geprüft. +> Beim ersten direkten Zugriff OS, Dienste und Compose-Projekte hier nachtragen. +> Verwandte Repos, noch nicht zugeordnet: `sorb/axion1337.chat-gitops`, +> `sorb/element-web`, `sorb/ThreadNet-Stack`. + +--- + +## MATRIX-01 — Klären, ob der Server Mail als `@matrix.axion1337.de` verschickt + +**Status:** offen — **blockiert die Mail-Härtung der Zone** + +Für `matrix.axion1337.de` existiert der komplette IONOS-Mail-Satz: `MX mx00/mx01`, +`TXT "v=spf1 include:_spf-eu.ionos.com ~all"`, `CNAME s1-ionos._domainkey` und +`CNAME autodiscover`. Bei `selendis` ist dasselbe Muster reine Altlast aus dem +Anlegen der Subdomain und wird entfernt. + +**Hier nicht ungeprüft übernehmen.** Matrix-Homeserver verschicken typischerweise +Mail für Registrierungsbestätigungen, Passwort-Resets und Benachrichtigungen. Wenn +der Absender `…@matrix.axion1337.de` lautet, sind SPF und DKIM auf diesem Namen +**funktional nötig** — sie zu löschen oder auf `-all` zu setzen würde den Mailversand +zerlegen, und zwar auf eine Weise, die erst auffällt, wenn sich jemand nicht +registrieren kann. + +**Nächster Schritt:** in der SMTP-Konfiguration des Homeservers den `From:`-Absender +nachsehen (bei Synapse: `email.notif_from` in der `homeserver.yaml`). Dann: + +- **Absender lautet auf `matrix.axion1337.de`:** SPF und DKIM behalten und prüfen, + ob sie den tatsächlichen versendenden Host abdecken. Läuft der Versand nicht über + IONOS, ist das aktuelle `include:_spf-eu.ionos.com` falsch und der echte Relay + muss aufgenommen werden. +- **Absender lautet anders** (z. B. auf den Apex): Härtung wie bei `selendis` — + Null-MX, `v=spf1 -all`, `_dmarc` mit `p=reject`. + +In beiden Fällen kann `autodiscover.matrix` weg: das ist ein Exchange-/Outlook- +Mechanismus, für Matrix-Föderation irrelevant — die läuft über `.well-known` bzw. +SRV-Records. + +Dieser Punkt blockiert außerdem `sp=reject` am Apex, siehe +[ZONE-02](../shared/zone-axion1337.md). + +--- + +## MATRIX-02 — Pusht per Remote-Write auf einen offenen Prometheus + +**Status:** offen — Gegenstück zu [CFGMON-03](cfgmon.md#cfgmon-03--prometheus-remote-write-und-loki-sind-öffentlich-ohne-auth) + +Dieser Host schreibt Metriken per Remote-Write an Prometheus auf CFGMON. Der +Receiver dort ist auf `188.245.193.243:9090` öffentlich und **ohne +Authentifizierung** erreichbar. + +**Nächster Schritt:** prüfen, ob dieser Host in das private Hetzner-Netz aufgenommen +werden kann, in dem CFGMON (`10.0.0.3`) und der k3s-Host (`10.0.0.2`) liegen. Dann +kann der Push über die private Adresse laufen und Port 9090 muss nicht mehr +öffentlich offen sein. Falls nicht möglich: Firewall-Regel auf CFGMON, die 9090 auf +`49.13.132.245` und die k3s-Absender einschränkt. + +Wichtig bei der Umstellung: die Remote-Write-URL liegt in der Konfiguration **dieses** +Hosts, die Firewall-Regel auf CFGMON. Beides muss zusammen geändert werden, sonst +brechen die Metriken ab. + +--- + +## MATRIX-03 — `www.matrix.axion1337.de` ist überflüssig + +**Status:** offen, geringe Priorität + +A-Record `www.matrix.axion1337.de` → `49.13.132.245`, nach IONOS-Default-Muster +angelegt. Begründung siehe [ZONE-01](../shared/zone-axion1337.md). **Nicht +verifiziert**, ob auf dem Host etwas auf den Namen hört. + +**Nächster Schritt:** prüfen und sonst löschen. diff --git a/shared/zone-axion1337.md b/shared/zone-axion1337.md new file mode 100644 index 0000000..575acf9 --- /dev/null +++ b/shared/zone-axion1337.md @@ -0,0 +1,161 @@ +# DNS-Zone `axion1337.de` und Mail-Policy + +Übergreifend, weil die Zone alle Hosts abdeckt und die Mail-Policy sich nicht pro +Host trennen lässt. + +| | | +|---|---| +| **Registrar / DNS** | IONOS (`ns1098.ui-dns.biz`, `ns1022.ui-dns.com`, `ns1070.ui-dns.org`, `ns1059.ui-dns.de`) | +| **Apex** | `217.160.0.140` / `2001:8d8:100f:f000::2e9` — IONOS-Hosting, nicht eigene Infrastruktur | +| **Mail** | IONOS (`mx00.ionos.de`, `mx01.ionos.de`) | +| **Stand** | 2026-07-30 | + +> **Die Aufstellung ist nicht garantiert vollständig.** Zonentransfer ist verweigert, +> DNS erlaubt kein Enumerieren. Die Records unten stammen aus gezielten Abfragen und +> der IONOS-Oberfläche. Für eine vollständige Prüfung entweder die ungefilterte +> IONOS-Liste durchgehen oder die Certificate-Transparency-Logs abfragen (zeigt alle +> Namen, für die je ein Cert ausgestellt wurde). + +## Bekannter Bestand + +| Name | A | AAAA | Ziel | +|---|---|---|---| +| `axion1337.de` | `217.160.0.140` | `2001:8d8:100f:f000::2e9` | IONOS-Hosting | +| `www` | `217.160.0.140` | dito | IONOS-Hosting — hier ist `www` **legitim** | +| `rohana` | `188.245.193.243` | `2a01:4f8:c17:93eb::1` | CFGMON, Gitea | +| `selendis` | `188.245.193.243` | `2a01:4f8:c17:93eb::1` | CFGMON, Grafana | +| `game` | `157.90.155.206` | — | Pterodactyl | +| `matrix` | `49.13.132.245` | — | Matrix-Homeserver | +| `ftp` | `217.160.233.227` | `2001:8d8:1000:30f5:…` | IONOS-Default | +| `www.rohana`, `www.selendis`, `www.game`, `www.matrix` | wie ohne `www` | teils | überflüssig, siehe ZONE-01 | + +Mail-Records existieren auf `selendis` und `matrix` (MX ×2, SPF, DKIM-CNAMEs, +`autodiscover`), auf `rohana` und `game` nicht. + +--- + +## ZONE-01 — IONOS-Default-Records bereinigen + +**Status:** `rohana` und `selendis` in Arbeit (User setzt direkt um, 2026-07-30); +`game`, `matrix`, `ftp` offen + +IONOS legt beim Anlegen einer Subdomain automatisch einen kompletten Mail-Satz mit an +— MX, SPF, DKIM-CNAMEs, `autodiscover` — und ein `www.`-Paar. Auch für Hosts, auf +denen nie Mail läuft. + +### Warum `www.` bei einer Subdomain überflüssig ist + +Das Muster stammt aus der Zeit, als der Webserver einer Domain der Host `www` war, +und hält sich für **Apex-Domains**: `axion1337.de` und `www.axion1337.de` sollen +dasselbe liefern, weil Leute beides tippen. Beim Apex ist das also richtig und bleibt. + +Auf eine Subdomain überträgt es sich nicht. `rohana.axion1337.de` **ist** schon der +Hostname des Dienstes; `www.rohana.axion1337.de` heißt wörtlich „der Webserver des +Webservers". Das tippt und verlinkt niemand. + +Verifiziert für `www.rohana` und `www.selendis`: kein Traefik-Router matcht die Namen, +Traefik liefert sein `TRAEFIK DEFAULT CERT` aus, TLS bricht mit Zertifikatsfehler ab. +Die Namen funktionieren also ohnehin nicht — wer dort landet, sieht etwas Kaputtes. +Sie zum Funktionieren zu bringen hieße Router plus Cert-SAN, also mehr Teile, die bei +jeder Erneuerung mitvalidieren müssen, für null Nutzen. + +### Warum die Mail-Records nicht ersatzlos gelöscht werden + +Zwei Fallstricke, die Löschen zur schlechteren Variante machen: + +- Ohne SPF existiert **keine Aussage** darüber, wer als dieser Name senden darf. Das + bestehende `~all` ist nur Softfail und wird von vielen Empfängern akzeptiert — eine + ungenutzte Subdomain mit gültigen MX und Softfail-SPF ist ein brauchbarer + Spoofing-Vektor. +- **Fehlt ein MX-Record, weichen Absender per RFC 5321 auf A/AAAA aus.** Mail an + `@rohana.axion1337.de` würde also Port 25 auf `188.245.193.243` ansprechen. + +Richtig ist, „hier gibt es keine Mail" explizit zu erklären: Null-MX (RFC 7505), SPF +`-all`, DMARC `p=reject`. + +### rohana.axion1337.de + +**Löschen:** `A www.rohana`, `AAAA www.rohana` + +**Anlegen:** + +| Typ | Name | Wert | +|---|---|---| +| MX | `rohana` | `.` (Priorität 0) | +| TXT | `rohana` | `v=spf1 -all` | +| TXT | `_dmarc.rohana` | `v=DMARC1; p=reject;` | + +**Nicht anfassen:** `A rohana`, `AAAA rohana` — daran hängen Gitea und das Zertifikat. + +### selendis.axion1337.de + +**Löschen:** `MX mx00.ionos.de`, `MX mx01.ionos.de`, +`CNAME s1-ionos._domainkey.selendis`, `CNAME s2-ionos._domainkey.selendis`, +`CNAME s42582890._domainkey.selendis`, `CNAME autodiscover.selendis`, +`A www.selendis`, `AAAA www.selendis` + +**Ändern** — den bestehenden TXT editieren, **nicht** einen zweiten anlegen: zwei +SPF-Records auf einem Namen ergeben einen PermError, die Prüfung fällt dann komplett +aus. + +| Typ | Name | von | auf | +|---|---|---|---| +| TXT | `selendis` | `v=spf1 include:_spf-eu.ionos.com ~all` | `v=spf1 -all` | + +**Anlegen:** `MX selendis` = `.` (Priorität 0), `TXT _dmarc.selendis` = +`v=DMARC1; p=reject;` + +**Nicht anfassen:** `A selendis`, `AAAA selendis`. + +**Vorab prüfen:** ob im IONOS-Mail-Bereich ein Postfach oder eine Weiterleitung für +`selendis.axion1337.de` existiert. Wenn ja, entfallen die MX-Änderungen. + +**Falls IONOS `.` als MX-Ziel ablehnt** — kommt vor: MX löschen und nur die +TXT-Records setzen. `-all` und `p=reject;` greifen trotzdem, es fehlt nur die +explizite „nimmt keine Mail an"-Aussage. + +### Noch offen + +- **`game`** und **`matrix`**: `www.`-Records, siehe + [GAME-02](../hosts/game.md#game-02--wwwgameaxion1337de-ist-überflüssig) und + [MATRIX-03](../hosts/matrix.md#matrix-03--wwwmatrixaxion1337de-ist-überflüssig). +- **Mail-Records auf `matrix`**: **erst** [MATRIX-01](../hosts/matrix.md) klären. Wenn + der Homeserver Mail als `@matrix.axion1337.de` verschickt, sind SPF und DKIM dort + funktional nötig. +- **`ftp.axion1337.de`** → `217.160.233.227`: IONOS-Default aus dem Hosting-Paket, + zeigt auf IONOS und nicht auf eigene Infrastruktur — also keine zusätzliche + Angriffsfläche, aber Ballast. Löschen, falls dort kein FTP genutzt wird. + +**Nach der Umsetzung von hier aus verifizieren:** dass `www.rohana` und +`www.selendis` nicht mehr auflösen; dass MX, SPF und DMARC greifen und **nur ein** +SPF-Record pro Name existiert; und dass `A`/`AAAA` von `rohana` und `selendis` +unverändert sind und Gitea sowie Grafana weiter über HTTPS antworten. Der letzte +Punkt ist der, bei dem ein Verklicker in der IONOS-Oberfläche wehtun würde. + +--- + +## ZONE-02 — Apex-DMARC ist `p=none` und schützt nichts + +**Status:** offen — **blockiert durch [MATRIX-01](../hosts/matrix.md)** + +`_dmarc.axion1337.de` enthält `v=DMARC1; p=none;` (via `dmarc.ionos.de`). Das ist +reines Monitoring: Empfänger melden Verstöße, verwerfen aber nichts. Für gefälschte +Mail in deinem Namen ist das kein Hindernis. + +Zusätzlich fehlt eine `sp=`-Angabe. Ohne sie **erben alle Subdomains dieses +`p=none`** — auch solche, die nie Mail senden, und auch künftige. + +`sp=reject` am Apex wäre daher der effiziente Hebel: deckt alle Subdomains auf einmal +ab, inklusive der noch nicht angelegten, und macht die einzelnen `_dmarc`-Records aus +ZONE-01 auf Dauer entbehrlich. + +**Reihenfolge ist hier wichtig.** Zuerst [MATRIX-01](../hosts/matrix.md) klären, dann +für jeden Namen, der tatsächlich Mail versendet, SPF und DKIM korrekt setzen — erst +danach `sp=reject`. Umgekehrt zerlegt es den Mailversand der betroffenen Dienste, und +zwar unbemerkt, weil verworfene Mail beim Absender keinen Fehler produziert. + +Für den Apex selbst (dort läuft echte Mail über IONOS) wäre der übliche Weg, `p=none` +erst auf `p=quarantine` zu ziehen, die DMARC-Reports eine Weile zu beobachten und dann +auf `p=reject` zu gehen. Auffällig: unter `s1._domainkey.axion1337.de` war kein +DKIM-Record auffindbar, obwohl die Subdomains DKIM-CNAMEs von IONOS haben — das wäre +beim Härten mitzuprüfen.