rwx-права — девять битов, которые управляют доступом к каждому файлу
Когда вы делаете ls -l и видите -rw-r--r-- — это девять битов в inode файла, которые kernel читает миллиарды раз в день. На каждом open(), на каждом read(), на каждом cd — kernel смотрит эти биты и решает «можно или нельзя». Понимание этой системы — абсолютная база Unix-мира, и одновременно одна из самых частых причин «у меня permission denied», на которые тратятся часы.
В этом уроке разберём: как kernel принимает решение, что значат rwx для разных типов файлов, как chmod конвертирует число в биты, и что делают волшебные setuid/setgid/sticky.
Девять битов и кому они соответствуют
Каждый файл в Linux имеет:
- Владельца (user, owner) — один UID.
- Группу — один GID.
- Девять битов доступа — три набора по три (r, w, x).
Девять битов делятся на три категории:
-rwxr-xr-x
^^^^^^^^^
u g o
- u (user, owner) — права владельца.
- g (group) — права группы файла.
- o (other) — права всех остальных.
Когда процесс пытается открыть файл, kernel выполняет проверку:
- Если EUID процесса == UID владельца файла — проверяет биты u.
- Иначе, если GID файла есть среди групп процесса (primary или supplementary) — проверяет биты g.
- Иначе — проверяет биты o.
Важно: только ОДНА проверка. Если вы владелец и u-r не стоит, вы НЕ читаете файл, даже если o-r стоит. Это часто непонимают: «но other может читать, почему я не могу?» Потому что для kernel ваш категория — owner, и эта категория должна разрешить.
Root (UID=0) — исключение. Он игнорирует обычные проверки прав (кроме особых случаев типа immutable bit и capabilities).
Что значат r, w, x для файлов
Для обычного файла:
- r (read) — можно читать содержимое (
cat,less,cp). - w (write) — можно изменять содержимое (
echo > file, редактор сохраняет). - x (execute) — можно запускать как программу (
./script.sh,./binary).
Несколько неочевидных моментов:
- w без r — редко полезно. Можно затереть, но не прочитать. Используется в логах, куда сервис должен только append’ить.
- x на скрипте. Нужны и r, и x. Чтобы выполнить bash-скрипт, shell должен его прочитать.
- x на бинарнике. Достаточно только x (kernel сам читает бинарник через специальный канал, не через права).
- Удалить файл — это W на ДИРЕКТОРИИ, не на файле. См. ниже.
# Создать файл и поиграть с правами:
echo "secret" > /tmp/secret.txt
ls -l /tmp/secret.txt
# -rw-r--r-- 1 levoely levoely 7 May 18 12:00 /tmp/secret.txt
# Убрать чтение для других:
chmod o-r /tmp/secret.txt
ls -l /tmp/secret.txt
# -rw-r----- 1 levoely levoely 7 May 18 12:00 /tmp/secret.txt
# Группа всё ещё читает, others -- нет
# Полностью приватный (только мне):
chmod 600 /tmp/secret.txt
ls -l /tmp/secret.txt
# -rw------- 1 levoely levoely 7 May 18 12:00 /tmp/secret.txt
Что значат r, w, x для директорий
Для директорий значение битов меняется:
- r (read) — можно ПОЛУЧИТЬ СПИСОК файлов (
ls dir/). Без r вы не увидите, какие файлы там есть. - w (write) — можно СОЗДАВАТЬ и УДАЛЯТЬ файлы в этой директории (даже не свои!).
- x (execute) — можно ВОЙТИ в директорию (
cd) и обращаться к файлам по имени.
Это контр-интуитивно: чтобы удалить файл secret.txt, не нужны права на сам файл — нужны w на директории, в которой он лежит. Потому что «удалить» — это убрать запись из директории, не из файла.
mkdir /tmp/test
cd /tmp/test
touch myfile
chmod 000 myfile # никаких прав на файл!
ls -l myfile
# ---------- 1 levoely levoely 0 May 18 12:00 myfile
cat myfile # cannot open: permission denied (нет r)
echo "x" > myfile # permission denied (нет w)
rm myfile # СРАБОТАЕТ! Потому что у вас w на /tmp/test
Сочетание x без r на директории — интересный кейс. Вы можете обращаться к файлу по имени (если знаете), но ls запрещён. Иногда используется для shared-директорий, где никто не должен видеть, какие там файлы, но конкретные адреса работают:
chmod 711 /tmp/private # x для всех, r только владельцу
touch /tmp/private/known_file
# Из другого аккаунта:
ls /tmp/private # permission denied
cat /tmp/private/known_file # works! если файл доступен сам
Восьмеричная запись chmod
Девять битов — это число от 0 до 0777 в восьмеричной. Каждая тройка rwx — одна восьмеричная цифра:
| Биты | Восьмеричное | Что значит |
|---|---|---|
--- | 0 | ничего |
--x | 1 | только execute |
-w- | 2 | только write |
-wx | 3 | write + execute |
r-- | 4 | только read |
r-x | 5 | read + execute |
rw- | 6 | read + write |
rwx | 7 | всё |
Часто встречающиеся комбинации:
| Mode | Смысл |
|---|---|
644 | rw-r—r— — обычный файл, владелец редактирует, все читают |
600 | rw------- — приватный файл (SSH ключи, токены) |
755 | rwxr-xr-x — исполняемая программа или директория |
700 | rwx------ — приватная директория (например, ~/.ssh) |
777 | rwxrwxrwx — НИКОГДА в production |
666 | rw-rw-rw- — почти всегда плохая идея |
# Восьмеричной записью:
chmod 644 file.txt
chmod 755 script.sh
# Символьной (mnemonic):
chmod u+x script.sh # добавить execute владельцу
chmod g-w file.txt # убрать write у группы
chmod o=r file.txt # set: only read у others
chmod a+r file.txt # all (u+g+o): добавить read
‘chmod 777’ встречается в туториалах в стиле «помогло, теперь работает». Это маскировка проблемы. Любой пользователь в системе сможет переписать файл — катастрофа в production. Правильный путь: понять, какой UID/GID должен иметь доступ, и дать минимальные нужные права.
Setuid: программа выполняется от имени владельца
Бывают ситуации, когда обычному пользователю нужно временно получить root-права для конкретной операции. Классический пример — passwd: пользователь должен изменить свой пароль, который хранится в /etc/shadow (доступен только root для записи). Как это работает?
/usr/bin/passwd имеет специальный бит setuid (SUID). При запуске такой программы effective UID процесса становится UID владельца файла, а не запустившего:
ls -l /usr/bin/passwd
# -rwsr-xr-x 1 root root 68208 May 18 12:00 /usr/bin/passwd
# ^ маленькая s вместо x в позиции u-x = setuid bit
s в позиции execute владельца — это SUID. Файл принадлежит root, и при запуске EUID процесса становится 0 (root), хотя RUID остаётся вашим. Поэтому passwd может модифицировать /etc/shadow.
Это мощный механизм, но опасный. Каждая setuid-программа — потенциальная дыра в безопасности. Если в ней есть buffer overflow или command injection, атакующий получает права владельца (часто root).
# Найти все setuid-программы в системе:
find / -perm -4000 -type f 2>/dev/null
# Список обычно небольшой: passwd, sudo, ping, mount, su, ...
# Установить SUID:
chmod u+s /path/to/program # или
chmod 4755 /path/to/program # 4 = setuid
# Снять:
chmod u-s /path/to/program
В современном Linux setuid постепенно заменяют capabilities (следующий урок). Например, ping исторически был setuid root (чтобы открывать raw socket), теперь чаще имеет capability CAP_NET_RAW — более точечное право.
Setgid: программа выполняется от имени группы файла
Аналогично, но для группы. SGID-бит на исполняемом файле делает EGID = GID файла при запуске.
s в позиции execute группы:
ls -l /usr/bin/wall
# -rwxr-sr-x 1 root tty 36320 May 18 12:00 /usr/bin/wall
# ^ s вместо x на g
Wall (write-to-all-users) имеет EGID=tty при запуске — получает право писать в терминалы всех пользователей.
Setgid на ДИРЕКТОРИИ имеет другой смысл: новые файлы в этой директории получают её GID, а не primary GID создателя. Это удобно для shared-папок проекта:
mkdir /shared
sudo chown :developers /shared
chmod 2775 /shared # 2 = setgid в начале
# Теперь любой файл, созданный в /shared, имеет GID=developers,
# даже если создавший пользователь имеет primary group 'levoely'
Sticky bit: только владелец удалит свой файл
Помните: чтобы удалить файл, нужно w на ДИРЕКТОРИИ, а не на файле. Это значит, что в shared-директории с правами 777 кто угодно может удалить чужие файлы. Решение — sticky bit.
С sticky bit на директории файл может удалить только владелец файла (или владелец директории, или root). Видно как t в позиции execute для others:
ls -ld /tmp
# drwxrwxrwt 28 root root 4096 May 18 12:00 /tmp
# ^ t = sticky bit
/tmp — классический пример. Любой может создавать файлы, но удалять только свои. Без sticky любой пользователь смог бы удалить чужой /tmp/foo.
# Установить sticky:
chmod +t /shared/dir # или
chmod 1777 /shared/dir
# 1 = sticky в первой позиции
# Проверить:
ls -ld /shared/dir
Все 12 бит вместе
Полный набор: 9 базовых rwx + setuid + setgid + sticky. Кодируется четырёхзначным восьмеричным числом:
- Первая цифра — setuid (4) + setgid (2) + sticky (1).
- Остальные — стандартные u, g, o.
chmod 4755 /usr/bin/passwd # setuid + 755
chmod 2775 /shared/projects # setgid + 775
chmod 1777 /tmp # sticky + 777
chmod 0644 /etc/hosts # обычный (можно и просто 644)
Реальный пример отладки permission denied
# Что делать, если 'cat file.txt' даёт permission denied:
# 1. Что за файл -- права и владелец:
ls -l file.txt
# -rw-r----- 1 alice developers 245 May 18 12:00 file.txt
# 2. Какой у меня UID и группы:
id
# uid=1000(levoely) gid=1000(levoely) groups=1000(levoely),27(sudo)
# 3. Проверка:
# - я owner? Нет (1000 != UID alice)
# - я в группе developers? Нет (developers не среди моих групп)
# - other rwx? --- (нет read для others)
# Результат: permission denied -- правильно.
# 4. Что я могу сделать:
# (a) Попросить alice сделать chmod o+r file.txt
# (b) Попросить admin'а добавить меня в группу developers
# (c) Если есть sudo -- 'sudo cat file.txt' (как root, видит всё)
# 5. Если файл должен быть доступен:
sudo chown alice:developers file.txt # установить владельца
sudo chmod 640 file.txt # u=rw, g=r, o=
sudo usermod -aG developers levoely # добавить меня в группу
# Перелогиниться, чтобы новая группа применилась к shell
Попробуй сам
# 1. Создать файл и посмотреть права:
touch /tmp/exp.txt
ls -l /tmp/exp.txt
# Какой umask определил начальные права?
umask # обычно 022 -> файлы по умолчанию 644
# 2. Поиграть с chmod:
chmod 600 /tmp/exp.txt
ls -l /tmp/exp.txt
chmod 644 /tmp/exp.txt
chmod a+x /tmp/exp.txt # хотя это не скрипт, можно дать execute
# 3. Тест удаления через директорию:
mkdir /tmp/td
echo "important" > /tmp/td/file
chmod 000 /tmp/td/file
cat /tmp/td/file # permission denied
rm /tmp/td/file # сработает, у вас w на /tmp/td
ls /tmp/td # пусто
# 4. Найти setuid-программы:
find /usr/bin /usr/sbin -perm -4000 -type f 2>/dev/null
# 5. Посмотреть права /tmp:
ls -ld /tmp
# Обратите внимание на 't' в конце
# 6. Эксперимент с sticky:
mkdir /tmp/shared
chmod 1777 /tmp/shared
ls -ld /tmp/shared # drwxrwxrwt
# Создайте файл, попросите другого пользователя его удалить -- не получится
# 7. Найти все доступные для записи файлы в /etc (потенциальные риски):
find /etc -type f -perm -o=w 2>/dev/null
# Хорошо настроенная система покажет пустой результат