Модель прав доступа 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Файлы получаютДиректории получаютГде используется
0022644 (rw-r—r—)755 (rwxr-xr-x)Стандарт для десктопа
0027640 (rw-r-----)750 (rwxr-x---)Серверы (группа читает, others нет)
0077600 (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:

  1. sudo проверяет /etc/sudoers — разрешено ли пользователю
  2. Запрашивает пароль пользователя (не root)
  3. Запускает 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

sudosu
ПарольПользователяЦелевого (обычно 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 Cap

Capabilities — причина, почему 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--, нет xchmod +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