Чего у меня не отнять, дак это мастерства заголовка...

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

Данная статья является легким переосмыслением того, что я написал на медиуме. Ибо думать я продолжаю на русском (:

TL;DR исходники к вашим услугам.

В рамках любых взаимодействий мы сталкиваемся с такими сущностями как авторизация и аутентификация. Повторять в 100500 раз что есть что я не буду (но мне не лень такую длинную ремарку напечатать, ага). В рамках PostgreSQL первое обеспечивается через Roles, а второе через Privileges.

Если покопаться в документации PostgreSQL, то можно обнаружить, что эта БД поддерживает довольно много типов авторизации. Однако, нас интересует что-то, что могло бы заменить связку логина и пароля. И в этот момент авторизация на основе сертификатов приходит на гугл ум. В общем-то, при корректной настройке, мы не только избавляемся от необходимости ввода пароля для нашего пользователя, но и повышаем уровень защищенности (подделка сертификата немножко сложнее угадывания любимого пароля, который у меня - "qwerty").


Давайте подумаем над тем, чего мы хотим достичь. Но думать просто так скучно, поэтому запишем наши требования в формате пользовательских историй!

  1. Как пользователь, я хочу иметь доступный инстанс PostgreSQL.

  2. Как пользователь, я хочу иметь возможность залогиниться туда и выполнять доступные мне команды.

  3. Как пользователь, я ленив и не хочу вводить пароль (так и быть, согласен поставить флаг "Запомнить пароль" в соответствующем диалоге pgAdmin).

  4. Как пользователь, я хочу чтобы все взаимодействие было обмазано сертификатами.


Начинаем готовить наш коктейль. Нам понадобятся:

  1. Docker-compose.

  2. OpenSSL.

  3. Любимый текстовый редактор, или IDE.


С учетом ингридиентов для нашего коктейля, давайте подумаем над сетевым взаимодействием элементов. Для начала, конечно же, определим их:

  1. Сервис PostgreSQL

  2. Сервис pgAdmin

  3. Любимый браузер

И попробуем отрисовать их взаимодействие:

1-2 - TLS канал, и сервер, и клиент проверяют сертификаты противоположной стороны. Это соединение mTLS

3-4 - Стандартная request / response последовательность, выполняемая в рамках канала, установленного в шаге 1-2.

5-6 - TLS канал, проверка сертификата осуществляется только на стороне клиента (браузера). Данное соединение является просто TLS, т.к. валидация сертификата выполняется только одной из сторон.

7-8 - Аналогично шагу 3-4, но исполняется в рамках канала, установленного в шаге 5-6.


Теперь мы видим, какие типы сертификатов нам понадобятся:

  • Корневой сертификат (1) для подписания сертификатов связанных с mTLS соединением

  • Сертификат для PostgreSQL сервиса, подписанный корневым сертификатом (1)

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

  • Корневой сертификат (2) для подписания сертификатов, связанных с HTTPS соединением

  • HTTPS сертификат, подписанный корневым сертификатом (2)

Эмпирическим путем было установлено, что Apple Keychain не поддерживает корневые сертификаты с размером ключа больше чем 8192 бита (что очевидно следует из текста ошибки "Error: -67762"), так что, ограничимся этой размерностью.


Поехали генерировать сертификаты. Мы же программисты, поэтому, пишем скрипт и необходимый конфиг.

Для начала, определяем какие-то общие переменные:

Теперь можно сгенерировать корневой сертификат для целей mTLS:

Генерируем сертификат для PostgreSQL сервиса и подписываем его ранее полученным:

И генерируем по сертификату для каждого из пользователей:

Генерация сертификатов для HTTPS соединения точно такая же, за исключением того, что пользовательские сертификаты генерировать не нужно.


Немного посмотрим на конфигурацию. Конфигурационные файлы в данном случае имеют ini-подобную структуру, т.е. имеются разделы, а в них ключи и значения. При этом, значения могут ссылаться на какой-то из разделов. Например:

В данном случае, значение ключа basicConstraints ссылается на раздел mtls_root_basic_constraints в котором указано, что мы генерируем Certificate Authority (не смог вменяемый перевод вспомнить), который не может выдавать промежуточные сертификаты.

Применяемую секцию из конфигурационного файла мы передаем как значение параметра -extensions: -extensions v3_mtls_root.


Итак, все сертификаты сгенерированы. Пора конфигурировать PostgreSQL. Начнем с файла pg_hba.conf. Этот файл является частью системы аутентификации.

Наши требования довольно очевидны:

  1. Разрешаем любое локальное подключение изнутри контейнера

  2. Любое SSL соединение должно проходить полную валидацию пользовательского сертификата

  3. Иные соединения запрещены

Собственно, и содержимое данного конфигурационного файла это отражает:

Далее нам необходимо сконфигурировать параметры запуска сервиса, переменные окружения, пробросить порт и тома. Собственно, эта часть docker-compose.yml может выглядеть так:


Аналогичную конфигурацию, а именно: переменных окружения, проброса порта и томов мы проводим для нашего pgAdmin:

Собственно, из основного - пробрасываем пользовательские сертификаты и корневой сертификат mTLS. Так же пробрасываем серверный HTTPS сертификат. Ну и раз мы все локации сертификатов знаем, можем сразу же пробросить файл с настройками серверов (servers.json):


Итого, что мы имеем на выходе:

  • Сконфигурированный PostgreSQL проводящий валидацию на основе CN сертификата.

  • Сконфигурированный pgAdmin, который умеет устанавливать mTLS соединение с PostgreSQL и TLS соединение с браузером

Хочется думать, что данный контент оказался вам полезен. Всегда готов ответить вам по DEFAULT_EMAIL - не стесняйтесь.

Комментарии (10)


  1. sshikov
    31.03.2024 12:10

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

    Ну если честно, то это далеко не очевидно. У вас же много пользователей, правда? Представим что в какой-то момент вы их всех решили тоже аутентифицировать по сертификатам. Бац - и у вас в компании появился свой Удостоверяющий Центр (это перевод CA, если что). И у вас вместо записанных там и сям на бумажке паролей - валяющиеся там и сям на дисках пользовательские сертификаты. Много. В непонятном состоянии. Непонятно когда протухнут.

    А уж тихая и незаметная утечка корневого сертификата, которым можно подписать пользовательский, точно такой же, под которым у вас ходит в базу DBA... это вообще.

    Ну я надеюсь вы поняли. Перевод на сертификаты - это куча дополнительного высокоуровневого гемора на голову админов. В каком-то смысле уровень защищенности может и растет, но схема усложняется, в ней появляются новые неочевидные дыры в безопасности. Станет ли в итоге лучше - совсем не факт. А вот сложнее для всех - станет точно.

    Вы ведь не забыли себе в календарь записать, что нужно перевыпустить сертификат через годик, да? :)


    1. Delfik Автор
      31.03.2024 12:10

      Я в самом начале упомянул

      при локальной разработке

      намеренно, чтобы сгладить некоторые углы, которые вы подсветили =)

      Пользователей то много, да. Но вот если всем нужен доступ до прода, то, кажется, это больше про характеристику компании / процесса разработки будет, нежели про проблематику валяющихся пользовательских сертификатов.

      Опять же, если будет утечка корневого сертификата да еще из абстрактного УЦ с СКЗИ класса защиты КС3, то это будет новость интереснее свежей новости про xz.

      Вы ведь не забыли себе в календарь записать, что нужно перевыпустить сертификат через годик, да? :)

      Без шуток - да.

      Ну и говоря про проблематику ротации сертификатов, есть же уже действующие решения. Vault + agent / импортозамещенный вариант под названием SecMan

      Опять же, если хочется говорить про применимость к prod environment, там можно и скомбинировать полную взаимную проверку сертификатов с проверкой пароля. Добавим конфигурацию проверки по IP (все та же строчка в pg_hba.conf). Плюсом кажется сетевая инфраструктура организации должна быть настроена правильным образом ;)


      1. sshikov
        31.03.2024 12:10

        Опять же, если будет утечка корневого сертификата да еще из абстрактного УЦ с СКЗИ класса защиты КС3, то это будет новость интереснее свежей новости про xz.

        Да не то слово.

        Я знаю про vault, мы как раз в процессе внедрения. Поэтому и решил позадавать вопросы в том числе.


      1. stvoid
        31.03.2024 12:10

        Для локальной разработки вам хватит файла .pgpass


  1. ptr128
    31.03.2024 12:10

    Но почему не GSSAPI?


    1. Delfik Автор
      31.03.2024 12:10

      У автора лапки, macOS и в домашней сети не оказалось нужной инфраструктуры.


      1. ptr128
        31.03.2024 12:10

        Поднять хотя бы Samba DC можно и в контейнере. Зато это будет масштабируемо.

        LDAP с Kerberos можно запускать даже в той же вируталке, что и PostgreSQL. Раньше так и делал.


        1. Delfik Автор
          31.03.2024 12:10

          Зато это будет масштабируемо.

          Оставив за скобками момент, что я в первых же строчках писал про локальную разработку, в какой момент у сертификатов пропало масштабирование?

          LDAP с Kerberos можно запускать даже в той же вируталке, что и PostgreSQL. Раньше так и делал.

          Где-то я пропустил в тексте, что запускаю это все на виртуалке. Как обнаружу - дам более развернутый комментарий.

          В любом случае, получаем дополнительный сервис, а хочется побриться бритвой Оккама.

          Ну и просто вопрос. А GSSAPI точно удовлетворяет требованию 4? Я его, конечно, сформулировал в слишком фривольной манере, но подразумевалось что непосредственный канал связи с сервером БД должен быть mTLS.


          1. ptr128
            31.03.2024 12:10
            +1

            в какой момент у сертификатов пропало масштабирование?

            Как только появляются реальные пользователи, которым все равно необходимо будет получать билетики, механизм сертификатов для них окажется пятой ногой. Ведь механизм сертификатов потребует разработки целой инфраструктуры. Ведь в целях безопасности, эти сертификаты все равно нужно будет выдавать пользователям всего на несколько часов, так же, как и билетики. Последние выдаются, обычно, на 8-12 часов.

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

            Где-то я пропустил в тексте, что запускаю это все на виртуалке

            Вы пропустили слова даже и раньше. Я указал, что Kerberos я использовал еще до появления в природе докера. Лет 25 назад.

            В любом случае, получаем дополнительный сервис

            В подавляющем большинстве случаев, как только решение будет масштабировано за пределы локального хоста, Kerberos будет доступен. Более того, в моей практике, Kerberos всегда был доступен, пусть даже и через VPN. То, что из-за бюрократии не всегда оперативно добавлялись SPN для сервисов моего локального хоста - это уже другой вопрос.

            подразумевалось что непосредственный канал связи с сервером БД должен быть mTLS.

            Kerberos иначе и не умеет. Разница в том, как я указал выше, что сертификат (билетик) клиенту выдается KDC на конкретный сервис, а не на всё скопом.


  1. atshaman
    31.03.2024 12:10

    П.0 - я как пользователь _не хочу_ трогать pgAdmin даже ОЧЕНЬ длинной палкой. Можно? Ну позя-зя-зя?!