Learning Platform
Глоссарий Troubleshooting
Урок 12.03 · 22 мин
Начальный
rootsudosudoersSecurityLinuxPermissions

root и sudo — абсолютная власть и почему её не дают всем

UID 0 — особенный. Это root, суперпользователь. Для kernel UID 0 эквивалентен «игнорируй все права, разрешай всё». Read-only файл? Root запишет. Чужой процесс? Root убьёт. Защищённая системная конфигурация? Root перепишет. Не существует команды, которую обычный пользователь может выполнить, а root нет.

И именно поэтому почти никогда не нужно работать под root постоянно. В этом уроке — что физически даёт root, как sudo вместо постоянного root, и principle of least privilege, на котором стоит вся современная безопасность Linux.


Зачем понимать, что такое root «на самом деле»

«Я просто использую sudo, когда надо. Не достаточно?» Не достаточно, если:

  • Вы развёртываете сервис — надо решать, под каким UID он бежит и какие capability ему нужны.
  • Вы аудитите безопасность — понять, через какие пути атакующий мог получить root.
  • Вы пишете Dockerfile — запускать процесс под root или non-root в контейнере.
  • Вы дебажите systemd-units — понимать, какие пользователи unitов имеют какие права.

И главное: понимая, что физически означает UID=0, вы понимаете, почему ВСЕ профессионалы стараются НЕ запускать процессы под root. Это не паранойя, это прямые потери при компрометации.


Что физически может root

Root в Linux может:

  1. Игнорировать DAC (Discretionary Access Control) — обычные rwx-проверки на файлах. Открывает что угодно для чтения и записи.
  2. Менять UID/GID — через setuid(0) или другие. Process под root может «стать» любым пользователем.
  3. Подключать/отключать файловые системы — mount, umount.
  4. Управлять сетью — открывать privileged ports (порт меньше 1024), настраивать interfaces, iptables.
  5. Управлять процессами — kill любой процесс, менять приоритет (nice -20).
  6. Загружать kernel modules — modprobe, insmod. Эффективно расширять kernel.
  7. Читать память других процессов — через ptrace, /proc/PID/mem.
  8. Управлять /dev — доступ к raw устройствам, /dev/sda1 напрямую.
  9. Перезагружать систему — reboot, shutdown.

И один особый случай: обходить permissions, но не immutable. Если файл имеет атрибут i (immutable, ставится через chattr +i), даже root не может его модифицировать без снятия атрибута. Это редкая защита от случайных команд под root.

sudo touch /tmp/file
sudo chattr +i /tmp/file
sudo rm -f /tmp/file
# rm: cannot remove '/tmp/file': Operation not permitted
sudo lsattr /tmp/file
# ----i---------e----- /tmp/file
sudo chattr -i /tmp/file
sudo rm /tmp/file   # теперь работает

Почему root опасен

«Если root всемогущ — что плохого?» Очень многое:

  • Случайная ошибка фатальна. rm -rf / под root удаляет всё. Под пользователем — максимум ваш home.
  • Скомпрометированный процесс под root = скомпрометирована система. Bug в сервисе — атакующий получает root.
  • Невозможно изолировать. Если 10 сервисов бегут под root, любой может убить другой, прочитать его данные, переписать конфиги.
  • Аудит сложнее. «Кто это сделал?» — все root, не отличить.

Пример реальных последствий:

# 2017, разработчик в GitLab случайно выполнил на проде:
rm -rf /var/opt/gitlab/postgresql/data
# Под обычным пользователем -- permission denied.
# Под root -- 6 часов даунтайма GitLab.com и потеря данных.

# Десятки сервисов:
# Apache до 2.4 бежал как root для bind 80, потом drop privileges.
# Современный nginx -- master root, workers под www-data.
# Postgres -- НИКОГДА не root, всегда под postgres.

Поэтому современный стандарт: процессы бегут под минимально необходимыми правами. Root только для запуска (если нужно bind privileged port или mount FS), потом drop privileges.


sudo: временный root для одной команды

sudo — утилита, которая позволяет авторизованному пользователю выполнить команду под другим пользователем (обычно root). Удобство: не нужно знать root-пароль, не нужно делать su и постоянно сидеть под root.

# Просто выполнить как root:
sudo apt update

# Sudo попросит ВАШ пароль (не root). Если в /etc/sudoers вы указаны как
# разрешённый пользователь -- команда выполнится.

Под капотом происходит:

  1. Sudo — это setuid root программа (-rwsr-xr-x владелец root). При запуске EUID становится 0.
  2. Проверяет, разрешено ли вам это в /etc/sudoers.
  3. Спрашивает ваш пароль (если cache не активен).
  4. Логирует попытку в /var/log/auth.log.
  5. Делает setuid(0) для дочерней команды.
  6. Выполняет команду через exec.
Жизненный цикл sudo-команды
user: sudo cmdПользователь запускает sudo. Это setuid-root программа, EUID процесса = 0 при старте
check /etc/sudoersSudo проверяет, разрешён ли этому пользователю запуск этой команды (по правилам ALL=(ALL) и т.д.)
ask passwordЕсли требуется (NOPASSWD не стоит) -- запрашивает пароль пользователя. Кешируется на ~15 минут по умолчанию
log to auth.logЛогирует попытку: кто, что запустил, время. Это важно для аудита
setuid(0)Понизить RUID до 0, чтобы дочерний процесс был полностью root
exec
run commandЗапустить заданную команду. После exec sudo превращается в саму команду

/etc/sudoers: правила доступа

/etc/sudoers — ключевой файл. Определяет, кто может что и от чьего имени.

# Безопасный способ редактирования (с проверкой синтаксиса):
sudo visudo

# Никогда не редактируйте /etc/sudoers напрямую -- ошибка в синтаксисе
# заблокирует sudo, и вы не сможете её починить.

Базовая структура правила:

USER  HOST=(RUNAS_USER)  COMMANDS

Реальные примеры:

# levoely может выполнять что угодно под любым пользователем на любом хосте:
levoely  ALL=(ALL:ALL) ALL

# Сократить: можно как любой пользователь, любая команда:
%sudo    ALL=(ALL:ALL) ALL
# % -- группа. Любой член группы sudo подходит под это правило.

# Конкретное ограничение: deploy-юзер может только рестартить nginx:
deploy   ALL=(root) NOPASSWD: /usr/bin/systemctl restart nginx, /usr/bin/systemctl status nginx

# NOPASSWD -- без пароля. Полезно для скриптов и CI.

# Запрет: monitoring-пользователь НЕ может делать никаких опасных команд:
monitoring ALL=(ALL) NOPASSWD: /usr/bin/journalctl, /usr/bin/systemctl status *

Конкретные правила — основа security. Никогда не давайте ALL человеку или сервису, который не должен мочь всё. Минимум — конкретные команды.

# Посмотреть, что вам разрешено:
sudo -l
# User levoely may run the following commands on host:
#     (ALL : ALL) ALL

sudo -i, sudo -u, sudo su — разные виды

# 1. Просто команду:
sudo command         # одна команда как root, возврат

# 2. Открыть shell под root:
sudo -i              # interactive login shell, как если бы залогинились root
sudo -s              # shell с сохранением окружения вызывающего
sudo su -            # старый способ через su, аналогично sudo -i

# 3. Под другим пользователем:
sudo -u postgres psql    # запустить psql от имени postgres
sudo -u www-data -i      # interactive shell под www-data

# 4. Сохранить переменные окружения:
sudo -E command          # -E = preserve environment
# Полезно, когда команда зависит от ваших HTTP_PROXY, PATH, etc.

# 5. Без password prompt (только если NOPASSWD в sudoers):
sudo -n command

sudo -u service-user — хороший паттерн для отладки. Вы видите систему ровно так, как видит её этот сервис.


Логирование sudo: бесценно

Каждый sudo-вызов логируется (обычно в /var/log/auth.log или journalctl):

sudo grep sudo /var/log/auth.log | tail
# May 18 12:00:01 host sudo: levoely : TTY=pts/0 ; PWD=/home/levoely ; USER=root ; COMMAND=/usr/bin/apt update

# Через systemd:
journalctl _COMM=sudo | tail

В production это первое, куда смотрят при инциденте: «что эта команда делала под root в 3 ночи». Без sudo (когда люди использовали shared root password) такой аудит был невозможен.

WARNING

NOPASSWD: ALL — крайне опасный паттерн. Если у атакующего есть ваш shell (например, через скомпрометированный SSH ключ), он получает мгновенный root, не зная пароль. Используйте NOPASSWD ТОЛЬКО для конкретных безвредных команд (systemctl restart, journalctl и т.п.).


Principle of Least Privilege на практике

Главный принцип безопасности: каждый компонент должен иметь МИНИМАЛЬНЫЕ права для выполнения своей работы. Не больше.

Применения:

1. Сервисы под dedicated пользователями.

# Postgres под postgres, не root:
ps -eo user,cmd | grep postgres
# postgres /usr/lib/postgresql/14/bin/postgres -D /var/lib/postgresql/...

# Nginx -- master root (для bind 80), workers под www-data:
ps -eo user,cmd | grep nginx
# root      nginx: master process
# www-data  nginx: worker process

2. Docker без root.

# Плохо:
FROM ubuntu
CMD ["python", "app.py"]   # бежит под root!

# Хорошо:
FROM ubuntu
RUN useradd -r -u 1000 appuser
USER 1000:1000
CMD ["python", "app.py"]
USER и безопасность в Dockerfile: запуск контейнера без root

3. Sudo с конкретными командами.

# Deploy-боту разрешено только рестартить сервис:
deploybot ALL=(root) NOPASSWD: /usr/bin/systemctl restart myapp.service

4. SystemD-сервисы с DropPrivileges.

[Service]
User=myapp
Group=myapp
NoNewPrivileges=yes
ProtectSystem=strict
PrivateTmp=yes
CapabilityBoundingSet=CAP_NET_BIND_SERVICE

Каждый из этих механизмов уменьшает blast radius компрометации.


Реальный пример: «должно ли это бежать под root?»

Допустим, у вас сервис, который слушает порт 80 (privileged), читает конфиг из /etc/myapp/, пишет логи в /var/log/myapp/, и общается с PostgreSQL.

«Простое решение»: бежать под root, тогда все привилегии есть.

Правильное решение:

  1. Bind to port 80 — root не нужен, нужна capability CAP_NET_BIND_SERVICE. SystemD умеет это дать без root.
  2. Read /etc/myapp/ — сделать chown root:myapp /etc/myapp -R и chmod 640. Сервис под myapp читает через группу.
  3. Write /var/log/myapp/chown myapp:myapp /var/log/myapp. Сервис пишет как owner.
  4. Talk to PostgreSQL — через сетевой socket. Не требует root.

Итог: сервис бежит под myapp (UID >1000), имеет capability для порта 80, и доступ только к нужным файлам. Если его взломают, атакующий не получит root.

# Посмотреть пример хорошего systemd unit:
cat /lib/systemd/system/nginx.service
# Часто содержит:
# User=...
# Group=...
# ProtectSystem=full
# NoNewPrivileges=yes
# CapabilityBoundingSet=...

Что НЕ делать с root

  • Не сидеть в sudo -i весь день. Открыли shell на час — забыли. Любой rm опасен.
  • Не выполнять незнакомые скрипты под sudo. curl ... | sudo bash — классический способ получить мaлварь.
  • Не давать sudo ALL пользователям-людям. Им хватит конкретных команд.
  • Не делать процессы-демоны под root без необходимости. Если нужно стартовать — drop privileges как можно раньше.
  • Не использовать root для повседневной работы. Даже на своём ноутбуке.

Попробуй сам

# 1. Кем вы сейчас:
whoami
id
# uid=1000(levoely)... -- хорошо, не 0

# 2. Что вам разрешено через sudo:
sudo -l

# 3. Найти все процессы, бегущие под root:
ps -eo pid,user,cmd | awk '$2 == "root"' | head
# Сравните: 'should this be root?'

# 4. Кто что делал через sudo за последний час:
sudo journalctl _COMM=sudo --since "1 hour ago"

# 5. Посмотреть содержимое sudoers (без редактирования):
sudo cat /etc/sudoers
ls /etc/sudoers.d/   # доп. правила обычно тут

# 6. Запустить shell под другим пользователем (если есть права):
sudo -u nobody whoami    # нужно иметь право run-as nobody

# 7. Какие capability у бинарника?
getcap /usr/bin/ping
# /usr/bin/ping cap_net_raw=ep
# Если так -- ping не setuid, использует точечную capability

# 8. Поиграть с immutable:
sudo touch /tmp/imm
sudo chattr +i /tmp/imm
sudo rm /tmp/imm     # Operation not permitted, даже root!
sudo chattr -i /tmp/imm
sudo rm /tmp/imm     # теперь можно

Проверка знанийKnowledge check
Junior разработчик добавил в /etc/sudoers строку 'developer ALL=(ALL) NOPASSWD: ALL'. Объясни на конкретных сценариях, чем это плохо, и предложи правильное решение для типичных задач developer-а (deploy, рестарт сервиса, посмотреть логи).
ОтветAnswer
Эта строка даёт developer полный root без пароля. Плохо это тем, что: 1) Любая компрометация = root. Если кто-то получил SSH-сессию developer (украл ключ, перехватил SSH-агент, malicious extension в IDE), он мгновенно получает полный контроль над сервером. Без пароля даже барьера нет. 2) Случайная ошибка фатальна. `sudo rm -rf /var/lib/postgresql` -- developer не сидит под root, но один опечатанный sudo превращает обычную ошибку в катастрофу. Без NOPASSWD появилась бы хотя бы секунда на размышление при вводе пароля. 3) Аудит превращается в кашу. `sudo journalctl ...` -- developer выполнил, ОК. `sudo rm -rf /` -- developer выполнил, тоже OK с точки зрения policy. По логам невозможно отличить рутину от инцидента. 4) Нарушение principle of least privilege. Developer не должен иметь право, например, удалить базу или поменять кернел. ALL ему даёт всё это. Правильное решение -- конкретные команды: ``` # Deploy: рестарт конкретного сервиса developer ALL=(root) NOPASSWD: /usr/bin/systemctl restart myapp.service, /usr/bin/systemctl status myapp.service # Логи: только чтение journalctl developer ALL=(root) NOPASSWD: /usr/bin/journalctl # Доступ к runtime сервиса: developer ALL=(myapp) /bin/bash, /usr/bin/psql, /usr/bin/python3 ``` С такими правилами: - Можно делать `sudo systemctl restart myapp.service` без пароля -- routine deploy. - Можно `sudo journalctl -u myapp -f` -- смотреть логи. - Можно `sudo -u myapp -i` -- залогиниться под сервис-юзера для debug. - Нельзя `sudo rm -rf /` -- эта команда не в разрешённых. - Нельзя `sudo bash` -- бан попыток обойти. Дополнительно: - Logging остаётся: каждый sudo-вызов в /var/log/auth.log с конкретной командой. Удобно для аудита. - Если нужны новые команды -- добавляются в sudoers explicitly. Это явное расширение прав, видимое. - Для команд, не вписывающихся в whitelist -- использовать interactive sudo с паролем, как замедляющий фактор. Бонус: для CI/deploy лучше вообще выделить отдельного пользователя deploybot с очень узкими NOPASSWD-правилами. Developer -- человек, у него полный SSH; deploybot -- service account, у него только команды rollout. Что НЕ работает: `sudoers` с `ALL` под `!something` для исключения опасных команд. Это легко обходится (alias, путь через симлинк, env vars). Whitelist > blacklist для безопасности.

Проверьте понимание

Результат: 0 из 0
Концептуальный
Вопрос 1 из 6. Что физически означает 'UID = 0' для kernel?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 4