# Занятие 07: Методы аутентификации / Внешняя аутентификация

> Исходная страница в нативном Markdown: Методы аутентификации / Внешняя аутентификация

---

Индекс LLMS: [llms.txt](/ru/llms.txt)

---

<!-- Generated by scripts/generate_skeleton.py. -->

> [!NOTE]
> Эта страница извлечена из официального PDF в нативный Markdown. Текст слайдов, код и комментарии сохранены в порядке чтения; исходные PDF и HTML остаются эталоном макета.
> **HTML source:** [dbs_07_auth_external.html](https://edu.postgrespro.ru/16/dbs-16/dbs_07_auth_external.html)
> **Archive:** [DBS-handouts-16.zip](https://edu.postgrespro.ru/16/DBS-handouts-16.zip)
> **Страниц PDF:** 29

## Page 1: Методы аутентификации / Внешняя аутентификация {#source-page-001}

### Авторские права

© Postgres Professional, 2017–2026 Авторы: Егор Рогов, Павел Лузанов, Илья Баштанов, Алексей Береснев Фото: Олег Бартунов (монастырь Пху и пик Бхрикути, Непал)
### Использование материалов курса

Некоммерческое использование материалов курса (презентации, демонстрации) разрешается без ограничений. Коммерческое использование возможно только с письменного разрешения компании Postgres Professional. Запрещается внесение изменений в материалы курса.
### Обратная связь

Отзывы, замечания и предложения направляйте по адресу: edu@postgrespro.ru
### Отказ от ответственности

Компания Postgres Professional не несет никакой ответственности за любые повреждения и убытки, включая потерю дохода, нанесенные прямым или непрямым, специальным или случайным использованием материалов курса. Компания Postgres Professional не предоставляет каких-либо гарантий на материалы курса. Материалы курса предоставляются на основе принципа «как есть» и компания Postgres Professional не обязана предоставлять сопровождение, поддержку, обновления, расширения и изменения.

## Page 2: Темы {#source-page-002}

- Внешняя аутентификация
- Аутентификация LDAP
- Аутентификация GSSAPI
- Аутентификация PAM

## Page 3: Внешняя аутентификация {#source-page-003}

**peer &#91;параметры&#93;**
- проверка в операционной системе ldap &#91;параметры&#93;
  - проверка в службе каталогов LDAP
- gssapi &#91;параметры&#93;
  - использование GSSAPI, обычно Kerberos
- sspi &#91;параметры&#93;
- проверка средствами аутентификации Windows pam &#91;параметры&#93;
  - проверка посредством подключаемых модулей PAM

### Комментарии преподавателя

Вместо того чтобы хранить пароль в системном каталоге и сверять его с паролем, который передал пользователь, PostgreSQL может поручить проверку подлинности пользователя внешней системе.

Метод аутентификации peer, рассмотренный в теме «Подключение и аутентификация», относится к внешним, так как запрашивает имя текущего пользователя у ядра ОС.

При использовании метода ldap PostgreSQL обращается к службе каталогов LDAP.

GSSAPI (Generic Security Service Application Program Interface) — универсальный интерфейс для служб аутентификации, соответствующий спецификации RFC 2743. На практике используется для аутентификации в UNIX/Linux средах Kerberos, где в PostgreSQL используется метод аутентификации gssapi.

В аналогичных средах Windows, реализующих подход одноразовой аутентификации (single sign-on), используется метод sspi.

Метод pam использует подключаемые модули аутентификации (Pluggable Authentication Modules) — стандартный механизм Linux, предоставляющий приложениям высокоуровневый API для аутентификации с помощью внешних модулей.

## Page 4: Аутентификация LDAP {#source-page-004}

**Служба каталогов**
- Простое связывание
- Поиск + связывание

## Page 5: Служба каталогов {#source-page-005}

![Source slide page 5: Служба каталогов](images/page-005.png){#source-figure-005 caption="Source slide page 5: Служба каталогов"}


### Комментарии преподавателя

LDAP (Lightweight Directory Access Protocol) — протокол для доступа к службе каталогов. Каталог представляет собой иерархию записей, которые состоят из различных атрибутов и описывают какой-либо объект: учетную запись пользователя, группу пользователей, организацию, доменное имя, сетевую службу и т. п. В учетной записи может храниться пароль, обычно в виде хеш-кода в атрибуте userPassword.

Запись идентифицируется уникальным именем (DN, distinguished name), составленным из DN родительской записи и, как правило, некоторого атрибута текущей. Например, DN учетной записи Алисы на слайде — «uid=alice,ou=BioLab,dc=course,dc=local»:

- uid — идентификатор пользователя (user ID);

- ou — подразделение (organization unit);

- dc — домен (domain component).

<https://ru.wikipedia.org/wiki/LDAP>

## Page 6: Простое связывание {#source-page-006}

![Source slide page 6: Простое связывание](images/page-006.png){#source-figure-006 caption="Source slide page 6: Простое связывание"}


### Комментарии преподавателя

В простом варианте СУБД связывается с сервером LDAP по уникальному имени, составленному из префикса, имени подключающейся роли и суффикса. Например, при подключении клиента под ролью alice с параметрами метода ldap

● префикс ldapprefix = «uid=»,

● суффикс ldapsuffix = «,ou=BioLab,dc=course,dc=local»

PostgreSQL пошлет запрос аутентификации (bind) к серверу LDAP, используя DN «uid=alice,ou=BioLab,dc=course,dc=local» и переданный клиентом пароль. Адрес и порт сервера LDAP задаются параметрами ldapserver и ldapport.

Если запрос выполняется успешно, аутентификация считается пройденной.

<https://postgrespro.ru/docs/postgresql/16/auth-ldap>

## Page 7: LDAP: простое связывание {#source-page-007}

При аутентификации в LDAP роли должны быть зарегистрированы в PostgreSQL, а пароли хранятся на сервере LDAP.
```sql
student=# CREATE ROLE alice LOGIN;
```
```sql
CREATE ROLE
```
```sql
student=# CREATE ROLE bob LOGIN;
```
```sql
CREATE ROLE
```
```sql
student=# CREATE ROLE charlie LOGIN;
```
```sql
CREATE ROLE
```
Учетные записи пользователей alice и bob уже заведены в дереве LDAP, их можно посмотреть с помощью утилиты:
```sh
student$ ldapsearch \
  -x                              `# аутентификация по DN и паролю` \
  -LLL                            `# краткая информация` \
  -H ldaps://$(hostname)          `# сервер LDAP` \
  -b ou=BioLab,dc=course,dc=local `# ветвь дерева` \
  "(uid=*)"                       `# фильтр` \
  cn                              `# выводить только common name`
```
```text
dn: uid=bob,ou=BioLab,dc=course,dc=local
cn: Bob
```
```text
dn: uid=alice,ou=BioLab,dc=course,dc=local
cn: Alice
```
Для PostgreSQL мы хотим использовать аутентификацию LDAP без шифрования (PostgreSQL и сервер LDAP обмениваются сообщениями в открытом виде). Добавим правило с методом аутентификации ldap для Алисы. В параметрах метода указываем префикс и суффикс:
```text
/etc/postgresql/16/main/pg_hba.conf.d/ext.conf
```
```text
# TYPE  DATABASE  USER   ADDRESS  METHOD
host    all       alice  samehost ldap ldapserver=course.local ldapprefix="uid="
ldapsuffix=",ou=BioLab,dc=course,dc=local"
```
Попросим PostgreSQL принимать подключения со всех сетевых интерфейсов и записывать информацию о соединениях в журнал сообщений:
```sql
student=# ALTER SYSTEM SET listen_addresses = '*';
```
```sql
ALTER SYSTEM
```
```sql
student=# ALTER SYSTEM SET log_connections = on;
```
```sql
ALTER SYSTEM
```
Изменение первого параметра требует перезапуска сервера.
```sh
student$ sudo pg_ctlcluster 16 main restart
```
Подключимся от имени Алисы. В строке соединения передаем те же параметры, что и при парольной аутентификации:
```sh
student$ psql "host=localhost user=alice password=alice dbname=student" -c "\conninfo"
```
```text
You are connected to database "student" as user "alice" on host "localhost" (address
"127.0.0.1") at port "5432".
```
Проверим журнал сообщений:
```sh
student$ tail -3 /var/log/postgresql/postgresql-16-main.log
```
```text
2026-07-30 16:35:46.062 MSK [58393] [unknown]@[unknown] LOG:  connection received:
host=127.0.0.1 port=45556
2026-07-30 16:35:46.069 MSK [58393] alice@student LOG:  connection authenticated:
identity="uid=alice,ou=BioLab,dc=course,dc=local" method=ldap
(/etc/postgresql/16/main/pg_hba.conf.d/ext.conf:2)
2026-07-30 16:35:46.069 MSK [58393] alice@student LOG:  connection authorized: user=alice
database=student application_name=psql
```

## Page 8: Поиск + связывание {#source-page-008}

![Source slide page 8: Поиск + связывание](images/page-008.png){#source-figure-008 caption="Source slide page 8: Поиск + связывание"}


### Комментарии преподавателя

Если пользователи хранятся в разных поддеревьях, суффиксы уникальных имен записей будут разными и возможностей простого связывания не хватит. Например, записи alice и charlie заведены в разных лабораториях: суффикс Алисы будет 'ou=BioLab', а Чарли — 'ou=ChemLab'.

Для таких случаев есть возможность провести аутентификацию в два этапа.

Сначала выполняется поиск нужной записи (search). Для этого PostgreSQL подключается к серверу LDAP под фиксированным именем и паролем (ldapbinddn, ldapbindpasswd) и ищет в поддереве ldapbasedn запись, у которой атрибут ldapsearchattribute равен имени подключающейся роли.

Например, для роли charlie с настройками

● ldapbasedn = 'dc=course,dc=local',

● ldapsearchattribute = 'uid'

PostgreSQL обнаружит запись «uid=charlie,ou=ChemLab,dc=course,dc=local».

## Page 9: Поиск + связывание {#source-page-009}

![Source slide page 9: Поиск + связывание](images/page-009.png){#source-figure-009 caption="Source slide page 9: Поиск + связывание"}


### Комментарии преподавателя

Второй этап выполняется так же, как при обычном связывании. PostgreSQL отправляет запрос на аутентификацию, используя найденную запись и пароль, переданный клиентом. Если запрос выполняется, аутентификация считается пройденной.

## Page 10: LDAP: поиск + связывание {#source-page-010}

Испытаем аутентификацию LDAP с поиском и связыванием на Чарли. Его учетная запись зарегистрирована в другом поддереве LDAP, поэтому простой режим не подходит:
```sh
student$ ldapsearch \
  -x                              `# аутентификация по DN и паролю` \
  -LLL                            `# краткая информация` \
  -H ldaps://$(hostname)          `# сервер LDAP` \
  -b dc=course,dc=local           `# ветвь дерева` \
  -D cn=admin,dc=course,dc=local  `# от кого запрос` \
  -w admin                        `# пароль` \
  "(uid=*)"                       `# фильтр` \
  cn                              `# выводить только common name`
```
```text
dn: uid=alice,ou=BioLab,dc=course,dc=local
cn: Alice
```
```text
dn: uid=bob,ou=BioLab,dc=course,dc=local
cn: Bob
```
```text
dn: uid=charlie,ou=ChemLab,dc=course,dc=local
cn: Charlie
```
Изменим правило аутентификации. Значения отдельных параметров можно указать в более компактном виде (ldapurl):
ldaps — аутентификация LDAP, в данном случае — с шифрованием SSL; course.local — адрес сервера (ldapserver); dc=course,dc=local — поддерево для поиска (ldapbasedn); uid — атрибут для поиска (ldapsearchattribute); sub — указание искать в указанном поддереве.
Параметры ldapbinddn и ldapbindpasswd не указываем: в этом случае происходит анонимный запрос, который разрешен конфигурацией нашего LDAP-сервера.
```text
/etc/postgresql/16/main/pg_hba.conf.d/ext.conf
```
```text
# TYPE  DATABASE  USER     ADDRESS   METHOD
host    all       charlie  samehost  ldap
ldapurl="ldaps://course.local/dc=course,dc=local?uid?sub"
```
```sh
student$ sudo pg_ctlcluster 16 main reload
```
Пробуем.
```sh
student$ psql "host=$(hostname) user=charlie password=charlie dbname=student" -c "\conninfo"
```
```text
You are connected to database "student" as user "charlie" on host "course.local" (address
"127.0.1.1") at port "5432".
```
```sh
student$ tail -3 /var/log/postgresql/postgresql-16-main.log
```
```text
2026-07-30 16:35:47.150 MSK [58624] [unknown]@[unknown] LOG:  connection received:
host=127.0.0.1 port=41714
2026-07-30 16:35:47.171 MSK [58624] charlie@student LOG:  connection authenticated:
identity="uid=charlie,ou=ChemLab,dc=course,dc=local" method=ldap
(/etc/postgresql/16/main/pg_hba.conf.d/ext.conf:2)
2026-07-30 16:35:47.172 MSK [58624] charlie@student LOG:  connection authorized:
user=charlie database=student application_name=psql
```

## Page 11: Аутентификация GSSAPI {#source-page-011}

**Kerberos и GSSAPI**
- Настройка клиента
- Настройка сервера
- Аутентификация
- Запрос сеансового ключа
- Передача ключа сервису
- Коммуникация с сервисом

## Page 12: Kerberos и GSSAPI {#source-page-012}

**Kerberos — протокол аутентификации**
  - надежный обмен ключами и взаимная аутентификация
  - между клиентом и сервисом
  - симметричные ключи, доверенная третья сторона
  - разграничение доступа по областям (realm)
- GSSAPI — универсальный интерфейс безопасности
  - поддерживает разные протоколы, в том числе Kerberos

### Комментарии преподавателя

Kerberos — протокол аутентификации, позволяющий клиентам и сервисам (в нашем случае PostgreSQL) выполнить взаимную аутентификацию с помощью доверенной третьей стороны — службы Kerberos. Kerberos позволяет клиенту и сервису надежно обменяться ключами для передачи данных по незащищенному каналу связи, используя криптографию симметричных ключей.

Клиент один раз аутентифицируется службой Kerberos, а затем получает билеты-разрешения, с которыми обращается к сервисам. Билет гарантирует клиенту, что он подключится именно к запрошенному сервису, а сервису — что клиент имеет право на подключение.

Kerberos рассчитан на большое количество пользователей и сервисов, и не каждому пользователю нужно разрешать подключаться к любому сервису. Для разграничения доступа существуют области (realm): пользователь может получать билет для сервиса, только если они входят в одну область. В нашем примере это область COURSE.LOCAL.

PostgreSQL работает с протоколом Kerberos не напрямую, а через универсальный прикладной интерфейс GSSAPI (Generic Security Services API), который, помимо Kerberos, поддерживает NTLM и другие протоколы.

## Page 13: Настройки клиента {#source-page-013}

**Параметр gssencmode**
  - disable
  - не использовать шифрование GSSAPI
  - prefer
  - попытка использовать шифрование GSSAPI,
  - при неудаче — попытка соединиться без него
  - (по умолчанию)
  - require
  - потребовать шифрование GSSAPI
- Параметр krbsrvname
  - имя службы Kerberos для аутентификации

### Комментарии преподавателя

Аналогично тому, как при подключении клиент может попросить или потребовать SSL-соединение с помощью параметра sslmode библиотеки libpq, пожелания по использованию шифрования GSSAPI определяются значением параметра gssencmode.

Клиент может запретить (disable) или потребовать (require) соединение с шифрованием GSSAPI. Если сервер не разрешает желаемое соединение, оно не будет установлено.

Клиент также может попросить шифрованное соединение, но если такой возможности нет, согласиться на обычное (prefer, по умолчанию).

<https://postgrespro.ru/docs/postgresql/16/gssapi-enc>

<https://postgrespro.ru/docs/postgresql/16/libpq-connect#LIBPQ-CONNECT-GSSENCMODE>

Также клиент может указать имя службы Kerberos, которая должна использоваться для аутентификации (по умолчанию — postgres).

<https://postgrespro.ru/docs/postgresql/16/libpq-connect#LIBPQ-CONNECT-KRBSRVNAME>

## Page 14: Настройки сервера {#source-page-014}

**Параметры**
  - krb\_server\_keyfile
  - имя keytab-файла с ключом PostgreSQL
- Типы сетевых подключений в pg\_hba.conf
  - host
  - любое сетевое подключение
  - hostgssenc
  - подключение с шифрованием GSSAPI
  - hostnogssenc
  - подключение без шифрования GSSAPI
- Метод аутентификации
  - gss
  - аутентификация с помощью GSSAPI
  - sspi
  - аутентификация SSPI для Windows

### Комментарии преподавателя

Для аутентификации с помощью Kerberos необходимо указать метод аутентификации gss. (В ОС Windows для работы с Kerberos используется собственный интерфейс SSPI и соответствующий ему метод аутентификации sspi, которые мы здесь не рассматриваем.)

В параметре krb\_server\_keyfile указывается расположение файла с ключом PostgreSQL. Такие файлы, содержащие ключ Kerberos определенного сервиса или клиента, называются keytab-файлами.

В качестве типа подключения при этом можно указать как простой тип host, так и явно разделить соединения на использующие шифрование GSSAPI (hostgssenc) и не использующие (hostnogssenc).

<https://postgrespro.ru/docs/postgresql/16/gssapi-auth>

## Page 15: Аутентификация {#source-page-015}

![Source slide page 15: Аутентификация](images/page-015.png){#source-figure-015 caption="Source slide page 15: Аутентификация"}


### Комментарии преподавателя

Служба Kerberos хранит ключи всех пользователей и сервисов, которые заранее созданы администратором. На иллюстрации это ключ пользователя Алисы и ключ сервиса PostgreSQL. У Kerberos есть также свой ключ, не известный никому.

Алиса входит в корпоративную систему и проходит аутентификацию. Она вводит свой пароль, из которого с помощью специального алгоритма генерируется ее ключ (тот самый, что хранится в Kerberos).

Клиент Алисы обращается к службе аутентификации Kerberos AS (Authentication Service). Если Алиса ввела правильный пароль, ключи совпадут. Тогда Kerberos генерирует временный ключ, предназначенный для сеанса общения клиента и самого Kerberos, и отправляет клиенту два сообщения:

● Временный ключ, зашифрованный ключом Алисы. Клиент сможет его расшифровать.

● Временный ключ (плюс дополнительная служебная информация), зашифрованный ключом Kerberos. Это сообщение называется TGT, Ticket Granting Ticket — билет на получение билетов. Клиент не может его расшифровать, но будет предъявлять его Kerberos в доказательство того, что пользователь уже прошел аутентификацию.

## Page 16: Аутентификация в Kerberos {#source-page-016}

Kerberos установлен и настроен в виртуальной машине курса, учетные записи (principals) зарегистрированы.
```sh
student$ sudo kadmin.local list_principals | grep '^[abc]'
```
```text
alice@COURSE.LOCAL
bob@COURSE.LOCAL
charlie@COURSE.LOCAL
```
alice@COURSE.LOCAL — Алиса хранит пароль в специальном файле alice.keytab. bob@COURSE.LOCAL — Боб — в файле bob.keytab. charlie@COURSE.LOCAL — Чарли не хранит пароль в файле и должен вводить его вручную. Вы можете использовать его для собственных экспериментов.
Сначала Алисе нужно аутентифицироваться и получить билет, дающий возможность получать билеты сервисов (TGT, Ticket Granting Ticket).
Аутентификацию в Kerberos можно пройти с помощью команды kinit. Она запрашивает у пользователя пароль, из которого генерируется ключ, подтверждающий право на получение TGT. Мы воспользуемся заранее подготовленным ключом Алисы (в виртуальной машине он находится в файле alice.keytab), чтобы не вводить пароль интерактивно:
```sh
student$ kinit -kt alice.keytab alice
```
В результате Kerberos сформировал TGT и сохранил его в кеше:
```sh
student$ klist
```
```text
Ticket cache: FILE:/tmp/krb5cc_1000
Default principal: alice@COURSE.LOCAL
```
```text
Valid starting       Expires              Service principal
30.07.2026 16:35:47  31.07.2026 02:35:47  krbtgt/COURSE.LOCAL@COURSE.LOCAL
renew until 31.07.2026 16:35:47
```
Билет ограничен по времени действия (valid starting, expires/renew until); Термин principal означает учетную запись Kerberos: это может быть как клиент, так и сервис; COURSE.LOCAL — название области Kerberos, которая используется в этом примере.

## Page 17: Запрос сеансового ключа {#source-page-017}

![Source slide page 17: Запрос сеансового ключа](images/page-017.png){#source-figure-017 caption="Source slide page 17: Запрос сеансового ключа"}


### Комментарии преподавателя

Теперь Алиса хочет подключиться к PostgreSQL. Для этого ее клиент отправляет запрос на получение доступа к сервису, подтверждая аутентификацию Алисы с помощью полученного ранее TGT. Это позволяет Алисе не вводить пароль каждый раз при подключении к другим сервисам — то, что называется single sign-on.

Служба Kerberos TGS (Ticket Granting Service) генерирует сеансовый ключ, которым должны обладать как клиент, так и сервис. Клиенту снова передаются два сообщения:

● Сеансовый ключ, зашифрованный временным ключом (который уже есть у клиента).

● Сеансовый ключ, зашифрованный ключом сервиса (которого у клиента, конечно, нет). Это сообщение называется ST, Service Ticket — билет сервиса, и предназначено для сервиса.

## Page 18: Передача ключа сервису {#source-page-018}

![Source slide page 18: Передача ключа сервису](images/page-018.png){#source-figure-018 caption="Source slide page 18: Передача ключа сервису"}


### Комментарии преподавателя

Клиент Алисы подключается к PostgreSQL (который использует метод аутентификации gssapi) и предъявляет полученный от Kerberos билет. PostgreSQL расшифровывает его своим ключом, который хранится в keytab-файле. Факт того, что билет зашифрован ключом PostgreSQL, говорит о том, что он получен от Kerberos (больше никто не знает этот ключ), следовательно, пользователь прошел аутентификацию и ему можно доверять.

Из расшифрованного билета PostgreSQL извлекает сеансовый ключ для общения с клиентом.

## Page 19: Коммуникация с сервисом {#source-page-019}

![Source slide page 19: Коммуникация с сервисом](images/page-019.png){#source-figure-019 caption="Source slide page 19: Коммуникация с сервисом"}


### Комментарии преподавателя

Теперь сеансовый ключ имеется и у клиента, и у сервиса. Он используется для шифрования данных, пересылаемых по клиент-серверному протоколу.

Билет TGT остается у клиента и будет использован для запросов на подключения к другим сервисам, если они понадобятся пользователю.

## Page 20: Подключение к PostgreSQL {#source-page-020}

Включим в дополнительный файл аутентификации правило, разрешающее Алисе аутентифицироваться в Kerberos:
```text
/etc/postgresql/16/main/pg_hba.conf.d/ext.conf
```
```text
# TYPE      DATABASE  USER   ADDRESS   METHOD
hostgssenc  student   alice  samehost  gss include_realm=0
```
Параметр include\_realm=0 убирает из имени учетной записи пользователя название области Kerberos. Например, alice@COURSE.LOCAL преобразуется в alice, то есть в обычное имя учетной записи PostgreSQL.
Перечитаем конфигурацию.
```sh
student$ sudo pg_ctlcluster 16 main reload
```
При подключении к PostgreSQL psql запрашивает у Kerberos билет сервиса и передает его PostgreSQL. Имя сервиса задается в строке подключения libpq в параметре krbsrvname; в нашем случае годится значение по умолчанию — postgres.
Проверим, сможет ли Алиса начать защищенный сеанс:
```sh
student$ psql "host=$(hostname) user=alice dbname=student" -c "\conninfo"
```
```text
You are connected to database "student" as user "alice" on host "course.local" (address
"127.0.1.1") at port "5432".
GSSAPI-encrypted connection
```
```sh
student$ tail -3 /var/log/postgresql/postgresql-16-main.log
```
```text
2026-07-30 16:35:48.479 MSK [58922] [unknown]@[unknown] LOG:  connection received:
host=127.0.0.1 port=41730
2026-07-30 16:35:48.485 MSK [58922] alice@student LOG:  connection authenticated:
identity="alice@COURSE.LOCAL" method=gss
(/etc/postgresql/16/main/pg_hba.conf.d/ext.conf:2)
2026-07-30 16:35:48.485 MSK [58922] alice@student LOG:  connection authorized: user=alice
database=student application_name=psql GSS (authenticated=yes, encrypted=yes,
delegated_credentials=no, principal=alice@COURSE.LOCAL)
```
Теперь в кеше будет и билет сервиса PostgreSQL:
```sh
student$ klist
```
```text
Ticket cache: FILE:/tmp/krb5cc_1000
Default principal: alice@COURSE.LOCAL
```
```text
Valid starting       Expires              Service principal
30.07.2026 16:35:47  31.07.2026 02:35:47  krbtgt/COURSE.LOCAL@COURSE.LOCAL
renew until 31.07.2026 16:35:47
30.07.2026 16:35:48  31.07.2026 02:35:47  postgres/course.local@
renew until 31.07.2026 16:35:47
Ticket server: postgres/course.local@COURSE.LOCAL
```
Кроме учетной записи Алисы, в Kerberos зарегистрирована и учетная запись Боба: bob@COURSE.LOCAL. В случае Алисы мы просто отбросили из имени учетной записи название области, пользуясь тем, что оставшаяся часть совпадает с именем роли в PostgreSQL. Можно использовать более универсальный подход: превращать имена учетных записей Kerberos в имена пользователей PostgreSQL с помощью сопоставлений имен, с которыми мы уже встречались в теме «Подключение и аутентификация».
Заменим правило аутентификации для Алисы универсальным правилом с именем сопоставления krb:
```text
/etc/postgresql/16/main/pg_hba.conf.d/ext.conf
```
```text
# TYPE      DATABASE  USER   ADDRESS   METHOD
hostgssenc  student   all    samehost  gss map=krb
```
Теперь зададим сопоставление, которое отбрасывает имя области с помощью регулярного выражения:
```text
/etc/postgresql/16/main/pg_ident.conf.d/ext.conf
```
```text
krb /^(.+)@ \1
```
Перечитаем конфигурацию:
```sh
student$ sudo pg_ctlcluster 16 main reload
```

## Page 21: Page 21 {#source-page-021}

Аутентифицируем Боба в Kerberos:
```sh
student$ kinit -kt bob.keytab bob
```
Боб начинает сеанс:
```sh
student$ psql "host=$(hostname) user=bob dbname=student" -c "\conninfo"
```
```text
You are connected to database "student" as user "bob" on host "course.local" (address
"127.0.1.1") at port "5432".
GSSAPI-encrypted connection
```
```sh
student$ tail -3 /var/log/postgresql/postgresql-16-main.log
```
```text
2026-07-30 16:35:49.624 MSK [59170] [unknown]@[unknown] LOG:  connection received:
host=127.0.0.1 port=41746
2026-07-30 16:35:49.630 MSK [59170] bob@student LOG:  connection authenticated:
identity="bob@COURSE.LOCAL" method=gss (/etc/postgresql/16/main/pg_hba.conf.d/ext.conf:2)
2026-07-30 16:35:49.631 MSK [59170] bob@student LOG:  connection authorized: user=bob
database=student application_name=psql GSS (authenticated=yes, encrypted=yes,
delegated_credentials=no, principal=bob@COURSE.LOCAL)
```
Билеты, полученные Бобом:
```sh
student$ klist
```
```text
Ticket cache: FILE:/tmp/krb5cc_1000
Default principal: bob@COURSE.LOCAL
```
```text
Valid starting       Expires              Service principal
30.07.2026 16:35:49  31.07.2026 02:35:49  krbtgt/COURSE.LOCAL@COURSE.LOCAL
renew until 31.07.2026 16:35:49
30.07.2026 16:35:49  31.07.2026 02:35:49  postgres/course.local@
renew until 31.07.2026 16:35:49
Ticket server: postgres/course.local@COURSE.LOCAL
```

## Page 22: Аутентификация PAM {#source-page-022}

![Source slide page 22: Аутентификация PAM](images/page-022.png){#source-figure-022 caption="Source slide page 22: Аутентификация PAM"}

**pam &#91;параметры&#93;**
  - проверяет пары «имя пользователя — пароль»

### Комментарии преподавателя

Система PAM (Pluggable Authentication Modules — подключаемые модули аутентификации) является стандартной инфраструктурой в Linux и многих UNIX системах. Модули этой системы представляют собой разделяемые библиотеки, реализующие четыре сервиса PAM:

● библиотеки аутентификации (Authentication Service Modules), посредством которых выполняется проверка подлинности;

● библиотеки учетных записей (Account Service Modules), выполняющих такие задачи, как проверка устаревания учетных записей;

● библиотеки сеансов (Session Service Modules), отвечающие за действия, выполняемые при входе в сеанс;

● библиотеки управления паролями (Password Service Modules).

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

В PostgreSQL аутентификация PAM работает подобно методу password, используя пары «имя пользователя — пароль». Сам же механизм аутентификации может быть реализован, например, с помощью LDAP, к которому будет обращаться модуль PAM.

## Page 23: Итоги {#source-page-023}

**При аутентификации PostgreSQL может полагаться на сторонние средства**
- Метод ldap запрашивает у клиента пароль и проверяет его с помощью службы каталогов
- Методы gss и sspi аутентифицируют соединения через протокол Kerberos
- Метод pam позволяет использовать для аутентификации внешнюю библиотеку

## Page 24: Практика {#source-page-024}

1. Настройте аутентификацию ldap по методу «поиск + связывание», как в демонстрации. Поддеревом для поиска укажите ou=BioLab. Проверьте, что Алиса и Боб могут подключиться к серверу, а Чарли — нет.
- 2. Измените настройку так, чтобы все сетевые соединения использовали шифрование GSSAPI, но аутентификация по-прежнему происходила с помощью LDAP.

### Комментарии преподавателя

2. Такая настройка не имеет большого практического смысла, поскольку для шифрования GSSAPI клиент все равно должен пройти аутентификацию сервисом Kerberos.

## Page 25: 1. Аутентификация LDAP {#source-page-025}

Создаем роли:
```sql
student=# CREATE ROLE alice LOGIN;
```
```sql
CREATE ROLE
```
```sql
student=# CREATE ROLE bob LOGIN;
```
```sql
CREATE ROLE
```
```sql
student=# CREATE ROLE charlie LOGIN;
```
```sql
CREATE ROLE
```
Попросим PostgreSQL принимать подключения со всех сетевых интерфейсов:
```sql
student=# ALTER SYSTEM SET listen_addresses = '*';
```
```sql
ALTER SYSTEM
```
```sql
student=# ALTER SYSTEM SET log_connections = on;
```
```sql
ALTER SYSTEM
```
Настройка аутентификации:
```text
/etc/postgresql/16/main/pg_hba.conf.d/ext.conf
```
```text
# TYPE  DATABASE  USER  ADDRESS   METHOD
host    all       all   all       ldap ldapserver=course.local
ldapbasedn="ou=BioLab,dc=course,dc=local"
```
```sh
student$ sudo pg_ctlcluster 16 main restart
```
```sh
student$ psql student
```
Пробуем.
```sh
student$ psql "host=$(hostname) user=alice password=alice dbname=student" -c "\conninfo"
```
```text
You are connected to database "student" as user "alice" on host "course.local" (address
"127.0.1.1") at port "5432".
```
```sh
student$ psql "host=$(hostname) user=bob password=bob dbname=student" -c "\conninfo"
```
```text
You are connected to database "student" as user "bob" on host "course.local" (address
"127.0.1.1") at port "5432".
```
Алиса и Боб подключаются. А Чарли — нет:
```sh
student$ psql "host=$(hostname) user=charlie password=charlie dbname=student" -c "\conninfo"
```
```text
psql: error: connection to server at "course.local" (127.0.1.1), port 5432 failed: SSL
error: certificate verify failed
connection to server at "course.local" (127.0.1.1), port 5432 failed: FATAL:  LDAP
authentication failed for user "charlie"
```
### 2. Шифрование GSSAPI
Первым правилом запрещаем все соединения без шифрования GSSAPI. Второе правило аналогично правилу из предыдущего задания:
```text
/etc/postgresql/16/main/pg_hba.conf.d/ext.conf
```
```text
# TYPE        DATABASE  USER   ADDRESS   METHOD
hostnogssenc  all       all    all       reject
host          all       all    all       ldap ldapserver=course.local
ldapbasedn="ou=BioLab,dc=course,dc=local"
```
```sql
student=# SELECT pg_reload_conf();
```

## Page 26: Page 26 {#source-page-026}

```text
 pg_reload_conf
----------------
 t
(1 row)
```
Пробуем подключиться:
```sh
student$ psql "host=$(hostname) user=alice password=alice dbname=student" -c "\conninfo"
```
```text
psql: error: connection to server at "course.local" (127.0.1.1), port 5432 failed: SSL
error: certificate verify failed
connection to server at "course.local" (127.0.1.1), port 5432 failed: FATAL:  pg_hba.conf
rejects connection for host "127.0.0.1", user "alice", database "student", no encryption
```
Что произошло:
Утилита psql не пытается установить соединение с шифрованием GSSAPI: Алиса не аутентифицирована в Керберос, у нее нет билета на подключение к сервисам. Сначала psql попытался установить SSL-соединение (параметр sslmode по умолчанию равен prefer). Затем psql попытался установить открытое соединение.
Ни одна попытка не увенчалась успехом, поскольку сервер принимает только соединение с шифрованием GSSAPI.
Теперь Алиса получает билет на соединение с сервисами:
```sh
student$ kinit -kt alice.keytab alice
```
На этот раз подключение срабатывает: параметр gssencmode по умолчанию равен prefer и у Алисы есть билет.
```sh
student$ psql "host=$(hostname) user=alice password=alice dbname=student" -c "\conninfo"
```
```text
You are connected to database "student" as user "alice" on host "course.local" (address
"127.0.1.1") at port "5432".
GSSAPI-encrypted connection
```
В журнале сообщений видно, что для аутентификации использовался LDAP.
```sh
student$ tail -2 /var/log/postgresql/postgresql-16-main.log
```
```text
2026-07-30 16:40:26.302 MSK [74724] alice@student LOG:  connection authenticated:
identity="uid=alice,ou=BioLab,dc=course,dc=local" method=ldap
(/etc/postgresql/16/main/pg_hba.conf.d/ext.conf:3)
2026-07-30 16:40:26.302 MSK [74724] alice@student LOG:  connection authorized: user=alice
database=student application_name=psql GSS (authenticated=no, encrypted=yes,
delegated_credentials=no)
```
### Уборка
Очистим кеш Керберос:
```sh
student$ kdestroy
```
```sh
student$ sudo rm /etc/postgresql/16/main/pg_hba.conf.d/*
```
```sh
student$ sudo pg_ctlcluster 16 main reload
```
```sql
student=# \c student
```
```text
You are now connected to database "student" as user "student".
```
```sql
student=# DROP ROLE alice, bob, charlie;
```
```sql
DROP ROLE
```

## Page 27: Практика+ {#source-page-027}

Настройте аутентификацию PAM, чтобы пароль пользователя проверялся на соответствие паролю в операционной системе.
- 1. Настройте журналирование подключений PostgreSQL. 2. Зарегистрируйте средствами операционной системы пользователя pamuser и назначьте ему пароль. Зарегистрируйте одноименную роль pamuser в СУБД. 3. В pg\_hba.conf настройте для pamuser аутентификацию PAM с помощью сервиса login. 4. Обеспечьте доступ на чтение файла /etc/shadow пользователю postgres, добавив его в группу shadow. 5. Перезапустите СУБД и проверьте возможность подключения пользователем pamuser.

### Комментарии преподавателя

2. Для регистрации пользователя в операционной системе используйте команду: sudo useradd pamuser

Назначить пароль можно следующей командой (предложит ввести пароль и подтвердить его): sudo passwd pamuser

Также пароль для пользователя можно установить так: echo 'pamuser:пароль' | sudo chpasswd

3. Вариант настройки аутентификации PAM для сервиса login:

```text
# TYPE DATABASE USER    ADDRESS  METHOD
local  all      pamuser          pam pamservice=login
```

4. Для предоставления доступа на чтение файла /etc/shadow для пользователя postgres, добавьте его в группу shadow: sudo gpasswd -a postgres shadow

После выполнения задания удалите postgres из группы: sudo gpasswd -d postgres shadow

Доступ пользователей к /etc/shadow не рекомендуется в реальной эксплуатации по соображениям безопасности.

## Page 28: 1. Журналирование подключений {#source-page-028}

Включим запись в журнал отчета о подключениях:
```sql
student=# ALTER SYSTEM SET log_connections TO on;
```
```sql
ALTER SYSTEM
```
### 2. Регистрация пользователя
Создадим в PostgreSQL роль pamuser и зарегистрируем одноименного пользователя операционной системы.
```sql
student=# CREATE ROLE pamuser LOGIN;
```
```sql
CREATE ROLE
```
```sh
student$ sudo useradd pamuser
```
Установим пароль пользователю pamuser, совпадающий с его именем:
```sh
student$ echo 'pamuser:pamuser' | sudo chpasswd
```
Проверим, что пароль установлен:
```sh
student$ sudo passwd -S pamuser
```
```text
pamuser P 2026-07-30 0 99999 7 -1
```
Символ P в выводе означает, что пароль установлен и не заблокирован.
### 3. Настройка аутентификации
```text
/etc/postgresql/16/main/pg_hba.conf.d/ext.conf
```
```text
# TYPE  DATABASE  USER    ADDRESS  METHOD
local   all       pamuser          pam pamservice=login
```
### 4. Права доступа для пользователя ОС postgres
Проверим права доступа на /etc/shadow
```sh
student$ ls -l /etc/shadow
```
```text
-rw-r----- 1 root shadow 1286 июл 30 16:40 /etc/shadow
```
Пользователи операционной системы, входящие в группу shadow, могут читать файл. В этом файле находятся зашифрованные пароли, необходимые PostgreSQL для локальной аутентификации PAM. Добавим пользователя ОС postgres в группу shadow:
```sh
student$ sudo gpasswd -a postgres shadow
```
```text
Adding user postgres to group shadow
```
```sh
student$ id postgres
```
```text
uid=116(postgres) gid=124(postgres) groups=124(postgres),42(shadow),105(ssl-cert)
```
```sh
postgres$ grep pamuser /etc/shadow
```
```text
pamuser:$y$j9T$miZzcSUuy/Ra5ogTFGGvO/$nidLLgPMXeG3NMStu1HHny.JYh.d5Ngsn0EIdhjZzSA:20664:0:
99999:7:::
```
### 5. Перезапуск СУБД и подключение
Перезапустим экземпляр.
```sh
student$ sudo pg_ctlcluster 16 main restart
```
```sh
student$ psql student
```
Проверим, сможет ли pamuser подключиться.
```sh
student$ psql "user=pamuser password=pamuser dbname=student" -c "\conninfo"
```

## Page 29: Page 29 {#source-page-029}

```text
You are connected to database "student" as user "pamuser" via socket in
"/var/run/postgresql" at port "5432".
```
```sh
student$ tail -3 /var/log/postgresql/postgresql-16-main.log
```
```text
2026-07-30 16:40:39.541 MSK [75516] [unknown]@[unknown] LOG:  connection received:
host=[local]
2026-07-30 16:40:39.614 MSK [75516] pamuser@student LOG:  connection authenticated:
identity="pamuser" method=pam (/etc/postgresql/16/main/pg_hba.conf.d/ext.conf:2)
2026-07-30 16:40:39.614 MSK [75516] pamuser@student LOG:  connection authorized:
user=pamuser database=student application_name=psql
```
### Уборка
Выведем пользователя ОС postgres из группы shadow и удалим пользователя pamuser:
```sh
student$ sudo gpasswd -d postgres shadow
```
```sql
Removing user postgres from group shadow
```
```sh
student$ sudo userdel pamuser
```
```sh
student$ sudo rm /etc/postgresql/16/main/pg_hba.conf.d/*
```
```sh
student$ sudo pg_ctlcluster 16 main restart
```
```sql
student=# \c student
```
```text
You are now connected to database "student" as user "student".
```
```sql
student=# DROP ROLE pamuser;
```
```sql
DROP ROLE
```
