--- name: server-audit description: Сверить фактическое состояние сервера с его описанием в servers/*.md и обновить описание. Использовать после любых работ на сервере (поднял/погасил сервис, поменял конфиг, удалил каталог, завёл ключ), а также когда просят проверить, соответствует ли дока реальности, актуализировать описание хоста или разобраться, что на сервере вообще происходит. --- # Сверка описания сервера с фактом Цель — чтобы `servers/<хост>.md` описывал сервер таким, какой он есть, и чтобы по нему можно было принимать решения. Правила оформления — в `CLAUDE.md` репозитория, здесь метод и грабли. ## Порядок 1. **Собери факт до чтения доки.** Сначала `./scripts/snapshot.sh <хост>`, потом читай описание. Наоборот — начнёшь подгонять наблюдения под текст. 2. **Сверяй по пунктам, не по впечатлению.** Прогоняй утверждения доки через факт по одному: версии, размеры, пути, порты, «включено/выключено». При сверке fr1 2026-09-08 неверными оказались десять утверждений, включая «IPv6 не работает» про работавший IPv6 и версию Gitea на два минора назад. 3. **Добери то, что требует root** — snapshot это помечает. Неинтерактивный sudo не проходит: нужен `ssh -t <хост> 'sudo ...'`, то есть попроси пользователя выполнить, а не пытайся обойти. 4. **Правь описание в том же прогоне.** Не откладывай на «потом опишу». 5. **Незакрытое — в «Открытые хвосты»** файла хоста. Токен, который не удалось отозвать; права, которые не поправил; решение, которое за пользователя не принимаешь. ## Где дока врёт чаще всего - **Версии и размеры** — меняются молча при обновлении контейнера. Смотри фактический тег у запущенного контейнера, а не запись в compose: они расходятся, и тогда `docker compose up -d` откатывает сервис. - **«Не работает»** — могло починиться на стороне провайдера. И наоборот: IPv6 на fr1 за один день был `FAILED`, потом `REACHABLE`, потом снова `FAILED` — такое надо описывать как мигающее, а не как состояние. - **Открытые порты.** `ufw status` не отвечает на вопрос «что торчит наружу»: docker публикует порты в iptables напрямую, минуя ufw. Только `ss -tlnH` + `docker ps`. Непубликуемый порт контейнера всё равно доступен с хоста по адресу контейнера — «внутренняя сеть» не периметр. - **Процессы вне контейнеров.** `docker ps` их не покажет, а они не переживут перезагрузку. Ищи `ps` с `PPID=1` и чужими пользователями. - **Кроны других пользователей** — в `docker ps` и в `crontab -l` их нет. Осиротевшее задание может годами падать молча: MTA не установлен, вывод крона теряется. - **Планы, записанные как факт.** «Планируется перенести DNS» при уже перенесённом DNS. Проверяй раздел «планируется» отдельно. ## Грабли, которые стоили времени (не повторяй) - **Пути в БД абсолютные.** Прежде чем контейнеризовать сервис, проверь **все** префиксы путей в его базе, а не только основной каталог данных: `select substr(file_path,1,30), count(*) from images group by 1;` Монтируй в тот же путь, что на хосте, иначе часть данных пропадёт молча. - **`up -d --dry-run` перед любым compose-действием.** Он показывает, что compose собирается сделать, — например поднять забытый контейнер того же проекта и словить конфликт портов. - **Задание может глушить свои ошибки.** У imgbot `analyze` логирует исключение и возвращается нормально, поэтому планировщик пишет `executed successfully` после падения. Отсутствие ошибок не доказывает работу — проверяй результат, а не статус. - **Ретенция.** journald был на 500M (11 дней), sysstat на `HISTORY=7`. Сервер, где разбирают аварии, обязан хранить историю дольше, чем живёт проблема. У sysstat при `HISTORY>28` имена переключаются на `saYYYYMMDD` — иначе поднятие числа бессмысленно. - **Группа `docker` = root.** Пользователь без sudo, но в группе docker, монтирует `/` в контейнер и делает что угодно. Учитывай при описании доступа. ## Проверка результата Перед отчётом прогони по получившемуся файлу: каждое ⚠️ подтверждено наблюдением? у каждого изменения указана дата и прежнее значение? не попали ли в текст секреты? если роль хоста изменилась — поправлена ли строка в `AGENTS.md`?