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

PAM и вход в систему

PAM — промежуточный слой аутентификации между программами (sshd, login, sudo) и реальным механизмом проверки. Стек имеет четыре группы: auth, account, password, session. Флаги управления (required/requisite/sufficient) определяют, фатален ли сбой модуля.

LIN Senior ◷ 24 min
Уровень
ОсновыJuniorMiddleSenior

Ты добавляешь LDAP-аутентификацию на сервер — и теперь локальные пользователи не могут войти, даже root. Или модуль pam_faillock блокирует учётки после трёх неверных попыток, но он останавливает и SSH-входы по ключу, и ты не понимаешь почему. Или нужно принудительно задать лимиты ресурсов (дескрипторы файлов, максимум процессов) на пользователя, а настройки ulimit сбрасываются при каждом входе. Или аудитор просит объяснить каждый шаг от ввода пароля до получения оболочки. У всех этих вопросов один ответ: PAM — промежуточный слой аутентификации, которому делегирует каждая программа входа в систему на современном Linux.

Цель

После этого урока ты сможешь читать PAM-конфиг и предсказывать результат при успехе или сбое любого модуля, объяснять четыре группы PAM и четыре флага управления, отслеживать поток входа sshd через PAM, понимать назначение nsswitch.conf для поиска пользователей и безопасно изменять PAM-стек для типовых задач.

1

PAM отделяет политику аутентификации от реализации. До PAM (начало 1990-х) каждая программа, требующая аутентификации пользователей — login, ftp, rsh — содержала собственный жёстко закодированный вызов функции проверки Unix-пароля. Переход с паролей на токены означал перекомпиляцию каждой программы. PAM (Pluggable Authentication Modules, 1995) вставил стандартный API между программами и бэкендами аутентификации: программы вызывают pam_authenticate(), PAM читает политику из /etc/pam.d/<service> и загружает указанные модули в рантайме.

# Каталог /etc/pam.d/: один файл на сервис
ls /etc/pam.d/
# common-auth      common-account   common-password  common-session
# login            sshd             sudo             su
# gdm-password     cron             ...

# Программы компонуются с libpam и вызывают её — они не содержат логики аутентификации
ldd /usr/bin/sudo | grep pam
# libpam.so.0 => /lib/x86_64-linux-gnu/libpam.so.0

# PAM API с точки зрения программы:
# 1. pam_start(service, user, conv, &handle)  — открывает PAM-транзакцию
# 2. pam_authenticate(handle, flags)           — запускает стек 'auth'
# 3. pam_acct_mgmt(handle, flags)             — запускает стек 'account'
# 4. pam_open_session(handle, flags)           — запускает стек 'session'
# 5. pam_end(handle, retval)                  — очищает ресурсы

# Функция conversation (conv) — как PAM запрашивает пароль или токен у пользователя;
# программа обеспечивает ввод-вывод, PAM обеспечивает политику

Эта архитектура означает, что 2FA, LDAP, смарт-карты или биометрию можно добавить во все программы входа одновременно, отредактировав конфигурацию PAM — без перекомпиляции чего-либо. Она также означает, что ошибка в конфигурации PAM может одновременно сломать все каналы аутентификации.

2

PAM имеет четыре группы модулей (называемых «типами»), каждая с отдельной ролью. PAM-конфиг — это последовательность строк, каждая привязывает один модуль к одной группе.

# Формат файла: type  control  module  [arguments]
cat /etc/pam.d/sshd
# @include common-auth       <- включить общий стек auth
# @include common-account
# @include common-session
# session    optional   pam_motd.so motd=/run/motd.dynamic
# session    optional   pam_motd.so noupdate
# session    required   pam_limits.so   <- применить ulimits
# session    [success=1 default=ignore] pam_succeed_if.so service in cron quiet use_uid
# session    required   pam_unix.so

# Четыре группы:
# auth:     Является ли пользователь тем, за кого себя выдаёт? (пароль, токен, ключ)
# account:  Разрешён ли вход этой учётке? (истекла? заблокирована? временное ограничение?)
# password: Как меняются пароли (вызывается passwd(1))
# session:  Что происходит после успешной аутентификации (монтирование home, лимиты, лог, env)

Директива @include подтягивает /etc/pam.d/common-auth, /etc/pam.d/common-account и т.д. Debian/Ubuntu централизует общую политику в этих common-файлах, чтобы включение 2FA в common-auth применялось ко всем сервисам сразу. На RHEL/Fedora authselect управляет этими общими файлами через профили.

3

Флаги управления определяют, является ли сбой модуля фатальным или пропускаемым. Это тончайшая часть PAM и источник большинства ошибок конфигурации.

# Четыре основных флага управления:
# required:   модуль запускается; сбой означает провал стека — НО продолжать выполнение
#             оставшихся модулей (чтобы вызывающий не знал, КАКОЙ модуль провалился)
# requisite:  как required, но при сбое НЕМЕДЛЕННО завершает стек
#             (останавливает стек; быстрый сбой; раскрывает позицию сбоя)
# sufficient: если этот модуль УСПЕШЕН (и ни один required до него не провалился),
#             стек немедленно успешен — пропустить оставшиеся модули
# optional:   запустить; игнорировать возвращаемое значение для прохода/провала

# Читаем /etc/pam.d/common-auth:
cat /etc/pam.d/common-auth
# auth    [success=1 default=ignore]  pam_unix.so nullok
# auth    requisite                   pam_deny.so
# auth    required                    pam_permit.so

# Скобочная нотация [success=N default=ignore] — обобщение:
# success=1 означает "при успехе пропустить СЛЕДУЮЩУЮ 1 строку" (прыгнуть через pam_deny.so)
# default=ignore означает "при любом другом результате игнорировать и продолжать"
# Вместе: если pam_unix.so (проверка пароля) успешен, прыгнуть через pam_deny.so
# и попасть на pam_permit.so (явный успех). Если pam_unix.so провалился,
# упасть в pam_deny.so (явный провал).

# Почему 'required' продолжает при сбое вместо немедленной остановки:
# Защита от атаки по времени. Если сбой проверки пароля выходит немедленно,
# а проверка LDAP занимает 200 мс, атакующий по измерению времени аутентификации
# может определить, существует ли пользователь локально или удалённо.
# Запуск всех required-модулей независимо от результата нормализует время.

Различие requisite и required важно: используй requisite в начале стека, когда быстрый сбой безопасен и желателен (например, проверка, что учётка не заблокирована, до запроса пароля). Используй required, когда нужна нормализация времени или последующие модули должны выполняться независимо от результата.

4

nsswitch.conf управляет ТЕМ, ГДЕ ищется информация о пользователях и группах — ещё до запуска PAM. PAM аутентифицирует, но не может аутентифицировать пользователя, которого нет в службе имён.

# /etc/nsswitch.conf: конфигурация переключателя служб имён
cat /etc/nsswitch.conf
# passwd:     files systemd
# group:      files systemd
# shadow:     files
# hosts:      files mdns4_minimal [NOTFOUND=return] dns myhostname
# networks:   files
# ...

# 'passwd: files systemd' означает:
# 1. Сначала искать в /etc/passwd ('files')
# 2. Если не найдено, спросить systemd-userdb ('systemd') — обрабатывает systemd-homed пользователей
# В корпоративной среде: 'passwd: files ldap' добавляет LDAP как запасной вариант

# getent учитывает nsswitch — grep /etc/passwd не учитывает
getent passwd alice        # проверяет files, затем systemd
grep '^alice:' /etc/passwd # проверяет только /etc/passwd

# Порядок разрешения важен: 'files' первым означает, что локальные пользователи
# перекрывают LDAP-пользователей с тем же именем — важное свойство безопасности
# 'ldap' перед 'files' позволил бы LDAP перекрывать локальных пользователей (опасно)

# При добавлении LDAP:
# passwd: files ldap   — локальные пользователи всегда побеждают; LDAP заполняет пробелы
# group:  files ldap
# shadow: files        — shadow ОСТАЁТСЯ только files (LDAP обрабатывает свою аутентификацию)

# Тест разрешения имён без входа в систему
getent passwd someuser     # работает даже для LDAP-пользователей
id someuser                # разрешает UID, GID, все группы

Слой nsswitch отвечает на вопрос «существует ли этот пользователь и каковы его UID/GID?». Затем PAM отвечает на вопрос «может ли этот пользователь аутентифицироваться?». Оба необходимы для успешного входа. Классическая ошибка при добавлении LDAP — поставить ldap перед files в nsswitch, что позволяет скомпрометированному LDAP-серверу затенять локальных root или системные учётки.

5

Группа session — место настройки пост-аутентификационного окружения — и источник многих производственных неожиданностей. pam_limits.so применяет настройки ulimit; pam_env.so задаёт переменные окружения; pam_unix.so в session логирует вход в wtmp/utmp.

# pam_limits.so читает /etc/security/limits.conf и /etc/security/limits.d/
cat /etc/security/limits.d/myapp.conf
# @myapp    soft    nofile    65536
# @myapp    hard    nofile    131072
# @myapp    soft    nproc     4096
# @myapp    hard    nproc     8192
# domain: @group означает всех членов группы 'myapp'
# type: soft = по умолчанию; hard = потолок (пользователь может поднять до hard)
# item: nofile = открытые файловые дескрипторы; nproc = максимум процессов

# Эти лимиты применяются pam_limits.so в группе session
# Они применяются ТОЛЬКО к сессиям входа, проходящим через PAM
# Если сервис использует systemd, используй вместо этого директивы unit-файла:
# LimitNOFILE=65536   <- в секции [Service] unit-файла

# pam_env.so: задать переменные окружения из файла
cat /etc/security/pam_env.conf
# EDITOR    DEFAULT=/usr/bin/vim
# TZ        DEFAULT=UTC

# Трассировка полного SSH-входа через PAM (логическая последовательность):
# 1. sshd аутентифицирует ключ/пароль
# 2. sshd вызывает pam_authenticate() -> запускает стек common-auth
# 3. sshd вызывает pam_acct_mgmt()   -> запускает common-account (учётка валидна?)
# 4. sshd вызывает pam_open_session() -> модули session:
#    - pam_loginuid.so  (устанавливает /proc/self/loginuid — аудит-подсистема)
#    - pam_limits.so    (применяет limits.conf)
#    - pam_env.so       (задаёт окружение)
#    - pam_unix.so      (логирует в utmp/wtmp)
#    - pam_motd.so      (выводит /etc/motd)

# Sufficient-модуль в стеке auth, замыкающий цикл session:
# pam_sss.so (SSSD) с 'sufficient': если LDAP-аутентификация успешна,
# pam_unix.so (проверка локального пароля) пропускается — но session всё равно
# выполняется полностью, потому что модули session НЕ замыкаются 'sufficient' в auth

Флаг sufficient в группе auth НЕ пропускает модули session. Session всегда выполняется полностью после успеха auth. Это удивляет операторов, добавляющих sufficient LDAP-модуль в надежде обойти pam_limits — они применяются всё равно.

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

Диагностика ситуации, когда SSH-аутентификация по ключу успешна, но пользователь немедленно отключается.

Симптом: ssh alice@server показывает баннер, затем отключается с «Connection closed by server». Аутентификация по паролю тоже не работает. Root SSH работает нормально.

# Шаг 1: проверить лог аутентификации sshd
journalctl -u ssh --since '5 minutes ago' | grep alice
# Jun 21 14:50:01 server sshd[9821]: Accepted publickey for alice from 10.0.0.5
# Jun 21 14:50:01 server sshd[9821]: pam_unix(sshd:session): session opened for user alice
# Jun 21 14:50:01 server sshd[9821]: pam_limits(sshd:session): Could not set limit for 'nproc'
# Jun 21 14:50:01 server sshd[9821]: fatal: PAM: pam_open_session(): System error
# Jun 21 14:50:01 server sshd[9821]: Disconnected from user alice 10.0.0.5

# Аутентификация прошла! Отключение происходит в группе SESSION, а не auth.
# pam_limits.so не смог применить лимит nproc.

# Шаг 2: проверить limits.conf
grep -r 'alice\|@alice\|nproc' /etc/security/limits.conf /etc/security/limits.d/
# @myapp   hard   nproc   4096
# alice состоит в группе 'myapp' — этот лимит сам по себе нормален

# Шаг 3: проверить жёсткий лимит на уровне системы
cat /proc/sys/kernel/pid_max
# 32768
# limits.conf задал nproc hard=4096, что нормально

# Шаг 4: проверить, есть ли у alice личный лимит
grep 'alice' /etc/security/limits.d/*.conf
# alice   hard   nproc   0
# Нашли! Кто-то задал nproc hard=0 для alice конкретно — ни одного процесса нельзя

# Шаг 5: исправить и проверить
sudo sed -i '/alice.*nproc.*0/d' /etc/security/limits.d/alice.conf
# Повторить SSH — alice теперь получает оболочку

# Урок: сбои PAM в группе session выглядят для пользователя как сбои аутентификации,
# но логируются по-другому. Всегда проверяй журнал sshd на строки pam_*,
# а не только на строку 'Accepted'.
Почему это работает

Почему RHEL использует authselect вместо прямого редактирования PAM-файлов? На RHEL 8+ authselect управляет файлами common-auth, common-account, common-session и common-password через профили (такие как sssd, winbind, nis). Прямое редактирование common-файлов означает, что authselect перезапишет твои изменения при следующем запуске, ломая стек аутентификации незаметно. Правильный рабочий процесс для RHEL: authselect select sssd --force (переключить на SSSD-профиль), затем authselect apply-changes. В Debian/Ubuntu аналогичного инструмента нет — ты редактируешь common-файлы напрямую. Именно поэтому PAM-руководства для Debian часто ломаются на RHEL.

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

Наиболее опасная ошибка в PAM — добавление sufficient модуля в начало стека auth без понимания последствий: если этот модуль возвращает успех для ЛЮБОГО пользователя (включая root), весь остальной стек аутентификации обходится. Классический инцидент: оператор добавил auth sufficient pam_permit.so в common-auth «временно», чтобы отладить проблему входа, и забыл удалить. Следующие три дня любой мог зайти на сервер по SSH без пароля — pam_permit.so всегда возвращает PAM_SUCCESS. Всегда тестируй изменения PAM из второй открытой SSH-сессии перед закрытием первой; никогда не используй pam_permit.so в продакшне.

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

Стек auth PAM: (1) required pam_faillock.so preauth, (2) sufficient pam_unix.so, (3) required pam_deny.so. Пользователь вводит верный локальный пароль. Какие модули выполняются и каков результат?

Итог

PAM отделяет политику аутентификации от реализации. Программы вызывают libpam; PAM читает /etc/pam.d/<service> и запускает настроенные модули. Четыре группы: auth (проверка идентичности), account (проверка политики — заблокирована? истекла?), password (смена учётных данных), session (настройка окружения: лимиты, env, журнал). Четыре флага управления: required (сбой продолжает стек, весь результат провален), requisite (сбой немедленно останавливает стек), sufficient (успех немедленно останавливает стек — пропустить оставшиеся), optional (результат игнорируется). /etc/nsswitch.conf управляет поиском записей пользователей (files, ldap, systemd) — работает до PAM. Всегда ставь files перед ldap в nsswitch. Модули session (pam_limits.so, pam_env.so) НЕ пропускаются sufficient в группе auth — они всегда выполняются после успеха auth. Изменяй PAM из второй открытой сессии и тестируй перед закрытием первой.

Практика

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

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

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

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

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

Trademarks belong to their respective owners. Editorial reference only.