Модель прав доступа Linux
TL;DR: Каждый файл имеет владельца (user), группу (group) и права для остальных (other). Ядро проверяет права при каждом системном вызове: сначала по uid (совпал — применяет user-биты), потом по gid (group-биты), иначе — other. root (uid=0) обходит все проверки.
umaskопределяет какие права снимаются при создании файлов.
Зачем это знать
Модель прав — это механизм, которым ядро решает, разрешить или отказать в операции. Это не абстракция уровня приложения — каждый open(), exec(), chdir() проходит через проверку прав в ядре. Без понимания модели невозможно ответить на вопросы:
- Почему процесс nginx (работающий от
www-data) не может читать файл с правами600 root:root - Почему
passwdможет менять/etc/shadow, хотя запущен обычным пользователем - Почему
umask 077на сервере — не паранойя, а базовая гигиена
Владелец, группа, остальные
Каждый файл (и директория — тоже файл) хранит в inode три набора атрибутов:
-rwxr-x--- 1 john developers 4096 Feb 18 12:00 deploy.sh
│├─┤├─┤├─┤ │ │
│ u g o uid gid
│
тип файла
- user (u) — права владельца файла (uid, хранится в inode)
- group (g) — права группы файла (gid, хранится в inode)
- other (o) — права для всех остальных
Подробно о форматах uid/gid, файлах /etc/passwd, /etc/shadow, /etc/group — в user-files.
Числовые значения и таблица octal-кодов — в permissions-table.
Как ядро проверяет права
При каждом системном вызове (open, read, write, exec, chdir) ядро выполняет проверку в строгом порядке:
Процесс вызывает open("deploy.sh", O_RDONLY)
│
▼
1. effective uid процесса == 0 (root)?
└── да → РАЗРЕШЕНО (root обходит все проверки)
│ нет
▼
2. effective uid процесса == uid файла?
└── да → применить user-биты (rwx первой тройки)
│ нет
▼
3. effective gid процесса или supplementary groups == gid файла?
└── да → применить group-биты (rwx второй тройки)
│ нет
▼
4. Применить other-биты (rwx третьей тройки)
Важные следствия:
- Проверка останавливается на первом совпадении. Если uid совпал — group и other биты не проверяются, даже если они дают больше прав
- Владелец файла может иметь меньше прав, чем группа.
chmod 074 file— владелец не может ничего, группа может читать и выполнять
# Демонстрация: владелец без прав, группа с правами
$ chmod 074 secret.sh
$ ls -l secret.sh
----rwxr-- 1 john developers 100 Feb 18 12:00 secret.sh
$ whoami
john
$ cat secret.sh
cat: secret.sh: Permission denied
# uid совпал → применены user-биты (---) → отказ
# group-биты (rwx) даже не проверяютсяПрава на файлы vs директории
Одни и те же биты означают разное для файлов и директорий:
| Бит | Файл | Директория |
|---|---|---|
r | Читать содержимое (cat, open) | Листать содержимое (ls, readdir) |
w | Изменять содержимое | Создавать и удалять файлы внутри |
x | Выполнять как программу | Входить (cd) и обращаться к файлам по имени |
Неочевидный момент с директориями: r без x позволяет увидеть имена файлов, но не их атрибуты и не открыть их. x без r позволяет обратиться к файлу по точному имени, но не листать директорию.
$ chmod 711 mydir/ # owner=rwx, group/other=--x
$ ls mydir/ # Permission denied (нет r)
$ cat mydir/known-file.txt # Работает (есть x, имя файла известно)Удаление файла — это запись в директорию (удаление записи из неё), а не операция над файлом. Поэтому для удаления файла нужен w на директории, а не на самом файле.
umask — права по умолчанию
При создании файла процесс запрашивает набор прав (обычно 0666 для файлов, 0777 для директорий). Ядро вычитает из них umask:
результат = запрошенные права AND NOT umask
$ umask
0022
# Создание файла: 0666 AND NOT 0022 = 0644 (rw-r--r--)
$ touch newfile
$ ls -l newfile
-rw-r--r-- 1 john john 0 Feb 18 12:00 newfile
# Создание директории: 0777 AND NOT 0022 = 0755 (rwxr-xr-x)
$ mkdir newdir
$ ls -ld newdir
drwxr-xr-x 2 john john 4096 Feb 18 12:00 newdir| umask | Файлы получают | Директории получают | Где используется |
|---|---|---|---|
0022 | 644 (rw-r—r—) | 755 (rwxr-xr-x) | Стандарт для десктопа |
0027 | 640 (rw-r-----) | 750 (rwxr-x---) | Серверы (группа читает, others нет) |
0077 | 600 (rw-------) | 700 (rwx------) | Максимальная изоляция |
# Установить umask для текущей сессии
umask 0027
# Постоянно — в ~/.bashrc или ~/.profile
echo "umask 0027" >> ~/.bashrc
# Для сервисов — в unit-файле systemd
[Service]
UMask=0027Важно:
umask— это маска снятия битов, а не установки.umask 077не означает «права 077» — означает «убрать rwx у group и other». Результат для файлов:600, для директорий:700.
Real UID vs Effective UID
Каждый процесс имеет несколько идентификаторов:
| ID | Назначение |
|---|---|
| Real UID (ruid) | Кто запустил процесс (не меняется) |
| Effective UID (euid) | От чьего имени проверяются права (может отличаться от ruid) |
| Saved UID (suid) | Сохранённый euid для возможности вернуться |
В обычной ситуации ruid == euid. Расхождение возникает при SUID-битах и sudo.
Зачем это нужно: пример с passwd
$ ls -l /usr/bin/passwd
-rwsr-xr-x 1 root root 68208 Feb 18 12:00 /usr/bin/passwd
# ^
# s = SUID битКогда обычный пользователь (uid=1000) запускает passwd:
ruid = 1000 (john) — кто запустил
euid = 0 (root) — SUID бит: euid = uid владельца файла
Ядро проверяет права по euid. Поэтому passwd может писать в /etc/shadow (владелец root, права 640) — euid процесса равен 0.
Это контролируемое повышение привилегий: программа /usr/bin/passwd написана так, чтобы менять только пароль вызвавшего пользователя, хотя технически имеет доступ root.
Специальные биты
SUID (Set User ID)
При выполнении файла effective uid процесса устанавливается равным uid владельца файла, а не uid запустившего.
chmod u+s program # или chmod 4755 program
ls -l program
-rwsr-xr-x 1 root root ...
# ^ s вместо xНа практике SUID используется только для нескольких системных утилит: passwd, su, ping (в старых системах), mount. Устанавливать SUID на свои программы — серьёзный риск: любая уязвимость даёт злоумышленнику euid=0.
SGID (Set Group ID)
На файле: effective gid процесса = gid файла (аналогично SUID, но для группы).
На директории: файлы, создаваемые внутри, наследуют группу директории, а не основную группу пользователя. Полезно для общих рабочих директорий:
# Без SGID
$ mkdir shared && chown :developers shared
$ touch shared/file.txt
$ ls -l shared/file.txt
-rw-r--r-- 1 john john ... # группа john (основная)
# С SGID
$ chmod g+s shared/ # или chmod 2775 shared/
$ touch shared/file2.txt
$ ls -l shared/file2.txt
-rw-r--r-- 1 john developers ... # группа developers (от директории)Sticky bit
На директории: файл может удалить только его владелец (или root), даже если у других есть w на директорию. Классический пример — /tmp:
$ ls -ld /tmp
drwxrwxrwt 15 root root 4096 Feb 18 12:00 /tmp
# ^
# t = sticky bitБез sticky bit любой пользователь мог бы удалять чужие файлы в /tmp (права 777 дают w на директорию всем).
sudo и модель привилегий
sudo — механизм контролируемого повышения привилегий. При выполнении sudo command:
sudoпроверяет/etc/sudoers— разрешено ли пользователю- Запрашивает пароль пользователя (не root)
- Запускает command с
euid=0
Файл /etc/sudoers (редактировать только через visudo — валидирует синтаксис):
# Формат: кто откуда=(от_кого) что
john ALL=(ALL) ALL # john может всё, с паролем
deploy ALL=(ALL) NOPASSWD: ALL # deploy может всё, без пароля
%sudo ALL=(ALL) ALL # группа sudo может всё
john ALL=(ALL) NOPASSWD: /bin/systemctl restart nginx # только одна команда
sudo vs su
| sudo | su | |
|---|---|---|
| Пароль | Пользователя | Целевого (обычно root) |
| Гранулярность | Можно ограничить команды | Всё или ничего |
| Аудит | Логирует кто и что выполнил | Только факт входа |
| Рекомендация | Предпочтительнее | Для полной сессии root |
Linux Capabilities
Традиционная модель: обычный пользователь или root (uid=0) со всеми правами. Capabilities разбивают «всемогущество» root на гранулярные разрешения:
| Capability | Что разрешает |
|---|---|
CAP_NET_BIND_SERVICE | Привязка к портам < 1024 |
CAP_NET_RAW | Сырые сокеты (ping, tcpdump) |
CAP_SYS_ADMIN | Широкий набор админских операций |
CAP_CHOWN | Смена владельца файлов |
CAP_DAC_OVERRIDE | Обход проверки прав на файлы |
CAP_SETUID | Смена uid процесса |
# Вместо SUID — дать конкретную capability
sudo setcap cap_net_bind_service=ep /opt/app/server
# Посмотреть capabilities бинарника
getcap /opt/app/server
# /opt/app/server cap_net_bind_service=ep
# Посмотреть capabilities процесса
cat /proc/<pid>/status | grep CapCapabilities — причина, почему ping в современных дистрибутивах работает без SUID:
$ ls -l /usr/bin/ping
-rwxr-xr-x 1 root root ... # нет SUID
$ getcap /usr/bin/ping
/usr/bin/ping cap_net_raw=ep # capability вместо SUIDПодводные камни
| Проблема | Симптом | Решение |
|---|---|---|
| Владелец файла без прав, группа с правами | «Permission denied» для владельца | Ядро останавливается на первом совпадении uid. Исправить user-биты: chmod u+rw file |
chmod 777 как «решение» | Работает, но даёт всем полные права | Разобраться какие права реально нужны, использовать минимально необходимые |
| Новые файлы создаются с широкими правами | umask=0000 или 0002 | Установить umask 0027 в профиле или unit-файле |
| Скрипт не выполняется | -rw-r--r--, нет x | chmod +x script.sh |
| SUID на shell-скрипте | Не работает (ядро игнорирует SUID на скриптах) | Использовать sudo или capabilities |
Нет доступа к файлу, хотя есть r | Нет x на родительской директории | chmod +x parent_dir/ |
Связанные материалы
- user-files — /etc/passwd, /etc/shadow, /etc/group, формат полей, NSS
- process-model — PID, uid процесса, наследование при fork/exec
- permissions-table — octal-таблица, chmod, chown, типичные комбинации
- manage-users — useradd, usermod, группы, sudo, passwd