chmod 755 или u+x: в чем разница при настройке прав
Узнайте, как работают числовые и символьные режимы в команде chmod. Разберем, почему рекурсивный вызов 755 ломает права на файлы и как флаг X безопасно исправляет доступ к дереву.
Что меняет chmod
chmod изменяет биты прав доступа к файлу или каталогу, принимая их в одной из двух форм записи. Числовая форма представляет собой восьмеричное число, которое полностью заменяет все биты прав доступа. Символьная форма указывает класс пользователя и оператор, изменяя только указанные вами биты. Обе формы в конечном итоге вызывают один и тот же системный вызов. Разница заключается в том, что происходит с битами, которые вы не упомянули.
Чтение режима доступа и его изменение — это разные задачи. Если значение букв в длинном списке прав еще не очевидно, сначала ознакомьтесь с тем, что означает drwxr-xr-x по полям. Это руководство описывает только процесс изменения прав.
Выполняйте все приведенные здесь команды от имени обычного пользователя. Учетная запись root игнорирует эти проверки, поэтому демонстрация прав доступа, запущенная от root, не имеет смысла. Если вы все еще работаете под root на новом сервере, создайте обычного пользователя с минимальными привилегиями и вернитесь к этому руководству.
Настройка временной песочницы
cd "$(mktemp -d)"
touch notes.txtmktemp -d создает пустую директорию в /tmp и выводит путь к ней, а подстановка команды передает этот путь напрямую в cd. Никакие действия ниже не затрагивают важные для вас файлы.
Одна команда сообщает результат каждого изменения:
stat -c '%a %A %n' notes.txt%a выводит режим доступа в виде восьмеричного числа, %A выводит тот же режим в буквенном формате, который используется в длинном списке файлов, а %n выводит имя. В следующих блоках строка, начинающаяся с $, — это то, что вы вводите, а строка под ней — то, что вывела команда. Выполните их самостоятельно. Вывод на вашей машине должен совпадать строка в строку, так как числовой режим не зависит от параметров вашего окружения.
Числовой формат: одна восьмеричная цифра на класс
Первая цифра относится к владельцу файла. Вторая — к группе файла. Третья — ко всем остальным пользователям. Каждая цифра представляет собой сумму значений отдельных битов. Установите по одному биту за раз и посмотрите, что выведет stat.
$ chmod 400 notes.txt
$ stat -c '%a %A %n' notes.txt
400 -r-------- notes.txt
$ chmod 200 notes.txt
$ stat -c '%a %A %n' notes.txt
200 --w------- notes.txt
$ chmod 100 notes.txt
$ stat -c '%a %A %n' notes.txt
100 ---x------ notes.txtБуквенные обозначения соответствуют цифрам. После начального символа типа файла первая группа из трёх букв относится к владельцу, следующие три — к группе, а последние три — ко всем остальным. Складывайте значения, чтобы объединить биты внутри одного класса.
$ chmod 600 notes.txt
$ stat -c '%a %A %n' notes.txt
600 -rw------- notes.txt
$ chmod 700 notes.txt
$ stat -c '%a %A %n' notes.txt
700 -rwx------ notes.txt
$ chmod 750 notes.txt
$ stat -c '%a %A %n' notes.txt
750 -rwxr-x--- notes.txtЧисловой режим является абсолютным. Он записывает все девять битов при каждом запуске, поэтому изменения затрагивают и те классы, о которых вы не думали.
$ chmod 755 notes.txt
$ stat -c '%a %A %n' notes.txt
755 -rwxr-xr-x notes.txt
$ chmod 600 notes.txt
$ stat -c '%a %A %n' notes.txt
600 -rw------- notes.txtВторая команда ничего не говорила о группе или об остальных пользователях, но их биты всё равно изменились. В этом заключается особенность числового формата, и именно поэтому он является неподходящим инструментом для внесения точечных правок. (chmod также управляет битами setuid, setgid и sticky, которые находятся в четвёртой цифре перед основными тремя. Это отдельная тема, и данное руководство рассматривает только три основные цифры.)
Символьная форма: класс, оператор, затем биты
Сначала укажите класс. u — владелец, g — группа, o — все остальные, а a — все сразу. Затем оператор: + добавляет биты, - удаляет их, а = устанавливает для класса только указанные биты, сбрасывая остальные. Затем сами биты: r, w или x.
$ chmod 640 notes.txt
$ stat -c '%a %A %n' notes.txt
640 -rw-r----- notes.txt
$ chmod u+x notes.txt
$ stat -c '%a %A %n' notes.txt
740 -rwxr----- notes.txt
$ chmod g-r notes.txt
$ stat -c '%a %A %n' notes.txt
700 -rwx------ notes.txt
$ chmod o=r notes.txt
$ stat -c '%a %A %n' notes.txt
704 -rwx---r-- notes.txtКаждая команда затрагивает один класс, оставляя остальные без изменений. Объединяйте условия запятыми без пробелов; chmod применяет их слева направо.
$ chmod u=rw,go=r notes.txt
$ stat -c '%a %A %n' notes.txt
644 -rw-r--r-- notes.txt
$ chmod go= notes.txt
$ stat -c '%a %A %n' notes.txt
600 -rw------- notes.txtgo= без указания прав после знака равенства снимает все биты с группы и остальных пользователей. Это нужный вид для файла, который должен читать только владелец, например, для закрытого ключа из пары ключей SSH.
chmod может отчитываться о своей работе, что важно при выполнении скриптов, когда вывод на экран никто не контролирует.
chmod -v u+x notes.txt
chmod -v u+x notes.txt-v выводит строку для каждого обработанного файла. Выполните обе команды и сравните строки на экране: первая называет файл и сообщает, с какого режима права были изменены и на какой, а вторая сообщает, что режим остался прежним, так как требуемый бит уже был установлен. -c выводит строку только в случае реального изменения файла, поэтому этот флаг лучше подходит для записи в лог.
chmod 755 или u+x: какую нотацию использовать
Возьмите один и тот же файл и сравните, какой результат оставляет каждая из нотаций.
$ chmod 640 notes.txt
$ chmod 755 notes.txt
$ stat -c '%a %A %n' notes.txt
755 -rwxr-xr-x notes.txt
$ chmod 640 notes.txt
$ chmod u+x notes.txt
$ stat -c '%a %A %n' notes.txt
740 -rwxr----- notes.txtОбе команды сделали файл исполняемым для его владельца. Только одна из них оставила права для двух других классов без изменений. Используйте числовой режим, когда вы знаете точное конечное состояние и хотите его принудительно задать; это стандартный случай для скрипта развертывания, который должен приводить систему к одинаковому результату независимо от её исходного состояния. Используйте символьный режим, если ваша логика подразумевает добавление или удаление прав, так как числовая форма не позволяет сказать оставить эту часть без изменений.
Режим, с которым создается новый файл, не имеет отношения к chmod. Он определяется значением umask процесса, который создал этот файл. Выполните umask и umask -S в своей оболочке, чтобы увидеть текущее значение; ожидайте, что оно будет различаться в зависимости от дистрибутива, типа сессии (интерактивная оболочка или systemd-сервис) и конкретной учетной записи. Umask задает режим каждого нового файла, поэтому именно она определяет исходное состояние прав до того, как будет запущен chmod.
Что делает бит исполнения для каталога
Это заблуждение отнимает больше всего времени. Для обычного файла бит исполнения означает, что ядро может запустить этот файл. В каталоге нет инструкций, поэтому запускать там нечего. Для каталога бит исполнения означает возможность прохода (traversal): разрешение на разрешение (resolve) имени внутри него. Чтобы получить доступ к dir/file, этот бит должен быть установлен для dir. Бит чтения — это отдельное разрешение, он позволяет просматривать список имен, содержащихся в каталоге. Вы можете иметь один бит без другого, в чем легче убедиться, увидев это на практике.
$ mkdir vault
$ printf 'hello\n' > vault/secret.txt
$ chmod 600 vault/secret.txt
$ chmod 100 vault
$ echo vault/*
vault/*
$ cat vault/secret.txt
helloОболочка вывела шаблон без изменений, потому что раскрытие vault/* требует чтения каталога, а глоб, который ничего не находит передается как обычный текст, вместо того чтобы вызвать ошибку. cat по-прежнему работал, так как вы указали имя самостоятельно, а проход был разрешен. Теперь поменяйте эти два бита местами.
$ chmod 400 vault
$ echo vault/*
vault/secret.txt
$ cat vault/secret.txt
cat: vault/secret.txt: Permission deniedИмена видны, а содержимое — нет. Чтение дает вам список. Исполнение дает вам путь внутрь. Почти для любого каталога, который вы используете, требуются оба разрешения.
$ chmod 500 vault
$ cat vault/secret.txt
helloПочему chmod -R с числовым режимом повреждает дерево каталогов
-R обходит всё дерево и применяет указанный режим ко всему, что встречает. Для каталога и файла данных требуются разные биты прав, поэтому одно абсолютное число не может быть верным для обоих типов объектов. Создайте небольшое дерево каталогов и посмотрите, что пойдёт не так.
$ mkdir -p site/css
$ touch site/index.html site/css/main.css
$ printf '#!/bin/bash\necho hi\n' > site/deploy.sh
$ chmod 755 site site/css site/deploy.sh
$ chmod 644 site/index.html site/css/main.css
$ stat -c '%a %A %n' site site/css site/index.html site/css/main.css site/deploy.sh
755 drwxr-xr-x site
755 drwxr-xr-x site/css
644 -rw-r--r-- site/index.html
644 -rw-r--r-- site/css/main.css
755 -rwxr-xr-x site/deploy.shЭто структура прав, необходимая для каталога со статическими файлами. Вот первая рекурсивная ошибка.
$ chmod -R 755 site
$ stat -c '%a %A %n' site/index.html site/css/main.css
755 -rwxr-xr-x site/index.html
755 -rwxr-xr-x site/css/main.cssНичего не сломалось, и в этом заключается проблема. Эта ошибка никак не проявляется, поэтому она годами кочует в скопированных инструкциях. Теперь два файла данных помечены как исполняемые программы. Git сохраняет бит исполнения, поэтому ваш следующий коммит перенесёт это изменение всем, кто клонирует репозиторий, а rsync и tar распространят его на каждую машину, куда вы скопируете это дерево.
Вторая рекурсивная ошибка проявляется явно. Примените режим без бита исполнения к каталогу, и доступ к нему будет закрыт.
$ chmod 644 site
$ stat -c '%a %A %n' site
644 drw-r--r-- site
$ echo site/*
site/css site/deploy.sh site/index.html
$ cd site
bash: cd: site: Permission denied
$ cat site/index.html
cat: site/index.html: Permission deniedИмена файлов по-прежнему отображаются, так как бит чтения сохранился, но любой путь через этот каталог закрыт, поскольку бит исполнения был удалён. chmod -R 644 site делает это с каждым каталогом в дереве одной командой. Результат после выполнения не всегда однороден, так как chmod должен проходить через те же каталоги, доступ к которым он только что ограничил. Поэтому проверяйте фактический результат с помощью find site -type d -exec stat -c '%a %n' {} +, а не полагайтесь на предположения. Верните права обратно перед продолжением.
$ chmod 755 site
$ chmod 644 site/index.html site/css/main.css
$ stat -c '%a %A %n' site site/index.html
755 drwxr-xr-x site
644 -rw-r--r-- site/index.htmlРекурсивная команда chmod не имеет функции отмены. chmod не записывает, какие права были заменены, поэтому восстановление ложится на вас — вам придётся восстанавливать режимы доступа, основываясь на своих предположениях о том, какими они были раньше. Это такая же медленная и ненадёжная работа, как восстановление файлов после случайного rm -rf, и урок здесь тот же: проверяйте цель рекурсивной команды перед нажатием клавиши Enter.
Использование заглавной X: выполнение для каталогов, а не для каждого файла
Символьные режимы поддерживают X так же, как и x. X устанавливает бит выполнения только там, где это логично: для каталога или для обычного файла, у которого уже есть бит выполнения хотя бы для одного класса пользователей. Это правило в точности соответствует тому, как вы хотели бы использовать -R.
$ chmod -R u=rwX,go=rX site
$ stat -c '%a %A %n' site site/css site/index.html site/css/main.css site/deploy.sh
755 drwxr-xr-x site
755 drwxr-xr-x site/css
644 -rw-r--r-- site/index.html
644 -rw-r--r-- site/css/main.css
755 -rwxr-xr-x site/deploy.shОдной командой мы сохранили возможность входа в каталоги, оставив файлы данных без изменений. Скрипт сохранил свой бит выполнения, так как он уже был установлен, что является второй частью правила X. X считывает текущий режим файла, поэтому файл, который уже лишился всех битов выполнения, не получит их обратно с помощью этого флага.
$ chmod 644 site/deploy.sh
$ chmod -R u=rwX,go=rX site
$ stat -c '%a %A %n' site/deploy.sh
644 -rw-r--r-- site/deploy.sh
$ site/deploy.sh
bash: site/deploy.sh: Permission denied
$ chmod u+x site/deploy.sh
$ stat -c '%a %A %n' site/deploy.sh
744 -rwxr--r-- site/deploy.sh
$ site/deploy.sh
hiТо же правило работает и в обратную сторону. X распространит уже имеющийся у файла бит выполнения на другой класс пользователей:
$ chmod go+X site/deploy.sh
$ stat -c '%a %A %n' site/deploy.sh
755 -rwxr-xr-x site/deploy.shТаким образом, строчная x предназначена для одного конкретного файла, который вы хотите сделать исполняемым, а X — для массовой обработки дерева каталогов. Если вам нужно задать точный режим для каждого типа объектов, а не применять правило, основанное на текущем состоянии, разделите процесс обхода с помощью find.
find site -type d -exec chmod 755 {} +
find site -type f -exec chmod 644 {} +
chmod u+x site/deploy.sh-type d выбирает каталоги, а -type f — обычные файлы, поэтому каждый тип получает соответствующий режим. -exec ... + объединяет множество путей в один вызов chmod вместо запуска отдельного процесса для каждого файла, что важно при работе с деревом, содержащим тысячи элементов. Скрипту бит выполнения возвращается отдельной командой, намеренно.
Проверка результата перед завершением работы
Две команды позволяют выявить критические ошибки после любых рекурсивных изменений. Первая выводит список каталогов, в которые вы больше не можете войти.
find . -type d ! -perm -u+xВторая выводит список файлов, которые может изменить любая учетная запись на сервере.
find . -type f -perm -o+wВ -perm -o+w ведущий - означает как минимум эти биты, поэтому команда находит файлы, у которых в классе o установлен бит записи, независимо от других установленных прав. Отсутствие вывода обеих команд — это желаемый результат. Запускайте обе команды из корня любого дерева каталогов, которое вы только что изменили с помощью -R, и выполняйте stat -c '%a %A %n' для тех путей, которые вы планировали изменить. Это позволит убедиться, что права доступа, которые вы задали, соответствуют тем, что фактически установлены в ядре.
FAQ
В чем разница между chmod 755 и chmod u+x?
Числовой режим является абсолютным. Он записывает каждый бит прав доступа для файла, поэтому классы, которые вы не учитывали, также будут перезаписаны. Символьный режим — это правка. u+x добавляет один бит для владельца и оставляет все остальные биты без изменений. Используйте числовую форму, когда вам известно точное конечное состояние, которое вы хотите применить к файлу. Используйте символьную форму, когда нужно добавить или удалить что-то одно. Выполните stat -c '%a %A %n' <file> до и после любой из этих команд, и разница будет видна в выводе.
Почему chmod -R 755 для каталога с веб-файлами — плохая идея?
Потому что -R применяет один и тот же абсолютный режим как к каталогам, так и к обычным файлам, а им требуются разные биты. Каталогу нужен бит исполнения, чтобы его можно было просматривать. Файлам данных, таким как HTML и CSS, он не нужен, и пометка их как исполняемых — это изменение, которое git зафиксирует в следующем коммите, а rsync скопирует на следующую машину. Используйте chmod -R u=rwX,go=rX <dir>, чтобы бит исполнения был применен только к каталогам, или управляйте этим через find с помощью -type d и -type f, чтобы каждый тип получил свой режим.
Что делает бит исполнения для каталога?
Он предоставляет право на проход, а не на выполнение. Внутри каталога нечего запускать. Бит исполнения — это разрешение на разрешение имени внутри него, поэтому он необходим для каждого каталога вдоль пути, прежде чем вы сможете открыть файл в конце этого пути. Бит чтения отделен и предоставляет право на перечисление имен. Вы можете иметь один без другого: только с правом исполнения вы можете открыть файл, имя которого уже знаете, в то время как оболочка не сможет раскрыть шаблон (glob) в этом каталоге; а только с правом чтения вы можете видеть имена, но любая попытка открыть файл завершится неудачей.
Когда следует использовать заглавную X вместо строчной x?
Используйте X всегда, когда команда выполняется рекурсивно. X устанавливает бит исполнения для каталогов, а для обычных файлов — только в том случае, если хотя бы один бит исполнения уже установлен. Таким образом, один проход делает дерево доступным для прохода, не превращая файлы данных в программы. Используйте строчную x, когда вы указываете на один конкретный файл. Стоит помнить об одном ограничении: X не может восстановить бит исполнения, который был полностью удален у файла, так как ему не с чем сопоставить условие. Верните его обратно с помощью chmod u+x <file>.
Меняет ли chmod владельца файла?
Нет. chmod изменяет только биты прав доступа. Владение определяется chown для пользователя и chgrp для группы, а для передачи файла другому пользователю требуются права root. Это важно, так как биты проверяются относительно владельца и группы, записанных в файле, а не относительно того, кто записал его последним. Выполните stat -c '%U %G %a %n' <file>, чтобы увидеть владельца, группу и режим вместе, прежде чем решать, какая из трех команд вам действительно нужна.