From bb608af0b032dbad6906b359dc479de1b28d1515 Mon Sep 17 00:00:00 2001 From: Nikolay Miroshnichenko Date: Thu, 9 Jul 2026 19:19:14 +0200 Subject: [PATCH] fix(ha-git-backup): pin core.sshCommand + repo-local known_hosts in install.sh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The nightly HA sync runs via shell_command in the Core container, not the SSH add-on where install ran. Core has an empty known_hosts, so git push failed "Host key verification failed" — the commit was made locally but never reached GitHub. install.sh now seeds a repo-local known_hosts (ssh-keyscan) and pins git config core.sshCommand so every context (add-on, Core, cron) uses the same working ssh. Also drops a stray `.git-sync` arg from the pre-commit chmod line. Records LESSON-06 + a symptom-table row in SKILL.md and SKILL.ru.md; CHANGELOG Fixed entry. Field-validated on a live Home Assistant. --- CHANGELOG.md | 5 +++++ skills/ha-git-backup/SKILL.md | 8 ++++++++ skills/ha-git-backup/SKILL.ru.md | 8 ++++++++ skills/ha-git-backup/scripts/install.sh | 13 ++++++++++++- 4 files changed, 33 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2cd1bef..ccda8fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 (e.g. M-0073 → HDL, M-00B6 → Ekinex S.p.A.) instead of relying on a hardcoded id map that can never be complete; the static map remains as fallback. +### Fixed + +- **`skills/ha-git-backup`**: `install.sh` now pins `core.sshCommand` + a repo-local `known_hosts` + so the nightly sync pushes from HA's Core container (was failing `Host key verification failed`). + ## [0.8.0] — 2026-07-07 **Lessons from a second field project (a different integrator naming school):** positional diff --git a/skills/ha-git-backup/SKILL.md b/skills/ha-git-backup/SKILL.md index 4e0be2d..c3be416 100644 --- a/skills/ha-git-backup/SKILL.md +++ b/skills/ha-git-backup/SKILL.md @@ -70,6 +70,7 @@ tarball in Releases — plus a local NAS copy if you have one. | Broke YAML, HA won't start | `git checkout -- ` or `git reset --hard `, restart | | Secret leaked into the repo | `references/incident-secret-leak.md` — rotate the secret FIRST, history second | | Push silent/failing | `/config/.git-sync/log`; typical: read-only deploy key, stale known_hosts | +| Nightly commit is local but never on GitHub (`Host key verification failed` in the log) | `shell_command` runs from the Core container — pin `git config core.sshCommand` with a repo-local known_hosts (see LESSON-06); install.sh does this automatically | | Migrating to new hardware | `scripts/restore.sh` — the Circuit-2 tarball, NOT the git repo (git has no .storage) | | Sync sensor = error | Check the log; the automation already sends a persistent_notification | | Repo bloated | Check binaries (db, tar) are gitignored; history is cleaned with `git gc` | @@ -100,3 +101,10 @@ tarball in Releases — plus a local NAS copy if you have one. - **LESSON-05**: BusyBox grep treats patterns starting with `-` as options — always pass scan patterns with `grep -E -e "$pattern"`. The scanner was silently skipping its private-key pattern until this was caught by a canary test. Test your scanner with planted secrets. +- **LESSON-06**: HA `shell_command` (the nightly sync) runs in the **Core** container, not the + SSH add-on where install ran. Core has its own empty known_hosts → `git push` fails + `Host key verification failed` even though the deploy key is fine — the commit is made locally + but never reaches GitHub. The fix: pin `git config core.sshCommand` (the key + a repo-local + known_hosts + `accept-new`) into `.git/config` so it applies from ANY container. install.sh + does this automatically. Testing SSH from the add-on alone is not enough — it never exercises + the container the sync actually runs in. diff --git a/skills/ha-git-backup/SKILL.ru.md b/skills/ha-git-backup/SKILL.ru.md index 8a20d0f..d3f863f 100644 --- a/skills/ha-git-backup/SKILL.ru.md +++ b/skills/ha-git-backup/SKILL.ru.md @@ -57,6 +57,7 @@ Releases + локальный NAS при наличии. | Сломал YAML, HA не стартует | `git checkout -- ` или `git reset --hard `, рестарт | | Утёк секрет в репо | `references/incident-secret-leak.md` — ротация секрета ПЕРВОЙ | | Push молчит/падает | `/config/.git-sync/log`; типовое: deploy key read-only, протух known_hosts | +| Ночной коммит есть локально, но не в GitHub (`Host key verification failed` в логе) | `shell_command` идёт из Core-контейнера — закрепить `git config core.sshCommand` с repo-local known_hosts (см. УРОК-06); install.sh делает автоматически | | Переезд на новое железо | `scripts/restore.sh` — тарбол Контура 2, НЕ git-репо (в git нет .storage) | | Sync-сенсор = error | Смотреть лог; автоматизация шлёт persistent_notification | | Репо распух | Бинарники (db, tar) в .gitignore; история чистится `git gc` | @@ -74,3 +75,10 @@ Releases + локальный NAS при наличии. - **УРОК-05**: BusyBox-grep принимает паттерн, начинающийся с `-`, за опцию — паттерны сканера передавать только как `grep -E -e "$pattern"`. Сканер молча пропускал проверку приватных ключей, пока это не поймал тест с подсадной уткой. Проверяй сканер подсадными секретами. +- **УРОК-06**: HA `shell_command` (ночной синк) исполняется в контейнере **Core**, а не в SSH + add-on, где шёл install. У Core свой пустой known_hosts → `git push` падает + `Host key verification failed`, хотя deploy key исправен — коммит делается локально, но не + доходит до GitHub. Лечится закреплением `git config core.sshCommand` (ключ + repo-local + known_hosts + `accept-new`) в `.git/config` — применяется из ЛЮБОГО контейнера. install.sh + делает это автоматически. Проверять SSH только из add-on недостаточно — это не задевает + контейнер, в котором реально идёт синк. diff --git a/skills/ha-git-backup/scripts/install.sh b/skills/ha-git-backup/scripts/install.sh index d2df300..7f321a2 100755 --- a/skills/ha-git-backup/scripts/install.sh +++ b/skills/ha-git-backup/scripts/install.sh @@ -38,7 +38,7 @@ cat > .git/hooks/pre-commit </dev/null || true +chmod +x .git/hooks/pre-commit 2>/dev/null || true chmod +x "$SCRIPT_DIR"/*.sh echo "[+] pre-commit secret scan wired up" @@ -49,6 +49,17 @@ else echo "[!] SSH to GitHub failed — check the deploy key (references/setup.md §2)" fi +# 4b. Context-independent SSH for git — CRITICAL. +# HA automations run ha_git_sync via shell_command in the CORE container, NOT this add-on. +# That container has its own (empty) known_hosts, so a push would fail "Host key verification +# failed" even though the deploy key is fine. Pin the key + a repo-local known_hosts into the +# repo's own git config so EVERY context (add-on, Core container, cron) uses the same, working ssh. +KEY="${DEPLOY_KEY:-/config/.git-sync/deploy_key}" +KH="$CONFIG_DIR/.git-sync/known_hosts" +ssh-keyscan -t ed25519,rsa github.com 2>/dev/null > "$KH" || true +git config core.sshCommand "ssh -i $KEY -o IdentitiesOnly=yes -o UserKnownHostsFile=$KH -o StrictHostKeyChecking=accept-new" +echo "[+] core.sshCommand pinned (host key works from any container, incl. Core)" + # 5. First pass: show WHAT would enter the repo, before any real commit echo "[i] DRY-RUN — these files would land in the first commit:" git add -A -n | head -50