Три буквы - r, w, x - и число 755. Для кого-то набор символов, для кого-то язык, на котором Linux разговаривает о безопасности. Модель прав унаследована от UNIX семидесятых, и с тех пор принцип не изменился: у каждого файла есть владелец, группа и набор разрешений. Просто? Да. Достаточно? Если знать нюансы - вполне.
А нюансов хватает: специальные биты, расширенные списки доступа, атрибуты файловой системы, взаимодействие с системами мандатного контроля. Разберём, как работают chmod и chown, как читать права в обе стороны, что ставить на типовые каталоги и почему 777 - не решение, а начало проблем.
Если коротко
Команда ls -l выводит строку вида:
-rwxr-xr-- 1 admin webdev 4096 Jan 15 09:30 deploy.sh
Первые десять символов - это тип файла плюс три тройки разрешений. Разбираем по частям:
| Позиция | В примере | Что означает |
|---|---|---|
| 1-й символ | - | Тип: - обычный файл, d каталог, l символическая ссылка, c и b устройства, s сокет, p именованный канал |
| Символы 2-4 | rwx | Права владельца (admin): чтение, запись, выполнение |
| Символы 5-7 | r-x | Права группы (webdev): чтение и выполнение, записи нет |
| Символы 8-10 | r-- | Права всех остальных: только чтение |
Половина вопросов про права звучит как «а что означает вот это». Перевод делается в уме за несколько секунд, если помнить веса: r = 4, w = 2, x = 1. Внутри каждой тройки складываем то, что есть, и получаем одну цифру.
Для -rwxr-x--- считаем так:
rwx = 4 + 2 + 1 = 7 (владелец)
r-x = 4 + 0 + 1 = 5 (группа)
--- = 0 + 0 + 0 = 0 (остальные)
Итого: 750
Обратно так же: 640 разворачивается в 6 = 4+2 = rw-, 4 = r--, 0 = ---, то есть rw-r-----.
Две буквы выбиваются из схемы. Если вместо x стоит s - на файле специальный бит (SUID или SGID), если t в последней тройке - sticky bit. Строчная буква означает, что бит выполнения тоже стоит, заглавная (S или T) - что специальный бит есть, а выполнения нет; обычно это ошибка. Разбор специальных битов - ниже.
| Число | Разрешения | Где применяется |
|---|---|---|
| 777 | rwxrwxrwx | Полный доступ всем. На рабочем сервере - почти всегда ошибка |
| 755 | rwxr-xr-x | Скрипты, бинарники, обычные каталоги |
| 750 | rwxr-x--- | Каталог для своей группы, посторонним закрыт |
| 700 | rwx------ | Домашние каталоги, ~/.ssh |
| 664 | rw-rw-r-- | Файл, который правит вся группа |
| 644 | rw-r--r-- | Конфиги, статика веб-сервера |
| 640 | rw-r----- | Секреты для группы: /etc/shadow, пароли в конфигах |
| 600 | rw------- | Приватные ключи SSH |
| 2775 | rwxrwsr-x | Каталог совместной работы с SGID |
| 1777 | rwxrwxrwt | /tmp: пишут все, удаляют только своё |
Четырёхзначная запись - это те же три тройки плюс ведущая цифра специальных битов. Ноль впереди ничего не меняет: 0644 и 644 - одно и то же.
Октальная запись задаёт все девять бит разом, символьная правит отдельный бит и не трогает остальные. Команда chmod g+w config.yml добавит группе запись, ничего больше не меняя, а chmod o-rwx закроет доступ всем посторонним. Для точечных правок это безопаснее: октальное число легко выставить целиком и случайно снять то, что было нужно.
Команда chown меняет владельца и группу. Синтаксис простой:
chown user:group filename
chown -R www-data:www-data /var/www/html
Здесь ходит распространённое заблуждение, что chown -R провалится по символической ссылке и перепишет владельца половине системы. Это не так, и знать точный механизм полезно.
При рекурсивном обходе chown не заходит в каталоги, на которые указывают символические ссылки: режим -P (не разыменовывать) включён по умолчанию. Чтобы обход пошёл по ссылкам, нужен явный флаг -L, и вот его в рекурсии как раз стоит избегать - между проверкой и обработкой каталога кто-то может подменить ссылку.
Что действительно происходит: встретив символическую ссылку, chown без флага -h меняет владельца у цели ссылки. Если внутри /var/www/html лежит ссылка на /etc, владелец сменится у самого каталога /etc, но не у файлов внутри него. Неприятно, но это не катастрофа из страшилок. Флаг -h (или --no-dereference) заставляет менять владельца самой ссылки, а не того, куда она ведёт.
# файлы 644, каталоги 755, владелец www-data
find /var/www/html -type f -exec chmod 644 {} +
find /var/www/html -type d -exec chmod 755 {} +
chown -R www-data:www-data /var/www/html
Разделение по типу здесь обязательно: одинаковые права на файлы и каталоги либо сделают исполняемым весь контент, либо закроют вход в каталоги. Ключ + вместо \; в конце передаёт find сразу пачку аргументов и на больших деревьях работает заметно быстрее.
Две команды стоят рядом в любой инструкции, и это регулярно приводит к тому, что вместо настройки владельца люди правят разрешения (или наоборот). Разница простая: chown отвечает на вопрос «чьё», chmod - на вопрос «кому что можно».
| chown | chmod | |
|---|---|---|
| Что меняет | Владельца и группу файла | Разрешения для владельца, группы и остальных |
| Кто может выполнить | Владельца меняет только root. Группу владелец может сменить сам, но лишь на ту, в которой состоит | Владелец файла и root |
| Формат аргумента | Имя пользователя и группы: www-data:www-data | Число или символы: 644, g+w |
| Типичная задача | Передать каталог приложению после развёртывания | Закрыть конфиг с паролем от посторонних |
Практическое правило: сначала разбираются, кто должен работать с файлом, и приводят в порядок владельца и группу. И только потом раздают разрешения. Обратный порядок и приводит к желанию выставить 777 - когда владелец неправильный, никакие разрешения не выглядят достаточными.
Вопрос «а сколько поставить вот сюда» возникает чаще любого другого. Ориентиры для типовых путей:
| Путь | Права | Владелец | Почему так |
|---|---|---|---|
| /etc/shadow | 640 или 000 | root:shadow или root:root | Зависит от дистрибутива, см. предупреждение под таблицей |
| /etc/passwd | 644 | root:root | Читают все процессы, пишет только root |
| ~/.ssh | 700 | пользователь | При более широких правах sshd откажется работать с ключами |
| ~/.ssh/id_rsa | 600 | пользователь | То же: приватный ключ виден только владельцу |
| ~/.ssh/authorized_keys | 600 | пользователь | Запись для группы или для всех - и sshd откажется принимать ключ |
| /var/www/html (каталоги) | 755 | www-data | Веб-сервер заходит внутрь, но не пишет |
| /var/www/html (файлы) | 644 | www-data | Читает, не выполняет |
| Каталог загрузок приложения | 770 | app:www-data | Запись нужна - выдаём её группе, а не всему миру |
| Каталог совместной работы | 2770 | root:отдел | SGID: новые файлы наследуют группу каталога |
| /tmp | 1777 | root:root | Sticky bit: пишут все, удаляют только своё |
| Скрипт в /opt или /usr/local/bin | 755 | root:root | Запускают все, правит только root |
| Конфиг с паролем к БД | 640 | root:приложение | Приложение читает через группу, посторонние не видят |
Общие каталоги вроде /opt и /var отдельного «правильного» числа не имеют: смотреть надо на конкретный подкаталог и на то, какой службе он принадлежит. Сам /var штатно идёт с 755 и владельцем root, а вот /var/log/приложение уже принадлежит службе.
/etc/shadow: не приводите к одному числу
Это тот случай, когда «правильное» значение зависит от семейства дистрибутивов. В Debian и Ubuntu файл идёт с правами 640 и владельцем root:shadow: его читает служебная группа. В RHEL, Rocky, AlmaLinux и Fedora - 000 и root:root, то есть не читает никто, а root обходит проверку прав по определению.
Отсюда практический вывод: увидев на RHEL-системе нули, не «чините» их на 640 - такая правка файл ослабит. Сверяйтесь с тем, что ставит ваш дистрибутив, а не с числом из статьи.
Перед тем как менять права на системном пути, посмотрите, что там сейчас: ls -ld /путь покажет права и владельца самого каталога, а не его содержимого. Дистрибутив мог выставить их осознанно, и «исправление» сломает работающую службу.
Когда что-то не работает, появляется соблазн выставить 777 и разобраться потом. Потом обычно не наступает, а 777 означает, что любой процесс в системе может читать, писать и выполнять файл. Включая скомпрометированный PHP-скрипт, чужой cron и всё, что запустится от другого пользователя.
На рабочих серверах это прямое нарушение принципа наименьших привилегий. Если атакующий получил выполнение кода через уязвимость в веб-приложении, он работает от имени www-data. С правами 777 ему доступна запись куда угодно: подменить скрипты, залить веб-шелл, поправить конфиг базы. С 644 на файлах и 755 на каталогах тот же процесс может только читать.
Каталоги с 777 стоят первыми в списке того, что ищут сканеры уязвимостей, и один из первых пунктов в любом чек-листе при подготовке инфраструктуры к ИБ-аудиту.
Что делать вместо этого: разобраться, какому пользователю и группе нужен доступ. Обычно вопрос закрывается правильным chown и парой 755/644. Если приложению нужно писать в конкретный каталог (загрузки, кэш, сессии), создайте отдельный каталог с правами 770 и добавьте веб-сервер в нужную группу. Общесистемные принципы разобраны отдельно - в материале про защиту системы на Linux.
Кроме девяти основных бит есть три специальных, которые меняют поведение файлов и каталогов.
Исполняемый файл с битом SUID запускается с правами владельца, а не того, кто его вызвал. Канонический пример - утилита смены пароля:
ls -l /usr/bin/passwd
-rwsr-xr-x 1 root root 64152 May 30 2024 /usr/bin/passwd
Буква s вместо x в блоке владельца - признак SUID. Благодаря ему обычный пользователь меняет свой пароль, хотя /etc/shadow ему недоступен. Обратная сторона очевидна: SUID на скомпрометированном бинарнике превращается в root-шелл.
Важный нюанс: на скриптах SUID не работает. Ядро Linux игнорирует этот бит для файлов, запускаемых через интерпретатор, и это сознательное решение - слишком легко подсунуть интерпретатору не то. Если скрипту нужны повышенные права, используют sudo с точечным правилом.
Аудит SUID-файлов стоит делать регулярно:
find / -type f -perm /4000 2>/dev/null
На каталоге SGID работает иначе: все файлы, созданные внутри, наследуют группу каталога, а не основную группу пользователя. Незаменимо для совместной работы:
mkdir /opt/project
chown :developers /opt/project
chmod 2770 /opt/project
Теперь любой файл, созданный в /opt/project, автоматически принадлежит группе developers. Без SGID каждому пришлось бы менять группу руками на каждом файле.
На каталоге sticky bit запрещает удалять чужие файлы, даже если право на запись есть. Классика - /tmp:
ls -ld /tmp
drwxrwxrwt 15 root root 4096 Feb 15 10:00 /tmp
Буква t в конце и есть sticky bit. Без него любой пользователь мог бы стирать чужие временные файлы.
| Бит | Числовой префикс | Как поставить | Где применяется |
|---|---|---|---|
| SUID | 4 | chmod u+s или chmod 4755 | Исполняемые файлы (passwd, ping) |
| SGID | 2 | chmod g+s или chmod 2770 | Каталоги совместной работы |
| Sticky | 1 | chmod +t или chmod 1777 | /tmp и другие общие каталоги |
chmod и chown управляют доступом, но от root не спасают: суперпользователь переставит любые разрешения. Утилита chattr работает уровнем ниже - на атрибутах файловой системы (ext2/ext3/ext4, btrfs, XFS) - и добавляет защиту, которую root не обойдёт, пока явно не снимет атрибут.
# сделать файл неизменяемым
chattr +i /etc/hosts
# разрешить только дописывание
chattr +a /var/log/app/audit.log
# посмотреть атрибуты
lsattr /etc/hosts
Атрибут +i делает файл неизменяемым: его нельзя удалить, переименовать, изменить или сделать на него жёсткую ссылку. Атрибут +a разрешает только добавление данных - логика для журналов, которые должны расти, но не должны быть подчищены. Попытка обрезать такой файл через перенаправление вернёт «Operation not permitted». В обычном выводе ls -l атрибуты не видны, показывает их только lsattr.
Две ловушки, на которых спотыкаются чаще всего
/etc/resolv.conf «замораживать» бесполезно. На системах с systemd-resolved или NetworkManager это обычно символическая ссылка на файл в /run, и chattr либо откажется работать, либо вы получите тихо сломанное обновление DNS. Если резолвер переписывают - отключайте того, кто это делает, а не запирайте файл.
+a на ротируемом журнале ломает ротацию. logrotate не сможет ни переименовать файл, ни обрезать его, и записи просто перестанут ротироваться. Ставить +a имеет смысл на журналы, у которых ротация выключена или настроена через copytruncate с учётом атрибута.
И честная оговорка: chattr не серебряная пуля. Злоумышленник с root снимет атрибут командой chattr -i. Но от случайных ошибок, автообновлений и скриптов, которые лезут не туда, защищает надёжно.
Модель «владелец - группа - остальные» закрывает большую часть задач. Остальное - когда нужно дать доступ конкретному пользователю без смены группы или когда у каталога два отдела-потребителя с разными уровнями доступа.
Для этого есть POSIX ACL, управляемые через setfacl и getfacl. Они работают поверх стандартных разрешений:
# дать пользователю deployer доступ на чтение и запись
setfacl -m u:deployer:rw /etc/app/config.yml
# дать группе auditors только чтение
setfacl -m g:auditors:r /var/log/app.log
# посмотреть, что получилось
getfacl /etc/app/config.yml
Файл с ACL помечается знаком + в выводе ls -l - например, -rw-rw-r--+ 1 root root 2048 Jan 10 config.yml. Если видите плюс и не понимаете, почему доступ работает не так, как ожидалось, - смотрите getfacl.
Default ACL задаёт права, которые получат все новые объекты в каталоге:
setfacl -d -m g:devops:rwx /shared
Здесь x нужен и не опасен. Итоговые права нового объекта - это пересечение того, что запросила создающая программа, и default ACL. Файлы создаются без запроса на выполнение, поэтому исполняемыми они не станут; а вот новые подкаталоги бит выполнения получат, и в них можно будет зайти. С rw вместо rwx каждый созданный подкаталог оказался бы закрыт для группы.
А вот с рекурсивным применением к уже существующим файлам есть тонкость, из-за которой доступ регулярно «выдают, а он не работает».
Для рекурсии нужна заглавная X, а не строчная x
Права rw на каталоге означают, что в него нельзя войти: за вход отвечает бит выполнения. Команда setfacl -R -m g:devops:rw /shared выдаст группе доступ, которым нельзя воспользоваться. Правильная форма - rwX: заглавная X ставит бит выполнения только каталогам и тем файлам, у которых он уже есть, и не превращает текстовые файлы в исполняемые.
setfacl -R -m g:devops:rwX /shared
Когда у файла есть и обычные разрешения, и ACL, итоговый доступ ограничивается маской. Маска режет эффективные права всех именованных пользователей и групп: если она r--, то даже запись ACL с rwx даст только чтение. Именно поэтому ACL усложняют отладку - getfacl показывает и маску, и эффективные права после её применения.
Когда процесс создаёт файл, начальные права определяет umask. Стандартный umask 022 даёт файлам 644, каталогам 755.
Часто пишут, что umask «вычитается» из базовых 666 для файлов и 777 для каталогов. Для umask 022 результат совпадает, поэтому объяснение и живёт. Но на самом деле маска не вычитается, а снимает биты: система берёт базовые права и убирает те, что отмечены в маске.
Разница вылезает на «неровных» значениях. Для umask 023 арифметика дала бы 666 - 023 = 643, а файл получит 644: маска снимает только те биты, которые в базовых правах были. Проверить текущее значение можно командой umask без аргументов.
В скриптах маску задают явно, первой строкой после интерпретатора:
#!/bin/bash
umask 027 # файлы 640, каталоги 750
На одном сервере права настраивают руками. На десятках - уже нет.
Ansible, Salt, Puppet умеют описывать права декларативно и возвращать их к заданному состоянию при отклонении. Пример задачи для Ansible:
- name: Secure application configs
file:
path: /etc/myapp/
owner: appuser
group: appgroup
mode: '0750'
recurse: yes
Главное здесь - идемпотентность: задача применяется повторно без побочных эффектов, а дрифт прав виден при следующем прогоне.
Подсистема аудита ядра логирует обращения к файлам. Правило на отслеживание изменений атрибутов в /etc выглядит так:
auditctl -w /etc -p a -k etc_changes
Правило auditctl не переживёт перезагрузку
Всё, что задано через auditctl, живёт до ребута. Постоянные правила кладут в отдельный файл в /etc/audit/rules.d/ (например, permissions.rules) - служба auditd загружает их при старте. Иначе получится классический тихий отказ: настроили, проверили, всё пишется, а через месяц выясняется, что журнал оборвался на дате последней перезагрузки.
Логи пишутся в /var/log/audit/audit.log и содержат, кто, когда и что изменил. Для серверов, попадающих под 152-ФЗ или PCI DSS, это обязательная часть соответствия: аудиторы спросят, как отслеживаются изменения прав на файлы с персональными данными. Как выстроить сам процесс отслеживания, разобрано в материале про аудит файлов на файловом сервере.
Набор команд, который стоит гонять по расписанию:
# файлы с правами 777
find / -type f -perm 0777 2>/dev/null
# файлы без владельца - остались после удаления пользователя
find / \( -nouser -o -nogroup \) 2>/dev/null
# SUID-файлы, появившиеся за последнюю неделю
find / -type f -perm /4000 -mtime -7 2>/dev/null
# файлы, доступные на запись всем, в критичных каталогах
find /etc /var -type f -perm -o+w 2>/dev/null
Скобки во втором примере не для красоты: без них условие «или» соберёт не то, что вы ожидаете, как только к команде добавится любое действие вроде -exec или -ls.
Ещё одна деталь для регулярного запуска: find / обойдёт и /proc, и /sys, и всё примонтированное, включая сетевые шары. Вывод раздувается, а на отвалившемся NFS команда может подвиснуть. Для аудита добавляйте -xdev - обход останется в пределах одной файловой системы, - и запускайте отдельно по каждому нужному разделу.
Результаты стоит сохранять и сравнивать между запусками. Если в пятницу SUID-файлов было 47, а в понедельник стало 48 - есть о чём поговорить.
Всё, о чём шла речь выше, - это DAC, разграничение доступа по усмотрению владельца. Владелец решает, кому дать доступ. Для многих сред этого мало: скомпрометированный процесс с правами www-data получит всё, что доступно www-data.
Системы мандатного контроля (MAC) работают иначе: политику задаёт администратор, и её не обойдёт даже root, пока политика не изменена. Если chmod определяет, кто может обратиться к файлу, то MAC определяет, какой процесс может это сделать.
| Параметр | SELinux | AppArmor |
|---|---|---|
| Принцип | Метки на каждом объекте | Профили для программ |
| Сложность настройки | Высокая | Средняя |
| Где встречается | RHEL, Rocky, AlmaLinux, Fedora | Ubuntu, Debian, SUSE |
| Гранулярность | Процесс, файл, порт | Программа и путь |
| Режим обучения | Permissive: логирует, не блокирует | Complain: то же самое |
Отдельная история - отечественные серверные ОС, на которые сейчас переезжает значительная часть инфраструктуры. Базовая модель прав там ровно та же: rwx, chmod, chown, ACL работают как везде, переучиваться не нужно.
Различия начинаются выше. В Astra Linux Special Edition поверх обычных разрешений работает собственное мандатное разграничение доступа: объектам присваиваются метки конфиденциальности и целостности, и процесс с меньшим уровнем не получит доступ к более высокому, независимо от того, что написано в chmod. Настраивается это своими средствами, а не через SELinux. В РЕД ОС используется SELinux, в «Альт Сервере» - AppArmor. Разбор самих дистрибутивов - в сравнении Astra Linux, «Альт Сервер» и РЕД ОС.
Практический вывод: DAC остаётся первым рубежом в любом случае. Мандатная система проверяет доступ после обычной проверки прав, а не вместо неё. Неправильные 777 не спасёт ни один профиль.
Docker по умолчанию запускает процессы от root, и это стоит менять. Если контейнер скомпрометирован, атакующий получает root внутри, а при неудачной конфигурации хоста - шанс выбраться наружу. Принцип тот же, что и на обычном сервере: отдельный непривилегированный пользователь и только нужные права.
RUN groupadd -r appgroup && useradd -r -g appgroup -s /bin/false appuser
COPY --chown=appuser:appgroup ./app /app
USER appuser
Ключ --chown в COPY здесь принципиален: без него файлы в образе принадлежат root, и процесс от appuser их не прочитает. Копировать файлы, а потом удивляться «Permission denied» при запуске - типичный сценарий.
В Kubernetes то же самое задаётся через securityContext:
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 2000
readOnlyRootFilesystem: true
Один нюанс при копировании: этот блок разнесён по двум уровням. fsGroup задаётся только в securityContext пода, readOnlyRootFilesystem - только в securityContext контейнера; runAsUser и runAsNonRoot работают на обоих. Вставите всё одним куском не туда - получите ошибку валидации манифеста.
По смыслу: fsGroup задаёт группу для смонтированных томов, то есть работает как chown для файловых систем пода. А readOnlyRootFilesystem не даёт процессу писать за пределами выделенных томов. Остальные практики запуска контейнеров в бою собраны в отдельном материале про контейнеры в production.
Система прав в Linux - не про запоминание числовых комбинаций, хотя без пары десятков привычных чисел не обойтись. Она про понимание того, кто и зачем обращается к файлу. chmod задаёт разрешения, chown определяет владельца, ACL расширяют модель для нестандартных случаев, chattr защищает от случайных изменений, мандатные системы добавляют контроль поверх всего этого.
Если выносить из статьи одну мысль - пусть будет такая: не выставляйте 777 и не запускайте всё от root. Разберитесь, какие минимальные права нужны процессу, и дайте ровно столько. А find / -perm /4000 стоит запускать хотя бы раз в неделю - для собственного спокойствия.
По теме: перенаправление ввода-вывода в Linux · как устроена файловая система
Собираете файловый сервер под общие каталоги?
Всё, что описано выше - группы, SGID на каталогах отделов, ACL для смежных подразделений - упирается в дисковую подсистему и в то, как сервер собран: сколько дисков, какой контроллер, хватит ли памяти под кэш файловой системы. Инженеры ITTELO подберут конфигурацию под ваш объём данных и число пользователей, соберут и протестируют сервер под нагрузкой перед отгрузкой. С гарантией и поддержкой после продажи, на рынке серверов 11+ лет.
Сервер для файлов купить · +7 (800) 551-80-12 · info@ittelo.ru