Введение
Продолжаем серию рубрики Linux. В прошлой серии разобрали пользователей и группы – UID, GID, /etc/passwd. Теперь разберём что ядро делает с этими числами когда процесс пытается открыть файл или войти в директорию. Про права доступа легко выучить команды – chmod 755, chown user file – но не понимать что происходит под капотом. Тогда начинаются странные ситуации: «я же владелец, почему Permission denied?». Цель этой статьи – дать модель, а не только набор команд.
Вся статья построена на практике: каждый раздел можно пройти руками. Для большинства примеров достаточно обычного пользователя, для части нужен sudo. Где нужен – отмечено явно. Серия в основном под Ubuntu/Debian.
В конце статьи будет бонус который даст вам возможность изучить права доступа в Linux ещё доступнее и возможно проще.
1. Что такое права доступа и зачем они нужны
Linux – многопользовательская система. На одной машине одновременно работают процессы от разных пользователей: nginx читает файлы сайта, postgres работает со своими данными, разработчик ivan пишет код в своём каталоге. Без изоляции любой мог бы читать чужие ключи SSH, удалять чужие файлы, подменять конфиги сервисов.
Права доступа – механизм изоляции. Каждый файловый объект несёт метаданные: кто владелец, какая группа, кто что может делать. Метаданные хранятся в inode – структуре файловой системы которая описывает файл. Важно понимать: содержимое файла и его метаданные (включая права) хранятся раздельно. Когда вы делаете ls -la – вы читаете именно inode, а не сам файл.
Посмотрим на конкретный пример. Файл /etc/shadow хранит хеши паролей всех пользователей:
ls -la /etc/shadow
-rw-r----- 1 root shadow 1478 апр 16 12:03 /etc/shadow
Права гарантируют что читать его может только root и члены группы shadow. Обычный пользователь – нет. Разберём эту строку полностью, слева направо.
| Часть |
Что означает |
-rw-r----- |
тип файла и биты прав (разберём ниже) |
1 |
количество жёстких ссылок на файл |
root |
владелец файла |
shadow |
группа-владелец файла |
1478 |
размер в байтах |
апр 16 12:03 |
дата последнего изменения |
/etc/shadow |
путь к файлу |
2. Модель UGO – три класса доступа
Стандартная модель Linux делит всех на три класса относительно конкретного файла.
U – User (владелец). Один конкретный пользователь. Когда создаёте файл – автоматически становитесь его владельцем. G – Group (группа). Одна конкретная группа. По умолчанию – primary group пользователя который создал файл. O – Others (остальные). Все кто не попал в первые два класса.
Для каждого из трёх классов устанавливается свой набор прав. Вот как это выглядит в выводе ls -la:
veil@veilos:~$ ls -la
drwxr-x---+ 19 veil veil 4096 апр 24 13:14 .
Первый символ – тип файла:
| Символ |
Тип |
- |
обычный файл |
d |
директория |
l |
символическая ссылка |
b |
блочное устройство |
c |
символьное устройство |
p |
именованный канал (pipe) |
s |
сокет |
Следующие 9 символов – три блока по три бита для U, G и O. Вернёмся к /etc/shadow:
- rw- r-- ---
U G O
U=rw- → владелец (root) читает и пишет
G=r-- → группа (shadow) только читает
O=--- → все остальные – никаких прав
Каждый файл имеет ровно одного владельца и ровно одну группу – не двух, не список. Это ограничение стандартной модели UGO. Для более гибкого управления существуют ACL – разберём их в разделе 9.
3. Биты rwx – что они означают
Три бита в каждом блоке: r – read, w – write, x – execute. Установлен бит – операция разрешена. Сброшен (дефис) – запрещена. Смысл битов различается для файлов и директорий.
Для файлов
| Бит |
Что разрешает |
r |
читать содержимое (cat, less, cp) |
w |
изменять содержимое (редакторы, echo >>) |
x |
запускать как программу |
Для директорий
Здесь важно не путать – директория это тоже файл, просто специального типа. Биты работают иначе.
| Бит |
Что разрешает |
r |
читать список файлов внутри (ls) |
w |
создавать и удалять файлы внутри |
x |
входить (cd) и обращаться к файлам по имени |
На директориях бит х (проход) нужен, чтобы в дальнейшем войти через cd или обратиться к файлу по пути. r без x будет парадоксом, где список файлов через ls будет видно а открыть нельзя, поэтому r и x на директориях почти во многих случаях ставятся вместе.
4. Числовое и символьное представление
Существует два способа записывать права, и оба используются в реальной работе. Разберём оба.
Числовое (восьмеричное)
Каждый бит имеет числовое значение:
r = 4
w = 2
x = 1
- = 0
Чтобы получить число для класса – складываем биты:
rwx = 4+2+1 = 7
rw- = 4+2+0 = 6
r-x = 4+0+1 = 5
r-- = 4+0+0 = 4
--- = 0+0+0 = 0
Итоговые права – три цифры, по одной для U, G, O:
755 → rwx r-x r-x
644 → rw- r-- r--
700 → rwx --- ---
600 → rw- --- ---
777 означает что любой пользователь на системе может читать, изменять и запускать файл. На сервере где работают несколько пользователей – это дыра. Особенно опасно если это скрипт который запускается по cron от root – любой сможет подменить его содержимое. Используйте с осторожностью.
Шпаргалка по типовым комбинациям:
| Права |
Символьно |
Когда применять |
644 |
rw-r--r-- |
Обычные файлы, конфиги |
755 |
rwxr-xr-x |
Исполняемые файлы, директории |
600 |
rw------- |
Приватные ключи SSH, секреты |
640 |
rw-r----- |
Конфиги с паролями – группа читает, others нет |
700 |
rwx------ |
Домашняя директория сервисного пользователя |
750 |
rwxr-x--- |
Директории сервисов |
777 |
rwxrwxrwx |
Почти никогда – проблема безопасности |
Символьное
Удобно когда нужно добавить или убрать один бит не трогая остальные – не надо держать в голове итоговое число. Синтаксис: [кто][операция][что].
Кто: u – владелец, g – группа, o – остальные, a – все сразу (u+g+o).
Операция: + – добавить, - – убрать, = – установить точно (остальные биты сбросятся).
chmod u+x script.sh # добавить x владельцу
chmod g-w file.txt # убрать w у группы
chmod o=r file.txt # ровно r для others, остальное сбрасывается
chmod a+x file.sh # добавить x всем
chmod u+x,g-w file.txt # несколько операций через запятую
chmod go-rwx secret.txt # убрать всё у группы и others
Когда что удобнее: числовой – когда знаешь точное итоговое состояние. Символьный – когда хочешь изменить один бит не затрагивая остальные.
5. chmod – изменение прав
chmod (от change mode) – команда для изменения прав доступа.
# Числовым способом
chmod 644 file.txt
chmod 755 /var/www/html
chmod 600 ~/.ssh/id_rsa
# Символьным способом
chmod u+x script.sh
chmod g+w shared/
chmod o-rwx secret.txt
# Рекурсивно для всего дерева каталогов
chmod -R 750 /var/lib/app
Здесь есть типичная ловушка при настройке веб-серверов. Многие делают так:
chmod -R 755 /var/www/html
Эта команда поставит 755 на абсолютно все объекты – и директории, и файлы. Для директорий 755 нормально, им нужен x чтобы в них можно было входить. Но для файлов это означает что .html, .css, .jpg становятся исполняемыми – а этого не должно быть. Правильный способ – разделить файлы и директории:
# Директориям нужен x чтобы в них можно было входить
find /var/www/html -type d -exec chmod 755 {} \;
# Файлам x не нужен
find /var/www/html -type f -exec chmod 644 {} \;
После этого права будут корректные: директории – 755, файлы – 644, без лишнего x. Главное: не ставить права «на глаз» – всегда думайте кому реально нужен доступ.
6. chown и chgrp – смена владельца и группы
chown (от change owner) меняет владельца файла, группу, или оба сразу. chgrp (от change group) меняет только группу – по сути то же что chown :group, просто отдельная команда.
# Сменить только владельца
chown ivan file.txt
# Сменить только группу
chgrp developers file.txt
# Сменить и владельца и группу сразу
chown ivan:developers file.txt
# Рекурсивно
chown -R www-data:www-data /var/www/html
Обычный пользователь может поменять группу файла только на одну из своих собственных групп. Поменять владельца на другого пользователя – только root. Без этого ограничения можно было бы «подарить» вредоносный файл с SUID другому пользователю – и это стало бы простым способом эскалации привилегий.
7. Как ядро принимает решение о доступе
Это самый важный раздел статьи. Команды можно выучить, но без понимания алгоритма поведение системы будет казаться непредсказуемым. Особенно когда видишь Permission denied там где не ожидаешь.
7.1 Real vs Effective UID
Когда пользователь запускает программу, у процесса есть несколько идентификаторов. RUID (Real UID) – кто фактически запустил процесс. Не меняется в течение жизни процесса. EUID (Effective UID) – от чьего имени процесс работает прямо сейчас. Именно его проверяет ядро при обращении к файлу.
В большинстве случаев RUID == EUID. Но это различие становится важным при SUID – разберём в следующем разделе.
cat /proc/self/status | grep Uid
Uid: 1000 1000 1000 1000
Четыре числа в строке: Real UID, Effective UID, Saved UID (нужен для временного переключения прав), Filesystem UID (используется при проверке доступа к файлам – в большинстве случаев совпадает с EUID). Для понимания прав доступа ключевой – второй, EUID.
7.2 Алгоритм проверки прав
Когда процесс обращается к файлу, ядро проверяет права строго по порядку и останавливается на первом совпадении:
- Если EUID = 0 (root) → доступ почти всегда разрешён
- Если EUID совпадает с владельцем файла → используются права владельца
- Если группа процесса (EGID или дополнительные группы) совпадает с группой файла → используются права группы
- Во всех остальных случаях → используются права для остальных (others)
Система не комбинирует права – она берёт только один подходящий вариант и на этом заканчивает проверку.
7.3 Права не суммируются
Частая ловушка с которой сталкиваются многие. Допустим есть файл:
ls -la secret.txt
# ----rwx--- 1 ivan developers secret.txt
# U: --- G: rwx O: ---
Пользователь ivan является владельцем и одновременно входит в группу developers:
id ivan
# uid=1001(ivan) gid=1001(ivan) groups=1001(ivan),1005(developers)
Но если ivan попробует прочитать файл – получит Permission denied. Почему? Ядро проверило: EUID процесса совпадает с UID файла – совпадение на шаге 2. Применило биты owner – ---. Остановилось. Биты group rwx даже не рассматривались. ivan является владельцем, но у владельца нет прав. Группа имеет полный доступ – но до неё проверка не дошла.
Решение – дать владельцу права явно:
chmod u+r secret.txt
# или явно задать нужные права
chmod 740 secret.txt # u=rwx, g=r--, o=---
7.4 root и права
root (EUID=0) обходит проверку UGO для большинства операций – может читать любой файл, писать в любой файл независимо от прав. Но есть одно исключение: бит execute. Даже root не может запустить файл если ни у кого не установлен x:
chmod 644 script.sh
sudo ./script.sh
# ./script.sh: Permission denied – даже для root
Чтобы запустить – сначала chmod +x script.sh, потом sudo ./script.sh. Это защита от случайного запуска: если x не установлен, файл не является исполняемым ни для кого, включая root.
8. Специальные биты – SUID, SGID, Sticky bit
Помимо стандартных rwx существуют три специальных бита. Каждый работает по-разному в зависимости от того – установлен он на файл или на директорию.
SUID (Set User ID)
При запуске файла с SUID процесс получает EUID = UID владельца файла, а не UID пользователя который его запустил. Пример – команда passwd. Обычный пользователь должен иметь возможность сменить свой пароль, а значит записать новый хеш в /etc/shadow. Но /etc/shadow доступен только root. SUID решает эту проблему: /usr/bin/passwd принадлежит root и имеет SUID – при запуске процесс временно получает EUID=0 и может писать в shadow.
ls -la /usr/bin/passwd
# -rwsr-xr-x 1 root root 64152 Feb 6 /usr/bin/passwd
s вместо x в блоке владельца – SUID установлен. Заглавная S (без x) означает что SUID установлен но бит execute при этом не установлен – это подозрительная ситуация которой обычно не должно быть.
# Установить SUID
chmod u+s file
chmod 4755 file # 4 – это SUID в числовом виде
# Найти все файлы с SUID в системе
find / -perm -4000 -type f 2>/dev/null
SUID-бинарники – популярная цель при атаках на повышение привилегий. Если на стандартной утилите вроде vim, find или bash внезапно появился SUID – это серьёзный повод для расследования. Регулярно проверяйте список SUID-файлов и сравнивайте с эталоном.
Эталон – это список SUID-файлов, снятый в заведомо чистом состоянии системы (сразу после установки или после проверенного аудита). При следующих проверках вы сравниваете текущий список с ним и видите, что появилось нового.
SGID (Set Group ID)
SGID работает по-разному для файлов и директорий. На файл: процесс получает EGID = GID группы файла – аналог SUID но для группы. Используется редко. На директорию: все файлы и поддиректории созданные внутри автоматически наследуют группу этой директории, а не primary group создателя. Это ключевой механизм для совместной работы. В качестве примера возьмём несколько разработчиков которые работают с одной директорией. Без SGID каждый создаёт файлы со своей primary group – другие не могут их редактировать даже если входят в нужную группу. С SGID все файлы автоматически получают группу директории и все члены группы могут работать с ними без лишних телодвижений.
# Установить SGID на директорию
chmod g+s /project
chmod 2755 /project # 2 – это SGID в числовом виде
# Проверить – в выводе ls будет s вместо x в блоке группы
ls -la /project
# drwxr-sr-x 2 ivan developers 4096 апр 29 /project
Sticky bit
Sticky bit решает конкретную проблему: если у пользователя есть право w на директорию – он может удалить из неё любой файл, даже чужой. На директорию: пользователь может удалить файл только если он является владельцем файла или владельцем директории. Просто иметь w на директорию – недостаточно. Пример – /tmp, где директория доступна на запись всем, но никто не должен удалять чужие файлы:
ls -la /
# drwxrwxrwt 22 root root 4096 апр 29 19:07 tmp
t вместо x в блоке others – sticky bit установлен.
# Установить sticky bit
chmod +t /shared
chmod 1777 /shared # 1 – это sticky bit в числовом виде
Сводная таблица
| Бит |
Числовой |
Символьный |
На файл |
На директорию |
| SUID |
4000 |
u+s |
EUID = UID владельца файла |
Нет эффекта |
| SGID |
2000 |
g+s |
EGID = GID группы файла |
Новые файлы наследуют группу директории |
| Sticky |
1000 |
+t |
Нет эффекта |
Удалить файл может только его владелец |
Специальные биты идут первой цифрой в четырёхзначной записи:
chmod 4755 file # SUID + rwxr-xr-x
chmod 2755 dir # SGID + rwxr-xr-x
chmod 1777 dir # Sticky + rwxrwxrwx
chmod 3755 dir # SGID + Sticky + rwxr-xr-x
9. ACL – когда стандартных прав недостаточно
Стандартная модель UGO имеет фундаментальное ограничение: один владелец и одна группа. Что если нужно дать доступ двум разным пользователям с разными правами? Например, пользователь maria должна читать и писать файл, пользователь sergei – только читать, а все остальные – ничего. В UGO это не реализовать. ACL (Access Control Lists) решают эту задачу – они позволяют прописать права для произвольного количества пользователей и групп на один объект.
Для начала проверим что пакет установлен:
apt list --installed 2>/dev/null | grep acl
# Если пакета нет:
sudo apt install acl
9.1 Просмотр ACL
getfacl file.txt
# file: file.txt
# owner: veil
# group: veil
user::rw- ← права владельца (стандартные)
group::r-- ← права группы (стандартные)
other::r-- ← права остальных (стандартные)
Пока ACL не установлены – вывод совпадает с обычными правами. Как только добавляем ACL-запись, в выводе ls -la появляется символ + в конце строки прав:
ls -la file.txt
# -rw-r--r--+ 1 veil veil 34045 апр 29 15:18 file.txt
# ↑ этот плюс означает что на файле есть ACL
9.2 Установка ACL
# Дать конкретному пользователю права на файл
setfacl -m u:maria:rw file.txt
# Дать группе права на директорию
setfacl -m g:backend:rx /var/www/api
# Несколько записей сразу
setfacl -m u:maria:rw,g:backend:r file.txt
# Удалить конкретную ACL-запись
setfacl -x u:maria file.txt
# Удалить все ACL с файла
setfacl -b file.txt
После добавления ACL для maria – getfacl покажет дополнительные строки:
getfacl file.txt
# file: file.txt
# owner: veil
# group: veil
user::rw-
user:maria:rw- ← ACL-запись для конкретного пользователя
group::r--
mask::rw- ← маска появилась автоматически
other::r--
9.3 Маска
Маска – это максимально возможные эффективные права для всех ACL-записей, кроме владельца и others. Если маска r-x, то даже ACL с rwx реально даст только r-x. Маска появляется автоматически при добавлении первой ACL-записи и расширяется когда вы добавляете записи с большими правами. Можно задать явно:
# Посмотреть маску
getfacl file.txt | grep mask
# Явно задать маску
setfacl -m mask::rx file.txt
9.4 ACL по умолчанию – важный момент
Без флага -d ACL применяется только к уже существующим файлам. Новые файлы созданные в директории его не наследуют. Это частая ошибка – настроил ACL на директорию, а файлы которые потом создаются внутри его не получают.
Правильный паттерн – всегда два вызова:
# 1. Применить к существующим файлам и поддиректориям
setfacl -R -m g:developers:rwx /project
# 2. Установить ACL по умолчанию – для всех новых файлов
setfacl -d -m g:developers:rwx /project
# Проверить – в выводе появятся строки default:
getfacl /project
# file: project
# owner: ivan
# group: ivan
user::rwx
group::r-x
other::r-x
default:group:developers:rwx ← будет применяться к новым файлам
10. umask – права по умолчанию при создании файлов
Когда создаётся новый файл или директория – система не даёт максимально возможные права, а вычитает из них маску. Это и есть umask. Максимальные права при создании: файл – 666 (execute никогда не ставится автоматически), директория – 777.
# Посмотреть текущую маску
umask
# 0022
# Расчёт:
# файл: 666 - 022 = 644 (rw-r--r--)
# директория: 777 - 022 = 755 (rwxr-xr-x)
Строго говоря umask работает как побитовая операция исключения, а не простое вычитание. Разница проявляется если у вас нестандартные значения: например, 666 минус 033 математически даст 633, но побитовое исключение даст 644. Для стандартных значений (022, 027, 077) простое вычитание даёт верный результат и удобнее для понимания.
# Проверить на практике
umask 022
touch newfile.txt && ls -la newfile.txt
# -rw-r--r-- 1 ivan ivan 0 Mar 26 newfile.txt ← 644
mkdir newdir && ls -la | grep newdir
# drwxr-xr-x 2 ivan ivan 4096 Mar 26 newdir ← 755
Типичные значения
| umask |
Файлы |
Директории |
Когда применять |
022 |
644 |
755 |
Стандарт – публичный доступ на чтение |
027 |
640 |
750 |
Усиленный – others вообще без доступа |
077 |
600 |
700 |
Параноидальный – только владелец |
Где настраивается
# Для текущей сессии
umask 027
# Глобально для всех пользователей
grep UMASK /etc/login.defs
# UMASK 022
# Для конкретного пользователя
echo "umask 027" >> ~/.bashrc
# Для сервиса через systemd unit-файл
# [Service]
# UMask=0027
Бонус и Заключение
Мы рассмотрели всё начиная от базовой модели UGO и битов rwx до алгоритма принятия решений ядром, специальных битов и ACL. Отдельно хочу поделиться полезным материалом на YouTube от Дмитрия с заданиями для самостоятельной отработки навыков, очень полезно тем, кто только начинает учить Linux.
Увидимся в следующей части серии!