open atlas
↑ К треку
Linux: операционная система LIN · 05 · 01

passwd и shadow

Каждый процесс Linux выполняется как UID. /etc/passwd отображает UID на имена и оболочки; /etc/shadow хранит хеш пароля и политику устаревания. Понимание этого разделения — и формата хеша — основа для рассуждений об идентичности и хранении учётных данных.

LIN Junior ◷ 20 min
Уровень
ОсновыJuniorMiddleSenior

Ты создаёшь новую сервисную учётку и запускаешь sudo -u myservice /opt/app/run. Команда падает с ошибкой «No such user». Пользователь есть в провизионинг-скрипте, но getent passwd myservice не возвращает ничего. Или ты получаешь в наследство сервер, где в /etc/passwd значится root:x:0:0:root:/root:/bin/bash, а shadow-файл недоступен для чтения. Или аудит безопасности спрашивает, как на этом хосте хранятся пароли, — и нужен настоящий ответ, а не «как-то хешируются». Идентичность в Linux строится на двух плоских текстовых файлах, существующих с 1979 года. Знать их формат наизусть — это база для рассуждений о правах, разрешениях и аутентификации.

Цель

После этого урока ты сможешь прочитать каждое поле записи /etc/passwd, объяснить, зачем существует отдельный /etc/shadow, разобрать формат $id$salt$hash, различить системных и человеческих пользователей, а также создавать и изменять пользователей с помощью useradd/usermod.

1

Каждый процесс в Linux выполняется как числовой UID. Ядро отслеживает идентичность как 32-битное беззнаковое целое — имя пользователя, которое ты видишь в ls -l или ps, это лишь удобство отображения, обеспечиваемое поиском getpwuid() в libc. Ядро заботится только о числе.

# Свой UID и GID
id
# uid=1000(alice) gid=1000(alice) groups=1000(alice),27(sudo),1001(docker)

# Числовой UID работающего процесса
ps -o pid,uid,user,comm -p $$
# PID   UID USER     COMMAND
# 4231  1000 alice    bash

# Через procfs — syscall возвращает числовой UID напрямую
grep -m1 '^Uid' /proc/$$/status
# Uid:    1000    1000    1000    1000
# (real  effective  saved  filesystem UIDs)

Четыре значения UID важны при повышении привилегий — effective UID определяет, что тебе разрешено делать прямо сейчас, а real UID фиксирует, кто ты на самом деле. К этому различию мы вернёмся в следующем уроке при обсуждении setuid-бинарников.

2

/etc/passwd содержит семь полей, разделённых двоеточием. Каждый пользователь — человек или системный — живёт в этом файле. Он доступен для чтения всем, поскольку программы должны разрешать UID в имена.

# Просмотр конкретной записи
getent passwd alice
# alice:x:1000:1000:Alice Smith,,,:/home/alice:/bin/bash
# [1]  [2][3] [4]  [5]          [6]          [7]

# Разбивка полей:
# 1 — имя для входа (alice)
# 2 — поле пароля: 'x' означает «смотри в /etc/shadow»; исторически здесь был хеш
# 3 — UID (1000)
# 4 — первичный GID (1000) — группа, с которой стартует процесс
# 5 — GECOS: свободный комментарий, обычно Полное имя, Кабинет, Телефон
# 6 — домашний каталог (/home/alice)
# 7 — оболочка входа (/bin/bash); 'nologin' или 'false' блокируют интерактивный вход

Поле пароля x вместо реального хеша — это и есть shadow-разделение в действии. До shadow-паролей (Unix 1980-х) хеш хранился прямо здесь, а поскольку файл был доступен для чтения всем, любой пользователь системы мог скопировать его и провести офлайн-атаку по словарю. Перемещение хешей в файл, доступный только root, стало исправлением этой уязвимости.

3

/etc/shadow хранит реальный хеш и политику устаревания. Файл доступен только root (режим 640, владелец root:shadow в Debian/Ubuntu). Формат — девять полей, разделённых двоеточием.

# Только root может читать shadow напрямую
sudo grep '^alice:' /etc/shadow
# alice:$6$rounds=5000$SaltGoesHere$LongHashValue...:19800:0:99999:7:::

# Разбивка полей:
# 1 — имя для входа (совпадает с passwd)
# 2 — хеш пароля в формате $id$[param$]salt$hash
# 3 — последнее изменение: дни с 1970-01-01 (19800 = некоторая дата)
# 4 — мин. дней до смены пароля (0 = нет минимума)
# 5 — макс. дней до обязательной смены (99999 = никогда)
# 6 — дней предупреждения до истечения (7 = предупреждать за 7 дней)
# 7 — дней неактивности после истечения до блокировки
# 8 — дата истечения аккаунта (дни с эпохи; пусто = нет срока)
# 9 — зарезервировано

# Алгоритм хеша по префиксу $id$:
# $1$  = MD5 (устарело, НЕ использовать)
# $5$  = SHA-256
# $6$  = SHA-512 (текущий умолчальный в Debian/Ubuntu)
# $y$  = yescrypt (Ubuntu 22.04+, memory-hard)
# $2b$ = bcrypt (некоторые дистрибутивы)

Тройка $id$salt$hash — самоописывающая строка crypt(3). Идентификатор алгоритма указывает системе, какую функцию использовать для проверки. Именно поэтому можно мигрировать на более сильный алгоритм: новые смены пароля создают записи нового формата, старые хеши продолжают проверяться с исходным алгоритмом до тех пор, пока пользователь не сменит пароль.

4

Системные и человеческие пользователи различаются диапазоном UID и оболочкой. Соглашение (настраивается в /etc/login.defs) таково:

# Диапазоны UID в Debian/Ubuntu (из /etc/login.defs)
grep -E '^(UID_MIN|UID_MAX|SYS_UID_MIN|SYS_UID_MAX)' /etc/login.defs
# SYS_UID_MIN   100
# SYS_UID_MAX   999
# UID_MIN      1000
# UID_MAX     60000

# Системные пользователи (UID 100-999) запускают демонов; у них нет домашнего каталога или оболочки
getent passwd www-data
# www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin

getent passwd nobody
# nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin

# Человеческие пользователи начинаются с UID 1000
getent passwd alice
# alice:x:1000:1000:Alice Smith,,,:/home/alice:/bin/bash

Системная учётка с /usr/sbin/nologin в качестве оболочки не может использоваться для интерактивного входа — даже если ты каким-то образом знаешь её пароль, ядро запустит оболочку, но nologin немедленно выведет «This account is currently not available» и завершится. Это правильный способ создать учётку демона, в которую никогда не должны заходить по SSH.

5

useradd и usermod — инструменты для создания и изменения пользователей. Ключевые флаги кодируют именно поля, описанные выше.

# Создать системного пользователя для демона (без домашнего каталога, без оболочки)
sudo useradd \
  --system \           # UID из системного диапазона (100-999)
  --no-create-home \   # не создавать /home/myservice
  --shell /usr/sbin/nologin \
  --comment "MyService daemon" \
  myservice

# Проверить
getent passwd myservice
# myservice:x:123:123:MyService daemon:/:/usr/sbin/nologin

# Создать человеческого пользователя с домашним каталогом и bash
sudo useradd \
  --create-home \
  --shell /bin/bash \
  --comment "Bob Jones" \
  bob
sudo passwd bob          # установить пароль интерактивно (записывается в shadow)

# Изменить существующего пользователя: добавить в дополнительную группу
sudo usermod --append --groups docker alice
# --append КРИТИЧЕН: без него --groups ЗАМЕНИТ все дополнительные группы

# Заблокировать учётку (добавляет ! перед хешем в shadow — вход с паролем не работает,
# вход по ключу SSH по-прежнему работает)
sudo usermod --lock bob
sudo grep '^bob:' /etc/shadow
# bob:!$6$...:...   <- префикс !

Флаг --append в usermod -G — классический подводный камень. Без него sudo usermod --groups docker alice установит группы alice только как docker, убрав её из sudo и всех остальных групп. Всегда используй --groups вместе с --append.

Разбор примера

Диагностика сервисной учётки, которую не удаётся создать провизионинг-скриптом.

Скрипт развёртывания на новом сервере падает с ошибкой useradd: user 'apprunner' already exists. Но id apprunner возвращает «no such user».

# Шаг 1: проверить, не занят ли UID (а не имя)
grep ':999:' /etc/passwd
# oldservice:x:999:999:Old daemon:/:/usr/sbin/nologin
# (кто-то ранее запустил useradd --system oldservice; он занял UID 999)

# Шаг 2: найти реальную причину — имя есть в другом источнике
getent passwd apprunner
# (нет вывода — нет в /etc/passwd)

# Проверить nsswitch.conf — система может резолвить из LDAP или NIS
grep passwd /etc/nsswitch.conf
# passwd:         files systemd ldap
# ldap есть в цепочке — там есть запись для apprunner

# Шаг 3: подтвердить через LDAP
ldapsearch -x -b "dc=corp,dc=example,dc=com" "(uid=apprunner)" uid uidNumber 2>/dev/null
# dn: uid=apprunner,ou=service,dc=corp,dc=example,dc=com
# uid: apprunner
# uidNumber: 2500

# Шаг 4: создать локально с явным UID без конфликта
sudo useradd --system --uid 450 --no-create-home --shell /usr/sbin/nologin apprunner
# getent passwd apprunner теперь вернёт локальную запись (files имеет приоритет над ldap)

Ключевое понимание: useradd: user already exists означает, что имя найдено где-то в цепочке nsswitch — не обязательно в /etc/passwd. Всегда используй getent passwd (учитывает nsswitch), а не grep /etc/passwd (видит только локальный файл), при диагностике сбоев поиска пользователей.

Почему это работает

Зачем пароль вообще переехал из /etc/passwd? В раннем Unix /etc/passwd хранил сам DES-зашифрованный хеш, и файл был обязан быть читаем всем, чтобы программы вроде ls могли отображать имена пользователей. Любой локальный пользователь мог скопировать файл и спокойно провести офлайн-атаку по словарю. Система shadow-паролей (стандартизирована примерно в 1988 году) разделила файл: имена пользователей, UID и оболочки остаются читаемыми в passwd; хеши переезжают в /etc/shadow, принадлежащий root:shadow, режим 640. Именно поэтому группа shadow существует в Debian — программы вроде su и ssh могут читать shadow, вступив в эту группу, без работы от полного root.

Частая ошибка

На macOS ничего из этого не работает. macOS использует Directory Services (dscl) поверх Open Directory, а не плоские текстовые файлы. Нет /etc/shadow, нет useradd, а /etc/passwd на macOS — статическая заглушка совместимости для нескольких системных учёток, не являющаяся реальной базой пользователей. Если ты пишешь провизионинг-скрипты для Linux и macOS одновременно, всегда ограничивай логику с useradd/shadow проверкой uname -s или используй слой абстракции.

Проверь себя
Викторина

Ты запускаешь 'grep apprunner /etc/passwd' и не получаешь вывода, но 'sudo useradd apprunner' завершается ошибкой 'user already exists'. Что скорее всего происходит?

Итог

Идентичность в Linux числовая: каждый процесс выполняется как UID и GID. /etc/passwd отображает эти числа на имена, домашние каталоги и оболочки — файл читаем всем. /etc/shadow хранит реальный хеш пароля (формат $id$salt$hash: $6$ = SHA-512, $y$ = yescrypt) и поля устаревания — только для root (режим 640). Системные пользователи (UID 100–999) запускают демонов с /usr/sbin/nologin и без домашнего каталога. Человеческие пользователи начинаются с UID 1000. useradd создаёт пользователей; usermod изменяет — всегда используй --append с --groups, чтобы не заменить существующие группы. Используй getent passwd вместо grep /etc/passwd, когда nsswitch резолвит пользователей из LDAP или других источников.

Практика

Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.

вспомнитьприменитьуглубить0 из 4 завершено

Что-то непонятно?

Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.

хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources2
expand
  1. 01
  2. 02

Trademarks belong to their respective owners. Editorial reference only.