Скрипту в cron или CI нужно выполнить одну команду с sudo, но он не умеет вводить пароль - и NOPASSWD выглядит удобным решением. Разбираем, как настроить его правильно через отдельный файл в /etc/sudoers.d, а не редактированием /etc/sudoers напрямую: синтаксис, проверка перед установкой, права доступа и где проходит грань между разумной автоматизацией и реальной дырой в безопасности.
Скрипт деплоя падает на строке sudo systemctl reload nginx, потому что sudo ждёт пароль, а вводить его в cron или CI/CD некому. Первое, что находится в поиске, - разрешить пользователю выполнять sudo вообще без пароля. Проблема в том, что почти все примеры в интернете показывают именно то, чего делать не стоит: ALL=(ALL) NOPASSWD:ALL на живом сервере с сайтом или API, смотрящим в интернет. Разбираем, как настроить NOPASSWD правильно - для одной конкретной команды, через отдельный файл в /etc/sudoers.d/, а не правкой /etc/sudoers напрямую, - и где проходит граница между разумной автоматизацией и дырой, которую сам себе пробивает администратор.
Коротко. NOPASSWD - тег в правилах sudo, который отключает запрос пароля для конкретной команды и конкретного пользователя. Настраивается не правкой
/etc/sudoers, а отдельным файлом в/etc/sudoers.d/с правами0440и владельцемroot:root:username ALL=(root) NOPASSWD: /usr/bin/systemctl restart nginx. Перед установкой файл обязательно проверяется командойvisudo -cf путь_к_файлу- она ловит синтаксические ошибки до того, как файл попадёт в систему. Легитимные случаи - деплой-скрипты, CI/CD, systemd-юниты, которым нужна одна конкретная привилегированная команда без интерактивного ввода. Реальный риск -NOPASSWD:ALLна пользователе, под которым крутится сетевой сервис: взлом этого сервиса сразу даёт root без единого дополнительного пароля. Указывайте команды с полным путём и без пропущенных аргументов - иначе NOPASSWD незаметно откроет больше, чем планировалось.
Что такое NOPASSWD и откуда берётся запрос пароля у sudo
По умолчанию sudo перед выполнением привилегированной команды спрашивает пароль того пользователя, который его вызвал - не пароль root, а собственный. Это работает нормально, когда за терминалом сидит человек. Но у скрипта, systemd-юнита или CI-раннера нет терминала, в который можно ввести пароль - и попытка выполнить sudo внутри такого процесса просто зависает или падает с ошибкой, потому что вводить пароль некому.
NOPASSWD - это тег в правилах sudo, который убирает именно этот запрос для конкретной пары «пользователь - команда». Правило вида:
deploy ALL=(root) NOPASSWD: /usr/bin/systemctl restart nginx
разрешает пользователю deploy выполнить ровно одну команду - systemctl restart nginx - от имени root, без единого запроса пароля. Любая другая команда через sudo для этого пользователя по-прежнему потребует пароль как обычно, если не описана отдельным правилом с тем же тегом.
Почему не редактировать /etc/sudoers напрямую
Технически можно дописать нужное правило прямо в /etc/sudoers. На практике так не делают по трём причинам. Отдельный файл в /etc/sudoers.d/ живёт своей жизнью: не мешает обновлениям пакета sudo и чужим правкам общего файла, а одна строка вроде 90-deploy-nginx сразу видна в аудите и удаляется целиком, если доступ пора отозвать. И главное - штатный путь редактирования, команда visudo, сама проверяет синтаксис перед сохранением и не даёт выйти, пока ошибка не исправлена; при правке через nano или vim напрямую этой защиты нет.
При этом реальный масштаб риска трезвее оценить так: на тестовом сервере мы проверили, что даже заведомо битая строка, дописанная прямо в /etc/sudoers в обход visudo, не заблокировала sudo полностью - при каждом вызове sudo печатает точную ошибку синтаксиса с номером строки, в лог и на экран, но продолжает работать с теми правилами, которые смог разобрать. Это не официальная гарантия для всех версий sudo и не повод специально проверять степень фатальности ошибок на боевом сервере - просто трезвая причина не паниковать, если такое случилось, и не повод пропускать проверку синтаксиса заранее.
Шаг 1. Пишем правило в отдельном файле, не в /etc/sudoers
Создавайте черновик правила во временном файле, а не сразу в /etc/sudoers.d/ - так его можно спокойно проверить и поправить до того, как он попадёт в боевую конфигурацию:
sudo nano /tmp/deploy-nginx
Содержимое - одна строка с полным синтаксисом правила sudo:
deploy ALL=(root) NOPASSWD: /usr/bin/systemctl restart nginx, /usr/bin/systemctl reload nginx, /usr/bin/systemctl status nginx
deploy- пользователь, для которого действует правило (не группа, если явно не указано через%);- первое
ALL- на каких хостах действует правило, почти всегда корректно оставить как есть; (root)- от чьего имени можно выполнять команду, можно сузить и до другого пользователя;NOPASSWD:- тег, отключающий запрос пароля для всего, что идёт после него в строке, до следующего тега;- дальше - команды через запятую, каждая с полным путём:
/usr/bin/systemctl, а не простоsystemctl. Это не рекомендация, а требование синтаксиса: короткое имя без пути - это ошибка парсинга («expected a fully-qualified path name»), которуюvisudo -cfпокажет сразу же, ещё до установки файла.
Отдельно про аргументы. Если у команды в правиле они вообще не указаны - просто NOPASSWD: /usr/bin/systemctl - пользователь получает право выполнить её с любыми аргументами, а не только теми, что вы держали в уме: разница между «может перезапускать nginx» и «может выполнить любую подкоманду systemctl». Это одна из самых частых ошибок при настройке NOPASSWD - хотели разрешить одну операцию, а открыли весь инструмент.
Шаг 2. Проверяем синтаксис до установки файла
Прежде чем файл попадёт в /etc/sudoers.d/ и начнёт на что-то влиять, его нужно проверить командой visudo с флагами -c (check) и -f (file):
sudo visudo -cf /tmp/deploy-nginx
Что вы должны увидеть при успехе: /tmp/deploy-nginx: parsed OK. Если в строке опечатка - лишняя запятая, отсутствующая двоеточие после тега, неэкранированный спецсимвол в аргументах - visudo вместо этого покажет точную позицию ошибки, например /tmp/deploy-nginx:1:9: syntax error, с указанием строки и символа, на котором разбор споткнулся. Не переходите к следующему шагу, пока не увидите именно parsed OK.
Шаг 3. Переносим файл в /etc/sudoers.d/ с правильными именем, владельцем и правами
Когда синтаксис подтверждён, переносим файл в рабочий каталог и сразу выставляем ему владельца и права:
sudo cp /tmp/deploy-nginx /etc/sudoers.d/90-deploy-nginx
sudo chown root:root /etc/sudoers.d/90-deploy-nginx
sudo chmod 0440 /etc/sudoers.d/90-deploy-nginx
- права
0440означают «читать может только root и группа root, писать - никто без смены прав» - подтверждённое поведение по умолчанию для всех файлов в/etc/sudoers.d/на чистой Ubuntu, и его нужно повторить вручную для файла, который кладёте сами; - если права слабее, sudo не откажется работать совсем, но громко предупредит при каждом запуске: в нашей проверке права
0777далиsudo: ...is world writable, а неверный владелец -sudo: ...is owned by uid 1003, should be 0. Оставлять так не стоит: суть проверки прав в том, чтобы никто, кроме root, не мог незаметно дописать себе лишнюю команду; - имя файла не должно содержать точку. Это задокументированное в
man sudoersповедение:@includedirпропускает файлы, чьё имя заканчивается на~или содержит., - специально, чтобы не подхватывать временные файлы редакторов. Проверено вживую: файл с валидным правилом и именем вроде90-deploy-nginx.confустанавливается без единой ошибки, права и владелец правильные, - но его правило просто не появляется вsudo -l, будто файла не существует, без единого предупреждения. Это один из самых незаметных способов потратить полчаса на «почему это не работает».
Числовой префикс вроде 90- в начале имени - не обязательное требование, а практика для порядка: файлы обрабатываются в лексическом порядке имён, и такой префикс заранее говорит, какое правило применится раньше, если они вдруг пересекутся.
Шаг 4. Проверяем, что правило реально применилось
Первая проверка - посмотреть, что видит сам sudo для этого пользователя:
sudo -l -U deploy
Что вы должны увидеть: в списке разрешённых команд строку вида (root) NOPASSWD: /usr/bin/systemctl restart nginx, .... Если правило не появилось - дело либо в точке в имени файла, либо в правах, либо файл не долетел в /etc/sudoers.d/.
Вторая, более честная проверка - выполнить саму команду с флагом -n, который заставляет sudo не спрашивать пароль интерактивно, а сразу вернуть ошибку, если он всё-таки нужен:
sudo -u deploy sudo -n /usr/bin/systemctl status nginx
Что вы должны увидеть: обычный вывод systemctl status, без запроса пароля. Если вместо этого видите sudo: a password is required - правило либо не подхватилось, либо команда не совпадает буквально с той, что прописана в правиле.
Буквальное совпадение - частая причина, по которой NOPASSWD «не работает», хотя формально всё настроено верно. Мы проверили это на реальном примере: правило разрешало /usr/bin/systemctl restart nginx, а команда /usr/bin/systemctl restart nginx.service - тот же самый сервис, просто с явным суффиксом - потребовала пароль, потому что sudo сравнивает аргументы посимвольно, не «по смыслу». Если скрипт иногда добавляет аргументы вроде --now или полный суффикс юнита, правило должно предусматривать именно ту форму команды, которую скрипт реально вызывает.
Когда NOPASSWD - это разумная автоматизация, а не дыра
NOPASSWD придумали не для того, чтобы обходить sudo - это штатный инструмент именно для процессов, у которых нет и не должно быть способа ввести пароль интерактивно. Несколько ситуаций, где это оправдано:
- Деплой-скрипты. CI/CD-раннер по SSH заходит под отдельным пользователем и должен перезапустить один конкретный сервис после выкладки - больше ему в системе делать нечего.
- systemd-таймеры и cron-задачи. Скрипт бэкапа, которому нужно временно смонтировать раздел или скопировать системный файл по расписанию, без сессии человека рядом.
- Мониторинг и агенты проверки работоспособности. Проверка статуса сервиса или чтение системного лога, требующие root-доступа только на чтение.
- Оркестрация конфигурации (Ansible и подобные). Управляющий узел выполняет заранее известный, ограниченный набор операций без пароля на каждом шаге плейбука.
Общий признак всех этих случаев - список разрешённых команд известен заранее и не меняется на лету. Если можете написать список команд, которые скрипту реально нужны, - это ровно тот сценарий, для которого NOPASSWD и создан.
Когда NOPASSWD - реальный риск, а не удобство
Опасность начинается не с самого NOPASSWD, а с того, насколько широко он открыт. Классический пример из туториалов, который лучше никогда не копировать буквально на боевой сервер:
deploy ALL=(ALL) NOPASSWD:ALL
Эта строка разрешает пользователю deploy выполнить любую команду от имени любого пользователя, включая root, без единого пароля. По сути, это равносильно тому, что у пользователя вообще нет своего пароля для root-действий - разница только в том, что sudo хотя бы оставляет запись в логе о каждой выполненной команде, чего не будет при прямом входе под root.
Критичный сценарий - когда такой пользователь одновременно является тем, под кем крутится сетевой сервис: веб-приложение, API, бот, слушающий порт снаружи. Найдётся в этом сервисе уязвимость, позволяющая выполнить произвольную команду в контексте его же пользователя - а такие уязвимости в вебе не редкость, - и NOPASSWD:ALL превращает её напрямую в захват всего сервера. Не в «получение прав пользователя deploy», а сразу в root, одной командой, без единого дополнительного барьера. Разница между рабочим сервером и полностью скомпрометированной машиной здесь - буквально одна строка в sudoers.
Практическое правило простое: никогда не давайте NOPASSWD:ALL пользователю, под которым работает что-либо, принимающее входящие соединения из интернета. Для технических и деплой-пользователей описывайте точный список нужных команд, как показано в шагах выше, а не открывайте всё разом ради экономии пяти минут на настройке.
Правило | Что разрешает | Когда уместно |
|---|---|---|
| ровно одну команду с ровно этими аргументами, от root | деплой-скрипт, CI/CD, systemd-юнит с известным, узким набором задач |
| любую подкоманду systemctl с любыми аргументами | почти никогда на публичном сервисе - слишком широко для большинства задач автоматизации |
| вообще любую команду от любого пользователя без пароля | практически никогда на сервере с сетевым сервисом; в лучшем случае - изолированный CI-раннер, которому и так доверяют полный доступ |
Частые вопросы
Как разрешить sudo без пароля для одной команды?
Создайте отдельный файл в /etc/sudoers.d/ с правилом вида username ALL=(root) NOPASSWD: /usr/bin/полный/путь/к/команде. Перед установкой проверьте синтаксис через sudo visudo -cf путь_к_файлу, после установки выставьте владельца root:root и права 0440. Имя файла не должно содержать точку - иначе sudo молча его проигнорирует.
Почему нельзя редактировать /etc/sudoers напрямую?
Технически можно, но это добавляет риск синтаксических ошибок без встроенной защиты - visudo проверяет синтаксис перед сохранением и блокирует выход с ошибкой, а обычный редактор сохранит файл каким угодно. Отдельные файлы в /etc/sudoers.d/ также проще версионировать, откатывать и удалять по одному, не трогая остальную конфигурацию sudo.
Какие права должны быть у файла в sudoers.d?
0440, владелец и группа - root: sudo chown root:root файл && sudo chmod 0440 файл. Это поведение по умолчанию для файлов, которые ставятся туда пакетами Ubuntu, и его нужно выставлять вручную для файла, который вы добавляете сами. При слишком открытых правах sudo не откажет полностью, но будет громко предупреждать об этом при каждом запуске.
Опасен ли sudo NOPASSWD:ALL?
Да, если разрешён пользователю, под которым работает сервис, принимающий соединения из интернета: уязвимость в этом сервисе сразу превращается в полный root-доступ без единого дополнительного пароля. Для автоматизации описывайте точный, ограниченный список команд вместо ALL.
Как проверить синтаксис sudoers перед сохранением?
Командой sudo visudo -cf путь_к_файлу для отдельного файла или sudo visudo -c для всей текущей конфигурации, включая файлы в /etc/sudoers.d/. При успехе - parsed OK, при ошибке - точная строка и позиция, где разбор остановился.
Коротко
- NOPASSWD - тег в правилах sudo, который отключает запрос пароля для конкретного пользователя и конкретной команды, а не для sudo целиком.
- Настраивать - отдельным файлом в
/etc/sudoers.d/, не правкой/etc/sudoers: проще откатить, проще увидеть в аудите, не мешает системным обновлениям. - Перед установкой файла - обязательно
sudo visudo -cf путь, после установки -chown root:rootиchmod 0440. - Имя файла в
/etc/sudoers.d/не должно содержать точку - такие файлы@includedirмолча пропускает без единого предупреждения. - sudo сравнивает команду и аргументы буквально:
systemctl restart nginxиsystemctl restart nginx.service- разные правила для sudo, даже если по смыслу это одно и то же действие. - Легитимно - для деплой-скриптов, CI/CD, systemd-таймеров и оркестрации с заранее известным, узким списком команд.
- Реальный риск -
ALL=(ALL) NOPASSWD:ALLна пользователе, под которым работает сетевой сервис: одна уязвимость в этом сервисе сразу превращается в root без дополнительных барьеров.
Что дальше
Если параллельно разбираетесь, чем отличаются способы получить root-доступ в принципе - sudo, sudo -i, sudo su - и прямой вход под root, - в статье про вход под root в Ubuntu разобраны все варианты и разница между ними. Если нужно сменить пароль конкретному пользователю или root - смотрите статью про смену пароля в Ubuntu. А полный список типичных проблем на свежем Linux-сервере собран в материале частые проблемы Linux-сервера.
