Logrotate не ротирует логи приложения: «parent directory has insecure permissions» и директива su

Что было. На Ubuntu Server 22.04 для логов приложения написали свой файл в /etc/logrotate.d/:

/srv/app/logs/*.log {
    su appuser appgroup
    weekly
    rotate 6
    compress
    missingok
    create 660 appuser appgroup
}

При запуске logrotate пропускал файлы: «skipping … because parent directory has insecure permissions (It’s world writable or writable by group which is not “root”)». После правок появилось «Permission denied».

Почему. logrotate работает от root, и с версии 3.8 он отказывается трогать файлы в каталоге, куда может писать кто-то кроме root (иначе владелец каталога мог бы подменить лог символической ссылкой и заставить root перезаписать произвольный файл). Директива su user group говорит logrotate выполнять ротацию от имени этого пользователя, и тогда каталог должен принадлежать ему (или его группе) и не быть доступным на запись остальным.

Что сделал автор. Поменял su appuser appgroup на su root root, после чего ротация заработала без ошибок. Подтверждено. Это рабочий, но не самый аккуратный вариант: root пишет в каталог приложения, а созданный файл получает владельца из create 660 appuser appgroup, так что приложение продолжает писать.

Как сделать правильно.

  1. Каталог логов должен принадлежать пользователю приложения: chown appuser:appgroup /srv/app/logs && chmod 750 /srv/app/logs. Никаких 777 и группового доступа для посторонних групп.
  2. В конфиге su appuser appgroup: ротация выполняется от имени владельца, проверка безопасности проходит.
  3. Приложение после ротации должно переоткрыть файл: либо copytruncate (просто, но возможна потеря строки на границе), либо postrotate systemctl kill -s HUP app.service endscript, если приложение понимает сигнал, либо postrotate systemctl restart app.service endscript как крайняя мера.
  4. Проверка без изменений: logrotate -d /etc/logrotate.d/app (отладочный прогон) и принудительно logrotate -f /etc/logrotate.d/app.
  5. Состояние ротаций хранится в /var/lib/logrotate/status: если файл ротировали недавно, следующая ротация будет только через период; для теста можно удалить строку файла из статуса.

Почему ротация «не работает вообще». На systemd-системах logrotate запускается таймером logrotate.timer раз в сутки, а не из cron: systemctl list-timers logrotate* и journalctl -u logrotate покажут запуски и ошибки. Часто вся конфигурация верна, а ошибка одна и относится к другому файлу, но из-за неё пропускается весь блок.

Директивы описаны в документации Ubuntu.