Выберите продукт

Git dubious ownership и safe.directory в Linux: как чинить в CI/CD и на VDS без дыр в безопасности

Git dubious ownership появляется в CI/CD и на серверах деплоя после смены пользователя, запуска через sudo, Docker volume или shared runners. Разберём, как работает safe.directory, почему опасно ставить '*', и как точечно исправить права без отключения защиты.
Git dubious ownership и safe.directory в Linux: как чинить в CI/CD и на VDS без дыр в безопасности

Ошибка fatal: detected dubious ownership стала привычной болью админов и девопсов: она всплывает после обновления Git, смены пользователя в пайплайне, переноса проекта на сервер, запуска деплоя через sudo git или при работе со shared runners. С одной стороны, это ломает автоматизацию. С другой — это осознанный hardening: Git защищает от работы с репозиторием в каталоге, где владелец и права не совпадают с контекстом выполнения команды.

Ниже — практическая шпаргалка: что именно проверяет Git, когда достаточно поправить владельца, когда уместен safe.directory, и как сделать это точечно, не превращая исключение в дыру.

Что такое dubious ownership и почему Git начал ругаться

Сообщение чаще всего выглядит так:

fatal: detected dubious ownership in repository at '/path/to/repo'
To add an exception for this directory, call:

git config --global --add safe.directory /path/to/repo

Смысл проверки: Git не хочет работать с репозиторием, который находится в дереве каталогов с «чужим» владельцем или подозрительными правами. Это защита от сценариев подмены рабочей копии и конфигурации: в реальном мире уязвимость возникает, когда вы запускаете Git под одним пользователем (часто root), а каталог репозитория контролируется другим пользователем или процессом.

Типовые причины в проде и CI

  • Деплой через sudo: репозиторий в /var/www/app принадлежит deploy, а команды запускаются от root (или наоборот).

  • CI/CD меняет пользователя: checkout делает один UID, сборку запускают под другим.

  • Shared runners: рабочие каталоги и кеши могут жить в нестандартных местах и с нетипичными правами.

  • Docker bind-mount/volume: на хосте один UID/GID, в контейнере — другой.

  • NFS/SMB/кеши: владельцы и права «плывут» из-за особенностей монтирования и масок.

Если в пайплайне есть секреты (ключи, токены, доступ к реестрам/продакшену), то «разные пользователи трогают один и тот же workspace» — это повод остановиться и выровнять модель доступа.

Как работает safe.directory и где его настраивать

Настройка safe.directory — это список путей, которым Git разрешает доверять, даже если проверка владельца/прав сработала бы как «сомнительно». Исключение можно задавать на разных уровнях:

  • --global — для пользователя, под которым выполняется job/скрипт (самый частый вариант в CI).

  • --system — для всей системы (обычно не нужно и часто опасно на многопользовательских серверах).

Локальная конфигурация репозитория (--local) в этом кейсе почти не помогает: Git может не «дойти» до чтения локального конфига, если уже отказался открывать репозиторий.

Почему нельзя делать safe.directory '*'

Иногда советуют «быстро починить» так:

git config --global --add safe.directory '*'

Технически это отключает смысл защиты: Git начнёт доверять любому каталогу. На shared runners и на серверах, где разные аккаунты или сервисы могут писать в общие директории, это увеличивает риск подмены репозитория и выполнения нежелательных действий в пайплайне.

Проверка владельцев и прав каталогов репозитория на сервере

Диагностика: быстро понять, что именно не так с правами

Перед тем как добавлять исключения, проверьте факты: кто запускает команду и кому принадлежит дерево каталогов. Часто проблема решается одним выравниванием владельца.

1) Под кем выполняется команда

id
whoami

2) Кому принадлежат репозиторий и родительские каталоги

pwd
ls -ld . ..
ls -ld /path/to/repo
ls -ld /path /path/to

3) Кому принадлежит .git

ls -ld .git
ls -l .git | head

Типичный кейс после ручных операций/rsync: каталог проекта принадлежит deploy, а .git внезапно root’у (или наоборот). Тогда Git-операции от «не того» пользователя закономерно ломаются.

FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

Стратегии исправления (без отключения защиты)

Выбор стратегии зависит от контекста: сервер деплоя, корпоративный CI, контейнеры или shared runner.

Стратегия A: выровнять владельца и выполнять Git только одним пользователем

Для сервера деплоя это самый надёжный вариант: выделяете пользователя (например, deploy) и гарантируете, что все операции с репозиторием выполняются только им.

Аккуратно проверьте путь и затем выровняйте владельца:

sudo chown -R deploy:deploy /var/www/myapp

И запускайте Git так:

sudo -u deploy -H git -C /var/www/myapp status
sudo -u deploy -H git -C /var/www/myapp pull --ff-only
  • обычно не нужен safe.directory;

  • исчезает привычка делать sudo git;

  • проще аудит и повторяемость деплоя.

Если вы поднимаете деплой/раннер на сервере с нуля, проще и безопаснее делать это в изоляции на VDS, где вы контролируете пользователей, права на workspace и модель доступа.

Стратегия B: если «нужен sudo git» — меняем процесс, а не Git

Почти всегда «нужно запустить sudo git» означает, что Git используют не по назначению. Root должен заниматься только системными действиями (перезапуск сервиса, переключение симлинков, права на системные каталоги), а код и сборка — жить в пользовательской зоне.

Рабочий паттерн:

  • fetch/pull/checkout/build — под deploy;

  • копирование артефактов в привилегированные места — через минимальные sudo-разрешения на конкретные команды;

  • systemctl restart — отдельно и тоже через ограниченный sudoers.

Если деплой у вас завязан на rsync/SSH и вы хотите меньше сюрпризов с правами, пригодится материал про практичный пайплайн: CI/CD деплой через GitHub Actions и rsync.

Стратегия C: точечный safe.directory (когда вы не можете выровнять владельца)

В CI/CD иногда действительно невозможно заставить UID/GID совпасть (особенности раннера, политика безопасности, volume из хоста). Тогда safe.directory допустим, но с правилами:

  • добавляйте конкретный путь, а не *;

  • делайте это в рамках job-а (в одноразовом окружении), а не «навсегда» на машине;

  • доверяйте только workspace конкретной job, а не общим каталогам.

Пример (добавить текущий каталог как safe):

git config --global --add safe.directory "$(pwd)"

Если в job используется подкаталог:

git config --global --add safe.directory "$(pwd)/project"
git -C project status

CI/CD: практические рецепты для shared runners и контейнеров

В shared runner вы обычно не контролируете хост целиком, поэтому цель — минимизировать область доверия и не оставлять «широких» исключений.

Рецепт 1: safe.directory в начале job-а

Добавьте workspace в исключения перед любыми Git-операциями в job-е:

git config --global --add safe.directory "$(pwd)"
git rev-parse --is-inside-work-tree

Рецепт 2: не шарить workspace между пользователями на своём раннере

Если раннер у вас собственный (на вашей инфраструктуре), проверьте базовую гигиену:

  • workspace принадлежит пользователю раннера;

  • в этот каталог не пишут посторонние сервисы;

  • кеши зависимостей отделены и имеют понятные права.

ls -ld /var/lib/runner /var/lib/runner/work

Рецепт 3: Docker bind-mount и mismatch UID/GID

Когда репозиторий монтируется в контейнер, а процесс внутри запускается под другим UID, Git может посчитать владение «сомнительным». Рабочие варианты:

  • запускать контейнер с UID/GID владельца файлов на хосте;

  • делать checkout внутри контейнера (не монтируя рабочую копию);

  • если модель доверия ясна — точечный safe.directory для пути внутри контейнера.

Схема проблемы mismatch UID/GID при монтировании репозитория в контейнер CI

Deploy на сервере: как не влететь в права и безопасность

На сервере деплоя ошибка чаще всего появляется из-за привычки «всё делать от root» и держать рабочую копию прямо в /var/www. Более безопасная схема: отдельный пользователь деплоя, отдельные каталоги релизов, а root — только для операций уровня сервиса.

Плохой сценарий: root делает git pull в /var/www

  • появляются root-owned файлы, которые ломают последующие обновления;

  • увеличивается ущерб при ошибке или подмене содержимого репозитория;

  • Git начинает закономерно показывать dubious ownership.

Хороший сценарий: deploy-пользователь + ограниченный sudo

Минимальный принцип:

  • репозиторий, сборка и артефакты — только под deploy;

  • root — только перезапуск и переключение (например, симлинка), и только через ограниченные правила;

  • никаких «ALL:ALL» в sudoers ради удобства деплоя.

Если у вас деплой по SSH с возможностью отката, полезно держать под рукой схему с атомарным переключением и rollback: деплой по SSH с откатом релиза.

Security hardening: если вы всё-таки добавляете safe.directory

Если без safe.directory не обойтись, зафиксируйте модель доверия: кто может писать в каталог репозитория и кто запускает Git-команды. Дальше проверьте базовые ограничения.

  • Не используйте safe.directory * на shared runners и многопользовательских серверах.

  • Не добавляйте исключения в --system, если узел не изолирован и не предназначен строго для одной задачи.

  • Проверьте права на родительские каталоги: лишняя запись в /var/www или в каталог workspace — частая первопричина.

  • Минимизируйте root в CI: если job выполняется от root, цена ошибки резко выше.

FastFox SSL
Надежные SSL-сертификаты
Мы предлагаем широкий спектр SSL-сертификатов от GlobalSign по самым низким ценам. Поможем с покупкой и установкой SSL бесплатно!

Быстрые шпаргалки: что делать в популярных ситуациях

Repo в /var/www принадлежит deploy, а деплой-скрипт запускается от root

Запускайте Git от deploy:

sudo -u deploy -H git -C /var/www/myapp fetch --all
sudo -u deploy -H git -C /var/www/myapp reset --hard origin/main

CI падает на первой Git-команде с dubious ownership

Добавьте workspace в safe.directory в начале job-а:

git config --global --add safe.directory "$(pwd)"
git status

После rsync/копирования часть файлов стала root-owned

Выровняйте владельца дерева (обычно под пользователя деплоя):

sudo chown -R deploy:deploy /var/www/myapp

Итоги

git dubious ownership — это не «каприз Git», а сигнал, что у вас пересекаются контексты прав: один пользователь владеет/пишет, другой выполняет команды. В CI/CD это часто следствие shared runners и контейнеров; на сервере деплоя — симптом sudo git и неразделённых ролей.

Лучшее решение — выровнять владельца и выполнять Git под одним непривилегированным пользователем. Если это невозможно, используйте safe.directory точечно и только там, где модель доверия ясна.

Поделиться статьей

Вам будет интересно

Segfault в production на Linux: coredumpctl, gdb и debuginfo — разбор падений без паники OpenAI Статья написана AI (GPT 5)

Segfault в production на Linux: coredumpctl, gdb и debuginfo — разбор падений без паники

Segfault в проде — это не «рандом», а нехватка артефактов. Показываю пошагово: включить core dump в systemd, проверить core_patter ...
Linux: Too many links (EMLINK) на ext4/XFS — причины и рабочие обходы OpenAI Статья написана AI (GPT 5)

Linux: Too many links (EMLINK) на ext4/XFS — причины и рабочие обходы

EMLINK «Too many links» появляется внезапно: в CI при деплое, при rsync --link-dest, cp -al или распаковке архивов. Разберём лимит ...
Linux: Cannot fork / Resource temporarily unavailable — ulimit, cgroups v2 и pids.max OpenAI Статья написана AI (GPT 5)

Linux: Cannot fork / Resource temporarily unavailable — ulimit, cgroups v2 и pids.max

Cannot fork: Resource temporarily unavailable (EAGAIN) почти всегда означает исчерпание лимита процессов/потоков: ulimit -u (RLIMI ...