Ошибка в файле sudoers это классическая ловушка администрирования: достаточно одной опечатки в строке прав или потерянной кавычки, и механизм повышения привилегий отказывает всем пользователям сразу. Если альтернативного рута на машине нет, единственный путь это загрузка в однопользовательском режиме и ручное восстановление конфигурации. Процедура несложная, но требует понимания, как именно sudo читает конфигурацию и почему обычные команды больше не помогают. Разберём реальный сценарий восстановления с подробностями, которые чаще всего упускают.
Симптомы повреждённого sudoers и почему повседневные способы уже не работают
Повреждённый файл конфигурации sudo выдаёт себя характерными сообщениями. При попытке выполнить любую команду от sudo система отвечает ошибкой парсинга:
>>> /etc/sudoers: syntax error near line 23 <<<
sudo: parse error in /etc/sudoers near line 23
sudo: no valid sudoers sources found, quitting
sudo: unable to initialize policy plugin
Частая причина поломки это ручная правка файла через обычный редактор вроде nano без валидации: один незакрытый список команд или опечатка в имени алиаса, и файл превращается в некорректный. Ещё один повторяющийся сюжет это раскатка конфигурации через систему управления без этапа проверки, и на одной из сотен машин ошибка гарантированно всплывёт.
С этого момента механизм повышения привилегий полностью мёртв для всех пользователей группы sudo или wheel. Классические альтернативы работают только если их настроили заранее. Команда su срабатывает при условии известного пароля root, а в Ubuntu и многих пользовательских дистрибутивах учётка рута заблокирована по умолчанию, и пароля просто нет. Утилита pkexec из polkit даёт шанс на машинах, где служба polkit запущена и доступен агент аутентификации администратора, но на серверах без графической сессии чаще всего и она бессильна.
Диагностика занимает минуту: достаточно пары пробных команд, чтобы убедиться, что отказ системный, а не локальная опечатка конкретного пользователя. Если sudo не работает даже у члена группы wheel с гарантированно корректным паролем, проблема именно в файле политики.
Характерные симптомы выглядят так:
- любой вызов sudo завершается сообщением о синтаксической ошибке или о невалидных источниках sudoers;
- механизм polkit отсутствует или не настроен для интерактивного администрирования;
- аккаунт root заблокирован, и войти через su или консоль нельзя;
- автоматизация через Ansible или собственные скрипты терпит неудачу с теми же ошибками, так как они тоже работают через sudo.
Стоит проверить и соседние источники политики: иногда ошибка кроется не в основном файле, а в одном из сниппетов каталога sudoers.d, который поставила сторонняя служба. Команда visudo -c показывает, какой именно источник испорчен, и часто достаточно временно переименовать битый сниппет, вернуть доступ и потом разбираться с автором пакета.
Отдельный частный случай это удаление самого файла или превращение его в пустой. В такой конфигурации sudo не сможет найти подходящих источников политики и откажет всем кроме рута, а тот сам root остаётся за заблокированной дверью.
Загрузка в однопользовательский режим через редактирование строки ядра GRUB
Однопользовательский режим, он же rescue.target в терминологии systemd, поднимает систему без сети и лишних сервисов, но с работающей консолью рута. Для входа нужно прервать автозагрузку.
На этапе меню GRUB нажать клавишу e на нужном пункте загрузки. В открывшемся редакторе найти строку, начинающуюся с linux или linuxefi, и дописать в её конец параметр:
systemd.unit=rescue.target
После правки нажать Ctrl x или F10: ядро загрузится, systemd остановится на минимальном наборе служб и попросит пароль root. На системах, где пароль рута известен и аккаунт разблокирован, этого достаточно: вход идёт сразу в однопользовательскую консоль, корневой раздел уже смонтирован на чтение и запись.
На зашифрованном корне LUKS процедура остаётся такой же, только passphrase запросят ещё на этапе initramfs, до передачи управления в систему инициализации. Пароль от шифрования и пароль рута это разные секреты, и знание одного не отменяет нужды во втором.
Для совсем запущенных случаев подходит аварийный режим emergency.target. Он поднимает ещё меньше: корень монтируется в режиме только чтение, и поэтому потребуется перемонтирование:
mount -o remount,rw /
На Ubuntu с заблокированным рутом rescue.target не поможет, так как спросит пароль, которого не существует. Там применяют приём из следующего раздела.
Прямой запуск оболочки через параметр init когда пароль root неизвестен
Точная классика администрирования: на строку ядра вместо systemd unit добавляют параметр подмены процесса инициализации:
init=/bin/bash
Ядро загрузится и вместо systemd сразу запустит bash от имени суперпользователя, без запросов пароля. Корневой раздел при этом останется смонтирован только на чтение, и первое действие в консоли это перемонтирование:
mount -o remount,rw /
Дальше у оператора полная свобода. Если root заблокирован парольной звёздочкой или восклицательным знаком в файле теневых паролей, меняют пароль командой passwd и заодно правят конфигурацию. Важно помнить, что systemd не запущен, и завершать сеанс надо аккуратно:
sync
exec /sbin/init
Команда sync сбрасывает буферы на диск, exec /sbin/init передаёт управление в систему инициализации, и загрузка продолжается в обычном режиме. На системах с SELinux, RHEL и Fedora в их числе, после подобной процедуры стоит создать файл /.autorelabel, чтобы метки безопасности переустановились при следующем старте, иначе сервисы могут не подняться из за нарушенных контекстов.
Пересоздание корректного sudoers и сниппетов с правильными правами
В однопользовательской консоли файл конфигурации создают или чинят вручную. Безопасная минимальная заготовка выглядит так:
Defaults env_reset
Defaults mail_badpass
Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
root ALL=(ALL:ALL) ALL
%admin ALL=(ALL) ALL
%sudo ALL=(ALL:ALL) ALL
%wheel ALL=(ALL:ALL) ALL
#includedir /etc/sudoers.d
Этот шаблон покрывает Debian подобные и RHEL подобные дистрибутивы: группа sudo актуальна для Ubuntu и Debian, wheel для RHEL и Fedora. Строка includedir подтягивает сниппеты из каталога sudoers.d, где обычно лежат правила отдельных служб и автоматизации.
Жёсткое требование к правам: файл должен принадлежать root и иметь строго 0440, каталог сниппетов 0750. Любые послабления заставят sudo отказаться читать конфигурацию как небезопасную:
chown root:root /etc/sudoers
chmod 0440 /etc/sudoers
chmod 0750 /etc/sudoers.d
chmod 0440 /etc/sudoers.d/*
На системах с SELinux после правки восстанавливают контекст файлов командой restorecon -Rv /etc/sudoers.d и для основного файла, иначе служба откажет даже при корректном синтаксисе.
Отдельного внимания заслуживают системы без интерпретатора bash по пути /bin/bash, встраиваемые сборки и контейнерные хосты с BusyBox. Там параметр init указывают на /bin/sh или доступную оболочку, а дальнейшая логика действий сохраняется полностью. Принцип один и тот же: получить шелл рута раньше, чем стартует основная система.
Проверка синтаксиса через visudo и восстановление из live образа
До перезагрузки в многопользовательский режим обязательно проверяют синтаксис. Утилита visudo в режиме чистой проверки не трогает файл, а только валидирует его:
visudo -c
visudo -cf /etc/sudoers.d/deploy
Первая команда проверяет основной файл и каталог включений, вторая конкретный сниппет. Успешный вывод показывает "parsed OK" по каждому источнику. Ошибка на этом этапе ещё безболезненна, а после перезагрузки превратится в новый поход по консолям.
Дальше проверяют сам механизм: переключаются через su на обычного пользователя с правами sudo и выполняют пробную команду вроде sudo -l, которая распечатывает разрешённые правила. Если в выводе видны ожидаемые строки ALL и системы групп, восстановление завершено.
Бывают машины, где меню GRUB спрятано, загрузка мгновенная, а физический контакт с железом невозможен: удалённый сервер в датацентре, облачная виртуалка без проброса консоли. Тут спасает загрузка внешнего live образа через виртуальный привод или съёмный носитель.
После старта live окружения корневой раздел проблемной системы монтируют в каталог и переходят в chroot:
mount /dev/sda2 /mnt
mount --bind /dev /mnt/dev
mount --bind /proc /mnt/proc
mount --bind /sys /mnt/sys
chroot /mnt
Внутри chroot доступны все родные утилиты дистрибутива, и дальнейшая процедура повторяет однопользовательский сценарий: пересоздание файла конфигурации, валидация через visudo, восстановка контекстов SELinux. Для облачных образов полезно помнить про cloud-init: он пересоздаёт заранее описанные политики sudo при новом старте, и отсутствующие сниппеты для облачного пользователя иногда достаточно вернуть из шаблона /etc/cloud/cloud.cfg.d, если описание там сохранено.
Особый случай это виртуальные машины без доступа к хранилищу: диск подключают к вспомогательной ВМ, чинят файл, возвращают на место. Процедура выглядит громоздко, но на деле занимает считаные минуты и гарантированно вытаскивает машину из состояния потерянного административного доступа.
Частые ошибки при самостоятельном восстановлении и как их не повторять
Первая ошибка это правка основного файла вместо сниппета. Каталог sudoers.d создан именно для того, чтобы локальные правки жили отдельно от поставки дистрибутива: при обновлении пакета основной файл перезапишется эталоном, а локальные правила останутся на месте.
Вторая типовая ошибка это неправильные права. Попытка выставить 0644 для удобства чтения оборачивается отказом sudo принимать файл из соображений безопасности. Строгие 0440 и владелец root не декорация, а требование проверки целостности.
Третья ловушка ждёт на системах с включённым SELinux: даже идеальный файл без правильного контекста etc_t остаётся невидимым для механизма политики. Именно поэтому шаг с restorecon нельзя пропускать после ручного восстановления.
Четвёртая ошибка встречается в автоматизации: правка файла напрямую через sed или cat из скрипта, минуя валидацию, приводит к рассинхронизации между тем, что оператор видел на экране, и тем, что реально лежит на диске. Любое изменение в проде должно проходить валидацию visudo до перезаписи.
Профилактика повторного сбоя через дисциплину правок и автоматизацию
Опыт повторяемых инцидентов показывает, что проблемный деплой стоит городить строго с валидацией. Дисциплина начинается с простых привычек. Файл sudoers правят только через visudo, которая при сохранении сама проверяет синтаксис и отказывается записывать битую конфигурацию. Сниппеты в sudoers.d создают с шаблоном валидации, а не прямым редактором поверх живой системы.
Для автоматизации в Ansible есть модуль template с параметром validate, куда передают строку "visudo -cf %s": плейбук свалится на этапе проверки, и продуктив останется на рабочей конфигурации. Аналогичный приём встраивают в любые скрипты раскатки, чтобы ручные и автоматические правки подчинялись единому фильтру.
Полезно заодно вести артефакт восстановления: резервную копию эталонного файла sudoers и всех сниппетов в системе контроля версий. При инциденте достаточно вернуть проверенную версию командой в консоли, а не вспоминать, какие именно Defaults стояли раньше. Версионирование конфигурации это та самая страховка, о которой жалеют только после первой серьёзной аварии.
И последнее правило, выстраданное практикой: на боевых машинах логично держать разблокированный аккаунт root с надёжным паролем в запечатанном конверте или менеджере секретов. Знание того, что аварийный вход в rescue.target всегда доступен, превращает потенциальную аварию часовой давности в рядовую процедуру на пять минут.