Cron бэкапа tradein вызывает скрипт сырым путём — потеря бита +x сломает бэкап молча #3005

Closed
opened 2026-08-20 19:25:29 +00:00 by lekss361 · 2 comments
Owner

Найдено попутно при работе над #3004 (харденинг бэкапов, #2203). Не горит прямо сейчас, но это в точности тот класс отказа, который уже один раз стоил месяцев потерянных бэкапов.

Находка

Две cron-записи вызывают скрипты по-разному:

30 3 * * * bash /opt/gendesign/ops/backup.sh              ← через bash
30 4 * * * /opt/gendesign/tradein-mvp/deploy/backup-tradein-db.sh   ← сырым путём

Обёртка bash в первой строке стоит там не случайно. В шапке ops/backup.sh это объяснено дословно:

«cron should invoke via bash <path> so a missing +x bit can't break it»

Причина — инцидент из #71: git reset --hard origin/main на каждом деплое сбрасывал режим файла на 644, и cron, вызывавший скрипт напрямую, получал Permission denied при каждом запуске. Бэкап основной БД молча не делался — последний удачный дамп на тот момент был от 2026-05-27.

Для tradein-скрипта та же защита не применена.

Почему сейчас не горит

Режим файла в репозитории — 100755, git reset --hard его не меняет, а шаг деплоя дополнительно делает chmod +x ops/*.sh (.forgejo/workflows/deploy.yml:457). Но обратите внимание: этот chmod покрывает только ops/, а tradein-скрипт лежит в tradein-mvp/deploy/ и под него не попадает.

То есть у основного бэкапа защита двойная — и bash-обёртка, и chmod на деплое. У tradein нет ни одной: он держится исключительно на том, что бит в git не слетит.

Что сделать

Достаточно поменять cron-запись на проде:

30 4 * * * bash /opt/gendesign/tradein-mvp/deploy/backup-tradein-db.sh

Опционально — добавить chmod +x tradein-mvp/deploy/*.sh в шаг деплоя deploy-tradein.yml по образцу deploy.yml:457, чтобы защита стала такой же двойной.

Acceptance

  • Cron вызывает скрипт через bash, проверено в crontab -l
  • Прогон после правки отработал и оставил свежий дамп
  • Решено, добавлять ли chmod в deploy-tradein.yml

Смежное: #2203 (off-box бэкап, P0), #3004 (харденинг скриптов), #71 (исходный инцидент с битом +x).

Scope: crontab на прод-хосте, опционально .forgejo/workflows/deploy-tradein.yml.

Найдено попутно при работе над #3004 (харденинг бэкапов, #2203). Не горит прямо сейчас, но это в точности тот класс отказа, который уже один раз стоил месяцев потерянных бэкапов. ## Находка Две cron-записи вызывают скрипты по-разному: ``` 30 3 * * * bash /opt/gendesign/ops/backup.sh ← через bash 30 4 * * * /opt/gendesign/tradein-mvp/deploy/backup-tradein-db.sh ← сырым путём ``` Обёртка `bash` в первой строке стоит там не случайно. В шапке `ops/backup.sh` это объяснено дословно: > «cron should invoke via `bash <path>` so a missing +x bit can't break it» Причина — инцидент из **#71**: `git reset --hard origin/main` на каждом деплое сбрасывал режим файла на 644, и cron, вызывавший скрипт напрямую, получал `Permission denied` при каждом запуске. Бэкап основной БД молча не делался — последний удачный дамп на тот момент был от 2026-05-27. Для tradein-скрипта та же защита не применена. ## Почему сейчас не горит Режим файла в репозитории — `100755`, `git reset --hard` его не меняет, а шаг деплоя дополнительно делает `chmod +x ops/*.sh` (`.forgejo/workflows/deploy.yml:457`). Но обратите внимание: этот `chmod` покрывает **только `ops/`**, а tradein-скрипт лежит в `tradein-mvp/deploy/` и под него не попадает. То есть у основного бэкапа защита двойная — и `bash`-обёртка, и `chmod` на деплое. У tradein нет ни одной: он держится исключительно на том, что бит в git не слетит. ## Что сделать Достаточно поменять cron-запись на проде: ``` 30 4 * * * bash /opt/gendesign/tradein-mvp/deploy/backup-tradein-db.sh ``` Опционально — добавить `chmod +x tradein-mvp/deploy/*.sh` в шаг деплоя `deploy-tradein.yml` по образцу `deploy.yml:457`, чтобы защита стала такой же двойной. ## Acceptance - [ ] Cron вызывает скрипт через `bash`, проверено в `crontab -l` - [ ] Прогон после правки отработал и оставил свежий дамп - [ ] Решено, добавлять ли `chmod` в `deploy-tradein.yml` Смежное: #2203 (off-box бэкап, P0), #3004 (харденинг скриптов), #71 (исходный инцидент с битом +x). Scope: crontab на прод-хосте, опционально `.forgejo/workflows/deploy-tradein.yml`.
lekss361 added the
bug
observability
priority/p2
scope/devops
tradein
labels 2026-08-20 19:25:33 +00:00
Collaborator

Сделано 21.08 — и одна поправка к постановке

Постановка говорила, что у основного бэкапа защита двойная (bash-обёртка + chmod на деплое). На проде это не так. Фактический crontab -l перед правкой:

30 4 * * * /opt/gendesign/tradein-mvp/deploy/backup-tradein-db.sh >> /tmp/tradein-backup.log 2>&1
30 3 * * * /opt/gendesign/ops/backup.sh >> /tmp/gendesign-backup.log 2>&1

Оба — сырым путём. Комментарий в deploy.yml:539 («cron 30 3 * * * bash /opt/gendesign/ops/backup.sh is +x-independent») описывал желаемое, а не фактическое. Это ровно «объявлено ≠ исполняется»: у основного бэкапа была только одна линия (chmod на деплое), у tradein — ни одной.

Что изменено на хосте

Обе строки переведены на bash <path> (копия старого crontab — ~/crontab.bak.20260821):

30 4 * * * bash /opt/gendesign/tradein-mvp/deploy/backup-tradein-db.sh >> /tmp/tradein-backup.log 2>&1
30 3 * * * bash /opt/gendesign/ops/backup.sh >> /tmp/gendesign-backup.log 2>&1

bash -n обоих скриптов чистый; режимы сейчас 775, последние прогоны успешны (основной 00:43 UTC, tradein 01:31 UTC 21.08).

Что в PR (вторая линия + попутное)

  • deploy-tradein.yml: chmod +x deploy/*.sh по образцу deploy.yml:541.
  • aws s3 cp --no-progress в обоих скриптах — без него в лог уходит ~300 строк прогресса за прогон и итоговые строки тонут.
  • Честная шапка скрипта (cron через bash, время хоста = MSK).

Критерий приёмки — с датой

Правка cron проверяется только прогоном по расписанию: 22.08 00:30 UTC (основной) и 01:30 UTC (tradein) — в логах должны быть Backup done / backup ok и не должно быть Completed N MiB. Проверю и отпишусь; до этого issue не закрываю.

## Сделано 21.08 — и одна поправка к постановке **Постановка говорила, что у основного бэкапа защита двойная (`bash`-обёртка + `chmod` на деплое). На проде это не так.** Фактический `crontab -l` перед правкой: ``` 30 4 * * * /opt/gendesign/tradein-mvp/deploy/backup-tradein-db.sh >> /tmp/tradein-backup.log 2>&1 30 3 * * * /opt/gendesign/ops/backup.sh >> /tmp/gendesign-backup.log 2>&1 ``` Оба — сырым путём. Комментарий в `deploy.yml:539` («cron `30 3 * * * bash /opt/gendesign/ops/backup.sh` is +x-independent») описывал желаемое, а не фактическое. Это ровно «объявлено ≠ исполняется»: у основного бэкапа была только одна линия (chmod на деплое), у tradein — ни одной. ### Что изменено на хосте Обе строки переведены на `bash <path>` (копия старого crontab — `~/crontab.bak.20260821`): ``` 30 4 * * * bash /opt/gendesign/tradein-mvp/deploy/backup-tradein-db.sh >> /tmp/tradein-backup.log 2>&1 30 3 * * * bash /opt/gendesign/ops/backup.sh >> /tmp/gendesign-backup.log 2>&1 ``` `bash -n` обоих скриптов чистый; режимы сейчас 775, последние прогоны успешны (основной 00:43 UTC, tradein 01:31 UTC 21.08). ### Что в PR (вторая линия + попутное) - `deploy-tradein.yml`: `chmod +x deploy/*.sh` по образцу `deploy.yml:541`. - `aws s3 cp --no-progress` в обоих скриптах — без него в лог уходит ~300 строк прогресса за прогон и итоговые строки тонут. - Честная шапка скрипта (cron через `bash`, время хоста = MSK). ### Критерий приёмки — с датой Правка cron проверяется только прогоном по расписанию: **22.08 00:30 UTC (основной) и 01:30 UTC (tradein)** — в логах должны быть `Backup done` / `backup ok` и не должно быть `Completed N MiB`. Проверю и отпишусь; до этого issue не закрываю.
Collaborator

Приёмка: оба бэкапа отработали 22.08 через bash-cron

[2026-08-22T00:43:41Z] Backup done. Local dumps retained: 7 data + 2 globals   ← основной (ops/backup.sh)
[2026-08-22T01:31:23Z] backup ok: …/tradein-20260822-013001.sql.gz (311M) …    ← tradein

Дампы на диске датированы 22.08 (tradein-20260822-013001.sql.gz 311 МБ + globals; gendesign_20260822_003001 + globals). --no-progress из PR #3019 в развёрнутом скрипте есть (backup-tradein-db.sh:147), и в логе за 22.08 строк Completed N MiB нет — единственное совпадение относится к дампу 21.08 (до деплоя фикса).

Итого выполнены оба acceptance-пункта: cron вызывает скрипты через bash (проверено crontab -l 21.08, обе строки), защита +x двойная (chmod +x deploy/*.sh в деплое, #3019). Закрываю.

## Приёмка: оба бэкапа отработали 22.08 через `bash`-cron ``` [2026-08-22T00:43:41Z] Backup done. Local dumps retained: 7 data + 2 globals ← основной (ops/backup.sh) [2026-08-22T01:31:23Z] backup ok: …/tradein-20260822-013001.sql.gz (311M) … ← tradein ``` Дампы на диске датированы 22.08 (`tradein-20260822-013001.sql.gz` 311 МБ + globals; `gendesign_20260822_003001` + globals). `--no-progress` из PR #3019 в развёрнутом скрипте есть (`backup-tradein-db.sh:147`), и в логе за 22.08 строк `Completed N MiB` нет — единственное совпадение относится к дампу **21.08** (до деплоя фикса). Итого выполнены оба acceptance-пункта: cron вызывает скрипты через `bash` (проверено `crontab -l` 21.08, обе строки), защита +x двойная (`chmod +x deploy/*.sh` в деплое, #3019). Закрываю.
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#3005
No description provided.