Зачем DE знать про users и groups
Когда твой Airflow DAG падает с PermissionError: [Errno 13] Permission denied: '/var/log/airflow/dag_processor.log', первое что нужно понять — под каким пользователем работает Airflow scheduler и в какие группы этот пользователь входит. То же самое для chown-конфликтов при копировании файлов из S3, для прав на ~/.ssh/id_rsa, для sudo-проверок на CI runner.
Linux — multi-user OS с самого UNIX-а 1969 года. Каждый процесс, каждый файл, каждый сокет принадлежит какому-то пользователю и какой-то группе. Без этой модели нельзя разделять права, нельзя изолировать сервисы, нельзя ограничить rm -rf / от непривилегированного юзера.
UID: пользователь — это число
Когда ты делаешь whoami и видишь airflow, это имя. А для kernel пользователь — это число, UID (User ID). 32-битное беззнаковое целое, 0 до 4294967295. Имя airflow — это просто строка в /etc/passwd, которая мапит UID на читаемое для человека name.
$ id
uid=1000(levo) gid=1000(levo) groups=1000(levo),27(sudo),100(users),999(docker)
uid=1000 — это твой реальный идентификатор. Когда процесс делает open("/etc/shadow", O_RDONLY), kernel сравнивает не имя levo, а число 1000 с тем, что записано в inode файла. Имена нужны только людям — ядру они не интересны.
Категории UID
Разные UID-диапазоны несут разный смысл — от root до nobody.
Граница UID < 1000 важна на практике: useradd -r создаёт system user в диапазоне 100-999 (без home-директории, без shell для логина). А adduser без флагов на Debian/Ubuntu создаст regular user с UID >= 1000.
/etc/passwd: 7 полей через двоеточие
Формат /etc/passwd — это CSV с разделителем :, придуманный ещё для UNIX V1. Семь полей на строке, кодировка ASCII (на современных системах — UTF-8 для GECOS).
$ grep '^levo:' /etc/passwd
levo:x:1000:1000:Lev Neganov,,,:/home/levo:/bin/bash
Каждое поле имеет смысл — даже устаревший 'x' во втором.
Прочитать /etc/passwd может любой пользователь — это нормально. Там нет секретов, только маппинг. Поэтому getent passwd или cat /etc/passwd работают без sudo.
$ cat /etc/passwd | head -5
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
Обрати внимание на daemon, bin, sys — это исторические UNIX-юзеры, которые сохранились ради совместимости. Все имеют shell /usr/sbin/nologin, чтобы никто не мог зайти под ними.
/etc/shadow: где живут пароли
Когда пароль был в /etc/passwd, его мог прочитать любой и подобрать через словарь — passwd-файл readable для всех. В 1987 году ввели shadow passwords: хеш переехал в /etc/shadow, доступный только для root и группы shadow.
$ sudo cat /etc/shadow | grep '^levo'
levo:$y$j9T$abc...$xyz...:19850:0:99999:7:::
9 полей через ::
Хеш пароля плюс политика устаревания пароля.
Хеш проверить можно через mkpasswd -m yescrypt 'pwd' -s "j9T$abc" — алгоритмы детерминированы при одинаковой соли.
/etc/group: множественные группы
Пользователь имеет одну primary group (поле 4 в /etc/passwd) и может состоять в множестве secondary groups через /etc/group.
$ getent group | head -5
root:x:0:
daemon:x:1:
bin:x:2:
sys:x:3:
adm:x:4:syslog,levo
Поля: groupname:placeholder:GID:members. Последнее поле — список пользователей через запятую (это secondary members). Primary-членство в /etc/group не отображается: если у levo primary GID=1000 и группа levo имеет GID=1000, в группе levo:x:1000: поле members может быть пустым — членство неявное.
$ groups levo
levo : levo sudo users docker adm
Команда groups объединяет primary + secondary для удобства. То же показывает id.
Зачем secondary groups DE
- docker — без неё придётся писать
sudo docker runкаждый раз - sudo или wheel — даёт право на
sudo - adm или systemd-journal — позволяет читать
journalctlбез sudo - airflow — share-доступ к
/var/log/airflow/безchownкаждого файла
Команды идентификации
Каждая отвечает на свой вопрос.
$ whoami
levo
$ sudo whoami
root
$ id -u
1000
$ id -nG
levo sudo users docker adm
$ w
12:34:56 up 5 days, 4 users, load average: 0.42, 0.55, 0.61
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
levo pts/0 192.168.1.10 09:15 0.00s 1.23s 0.01s w
airflow pts/1 - 10:42 1h 45.3s 45.3s python
UID, GID и credential-набор процесса
Реальный/effective UID
Есть realUID (ruid) — кто ты на самом деле, и effectiveUID (euid) — чьими правами ты сейчас действуешь. Они отличаются после sudo или для SUID-программы:
$ id -u # real UID
1000
$ sudo id -u # effective стал 0
0
$ sudo id -ur # real тоже стал 0 (sudo делает setuid(0))
0
Внутри sudo оба UID становятся 0, но в окружении остаются переменные SUDO_USER=levo, SUDO_UID=1000 — чтобы скрипты могли узнать «кто меня вызвал». Подробнее про SUID будет в уроке 05.
Создание пользователей: useradd vs adduser
В Debian/Ubuntu есть две команды с похожими именами:
| Команда | Что это | Когда использовать |
|---|---|---|
useradd | Низкоуровневая утилита из shadow-utils. POSIX-стандарт. | Скрипты, Dockerfile, CI/CD. Предсказуемо. |
adduser | Высокоуровневый Perl-wrapper Debian/Ubuntu. Интерактивная. | Ручное создание на десктопе. |
# useradd: явно всё указываем
$ sudo useradd -m -s /bin/bash -G sudo,docker -c "ETL Worker" etl
# -m создаёт home, -s shell, -G secondary groups, -c GECOS
# adduser: задаёт вопросы интерактивно
$ sudo adduser etl
Adding user `etl' ...
Adding new group `etl' (1001) ...
Adding new user `etl' (1001) with group `etl' ...
Creating home directory `/home/etl' ...
Copying files from `/etc/skel' ...
New password: ...
В CI и Dockerfile всегда useradd:
RUN useradd -m -s /bin/bash -u 1001 airflow && \
mkdir -p /opt/airflow && \
chown -R airflow:airflow /opt/airflow
USER airflow
Флаг -r создаёт system user (UID < 1000, без home, без shell):
$ sudo useradd -r -s /usr/sbin/nologin postgres
Так делают postinst-скрипты пакетов: apt install postgresql создаст user postgres именно через useradd -r.
/etc/skel: шаблон home-директории
Когда useradd -m создаёт /home/etl/, он копирует туда всё содержимое /etc/skel/. Обычно там лежат .bashrc, .profile, .bash_logout — дефолтные dotfiles. Это удобный механизм для админа задать default-окружение для всех новых пользователей.
$ ls -la /etc/skel/
-rw-r--r-- 1 root root 220 .bash_logout
-rw-r--r-- 1 root root 3771 .bashrc
-rw-r--r-- 1 root root 807 .profile
Если ты хочешь, чтобы каждый новый user имел кастомный .bashrc с твоими aliases — клади его в /etc/skel/.
Попробуй сам
- Посмотри своего пользователя в
/etc/passwd:grep "^$(whoami):" /etc/passwd - Покажи только UID и primary GID:
id -u && id -g - Найди все system users (UID < 1000):
awk -F: '$3 < 1000 {print $1, $3}' /etc/passwd - Кто состоит в группе
sudo:getent group sudo - Когда ты последний раз менял пароль (только под sudo):
sudo chage -l "$(whoami)"
macOS-различия
/etc/passwdна macOS существует, но там нет реальных пользователей — только системные демоны. Реальные пользователи живут в Directory Services (dscl). Командаdscl . -list /Usersилиidвсё ещё работает.useraddиadduserна macOS отсутствуют или работают непредсказуемо — добавлять пользователей надо черезsysadminctlилиdscl./etc/shadowна macOS нет — хеши паролей хранятся в/var/db/dslocal/nodes/Default/users/.
Для Junior DE это редко критично — большинство работают в Linux-Docker-контейнерах даже с Mac-хоста.
Главное
- Пользователь для kernel — это число (UID), имя — это запись в
/etc/passwd. - UID 0 = root, UID < 1000 — системные сервисы, UID ≥ 1000 — обычные люди.
/etc/passwd— публичная (7 полей),/etc/shadow— приватная (только root, хеши паролей).- Primary group в
/etc/passwd, secondary — в/etc/group. whoami,id,groups,who,w— базовый toolkit идентификации.useraddдля скриптов и Docker,adduserдля интерактива./etc/skel/— шаблон для новых home-директорий.