Files
obsidian-vault/family/plans/t610-backup-to-truenas.md
T

26 KiB
Raw Blame History

title, created, updated, type, namespace, tags, related
title created updated type namespace tags related
t610 → TrueNAS автобэкап конфигов 2026-09-14 2026-09-14 tech family
truenas
t610
backup
smb
ssh
cron
ha-os
done
family/plans/t610-home-automation
family/how-to/truenas-access
family/how-to/truenas-rclone-backup
family/how-to/gitea-config

t610 → TrueNAS — автобэкап конфигов

Задача (п.8 / A3 плана family/plans/t610-home-automation): конфиги t610 (HA OS) регулярно складывать на TrueNAS, откуда они автоматически уезжают в Mail.ru Cloud через уже существующий rclone-бэкап TrueNAS.

Схема (принята 2026-09-14)

t610 (/config, /addons, /addon_configs)
   │  tar
   ▼
TrueNAS pull (крон от юзера nas, ssh с ключом)
   │
   ▼
/mnt/RED_2TB/backup/t610/      ← ZFS-датасет, владелец nas:nas, 770
   │  SMB-шара "t610" (id=3) — для ручного доступа Alex с Mac/телефона
   │
   ▼
rclone backup.sh (воскресенье 03:00, контейнер rclone)
   │  run_sync "backup" /data/backup "mailru-crypt:"
   ▼
Mail.ru Cloud (зашифровано)

Ключевые решения и почему так:

  1. Транспорт — PULL с TrueNAS, а не push с t610. На t610 не надо ставить креды TrueNAS и держать лишние журналы; TrueNAS знает, где лежит архив. Крон TrueNAS работает под юзером nas, не под truenas_admin и не под root.
  2. Пользователь nas (uid 1000) — владелец датасета и ключа. truenas_admin — админ (zsh, builtin_administrators) — держать его ключ на чужом хосте неправильно (Alex: «думаешь админом бэкап делать?»). nas — штатный NAS-юзер, nas_users gid=3000.
  3. Целевой датасет — /mnt/RED_2TB/backup/t610 (именно датасет, не папка). Причина: он попадает внутрь /mnt/RED_2TB/backup, который уже целиком уходит в rclone (run_sync "backup" /data/backup "mailru-crypt:"). Ничего в backup.sh править не надо — файлы уезжают в облако автоматически. (Альтернативы storage/ или docker/ требовали бы правки root-owned backup.sh.)
  4. SMB-шара t610 — не для доставки (HA OS не умеет SMB-пуш), а для ручного доступа Alex к бэкапам с Mac/телефона без ssh.
  5. Ключ ограничен на стороне t610: from="192.168.2.197" + no-port-forwarding,no-pty,no-X11-forwarding,no-agent-forwarding. Т.е. даже при утечке — только стянуть архив, шелла нет.

СТАТУС: РАБОТАЕТ (проверено живым прогоном 2026-09-14)

cron TrueNAS (юзер nas, 03:30 ежедневно)
  └─ /mnt/RED_2TB/backup/t610/backup-t610.sh
       └─ ssh t610-backup "tar czf - <14 путей>"  →  605 КБ архив
            └─ /mnt/RED_2TB/backup/t610/t610-config-<ts>.tar.gz  (ротация 14)
                 └─ rclone (вс 03:00, контейнер) → mailru-crypt: (Mail.ru, шифровано)

Что сделано

# Шаг Статус Детали
1 Датасет RED_2TB/backup/t610 UI → Storage → Datasets → Add Dataset; parent /mnt/RED_2TB/backup, Name t610, Type Filesystem
2 Права датасета chown nas:nas + chmod 770 (Alex, System → Shell)
3 SMB-шара id=3 name=t610 path=/mnt/RED_2TB/backup/t610 enabled=true ro=false
4 ssh-ключ для pull Создан на Mac: ~/tmp-t610/backup-key/t610_backup_ed25519 (+.pub), ed25519, без пароля, comment t610-backup-pull, fingerprint SHA256:PKDB/sTjM8WFitIjSG+vT27x1TOY3F2Vi6y8YFwK5wo
5 Публичный ключ на t610 /root/.ssh/authorized_keys (+1 строка, было 1 — ключ Alex). Бэкап: authorized_keys.bak-20260914-210532
6 Приватный ключ на TrueNAS /mnt/RED_2TB/backup/t610/.ssh/id_ed25519 (+ config с алиасом t610-backup), nas:nas 600. ⚠️ Итоговое место — внутри backup/t610, НЕ в ~nas/.ssh (см. питфолл №9)
7 Скрипт бэкапа /mnt/RED_2TB/backup/t610/backup-t610.sh (v3, nas:nas 755)
8 Крон-задача под nas cronjob id=3, user=nas, 30 3 * * *, enabled=true
9 Живой прогон + проверка архива 2 прогона: OK ... (605504 bytes), OK ... (605873 bytes), .err пустой
10 Проверка состава архива 111 файлов; ключевые на месте: configuration.yaml, automations.yaml, go2rtc.yaml, secrets.yaml, .storage/http (фикс trusted_proxies), addons/modbus-bridge/data/config.template.tmpl (реле slave 104), nodered/flows.json
11 Выход в облако run_sync "backup" /data/backup "mailru-crypt:" в backup.sh; контейнер rcloneUp 5 days; правок не требовалось

Путь для архивов на TrueNAS: /mnt/RED_2TB/backup/t610/t610-config-<YYYYmmdd-HHMMSS>.tar.gz, лог — backup.log рядом.

Как проверять (без ожидания 03:30)

# лог + список архивов
ssh truenas_admin@mallexxx.duckdns.org 'cat /mnt/RED_2TB/backup/t610/backup.log; ls -la /mnt/RED_2TB/backup/t610/'
# статус задачи
ssh truenas_admin@mallexxx.duckdns.org 'midclt call cronjob.query "[[\"id\",\"=\",3]]" | jq -r ".[] | {id,user,enabled,command,schedule}"'

⚠️ НЕ проверять через midclt call cronjob.run — даёт ложный exit 137/143. См. питфолл №10.

⚠️ НЕ ЗАКРЫТО — что ещё стоит добавить в архив (обсуждалось 2026-09-14, вечер-15)

Alex задал вопрос: «что НЕ входит в архив, но что мы задолбаемся восстанавливать?». Ответ (проверено на живом t610):

# Что Где лежит Почему критично
1 🔴 Опции всех 11 аддонов /data/options.json внутри контейнера аддона + Supervisor API /addons/<slug>/options mbusd (привязка by-path, гнездо 4), modbus-bridge (usb-0:3, пароль mosquitto mqtt1z3$, ZONT host/slave), zigbee2mqtt (network_key + порт /dev/ttyACM0), Node-RED (крипто-секрет 0ce08a59caa2077e607b5f34044af0b0c, пароль админки), Mosquitto (user/pass), Terminal&SSH (authorized_keys)
2 🔴 Zigbee network_key в опциях аддона zigbee2mqtt (не в /config/zigbee2mqtt/) coordinator_backup.json в архиве есть, но без network_key сеть не поднять — все устройства перепаривать
3 🔴 Node-RED flows_cred.json /addon_configs/a0d7b954_nodered/ (не читается обычным листингом; секрет $ — в опциях) без крипто-секрета креды не расшифруются → все ноды HA подключать заново
4 🟡 home-assistant_v2.db (9.2M) + -wal (4.1M) /config/ история сенсоров, статистика энергии, климат. Бэкапить на живой системе рискованно (неконсистентность) — через SQLite backup API
5 🟡 Файлы *.bak-* /config/ (6 config + 4 automations) точки отката миграции, размер мизерный

Полный список аддонов t610 (проверено через Supervisor API): core_ssh, core_mosquitto, a0d7b954_nodered, core_samba (stopped), core_configurator, 45df7312_zigbee2mqtt, local_mbusd, local_modbus-bridge, local_ustreamer (stopped), a889bffc_go2rtc (stopped), a889bffc_go2rtc-hardware.

Не нужно бэкапить: /config/.cache, deps, tts, .cloud (пустые/кеш), .HA_VERSION, home-assistant_v2.db-shm.

Решение (предложено, ожидает ответа Alex): добавить в backup-t610.sh выгрузку опций аддонов через Supervisor API в JSON перед tar. Целевой размер архива ~10–15 МБ.

Ключевые питфоллы (найдены в этой сессии)

1. 🔴 from="192.168.2.197" — Mac НЕ подходит для проверки ключа

Mac заходит на t610 как 192.168.2.157 (через NAT Rasputin), а не как .197. Поэтому ssh -i <ключ> root@192.168.2.176 с Mac всегда даёт Permission denied (publickey) — это правильное поведение ограничения, а не поломка ключа. Проверять pull-ключ только с TrueNAS (путь к конфигу — итоговый, см. питфолл №9):

ssh -F /mnt/RED_2TB/backup/t610/.ssh/config t610-backup 'echo ok'

⚠️ Это тот же питфолл .157 = NAT Rasputin, что уже задокументирован для логов HA.

2. 🔴 Failed to load datasets: /mnt/.ix-apps/app_configs — ломает ВЕСЬ UI датасетов

Симптом: UI TrueNAS (Storage → Datasets, Apps, Datasets) — «Failed to load datasets», дерево датасетов не открывается, невозможно создать датасет/шару. Причина: после пересоздания пула (2026-08-21) каталог app_configs внутри датасета ix-apps не был создан. UI на старте безусловно читает /mnt/.ix-apps/app_configs и валит всё дерево. Факты: RED_2TB/ix-apps (8.29G) существует, смонтирован на /mnt/.ix-apps, docker.configpool=RED_2TB, dataset=RED_2TB/ix-apps (пул Apps выбран корректно). Внутри был только docker/ (drwx--x--- root root). Choose Pool → не помогло. Фикс (под root, WebUI → System → Shell):

mkdir -p /mnt/.ix-apps/app_configs

📌 Проверено: фикс сработал, UI ожил. mkdir — shell-команда, не действие в UI.

3. 🔴 aclmode: DISCARD aclmode may not be set for NFSv4 acl type при создании датасета

Причина: в форме создания датасета выставлен ACL Mode = DISCARD, что запрещено при acltype=NFSv4 (весь пул RED_2TB на NFSv4). Фикс: ACL Mode = Passthrough (или пусто — подставится passthrough), ACL Type = NFSv4. Результат: RED_2TB/backup/t610aclmode=passthrough, acltype=nfsv4.

4. 🔴 POSIX setfacl на NFSv4-датасете НЕ работает

setfacl -m user:truenas_admin:rwx /mnt/RED_2TB/backup/t610
→ setfacl: Operation not supported

При acltype=nfsv4 POSIX-setfacl не поддерживается. Менять владельца/права — только chown под root или UI → Datasets → Edit → Permissions/ACL (NFSv4-редактор).

5. Шары SMB на TrueNAS хранятся в registry, НЕ в /etc/smb4.conf

/etc/smb4.conf содержит только [global] + include = registry + registry shares = True. Поэтому grep имя_шары /etc/smb4.conf и testparm ничего не находят — это не признак, что шара не создалась. Проверять через midclt call sharing.smb.query.

testparm/smbclient -L от truenas_admin дают regdb_init: Permission denied на /var/run/samba-cache/registry.tdb — проверка от непривилегированного юзера неинформативна.

6. zfs/zpool не в PATH у truenas_admin

zfs list → пусто/command not found. Вызывать по абсолютному пути: /sbin/zfs, /sbin/zpool. (Иначе можно ошибочно решить, что датасетов нет.)

7. truenas_admin не может писать в ~nas — итог: ключ держим в backup/t610/.ssh (см. №9)

~nas = /mnt/RED_2TB/storage/nas. Промежуточная попытка класть ключ туда провалилась (см. №9) — итоговое место ключа: /mnt/RED_2TB/backup/t610/.ssh/.

8. Пароль 1316261 НЕ от TrueNAS

В доке [[family/how-to/truenas-access]] строка «Пароль root: 1316261» ошибочна для TrueNAS. Alex: «Это не пароль от truenas». 1316261 — пароль от OpenWrt-роутера 192.168.2.2 (там он и правильный). Для TrueNAS root-операций использовать WebUI → System → Shell (работает под root, пароль не нужен).

📌 Alex работает под root по SSH сам — не предлагать ему WebUI-инструкции, если он уже сказал, что у него есть root-shell.

9. 🔴 Ключ класть в backup/t610/.ssh, НЕ в ~nas/.ssh — родительский storage/ непроходим для nas

Симптом: Can't open user config file /mnt/RED_2TB/storage/nas/.ssh/config: Permission denied — при том что сам .ssh был nas:nas 700. Причина: nas не может пройти сквозь родителей:

/mnt/RED_2TB/storage          drwxr-x---  truenas_admin:truenas_admin   ← nas не владелец, группы хватает только на r-x
/mnt/RED_2TB/storage/nas      drwxrwx---  truenas_admin:truenas_admin   ← nas не владелец и не в группе → нет x
/mnt/RED_2TB/storage/nas/.ssh drwx------  nas:nas                       ← сам в порядке, но путь закрыт

truenas_adminне владелец ~nas (после chown), а группа имеет только r-xсоздавать там файлы нельзя (mkdir: Permission denied). Решение (root НЕ нужен): /mnt/RED_2TB/backup/t610nas:nas 770, группа имеет rwx, truenas_admin в группе nas → пишет свободно. Ключ и config кладутся туда, затем chown nas:nas (работает без root, т.к. truenas_admin — владелец созданных файлов):

mkdir -p /mnt/RED_2TB/backup/t610/.ssh
cp /tmp/bk_key /mnt/RED_2TB/backup/t610/.ssh/id_ed25519
# ... config ...
chown nas:nas /mnt/RED_2TB/backup/t610/.ssh/{config,id_ed25519}   # ← БЕЗ root, работает

💡 Урок: chown nas:nas на свои файлы проходит без root. Root нужен только для чужих. Прежде чем просить Alex'а — проверить ls -ld по всей цепочке родителей и touch на запись.

10. 🔴 midclt call cronjob.run даёт ЛОЖНЫЙ exit 137/exit 143 — не признак поломки скрипта

Симптом: задача падает за ~1 сек с CronTask "..." exited with 137 (non-zero) exit status — хотя скрипт рабочий. Причина: middleware прибивает таск (SIGKILL=137 / SIGTERM=143), когда родительский job cronjob.run завершается раньше дочернего процесса. Это баг API-пути, а не скрипта. Как отличить: смотреть несколько job'ов и файловый лог скрипта. Реальные ошибки скрипта видны как его собственные коды (exit 2, exit 3) + запись в backup.log/.err. Правильная проверка: ставить задачу на * * * * *, ждать реального срабатывания и смотреть появившийся архив, потом вернуть расписание:

midclt call cronjob.update 3 '{"schedule":{"minute":"30","hour":"3","dom":"*","month":"*","dow":"*"}}'

⚠️ Не «проверять» циклом sleep вслепую — узнавать по факту появления файла/записи в логе.

11. 🔴 Список путей для tar через ssh: не передавать многострочной переменной

Симптом: tar: empty archive + bash: line N: config/configuration.yaml: Permission denied (пути выполняются как команды). Причина: многострочная $FILES, подставленная в ssh-команду, интерпретируется на удалённой стороне как отдельные команды. Фикс: список путей — одной строкой прямо в ssh-команде:

ssh -F "$SSHCFG" "$SSHHOST" 'tar czf - -C / config/configuration.yaml config/automations.yaml ...' > "$OUT"

12. scp поверх чужого файла не работает, но mv в групповом rwx-каталоге — работает

scp даёт Permission denied при перезаписи файла, принадлежащего nas. Обход: положить под новым именем и сделать mv (каталог 770 даёт группе rwx). ⚠️ После mv владелец/права сбрасываются на копирующего → обязателен chmod 755 + chown nas:nas.

13. cronjob.run вызывается кроном как midclt call cronjob.run <id> true — задачи от nas работают

Проверено: /etc/cron.d/middlewared содержит * * * * * root ... midclt call cronjob.run 2 true. Системный cron -f + busybox crond -f живут. Задачи от nas выполняются штатно (job state=SUCCESS, файлы появляются) — «крон не работает» было ложной тревогой от проверки файла до первой минуты.

14. set -u + exit в циклах на zsh-стороне ломает проверки

Проверки через ssh ... 'ls /path/*.tar.gz' на zsh-цели дают zsh:1: no matches found и рвут цикл (скрипт выходит до sleep). Использовать bash -c на удалённой стороне или test -f / ls -1 ... 2>/dev/null.

Команды (итоговые, воспроизведение)

# ─── 1. Создание ключа (Mac) ───
ssh-keygen -t ed25519 -N '' -C 't610-backup-pull' -f ~/tmp-t610/backup-key/t610_backup_ed25519
# fingerprint: SHA256:PKDB/sTjM8WFitIjSG+vT27x1TOY3F2Vi6y8YFwK5wo

# ─── 2. Публичный ключ на t610 (с ограничениями по IP и без шелла) ───
KEYBODY=$(awk '{print $2}' ~/tmp-t610/backup-key/t610_backup_ed25519.pub)
ENTRY="from=\"192.168.2.197\",no-port-forwarding,no-pty,no-X11-forwarding,no-agent-forwarding ssh-ed25519 ${KEYBODY} t610-backup-pull"
ssh root@192.168.2.176 'cp -a /root/.ssh/authorized_keys /root/.ssh/authorized_keys.bak-$(date +%Y%m%d-%H%M%S)'
printf '%s\n' "$ENTRY" | ssh root@192.168.2.176 'cat >> /root/.ssh/authorized_keys && chmod 600 /root/.ssh/authorized_keys'

# ─── 3. Приватный ключ на TrueNAS в backup/t610/.ssh (НЕ в ~nas/.ssh — см. питфолл 9) ───
scp ~/tmp-t610/backup-key/t610_backup_ed25519 truenas_admin@mallexxx.duckdns.org:/tmp/bk_key
ssh truenas_admin@mallexxx.duckdns.org '
  mkdir -p /mnt/RED_2TB/backup/t610/.ssh
  cp /tmp/bk_key /mnt/RED_2TB/backup/t610/.ssh/id_ed25519
  chmod 600 /mnt/RED_2TB/backup/t610/.ssh/id_ed25519
  cat > /mnt/RED_2TB/backup/t610/.ssh/config <<EOF
Host t610-backup
    HostName 192.168.2.176
    User root
    Port 22
    IdentityFile /mnt/RED_2TB/backup/t610/.ssh/id_ed25519
    IdentitiesOnly yes
    StrictHostKeyChecking no
EOF
  chmod 600 /mnt/RED_2TB/backup/t610/.ssh/config
  chown nas:nas /mnt/RED_2TB/backup/t610/.ssh /mnt/RED_2TB/backup/t610/.ssh/config /mnt/RED_2TB/backup/t610/.ssh/id_ed25519
'

# ─── 4. Под ROOT (ТОЛЬКО этот шаг требует root) ───
mkdir -p /mnt/.ix-apps/app_configs                 # лечит UI «Failed to load datasets»
chown nas:nas /mnt/RED_2TB/backup/t610 && chmod 770 /mnt/RED_2TB/backup/t610
groupmod -n nas -m truenas_admin 2>/dev/null || usermod -a -G nas truenas_admin   # truenas_admin в группу nas

# ─── 5. SMB-шара (от truenas_admin, midclt) ───
midclt call sharing.smb.query > /mnt/RED_2TB/storage/shares-before-$(date +%Y%m%d-%H%M%S).json  # ДАМП ПЕРЕД
midclt call sharing.smb.create '{"purpose":"DEFAULT_SHARE","path":"/mnt/RED_2TB/backup/t610",
  "name":"t610","comment":"Backup t610 (HA OS) configs","ro":false,"browsable":true,
  "guestok":false,"timemachine":false,"enabled":true}'

# ─── 6. Скрипт бэкапа (v3) + права ───
scp ~/tmp-t610/backup-t610-v3.sh truenas_admin@mallexxx.duckdns.org:/mnt/RED_2TB/backup/t610/backup-t610.sh
ssh truenas_admin@mallexxx.duckdns.org '
  chmod 755 /mnt/RED_2TB/backup/t610/backup-t610.sh
  chown nas:nas /mnt/RED_2TB/backup/t610/backup-t610.sh'

# ─── 7. Крон-задача (id=3, юзер nas, 03:30 ежедневно) ───
midclt call cronjob.create '{"user":"nas","command":"/mnt/RED_2TB/backup/t610/backup-t610.sh",
  "description":"Backup t610 HA configs","enabled":true,
  "schedule":{"minute":"30","hour":"3","dom":"*","month":"*","dow":"*"},
  "stdout":false,"stderr":false}'

# ─── 8. Проверка pull-ключа — ТОЛЬКО С TrueNAS ───
ssh -F /mnt/RED_2TB/backup/t610/.ssh/config t610-backup 'echo ok'

Состав бэкапа t610 ( фактический — 111 файлов, ~605 КБ)

Из /config: configuration.yaml, automations.yaml, scripts.yaml, scenes.yaml, secrets.yaml, .storage/ (без home-assistant_v2.db*), go2rtc.yaml, zigbee2mqtt/, www/. Плюс /addons/{mbusd,modbus-bridge,ustreamer} (код + data/config.template.tmpl — правка реле slave 104), /addon_configs/{45df7312_zigbee2mqtt,a0d7b954_nodered} (flows/settings). Ротация: KEEP=14 архивов (t610-config-<ts>.tar.gz).

⚠️ НЕ входит (и стоит добавить): опции аддонов, network_key Zigbee, flows_cred.json, home-assistant_v2.db. Детали — раздел «НЕ ЗАКРЫТО» выше.

Скрипт (суть)

DEST=/mnt/RED_2TB/backup/t610 ; SSHCFG=$DEST/.ssh/config ; SSHHOST=t610-backup ; KEEP=14
# tar собирается НА t610, пути — ОДНОЙ СТРОКОЙ (питфолл 11)
ssh -F "$SSHCFG" -o BatchMode=yes -o ConnectTimeout=15 "$SSHHOST" \
   'tar czf - -C / config/configuration.yaml config/automations.yaml ... ' > "$OUT" 2> "$DEST/.err"
# проверка размера <1024 → .BAD + exit 2 ; ssh-ошибка → exit 3 ; ротация до KEEP

Скрипт на Mac (рабочие копии): ~/tmp-t610/backup-t610-v3.sh, ~/tmp-t610/backup-t610.sh. Бэкап-ключ на Mac: ~/tmp-t610/backup-key/t610_backup_ed25519. Скрипты: mk_backup_key.sh, add_backup_key_t610.sh, push_backup_key_truenas.sh, move_key_to_backup.sh, cleanup_backup.sh, phase1_diag.sh, phase2_key.sh, wait_archive.sh.

Связанный синк в git (сделан в этой же сессии)

Репозиторий ~/Automation/HA-ZONT-Modbus → Gitea (git_admin/HA-ZONT-Modbus, private). Коммит 9d31118 «Sync from t610 prod: automations device_ids, go2rtc camera, bridge relay» — запушен, remote SHA = local SHA.

Файл Что синкнуто
homeassistant/configuration.yaml http-блок убран (перенесён в .storage/http); modbus.host 192.168.2.197192.168.2.176
homeassistant/automations.yaml перегенерированы device_id (миграция на t610), light.0xa4c13882a4b42db0light.bed_dimmer, +триггер illuminance, +условие is_occupied (ночной свет душевой)
homeassistant/go2rtc.yaml НОВЫЙ — камера go2rtc-hardware, ffmpeg H.264 + rotate=90
config.yml +«Boiler controller power (Zigbee relay)» slave 104 / рег 1
modbus_ha_bridge.py уже совпадал (sha идентичен)
scripts.yaml уже совпадал (sha идентичен)

📌 Паттерн: сначала сравнить прод↔репо по sha256, потом тащить только изменившееся. Креды Gitea — ~/.git-credentials (chmod 600) + credential.helper=store, remote — чистый URL без токена.