Přeskočit obsah

Audit repa + infrastruktury 2026-07-06 — checklist remediace

Kompletní nálezy z pětioblastního auditu (HA config, dokumentace, zálohy/DR, síť/Proxmox, monitoring/observabilita) + živé nálezy z incidentu 2026-07-05/06. Pracovní checklist — po dokončení všech položek soubor smazat, trvalé závěry přesunout do decision_log.md.

Kontext — incident 2026-07-05/06:

  • pmx2 náhle umřel ~23:31 (poslední logy v Loki = cleanup SSH session, pak ticho bez shutdown sekvence → hang/HW, softdog tentokrát nepomohl).
  • Ráno failover kritických VM (pfSense 100, HA 102, PBS 105) na pmx1 ze ZFS replik — runbook disaster_recovery.md fungoval.
  • pvecm expected 1 nepřežilo reboot pmx1 → guests nenastartovaly → celý dům ~25 min offline (pfSense = DNS/DHCP/routing).
  • Po startu LXC 107 selhal NFS mount /mnt/data (exit 32), docker.service spadl na „Dependency failed" a bez retry zůstal ležet — celý docker stack (Loki, Grafana, nginx, n8n, InfluxDB) vstal až ručním dotykem docker socketu v 09:13. Nikdo nebyl notifikován.
  • Souběžně: Storj sync PBS dat selhával od 3. 7. (viz P3) — v nejhorší možný moment (replikace mimo provoz, PBS = jediná záložní vrstva).

Rozhodnutí uživatele (2026-07-06):

  • pmx2 se neopravuje — Amazon vrací peníze (NiPoGi). Třetí plnohodnotný node nebude.
  • Quorum device (corosync-qnetd) bude na TrueNAS — výslovně NE na debian-services (běží na pmx1, arbitr uvnitř clusteru je k ničemu).

P — Akutní (dny)

  • [ ] P1 (P0) Secure wipe SSD pmx2 před vrácením Amazonu. Data rescue není potřeba (upřesněno 2026-07-06): Jellyfin ct/110 bylo prázdné LXC (budoucí projekt), veškerá Immich data (media) leží na TrueNAS NFS, žádná lokální metadata → na novém nodu čistá instalace + mount síťového disku = plnohodnotná obnova. Na SSD ale zůstaly ZFS repliky HA/pfSense/PBS (vč. secrets) → před odesláním disk bezpečně smazat.
  • [ ] P2 (P1) Nová Immich instalace (nový node): rovnou do PBS backup jobu (/etc/pve/jobs.cfg, dnes vmid 100,102,103,106,107,109). Obecné pravidlo: každý nový guest při vzniku zařadit do backup jobu (dnes žádný proces nehlídá, že guest zálohu má — Immich měl jedinou z 2/2026).
  • [~] P3 (P0) Storj sync pbs-data selhával od 2026-07-03 — root cause nalezen + opraveno 2026-07-06. Chyba: rclone error reading source directory: directory not found na top-level .chunks/ct/vm. Skutečná příčina: task má snapshot: true → sync jede z dočasného ZFS snapshotu přes .zfs/snapshot/ automount, který má zfs_expire_snapshot=300 s. Walk přes 65536 chunk adresářů proti 433k objektům na pomalé Storj gateway přeroste 5min okno → ZFS snapshot odmountuje → zdroj zmizí uprostřed běhu. (Dřívější 14min běhy se pod okno vešly; jak objem narostl, začalo padat.) Slepá ulička: fast_list: true (zkoušeno ráno) nepomohl — spadl stejně, jen dřív; vrácen na false. Fix (na příčinu): zfs_expire_snapshot 300 → 86400 s (živě echo + persist přes TrueNAS POSTINIT init script id 3, přežije reboot). Zachovává point-in-time konzistenci. Navíc uklizen stuck cloud_sync-3 snapshot z 2026-01-21 (nedouklizený pozůstatek). OVĚŘENO 2026-07-06: sync job 438419 SUCCESS po 185 min, plný march (153948 checks) proběhl bez „directory not found", CloudSyncTaskFailed alert se sám vyčistil. Offsite kopie PBS dat je zase aktuální. Pozn.: běh trvá ~3 h (bez fast_list, listing 65k chunk dirs po pomalé gateway) — u nočního jobu 04:00 (dokončení ~07:00) OK; fast_list by zrychlil, ale za cenu RAM (~0,5 GB) → ponecháno. Follow-up: propojit na M2/P6 (alert na failure — dnes selhání 3 dny nikdo neviděl).
  • [x] P4 (P0) Kvórum — vyřešeno 2026-07-06: pvecm delnode pmx2 → jednouzlový cluster, expected=1 trvale (reboot přežije, na rozdíl od runtime pvecm expected 1). Viz decision_log.md 2026-07-06. Zbývá po stavbě nového nodu: pvecm add → 2 uzly → corosync-qnetd na TrueNAS (samostatné železo mimo cluster) + corosync-qdevice na obou nodech = 3 hlasy, výpadek kteréhokoli nodu zůstane quorate. To je vlastní fix incidentu (viz M5, N3). Pozn.: stará todos „Proxmox qdevice" navrhovala debian-services — špatně, arbitr nesmí stát uvnitř clusteru (na pmx1).
  • [ ] P5 (P0) debian-services boot resilience: /mnt/data převést na x-systemd.automount (nebo mount unit s retry) + docker.service drop-in Restart=on-failure / StartLimitIntervalSec tak, aby jednorázové selhání NFS při bootu neznamenalo trvale ležící stack. Květnový fix (_netdev,nofail + dependency) řeší pořadí, ne retry — 2026-07-06 empiricky selhal.
  • [ ] P6 (P1) Alert na selhání TrueNAS Cloud Sync — selhávalo 3 dny bez povšimnutí. Nejjednodušší: TrueNAS Alert Services (e-mail) + Uptime Kuma push monitor (post-script tasku pinguje URL po úspěchu → Kuma alarmuje při absenci). Uzavírá i starý dluh ze zalohy/strategie.md:49 („alert osiřel po odchodu CheckMK").

Z — Zálohy & disaster recovery

  • [ ] Z1 (P1) První reálný restore test. Kandidát: PBS restore ct/106 (Scrypted) nebo ct/107 do scratch VMID na pmx1 (bez startu sítě / jiná IP). Změřit reálné RTO, zapsat do disaster_recovery.md. Následně aspoň jednou otestovat pull ze Storj (stáhnout část bucketu a ověřit čitelnost chunků v PBS).
  • [ ] Z2 (P2) Storj bucket immutabilita + lifecycle. Oprava předpokladu (ověřeno 2026-07-06): bucket pbs má object versioning zapnutý (rclone backend versioningEnabled) — takže i při transfer_mode: SYNC (zrcadlo) jsou smazané/přepsané objekty obnovitelné z historie verzí; hlavní obava (koruptní data propsaná offsite bez návratu) je tím z velké části pokrytá. Zbývá: (a) Object Lock (WORM/governance) pro tvrdou anti-ransomware neměnnost — versioning sám o sobě nechrání proti kompromitovanému S3 klíči, který umí verze smazat / versioning vypnout (neověřeno, rclone backend versioning Object Lock neukazuje); (b) lifecycle pravidlo na expiraci non-current verzí (jinak verze narůstají — náklad/růst, ne bezpečnost).
  • [ ] Z9 (P2, odloženo uživatelem 2026-07-06) Rotace Storj S3 credentialu (id 3). secret_access_key byl 2026-07-06 nechtěně vypsán do agent transcriptu + shell historie TrueNAS (dotaz na cloudsync.query provider bits). Storj je client-side šifrovaný a klíč dává přístup jen k bucketu pbs, ale správně rotovat: nový access grant / S3 credentials v Storj → přepsat credential id 3 v TrueNAS → revokovat starý. Váže se na Z2 (dokud běží starý klíč, immutabilita přes Object Lock má o důvod víc).
  • [ ] Z3 (P1) secrets.yaml → Bitwarden (šifrovaná kopie / secure note). Dnes existuje jen uvnitř PBS zálohy HA VM; standalone recovery cesta chybí (Storj/PBS/TrueNAS klíče v Bitwardenu jsou, HA secrets ne).
  • [ ] Z4 (P1) TrueNAS config export — pravidelný Export Config (vč. secret seed) mimo TrueNAS (např. do $HOMELAB_BACKUP_DIR + kopie k PBS zálohám). TrueNAS je nejcentrálnější SPOF záložní architektury a vlastní konfig nemá zálohovaný vůbec.
  • [ ] Z5 (P2) PBS verify job — ověřit aktuální stav (poslední generovaný doc z 2026-02-07 ukazoval ❌ failed) + přegenerovat pbs_generated.md + zapojit do alertingu (M2).
  • [ ] Z6 (P2) Mirror tank poolu — všechny tři TrueNAS pooly jsou single-disk vdev; na tanku (1× NVMe) leží PBS datastore = zálohy celého clusteru. Druhý NVMe + attach do mirroru.
  • [ ] Z7 (P2) Po obnově clusteru znovu nastavit ZFS replikaci (teď disabled, otočená pmx1→pmx2) dle re-promotion postupu; zvážit kratší interval pro pfSense (už je */30) a sjednotit RPO údaje v docs (viz D6).
  • [ ] Z8 (P2) backup_unifi.py + backup_configs.py: migrace na UniFi OS Server (tracked v todos), naplánovat (timer) a monitorovat — dnes ruční, bez rozvrhu, UniFi větev zálohuje prázdný legacy controller.

H — HA config (bezpečnost fyzického HW)

  • [ ] H1 (P0) Závlaha fail-safe: (a) automatizace na homeassistant.start → force-off všech switch.zavlaha_* (ventil nesmí přežít restart HA sepnutý); (b) nezávislý watchdog „kterýkoli zavlaha switch on > 30 min → vypnout + notifikace". Dnes drží ventil otevřený jen delay ve script.zavlaha_spust_okruh — pád HA uprostřed = relé sepnuté do příštího plánovaného běhu.
  • [ ] H2 (P1) Read-back verifikace kritických Modbus zápisů podle vzoru ftv_export_apply (zpožděné přečtení + persistent_notification při mismatch): minimálně atrea_zapis_vykonu_ventilatoru, atrea_zapis_rezimu_sezony, ftv_tc_boost, ftv_tc_nocni_reset.
  • [ ] H3 (P1) Climate entity: availability_template pro všech 10 jednotek v climate.yaml — dnes int(0)/float(0) fallback maskuje výpadek Modbusu jako „off / 0,0 °C" místo unavailable.
  • [ ] H4 (P2) recorder.exclude zrcadlící logiku influxdb.exclude (surové Modbus polling entity, command_line senzory, history_stats) — dnes recorder bez filtru při ~100 registrech á 30 s.
  • [ ] H5 (P2) binary_sensor.ha_gitops_stale (template.yaml:614) — komentář slibuje alert automatizaci, která neexistuje (zrušena 2026-06-15). Buď postavit (30 min debounce → push), nebo komentář opravit.
  • [ ] H6 (P2) Watchdog dlouho sepnutých relé mimo závlahu: switch.topeni_bazen, switch.ftv_do_akumulacky, switch.ftv_do_boileru — „on > 4 h → notifikace".
  • [ ] H7 (low) Doorbell TTS repeat větev (automations.yaml, doorbell_ring): doplnit vlastní timeout na repeat, ať nezávisí jen na sesterské větvi vypínající input_boolean.zvonek_zvoneni_aktivni.
  • [ ] H8 (low) Závlaha: sdílený mutex (input_boolean) pro zavlaha_rano/zavlaha_zahony_*/zavlaha_test_okruh místo párových state-kontrol (TOCTOU race, porušuje „max 1 okruh").
  • [ ] H9 (low) rest_command.doorbell_open_gate + carport pulse — bez failure feedbacku; ověřit HTTP status / stavový kontakt, notifikace při selhání.
  • [x] H10 (low) ~~check_buttons.py zapojit do CI~~ — bezpředmětné: button_manager + buttons.yaml + check_buttons.py odstraněny 2026-07-23 (přechod na nativní foxtron_dali_button_action, viz decision_log.md 2026-07-22).

M — Monitoring & alerting

  • [ ] M1 (P0) TČ + VZT alerting: automatizace na binary_sensor.ecoforest_alarm (dnes ho nikdo nesleduje!) + „Ecoforest/Atrea Modbus entity unavailable > 10 min → push" podle vzoru bazen_aseko_offline. Topení je nejkritičtější systém domu a dnes je slepý.
  • [ ] M2 (P1) Externí dead-man's switch (healthchecks.io, free tier): pingy po úspěchu z (a) log-monitor.timer, (b) PBS verify jobu, (c) Storj cloud sync post-scriptu. Jediná vrstva, kterou stack strukturálně nemá — všechen alerting dnes žije uvnitř domu a umře s debian-services / internetem (2026-07-06 empiricky).
  • [ ] M3 (P1) Uptime Kuma → notifikace („Fáze 3" z monitoring.md): dotáhnout push kanál (nativní Telegram v Kumě stačí), doplnit hosty (unifi-os — tracked). Bez toho Kuma pinguje do prázdna.
  • [ ] M4 (P1) Disk usage alerty: pool archive 84,8 %, nvr-truenas ~89 %, InfluxDB retence ∞ (migrace 1.8→2.x tracked). TrueNAS alert threshold + Grafana pravidlo.
  • [ ] M5 (P1) Grafana alert kernel soft lockup / hung_task (tracked todos) — po smrti pmx2 priorita ↑; přidat i pattern „node syslog zmlkl" (Loki freshness per hostname pmx1/pmx2, stejný vzor jako journal/syslog rules).
  • [ ] M6 (P2) systemd timery pro doc_gen_*.py — generované docs stárnou (pbs 5 měsíců, proxmox ukazoval ghost node); po stabilizaci clusteru přegenerovat všechny.
  • [ ] M7 (P2) Ověřit, zda 2026-07-05 ~23:41 vystřelil Grafana alert „Log source silent: journal" (HA journal zmlkl se smrtí pmx2). Pokud ne / zapadl v Telegramu → kalibrovat (severity, repeat) a řešit „critical" kanál (HA companion critical push přes M3).
  • [ ] M8 (low) Cert expiry monitor (Uptime Kuma umí nativně) pro *.marada.name.

D — Dokumentace

  • [ ] D1 (P0) Naplnit docs/navod_pro_rodinu/nouze.md — incident 2026-07-05 je přesný use-case. Obsah: výpadek internetu / proudu / celé sítě („nesvítí nic ze sítě → koukni na rack, tady je co zkusit, tady je koho volat"), HA nejede, topení v zimě nejede, únik vody + poloha hlavního uzávěru, kontakty (Veskom, Multi Klima, elektrikář, soused), „co funguje offline" matice (Zigbee/Modbus/kamery ano; TaHoma žaluzie, OTE, remote access ne — viz N3).
  • [ ] D2 (P1) Naplnit komfort.md (přeložit „co dělat když" tabulky z tepelne_cerpadlo.md/rekuperace.md/klimatizace.md do plain language) a media.md — vzor: energie.md, tiskarna.md.
  • [ ] D3 (P1) studna.md — doplnit reálný obsah (čerpadlo: kde je, jistič, co dělat když neteče voda); dnes stránka popisuje jen budoucí vodoměr projekt.
  • [ ] D4 (P2) bazen_vzt.md — dotáhnout stub (tracked i v audit_2026-06 D13).
  • [ ] D5 (P2) Incident 2026-07-05/06 → decision_log.md entry po uzavření (root cause pmx2 pokud zjistitelná, refund, plán náhrady, qdevice rozhodnutí, Storj fix).
  • [ ] D6 (P2) Sjednotit RPO údaje: proxmox.md tvrdí 15 min, disaster_recovery.md */01:30 (pfSense nově */30). Po Z7 aktualizovat podle reality.
  • [ ] D7 (P2) audit_2026-06.md — zbývající open položky (A5, A6, D1, D3, D13, D17, …) přenést sem/do todos a soubor smazat dle jeho vlastního lifecycle (ať neexistují dva konkurenční backlogy).
  • [ ] D8 (low) docs/index.md „zeptej se správce (táty)" → po D1 odkázat na nouze.md.

HW — co domu chybí

  • [ ] HW1 (P1) Zigbee leak senzory (pračka, myčka, bojler/AK, TČ + rozdělovače, bazénová technologie) + automatizace „leak → critical push". Největší nepokryté fyzické riziko za pár stovek. Návazně zvážit motorizovaný hlavní uzávěr (Zigbee/Modbus kulový ventil) — leak → zavřít.
  • [~] HW2 (P1) Náhradní node za pmx2 — vybrán 2026-07-06: ASUS ExpertCenter PN54-S1 (Ryzen 7 260, 32 GB DDR5, dual 2.5GbE, 2× M.2). Viz decision_log.md + todos „Nový uzel PN54-S1". Do doručení jednonodový provoz pmx1 — vědomě akceptovaný stav bez failoveru; nerestartovat pmx1 zbytečně (i když expected=1 teď reboot přežije, guests naběhnou sériově = krátký výpadek sítě).
  • [ ] HW3 (P2) Druhý NVMe do TrueNAS (= Z6 mirror).
  • [ ] HW4 (P2) Freeze-protection teploměry (technická místnost, přívod studny) + anomálie alert (vazba M1 vzor).
  • [ ] HW5 (info) Už tracked v todos, nezdvojovat: WS90 meteostanice (nákup odblokován), fyzický dešťový senzor, LTE WAN fallback, WiCAN Pro.

N — Síť

  • [ ] N1 (P1) Tailscale hardening: nasadit reálné ACL (dnes any→any na všechny VLANy vč. kamer a IoT — vpn.md ACL jsou jen ukázka), ověřit non-expiring key na pfSense uzlu (default 180 dní = tichá smrt remote přístupu) a MFA na účtu.
  • [ ] N2 (P2) TaHoma/Overkiz offline chování — ověřit, jestli žaluzie/okna fungují bez internetu (lokální API vs cloud), výsledek zapsat do velux.md + promítnout do D1 matice.
  • [ ] N3 (P2) Po obnově clusteru zvážit Proxmox ha-manager group pro VM 100 (pfSense) — auto-start na přeživším nodu místo ručního runbooku (vyžaduje funkční kvórum = po P4/qdevice).
  • [ ] N4 (info) Už tracked: MEDIA block rule no-op (todos), SNMP public (audit-06 A5), pfSense LTE failover (todos), USW-48 flapping porty — doplnit do todos i porty racku (8, 9, 16, 33, 48), dnes tracked jen dílna.

Co audit potvrdil jako silné (nechat být): decision_log kultura, CI + pre-commit + gitleaks, config-as-code (Grafana provisioning), VLAN segmentace vč. kamer bez internetu, Victron místo UPS (otestováno), lokální integrace kritických systémů (Modbus TČ/VZT, Aseko local, Victron MQTT, Hikvision NVR), ZFS replikace kritických VM — 2026-07-06 reálně zachránila provoz domu, bazen_* alert automatizace jako vzor, log_monitor.py honest-failure design, ftv_export_apply read-back vzor.