Пользователи, группы и права доступа в Linux
Кто может читать, писать и запускать: rwx, chmod, chown, sudo и баги, которые из них растут.
За 30 секунд
У каждого файла в Linux есть владелец, группа и три набора прав: чтение, запись и выполнение. Каждый процесс работает от имени какого-то пользователя. Большинство ошибок «permission denied» на серверах возникает из-за несовпадения пользователя, под которым работает приложение, и владельца нужных ему файлов. Разберитесь в этой модели — и вы сможете безопасно чинить такие ошибки, а не хвататься за chmod 777.
Почему я это использую
- Для каждого сайта или приложения я завожу отдельного системного пользователя, чтобы взломанный сайт не мог читать или менять другой.
- Ошибки прав доступа — одни из самых частых проблем в продакшене, которые я чиню; один из недавних случаев — каталог кэша, в который приложение не могло писать.
- Принцип минимальных привилегий входит в мой базовый набор безопасности для каждого сервера: люди входят под своими учётками, используют sudo по необходимости, и никто не работает под root.
Как это работает
01Пользователи, группы и root
Пользователь — это учётная запись с числовым идентификатором (UID). Пользователи — это люди, но также и сервисы: nginx в Ubuntu обычно работает от www-data (в RHEL — от nginx), PostgreSQL — от postgres. Группа — это именованный набор пользователей, который позволяет выдать нескольким пользователям одинаковый доступ. root (UID 0) может всё и обходит обычные проверки прав, поэтому повседневно работать под root не стоит. Учётные записи перечислены в /etc/passwd, хэши паролей — в /etc/shadow (читать может только root), группы — в /etc/group. Команда id показывает, кто вы и в каких группах состоите.
whoami # current user
id # uid, gid and all groups
id www-data # who is the web server user?
getent passwd your_user # account details
groups your_user # group membership
sudo adduser your_user # create a user (Debian/Ubuntu, interactive)
sudo useradd -m -s /bin/bash your_user # portable, non-interactive
sudo usermod -aG www-data your_user # ADD to a group (-a matters!)
02Как читать rwx и восьмеричные права
Выполните ls -l, и вы увидите что-то вроде -rw-r--r-- 1 deploy www-data. Первый символ — тип (- файл, d каталог, l ссылка). Затем идут три тройки: права владельца, права группы и права всех остальных. r — чтение, w — запись, x — выполнение. Для каталога смысл смещается: r позволяет выводить список имён, w — создавать и удалять файлы внутри, а x — входить в каталог и обращаться к файлам по пути. Каждая тройка — это ещё и число: r = 4, w = 2, x = 1, которые складываются. Так, 644 — это rw-r--r-- (владелец пишет, остальные читают), 755 — rwxr-xr-x, 600 — доступ только владельцу, а 640 позволяет группе читать.
- 644: обычные файлы. 755: каталоги и скрипты. 600: секреты вроде SSH-ключей и файлов .env.
- 640 / 750: у владельца полный доступ, у группы только чтение, у остальных ничего; хорошо подходит для конфигов приложения, общих с группой веб-сервера.
- 777 означает, что писать может кто угодно в системе. Это прячет реальную проблему и открывает дыру в безопасности.
- Специальные биты: setgid на каталоге (2775) заставляет новые файлы наследовать группу каталога; sticky bit (1777, как на /tmp) не даёт пользователям удалять чужие файлы.
03chmod, chown и исправление владельцев
chmod меняет права, chown — владельца и группу. Обе команды принимают -R для рекурсивных изменений, и это мощно и опасно: неосторожный chmod -R не на том каталоге может сломать SSH или всю систему. Используйте символьную запись для небольших точных изменений (u+x добавляет владельцу право на выполнение, g-w убирает у группы право на запись) и восьмеричную, когда нужен точный результат. Частый приём — выставлять файлам и каталогам разные права через find, потому что каталогам нужен x, а обычным файлам он, как правило, не нужен.
chmod 600 ~/.ssh/id_ed25519 # private key: owner only
chmod u+x deploy.sh # make a script executable
chmod g-w config.php # remove group write
sudo chown deploy:www-data -R /var/www/app
# Directories 755, files 644, in one tree
find /var/www/app -type d -exec chmod 755 {} +
find /var/www/app -type f -exec chmod 644 {} +
# Inspect a path permission by permission
namei -l /var/www/app/storage/logs/app.log
04sudo и /etc/sudoers
sudo выполняет одну команду от имени root (или другого пользователя), предварительно проверив, что вам это разрешено. Каждый вызов логируется, что для подотчётности гораздо лучше, чем общий пароль root. В Ubuntu участники группы sudo получают полные права sudo; в RHEL эта группа называется wheel. Правила лежат в /etc/sudoers и в файлах в /etc/sudoers.d. Никогда не редактируйте их обычным редактором: используйте visudo, который проверяет синтаксис перед сохранением. Сломанный файл sudoers может лишить sudo всех сразу. Можно также выдавать узкие права, например разрешить пользователю deploy перезапускать один сервис без пароля — и больше ничего. В свежих релизах Ubuntu поставляется sudo-rs — переписанная с безопасной работой с памятью версия с теми же командами и конфигом.
sudo usermod -aG sudo your_user # full sudo (wheel on RHEL)
sudo -l # what am I allowed to run?
sudo -u postgres psql # run as another user
sudo visudo -f /etc/sudoers.d/deploy
# Content of /etc/sudoers.d/deploy:
deploy ALL=(root) NOPASSWD: /usr/bin/systemctl restart myapp.service
05umask, сервисные пользователи и ACL
umask определяет права новых файлов по умолчанию, убирая биты. Распространённое значение 022 даёт новым файлам 644, а новым каталогам 755; umask 002 даёт 664 и 775, что подходит командам, работающим через общую группу. Для приложений создавайте отдельного системного пользователя без login shell и делайте его владельцем только того, во что приложение должно писать. ACL (списки контроля доступа) добавляют дополнительные правила поверх модели владелец/группа/остальные, например дают ещё одному пользователю право записи в один каталог. Они удобны, но не видны при беглом ls -l (ищите + в конце строки прав), поэтому используйте их умеренно и документируйте.
umask # show current mask, e.g. 0022
sudo useradd --system --no-create-home --shell /usr/sbin/nologin myapp
sudo chown -R myapp:myapp /var/lib/myapp
# ACL: let www-data write to one directory, now and for new files
sudo setfacl -R -m u:www-data:rwX /var/www/app/storage
sudo setfacl -R -d -m u:www-data:rwX /var/www/app/storage
getfacl /var/www/app/storage
06Классический баг: веб-сервер не может писать
Типичная ситуация: PHP- или Node-приложение работает локально, а в продакшене пишет в лог «Permission denied», когда пытается записать кэш, логи или загрузки. Причина почти всегда в том, что код задеплоил один пользователь (deploy или root), а приложение работает от другого (www-data или пользователя пула PHP-FPM). Решение — не 777. Вместо этого выясните, от какого пользователя работает процесс, оставьте код во владении пользователя deploy и доступным приложению только для чтения, а право записи дайте только тем каталогам, которым оно действительно нужно, например storage и bootstrap/cache в Laravel. Бит setgid сохраняет за новыми файлами правильную группу. И помните: деплой или запуск artisan-команд под root может создать файлы кэша во владении root, которые приложение потом не сможет перезаписать.
ps -o user,pid,cmd -C php-fpm8.3 # which user runs PHP? (adjust version)
ps -o user,pid,cmd -C node
cd /var/www/app
sudo chown -R deploy:www-data .
sudo chmod -R 750 .
sudo chmod -R 2775 storage bootstrap/cache # group write + setgid
sudo find storage bootstrap/cache -type f -exec chmod 664 {} +
sudo -u www-data php artisan cache:clear # run as the app user, not root
Когда применять
- Настройка любого нового сервера: личные пользователи с sudo и отдельный системный пользователь для каждого приложения.
- Отладка ошибок «Permission denied» или «EACCES» в логах.
- Выдача CI- или deploy-пользователю узких прав, например на перезапуск одного сервиса.
- Защита секретов: файлы .env, приватные ключи и учётные данные БД должны иметь права 600 или 640.
Когда не стоит
- Не используйте chmod 777 или chown -R root как быстрое решение: это прячет реальную причину и ослабляет безопасность.
- Не стройте всё на ACL: если правило выражается через модель владелец/группа, оставьте его простым и видимым.
- Не делите один логин (особенно root) между несколькими людьми: вы теряете подотчётность.
Альтернативы
- ACL (setfacl)
Дополнительные правила для отдельных пользователей или групп, когда модели владелец/группа/остальные не хватает.
- SELinux / AppArmor
Мандатный контроль доступа поверх обычных прав. SELinux включён по умолчанию в RHEL, AppArmor — в Ubuntu.
- Контейнеры
Docker дополнительно изолирует приложения, но пользователя внутри контейнера и на примонтированных томах всё равно нужно настраивать.
- Песочница systemd
Опции вроде DynamicUser, ProtectSystem и ReadWritePaths ограничивают сервис без ручного создания пользователей.
Лучшие практики
- Входите под именным пользователем и используйте sudo; отключите прямой вход root по SSH.
- Запускайте каждое приложение под собственным системным пользователем без login shell, владеющим только тем, во что нужно писать.
- Используйте 600 для приватных ключей и env-файлов, 644 / 755 для обычных файлов и каталогов.
- Всегда добавляйте группы через usermod -aG (с -a): без -a вы замените все группы пользователя.
- Редактируйте правила sudo только через visudo, желательно отдельными файлами в /etc/sudoers.d.
- Запускайте команды приложения (очистку кэша, миграции) от пользователя приложения, например sudo -u www-data.
- Учтите, что изменения групп вступают в силу только при следующем входе (или через newgrp).
Частые ошибки
- chmod -R 777 на корне сайта, после чего любой процесс на машине может переписать ваш код.
- Запуск artisan, npm или скрипта деплоя под root, после которого остаются файлы root, недоступные приложению.
- usermod -G вместо -aG, который молча удаляет пользователя из группы sudo.
- SSH отказывается принимать ключ, потому что ~/.ssh или authorized_keys доступны на запись группе или всем.
- Прямая правка /etc/sudoers и сохранение синтаксической ошибки, ломающей sudo.
Глубже
07Как ядро на самом деле проверяет доступ
При каждом open ядро сравнивает effective UID процесса и его группы (основную и дополнительные) с владельцем и группой файла. Проверка выбирает ровно один класс по порядку: если вы владелец, учитываются только биты владельца; иначе, если совпадает одна из ваших групп, только биты группы; иначе — биты остальных. Классы не складываются, поэтому файл с правами 0077 запрещает доступ собственному владельцу, хотя все остальные могут его читать. Чтобы добраться до файла, нужно также право x (поиск) на каждом каталоге пути, что namei -l показывает с первого взгляда. Удаление или переименование файла зависит от права записи на каталог, а не на файл. Root обходит большинство этих проверок не магией, а через capabilities, такие как CAP_DAC_OVERRIDE, а работающий процесс сохраняет список групп, который был у него при входе, поэтому новое членство в группе требует новой сессии.
cat /proc/self/status | grep -E '^(Uid|Gid|Groups)' # real/effective IDs
chmod 077 test.txt && cat test.txt # owner denied: only owner bits apply
namei -l /srv/app/shared/.env # x needed on every directory
sudo -u www-data test -r /srv/app/shared/.env && echo readable
newgrp www-data # pick up a new group without relogin
08setuid, setgid и capabilities
Программа с setuid работает с привилегиями своего владельца, а не вызывающего; passwd и sudo имеют setuid root, чтобы обычные пользователи могли контролируемо менять защищённые файлы. Каждый setuid-root бинарник — это поверхность атаки, поэтому проводите их аудит и никогда не ставьте этот бит на скрипты или собственные утилиты. Linux capabilities делят власть root примерно на сорок частей, так что программа может получить только одну: cap_net_bind_service позволяет процессу без root слушать порты ниже 1024, cap_net_raw позволяет ping отправлять raw-пакеты. Файловые capabilities задаются через setcap, а systemd может выдать их сервису через AmbientCapabilities, отбросив всё остальное через CapabilityBoundingSet. Файловые системы также можно монтировать с nosuid, nodev и noexec — дешёвый шаг харденинга для каталогов загрузок и временных файлов, где никогда не должно быть исполняемых программ.
find / -xdev -perm -4000 -type f 2>/dev/null # setuid binaries
getcap -r /usr/bin 2>/dev/null # files with capabilities
sudo setcap 'cap_net_bind_service=+ep' /usr/local/bin/myserver
# In a systemd unit, instead of setcap:
# AmbientCapabilities=CAP_NET_BIND_SERVICE
# CapabilityBoundingSet=CAP_NET_BIND_SERVICE
findmnt -o TARGET,OPTIONS /tmp # check nosuid,noexec
09Когда права верные, а доступ всё равно запрещён
Если ls -l выглядит идеально, а приложение всё равно получает EACCES, «нет» говорит что-то поверх классической модели. Обычный подозреваемый — mandatory access control: AppArmor в Ubuntu ограничивает программы путями из их профиля, а SELinux в RHEL помечает каждый файл и процесс метками и запрещает всё, что не разрешено политикой, независимо от владельца и прав. В SELinux файл, перемещённый (а не скопированный) в web root, сохраняет старую метку — классическая причина ошибок 403; restorecon это исправляет. Другие скрытые слои: атрибут immutable, установленный через chattr +i, который блокирует даже root, пока его не снимут; монтирование read-only или noexec; маски ACL, ограничивающие записи групп; и в контейнерах — user namespaces, отображающие root контейнера на непривилегированный UID хоста. Проверьте всё это, прежде чем трогать права.
sudo aa-status # AppArmor profiles in enforce mode
sudo journalctl -k | grep -i apparmor # AppArmor denials
ls -Z /var/www/html # SELinux labels (RHEL family)
sudo ausearch -m avc -ts recent # SELinux denials
sudo restorecon -Rv /var/www/html # reset labels to policy defaults
lsattr /etc/resolv.conf # 'i' = immutable
getfacl /srv/app/storage | grep mask # ACL mask limiting entries
10UID в контейнерах и томах
Ядро хранит числа, а не имена. Файл, принадлежащий UID 1000, принадлежит тому, у кого UID 1000 в текущей базе пользователей, а она различается на хосте и в образе контейнера. Поэтому каталоги, подключённые через bind mount, часто показывают странных владельцев или становятся недоступными для записи: процесс контейнера работает как UID 33 (www-data в образах Debian) или 1000 (node в официальных образах Node), а файлы на хосте принадлежат вашему deploy-пользователю. Решения, от самого простого: запускать контейнер с UID и GID хоста (user: "1000:1000" в Compose), сделать chown тома на UID контейнера или использовать именованные тома, которые инициализирует Docker. Rootless Docker и Podman добавляют user namespaces, так что root контейнера отображается на высокий непривилегированный UID хоста; это повышает безопасность, но сдвигает числа, которые вы видите на диске. DynamicUser в systemd в том же духе выделяет временный UID на каждый сервис.
docker compose exec app id # UID inside the container
stat -c '%u:%g %n' ./storage # numeric owner on the host
# docker-compose.yml
# services:
# app:
# user: "1000:1000" # match the host deploy user
cat /etc/subuid # ranges for rootless containers
Реальные ситуации
Подрядчику нужен доступ только к одному сайту
Проблема
Внешний разработчик должен деплоить и читать логи одного сайта на общем сервере, где размещены сайты нескольких клиентов. Он не должен видеть код и секреты других сайтов.
Как решить
Создайте для него личную учётную запись с собственным SSH-ключом и без пароля. Каждый сайт уже работает под своим пользователем, поэтому добавьте подрядчика только в группу этого сайта и убедитесь, что каталоги других сайтов имеют права 750 или строже, чтобы «остальные» не получали ничего. Если ему нужно перезапускать сервис сайта, добавьте через visudo одно правило в /etc/sudoers.d/, разрешающее ровно эту команду systemctl. Доступ на чтение логов сайта дайте через групповое владение или ACL, а не через членство в группе adm, которое открывает все логи. Задайте срок действия учётной записи через chage -E, просматривайте логи sudo в auth.log и удалите учётную запись, когда работа закончится.
SSH игнорирует правильный ключ
Проблема
Публичный ключ пользователя есть в authorized_keys, но SSH всё равно спрашивает пароль или падает с «Permission denied (publickey)». На другом сервере этот ключ работает.
Как решить
Смотрите на сторону сервера, а не клиента. sshd со StrictModes (по умолчанию) отвергает ключи, если домашний каталог, ~/.ssh или authorized_keys доступны на запись группе или остальным либо принадлежат кому-то другому. Запустите namei -l /home/user/.ssh/authorized_keys и исправьте через chmod go-w ~, chmod 700 ~/.ssh, chmod 600 ~/.ssh/authorized_keys и chown -R user:user ~/.ssh. Проверьте sudo journalctl -u ssh (sshd в RHEL) на строки «bad ownership or modes». В системах с SELinux восстановленному домашнему каталогу может понадобиться restorecon -Rv ~/.ssh. Также убедитесь, что строка не перенесена и что тип ключа поддерживается версией OpenSSH на сервере.
Файлы, созданные контейнером, принадлежат root
Проблема
После запуска composer install или npm ci внутри контейнера с проектом, подключённым через bind mount, deploy-пользователь на хосте больше не может обновлять или удалять vendor и node_modules.
Как решить
Процесс контейнера работал от root, поэтому созданные им файлы на хосте принадлежат UID 0. Один раз исправьте существующие файлы через sudo chown -R deploy:deploy vendor node_modules. Затем не дайте этому повториться: запускайте команды сборки с --user "$(id -u):$(id -g)" или задайте user: в Compose-файле, либо соберите образ с пользователем без root, чей UID совпадает с хостом. Для долгоживущих контейнеров приложения предпочитайте именованные тома или копируйте код в образ при сборке, а не подключайте дерево исходников через bind mount в продакшене. Не исправляйте это через chmod 777 на точке монтирования — это лишь превращает проблему в уязвимость.
Шпаргалка
- Владелец, группа и права пути
stat -c '%U:%G %a %n' /srv/app/.env- Проверить доступ от имени другого пользователя
sudo -u www-data test -w /srv/app/storage && echo ok- Найти файлы, доступные на запись всем
find /srv -xdev -type f -perm -0002- Найти файлы без действительного владельца
find / -xdev \( -nouser -o -nogroup \)- Список setuid-бинарников
find / -xdev -perm -4000 -type f- Заблокировать учётную запись и сделать её просроченной
sudo usermod -L -e 1 contractor- Задать дату окончания учётной записи
sudo chage -E 2026-12-31 contractor- История использования sudo (Ubuntu)
sudo grep 'sudo:' /var/log/auth.log | tail- Разрешить слушать порт 80 без root
sudo setcap 'cap_net_bind_service=+ep' /path/to/binary- Сделать файл immutable / mutable
sudo chattr +i file / sudo chattr -i file
В моих реальных проектах
- Отдельный системный пользователь для каждого сайта или приложения — часть базового набора безопасности, который я применяю к каждому серверу.
- Я исправил ошибку прав на кэш в продакшене после диагностики в режиме только чтения: кто владеет файлами и от какого пользователя работает приложение.
- На серверах, которые я веду, люди входят по SSH-ключам под именными пользователями, а не по паролям.
Вопросы и ответы
Q1Что означает chmod 755, и когда использовать 644 или 600?
Каждая цифра — это сумма чтения (4), записи (2) и выполнения (1) для владельца, группы и всех остальных. 755 — это rwxr-xr-x: владелец может всё, остальные могут читать и выполнять. Это подходит для каталогов, потому что x на каталоге позволяет в него войти, и для исполняемых скриптов. 644 — это rw-r--r--, обычный режим для файлов: владелец редактирует, остальные читают. 600 — это rw-------, читать и писать может только владелец, и именно это нужно приватным SSH-ключам, файлам .env и другим секретам. SSH вообще отказывается использовать ключи, доступные для чтения другим, так что режим не просто рекомендуется, а принудительно проверяется.
Q2Laravel-приложение выдаёт «Permission denied» при записи в storage. Как это исправить?
Сначала я выясняю, какой пользователь пишет: пользователь пула PHP-FPM, часто www-data, или отдельный пользователь сайта. Затем проверяю владельцев через ls -l и namei -l по всему пути. Обычно код был задеплоен или artisan-команда была запущена под root или пользователем deploy, поэтому файлы принадлежат кому-то другому. Я делаю владельцем пользователя deploy, а группой — группу веб-сервера, даю storage и bootstrap/cache право записи для группы с битом setgid (2775), чтобы новые файлы сохраняли группу, и в дальнейшем запускаю artisan-команды от пользователя приложения. 777 я никогда не использую: это просто позволило бы любому процессу менять приложение.
Q3Почему sudo, а не вход под root?
sudo даёт каждому человеку собственную учётную запись и логирует каждую привилегированную команду вместе с тем, кто её запустил, так что у вас есть журнал аудита. Он ограничивает время, на которое вы получаете права root, одной командой, что уменьшает случайный ущерб, и позволяет выдавать узкие права, например разрешить одному пользователю перезапускать один сервис. Отключение входа root по SSH ещё и убирает то самое имя учётки, которое каждый атакующий пробует первым. С root все делят один пароль, невозможно понять, кто что сделал, а одна опечатка в долгой сессии под root может навредить всей системе.
Q4Что такое umask?
umask — это маска битов прав, которые убираются у вновь создаваемых файлов и каталогов. Программы обычно запрашивают 666 для файлов и 777 для каталогов, а umask вычитает из этого. При распространённом значении 022 новые файлы получают 644, а каталоги 755: писать может только владелец. При 002 получается 664 и 775, то есть писать может и группа, что подходит команде или пользователю deploy и веб-серверу, разделяющим одну группу. При 077 всё новое становится приватным. Задать umask можно для shell, для пользователя в профильных файлах или для сервиса через UMask= в юните systemd.
Q5Почему каждое приложение должно работать под своим пользователем?
Потому что права — главная стена между процессами на одном сервере. Если все сайты работают от www-data, уязвимость в одном сайте позволяет атакующему прочитать конфиги и пароли от БД всех остальных сайтов и изменить их код. С отдельным пользователем на приложение каждое может читать только свои файлы и писать только в свои каталоги storage, так что взлом остаётся локализованным. Это также упрощает понимание, кому принадлежат потребление ресурсов и логи, и позволяет задавать лимиты systemd для каждого сервиса. У сервисного пользователя не должно быть ни login shell, ни прав sudo.
Q6Что такое ACL и когда их использовать?
Списки контроля доступа расширяют базовую модель владелец, группа и остальные дополнительными записями, например дают ещё одному пользователю право записи в один каталог без смены его группы. Управляют ими через setfacl, смотрят через getfacl, а ACL по умолчанию заставляют новые файлы наследовать правила. Они полезны, когда двум разным сервисам нужен разный доступ к одним и тем же файлам, или для общих каталогов загрузок. Минус — заметность: ls -l показывает только знак плюс, и при отладке их легко пропустить. Сначала я использую обычных владельца и группу и берусь за ACL только тогда, когда эта модель не может выразить нужное правило.
Вопросы уровня senior
S1Как спроектировать права на файлы PHP-приложения, чтобы взлом нанёс минимальный ущерб?
Разделите того, кто деплоит, и того, кто исполняет. Код принадлежит deploy-пользователю и доступен пользователю пула PHP-FPM только на чтение, поэтому атакующий, получивший выполнение кода через приложение, не сможет переписать приложение или спрятать в нём бэкдор. Для runtime-пользователя доступны на запись только каталоги, куда приложение обязано писать, — storage, cache и uploads, — а каталог загрузок отдаётся nginx без выполнения PHP и в идеале смонтирован с noexec. Секреты лежат в env-файле с правами 640, читаемом только runtime-группой. У каждого сайта свой пользователь пула, так что сайты не могут читать друг друга. Если возможно, добавьте ограничения systemd или AppArmor. Цена — трение: деплои и команды artisan нужно запускать от правильного пользователя, а это требует дисциплины в скриптах.
S2Безопасно ли давать deploy-пользователю sudo без пароля для systemctl?
Всё зависит от того, насколько узкое правило. Правило вида deploy ALL=(root) NOPASSWD: /usr/bin/systemctl restart myapp.service с полным списком аргументов разрешает только это одно действие. Правило, разрешающее /usr/bin/systemctl с любыми аргументами, фактически даёт root: пользователь может редактировать юниты, запускать произвольные сервисы или открыть пейджер, из которого запускается shell. Wildcard опасны по той же причине, как и команды, запускающие редакторы или принимающие пути к файлам, например tee, cp или tar. Лучшие варианты — маленький скрипт-обёртка без аргументов, принадлежащий root, или правила polkit, разрешающие управлять одним конкретным юнитом. Что бы вы ни выбрали, ограничьте deploy-ключ, логируйте использование sudo и пересматривайте правила так, будто deploy-ключ однажды утечёт.
S3Приложение получает «Permission denied», но ls -l показывает правильные владельца и права. Что ещё проверить?
Сначала подтвердите идентичность процесса, а не пользователя, которым вы его считаете: ps -o user,group,supgrp -p PID, потому что членство в группах фиксируется при старте процесса, и группа, добавленная позже, не активна до рестарта. Во-вторых, пройдите путь через namei -l: каждому родительскому каталогу нужно право на выполнение. Затем посмотрите выше дискреционных прав: отказы AppArmor или SELinux в журнале ядра или audit-логе, записи и маски ACL через getfacl, атрибут immutable через lsattr и опции монтирования через findmnt, например read-only или noexec. Для сервисов systemd проверьте опции песочницы вроде ProtectSystem, ProtectHome или ReadOnlyPaths через systemctl cat. В контейнерах сравните числовые UID внутри и снаружи — имена могут ввести в заблуждение.
Глоссарий
- UID / GID
- Числовые идентификаторы пользователя и группы. Ядро хранит и сравнивает именно числа; имена — лишь таблица соответствия.
- Effective UID
- Идентичность, которую ядро использует для проверки прав. При запуске setuid-программы она отличается от реального UID.
- Дополнительные группы
- Группы, в которые процесс входит помимо основной; фиксируются при старте сессии.
- Бит setuid
- Бит прав, из-за которого программа запускается от имени владельца файла, часто root, а не пользователя, который её запустил.
- Capability
- Отдельная часть привилегий root, например право слушать низкие порты, которую можно выдать процессу отдельно.
- MAC
- Mandatory access control: общесистемная политика (SELinux, AppArmor), которая может запретить доступ, даже если права файла его разрешают.
- Маска ACL
- Запись ACL, ограничивающая максимальные права именованных пользователей и групп. chmod на групповые биты меняет её.
- User namespace
- Механизм ядра, отображающий UID внутри контейнера на другие, обычно непривилегированные, UID хоста.
- StrictModes
- Настройка sshd, включённая по умолчанию: отвергает ключи, если у домашнего каталога или .ssh небезопасные владелец или права.
Что изучить дальше
Есть идея продукта?Давайте создадим его.
Расскажите о своей идее, продукте или команде. Обычно отвечаю в течение одного рабочего дня.