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 может:
- Игнорировать DAC (Discretionary Access Control) — обычные rwx-проверки на файлах. Открывает что угодно для чтения и записи.
- Менять UID/GID — через
setuid(0)или другие. Process под root может «стать» любым пользователем. - Подключать/отключать файловые системы — mount, umount.
- Управлять сетью — открывать privileged ports (порт меньше 1024), настраивать interfaces, iptables.
- Управлять процессами — kill любой процесс, менять приоритет (nice -20).
- Загружать kernel modules — modprobe, insmod. Эффективно расширять kernel.
- Читать память других процессов — через ptrace, /proc/PID/mem.
- Управлять /dev — доступ к raw устройствам, /dev/sda1 напрямую.
- Перезагружать систему — 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 вы указаны как
# разрешённый пользователь -- команда выполнится.
Под капотом происходит:
- Sudo — это setuid root программа (
-rwsr-xr-xвладелец root). При запуске EUID становится 0. - Проверяет, разрешено ли вам это в
/etc/sudoers. - Спрашивает ваш пароль (если cache не активен).
- Логирует попытку в
/var/log/auth.log. - Делает
setuid(0)для дочерней команды. - Выполняет команду через
exec.
/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) такой аудит был невозможен.
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, тогда все привилегии есть.
Правильное решение:
- Bind to port 80 — root не нужен, нужна capability
CAP_NET_BIND_SERVICE. SystemD умеет это дать без root. - Read /etc/myapp/ — сделать
chown root:myapp /etc/myapp -Rиchmod 640. Сервис под myapp читает через группу. - Write /var/log/myapp/ —
chown myapp:myapp /var/log/myapp. Сервис пишет как owner. - 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 # теперь можно