# PAM - Pluggable Authentication Modules _Безопасность · LinuxLab Knowledge Base_ **TL;DR:** PAM - фреймворк аутентификации в Linux. Программы (sudo, login, sshd) не проверяют пароли сами, а вызывают PAM, который через стек модулей в `/etc/pam.d/` решает: пускать или нет. ## Зачем PAM существует Без PAM каждое приложение читало `/etc/passwd` и `/etc/shadow` напрямую. Чтобы добавить LDAP, Kerberos, 2FA, fail-after-N-attempts - пришлось бы патчить ВСЕ программы. PAM решает это вынесением логики в shared-library и стек модулей. ``` user → ssh login ↓ sshd вызывает pam_authenticate("sshd") ↓ PAM читает /etc/pam.d/sshd ↓ Стек модулей по очереди: pam_unix.so → проверка /etc/shadow pam_faillock.so → счётчик неудач pam_google_authenticator.so → 2FA ↓ «authenticated» / «failed» → возврат в sshd ``` ## Где что лежит - `/etc/pam.d/` - конфиги для конкретных приложений (по одному на сервис: `/etc/pam.d/sshd`, `/etc/pam.d/sudo`, `/etc/pam.d/login`). - `/etc/pam.d/other` - fallback для приложений без своего конфига. Стандартный - paranoid: всё запрещать. **Не править на permissive.** - `/lib64/security/*.so` (RHEL/Fedora) или `/lib/x86_64-linux-gnu/security/*.so` (Debian/Ubuntu) - сами модули. - `/etc/security/*.conf` - конфиги отдельных модулей (limits.conf, faillock.conf и т.д.). Именование сервиса = имя файла в `/etc/pam.d/`. Когда `sudo` зовёт PAM - читается `/etc/pam.d/sudo`. ## Синтаксис строки конфига ``` type control module [arguments] ``` Пример из `/etc/pam.d/sshd`: ``` auth required pam_env.so auth substack password-auth account required pam_nologin.so session required pam_loginuid.so password include system-auth ``` ### Четыре `type` (что проверяем) | type | Что делает | |------------|-------------------------------------------------------------| | `auth` | Проверка кто ты (пароль, токен, биометрия) | | `account` | Можно ли тебе сейчас входить (юзер не заблокирован, не expired, есть в группе) | | `password` | Смена пароля (вызывается при `passwd`/`chpasswd`) | | `session` | Что делать ДО/ПОСЛЕ входа: монтирование home, лимиты, audit | Один service-файл обычно содержит все 4 типа. PAM запускает только тот тип, который нужен вызывающему приложению. ### Control flags (что делать с результатом модуля) | flag | Поведение при success | Поведение при fail | |--------------|--------------------------------------|------------------------------------------| | `required` | продолжить стек | вернуть fail В КОНЦЕ стека (отложенно) | | `requisite` | продолжить стек | СРАЗУ выйти с fail (не запускать дальше) | | `sufficient` | СРАЗУ выйти с success (если до этого не было required-fail) | продолжить стек | | `optional` | продолжить стек | продолжить стек, fail игнорируется | | `include` | вставить весь стек из другого файла | то же | | `substack` | как include, но изолирует «done»-флаг для возврата | то же | Ключевое различие `required` vs `requisite`: оба требуют успеха, но `requisite` падает мгновенно - это используют когда нужно **не показать следующий prompt** (если username не существует, не запрашивать пароль). `sufficient` пропускает остаток стека при успехе - частая модель для «либо ключ, либо пароль»: ``` auth sufficient pam_unix.so # пароль успешен → впустили auth required pam_deny.so # иначе fallback fail ``` ## Самые частые модули | Модуль | Что делает | |----------------------------|-----------------------------------------------------------| | `pam_unix.so` | Проверка `/etc/passwd` + `/etc/shadow` (стандарт) | | `pam_pwquality.so` | Policy для нового пароля (длина, классы, словарь) | | `pam_faillock.so` | Блокировка после N неудачных попыток (был `pam_tally2`) | | `pam_nologin.so` | Если есть `/etc/nologin` - пускает только root | | `pam_limits.so` | Применяет `/etc/security/limits.conf` (ulimit'ы) | | `pam_loginuid.so` | Записывает audit-uid в `/proc/self/loginuid` | | `pam_mkhomedir.so` | Создаёт `~/` при первом входе (LDAP-юзеры) | | `pam_env.so` | Подгружает env из `/etc/environment` | | `pam_succeed_if.so` | Условный пропуск: «если uid >= 1000» | | `pam_selinux.so` | Установка SELinux-контекста сессии (см. [selinux-apparmor](/kb/selinux-apparmor.md)) | | `pam_systemd.so` | Создание user.slice / `XDG_RUNTIME_DIR` | | `pam_google_authenticator` | TOTP 2FA | ## Shared-стек: common-auth / system-auth Чтобы не дублировать одни и те же модули в каждый `pam.d/`, есть общие файлы: - **Debian/Ubuntu**: `/etc/pam.d/common-{auth,account,password,session}` - подключаются через `@include common-auth`. - **RHEL/Fedora**: `/etc/pam.d/system-auth`, `password-auth` - подключаются через `auth substack system-auth`. Менять глобальное поведение (например требование 2FA для всех консольных логинов) → правишь общий файл, не каждый сервис. ## Типичные кейсы ### Заблокировать юзера после 5 неудачных попыток ``` # /etc/pam.d/system-auth (RHEL) или /etc/pam.d/common-auth (Debian) auth required pam_faillock.so preauth silent deny=5 unlock_time=900 auth sufficient pam_unix.so auth [default=die] pam_faillock.so authfail deny=5 unlock_time=900 account required pam_faillock.so ``` Проверка: `faillock --user serge`. Сброс: `faillock --user serge --reset`. ### Усилить пароли - minimum 12 chars, 3 класса ``` # /etc/security/pwquality.conf minlen = 12 minclass = 3 ``` Применяется через `pam_pwquality.so` который уже подключён в `password`-стеке. ### Запретить root-логин по SSH Не через PAM а через `PermitRootLogin no` в sshd_config. Через PAM - так: ``` # /etc/pam.d/sshd, в auth-стеке: auth required pam_succeed_if.so user != root ``` ## Дебаг - что делать когда сломал **Главное правило: НИКОГДА не редактируй PAM-конфиг без открытой второй root-сессии.** Если сломал - логинов не будет, в том числе через [sudo](/kb/sudo.md). ```bash # 1. Логи sudo journalctl -t sshd -t sudo --since "10 min ago" sudo tail -f /var/log/auth.log # Debian sudo tail -f /var/log/secure # RHEL # 2. Тест без перелогина sudo -k && sudo -v # сбросить sudo-кеш и переаутентифицироваться # 3. Проверка что модуль вообще загружается ls /lib64/security/pam_unix.so # или /lib/x86_64-linux-gnu/security/ # 4. Документация на конкретный модуль man pam_faillock man pam_unix ``` Если сломал так что войти нельзя - boot в single-user mode (kernel cmdline: `systemd.unit=rescue.target`) и починить файл. Безопасный fallback для `/etc/pam.d/`: ``` auth required pam_unix.so account required pam_unix.so password required pam_unix.so session required pam_unix.so ``` Это вернёт «как было до PAM» - проверка только `/etc/shadow`. ## Команды ```bash sudo cat /etc/pam.d/sudo ``` Конфиг для sudo - увидеть какие модули в стеке аутентификации ```bash sudo journalctl -t sshd -t sudo --since '10 min ago' ``` Логи последних PAM-проверок - главный инструмент дебага ```bash sudo faillock --user serge ``` Сколько неудачных попыток у юзера - узнать почему не пускает ```bash sudo faillock --user serge --reset ``` Сбросить счётчик блокировок после ошибки в пароле ```bash man pam_unix ``` Документация на конкретный PAM-модуль с описанием всех его аргументов ## См. также - [sudo - выполнить от имени root](/kb/sudo.md) - [SSH - secure shell](/kb/ssh.md) - [SELinux и AppArmor - Mandatory Access Control](/kb/selinux-apparmor.md) - [Управление секретами - Vault, k8s Secrets, sealed-secrets](/kb/secrets-management.md) - [CIS Benchmark и system hardening (lynis, OpenSCAP)](/kb/cis-benchmark-hardening.md) - [RADIUS - аутентификация для сети, VPN, Wi-Fi](/kb/radius.md)