В этом посте раскрываются основы интеграции СУБД CockroachDB с Active Directory. AD — коммерческий побратим Kerberos, предоставляемый компанией Microsoft.

Сегодня поговорим про интеграцию CockroachDB с Active Directory. В основе работы Cockroach лежит программный интерфейс сервисов безопасности GSSAPI. В настоящее время Cockroach поддерживает только сопоставление пользователей. А вот синхронизацию пользователей организационного подразделения (OU) AD с ролями в Cockroach — уже нет.

Моя тестовая среда состоит из контроллера Active Directory на виртуальной машине Virtual Box под управлением Windows Server 2016 и виртуальной машины Vagrant под управлением CentOS 7, на которой развёрнута CockroachDB. Виртуальные машины объединены в сеть и видны только хосту и друг другу (host-only). Это критически важно для моей конфигурации, потому что даёт узлу Cockroach возможность взаимодействовать с AD через порт 88.

Требования:


  1. Контроллер домена Active Directory.
  2. ОС Linux, в моём случае RHEL7 и CockroachDB 20.1.1.

Необходимые условия:


  • Я использую ознакомительную версию Windows Server 2016. Пробную версию с льготным периодом 180 дней можно скачать здесь.
  • Для кастомной установки Windows Server на ВМ Virtual Box я использовал, в частности, вот это руководство.
  • Установите дополнения VirtualBox Guest Additions по этой инструкции.
  • Откройте AD и хосту доступ к одной из директорий. Она понадобится, чтобы копировать файлы ключей (keytab) на узлы Cockroach.
  • Измените имя компьютера машины с AD на удобное для восприятия. У меня, например, это adserver.
  • Необходимо синхронизировать сервер AD и узлы Cockroach по времени, дате и часовому поясу. При работе с Kerberos это само собой разумеется, но нелишне будет напомнить и здесь.
  • Смените IP-адрес Windows на подсеть узла (или узлов) CockroachDB.
  • Добавьте узлы Cockroach в файл hosts в Windows (это опциональный шаг).
  • Установите контроллер домена Active Directory с помощью руководства.

Рисунки ниже приведены для справки — выполняя шаги в руководствах, вы вероятно уже достигли нужного результата.

Установка Windows Server



Смена имени компьютера Windows



Проверка наличия нового имени компьютера



Синхронизация по времени, дате, часовому поясу



Смена подсети AD на ту же, что и у CockroachDB



Опционально: добавление хостов CockroachDB к файлу hosts сервера AD



На данном этапе уже можно проверять связь с CentOS командой ping:


Аналогичным образом проверку можно провести с машины CentOS после заполнения файла /etc/hosts.

127.0.0.1   	node.example.com    	node
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1     	localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.33.15   adserver.example.com	adserver
[vagrant@node ~]$ ping adserver.example.com
PING adserver.example.com (192.168.33.15) 56(84) bytes of data.
64 bytes from adserver.example.com (192.168.33.15): icmp_seq=1 ttl=128 time=0.369 ms
64 bytes from adserver.example.com (192.168.33.15): icmp_seq=2 ttl=128 time=0.511 ms
64 bytes from adserver.example.com (192.168.33.15): icmp_seq=3 ttl=128 time=0.430 ms

В качестве перестраховки давайте удостоверимся, что к серверу AD можно подключиться через порт 88. Для этого потребуется установка пакетов telnet:

sudo yum install -y telnet
telnet adserver.example.com 88
Trying 192.168.33.15...
Connected to adserver.example.com.
Escape character is '^]'.

Теперь перейдём к добавлению субъекта-службы в AD. Сгенерируйте файл ключей и выполните конфигурацию файла krb5.conf на машине CentOS.

На машине с контроллером AD перейдите к консоли Active Directory Users and Computers.

Добавьте нового пользователя правым нажатием на Users под доменом — в моём случае это выглядит так:


И наконец, выберите нужные настройки пароля и нажмите Finish:


Важно также проверить две настройки пользователя, относящиеся к Kerberos. В пользовательском интерфейсе Windows Server мне оказалось нелегко их обнаружить. Они находятся на странице свойств пользователя, вкладка Account. Чтобы найти их в списке, может потребоваться прокрутить всю область с флажками до конца.


Для завершения шага примените изменения кнопкой Apply.

Теперь можем сопоставить имя субъекта-службы (SPN, Service Principal Name) и создать файл ключей. Для этой задачи подойдёт командная строка или PowerShell. Здесь окажется кстати утилита AD под названием ktpass:

ktpass -out node.keytab -princ postgres/node.example.com@EXAMPLE.COM -mapUser pguser@EXAMPLE.COM -mapOp set -pType KRB5_NT_PRINCIPAL -crypto AES256-SHA1 -pass CRDB123?

Эта команда создаёт файл ключей с именем node.keytab, сопоставляя имя субъекта-службы postgres с FQDN узла Cockroach node.example.com@EXAMPLE.COM, а также пользователя AD pguser с именем субъекта-службы. Укажем режим шифрования AES256-SHA1, и наконец передадим пароль к недавно созданному SPN.

Создание файла ключей и сопоставление SPN с субъектом



Можем также проверить SPN следующей командой:

setspn -l pguser


На данном этапе у нас есть файл ключей, с помощью которого можно подключаться к AD с машины CentOS. Скопируем его в файл hosts в Cockroach.

Перед интеграцией с AD в файле hosts в CentOS нужно выполнить ряд предварительных действий. Для начала удостоверимся, что настройки времени, даты и часового пояса корректны:

timedatectl
timedatectl list-timezones | grep New_York
[vagrant@node ~]$ timedatectl
  	Local time: Wed 2020-06-03 17:00:01 UTC
  Universal time: Wed 2020-06-03 17:00:01 UTC
    	RTC time: Wed 2020-06-03 16:59:59
   	Time zone: UTC (UTC, +0000)
 	NTP enabled: yes
NTP synchronized: yes
 RTC in local TZ: no
  	DST active: n/a
[vagrant@node ~]$ timedatectl list-timezones | grep New_York
America/New_York
[vagrant@node ~]$ sudo timedatectl set-timezone America/New_York
[vagrant@node ~]$ timedatectl
  	Local time: Wed 2020-06-03 13:01:06 EDT
  Universal time: Wed 2020-06-03 17:01:06 UTC
    	RTC time: Wed 2020-06-03 17:01:05
   	Time zone: America/New_York (EDT, -0400)
 	NTP enabled: yes
NTP synchronized: yes
 RTC in local TZ: no
  	DST active: yes
 Last DST change: DST began at
              	Sun 2020-03-08 01:59:59 EST
              	Sun 2020-03-08 03:00:00 EDT
 Next DST change: DST ends (the clock jumps one hour backwards) at
              	Sun 2020-11-01 01:59:59 EDT
              	Sun 2020-11-01 01:00:00 EST

Установите пакет krb5-workstation и заполните его свойствами, уникальными для AD:

yum install -y krb5-workstation

Проведите конфигурацию файла /etc/krb5.conf, указав следующие свойства:

[logging]
 default = FILE:/var/log/krb5libs.log
 kdc = FILE:/var/log/krb5kdc.log
 admin_server = FILE:/var/log/kadmind.log
 
[libdefaults]
 default_realm = EXAMPLE.COM
 
[realms]
 EXAMPLE.COM = {
  kdc = adserver.example.com
  admin_server = adserver.example.com
  default_domain = example.com
 }
 
[domain_realm]
 .example.com = EXAMPLE.COM
 example.com = EXAMPLE.COM

Измените права доступа в файле ключей:

chmod 600 node.keytab

Задайте переменной KRB5_KTNAME значение, соответствующее расположению данного файла:

export KRB5_KTNAME=node.keytab

По идее, теперь можно пройти аутентификацию в AD под именем pguser:

[vagrant@node ~]$ kinit pguser
Password for pguser@EXAMPLE.COM:
[vagrant@node ~]$ klist
Ticket cache: FILE:/tmp/krb5cc_1000
Default principal: pguser@EXAMPLE.COM
 
Valid starting   	Expires          	Service principal
06/03/2020 13:23:06  06/03/2020 23:23:06  krbtgt/EXAMPLE.COM@EXAMPLE.COM
    	renew until 06/04/2020 13:23:06

Давайте проверим ещё пару моментов в файле ключей, чтобы избежать долгих часов мучений в будущем. Поверьте моему горькому опыту!

Согласно требованиям Kerberos номер KVNO для субъекта в системе под управлением ОС семейства Unix должен быть больше или равняться 3. Проверить его можно так:

kvno pguser@EXAMPLE.COM
pguser@EXAMPLE.COM: kvno = 3

Давайте убедимся, что номер KVNO нашего имени SPN соответствует субъекту:

kvno postgres/node.example.com@EXAMPLE.COM
[vagrant@node ~]$ kvno postgres/node.example.com@EXAMPLE.COM
postgres/node.example.com@EXAMPLE.COM: kvno = 3

Можно также проверить соответствие записи в файле ключей:

[vagrant@node ~]$ klist -kt node.keytab
Keytab name: FILE:node.keytab
KVNO Timestamp       	Principal
---- ------------------- ------------------------------------------------------
   3 12/31/1969 19:00:00 postgres/node.example.com@EXAMPLE.COM

То же самое проверяется и с помощью ktutil:

[vagrant@node ~]$ ktutil
ktutil:  read_kt node.keytab
ktutil:  list
slot KVNO Principal
---- ---- ---------------------------------------------------------------------
   1	3	postgres/node.example.com@EXAMPLE.COM

Хотя мы уже проверили оба номера KVNO, давайте для перестраховки повторим процедуру на сервере AD.
Чтобы получить значение KVNO, выполните в PowerShell команду:

Get-ADUser pguser -property msDS-KeyVersionNumber


Раз все значения совпадают, можем переходить к настройке GSSAPI в Cockroach.

Установите CockroachDB:

COCKROACH_VERSION=v21.2.0
wget -qO- https://binaries.cockroachdb.com/cockroach-$COCKROACH_VERSION.linux-amd64.tgz | tar  xvz
sudo cp -i cockroach-$COCKROACH_VERSION.linux-amd64/cockroach /usr/local/bin/
cockroach version
Build Tag:	v21.2.0
Build Time:   2021/11/15 14:00:58
Distribution: CCL
Platform: 	linux amd64 (x86_64-unknown-linux-gnu)
Go Version:   go1.16.6
C Compiler:   Clang 10.0.0
Build SHA-1:  6123c0c73ff0eea223cfd25e1e557648413126f8
Build Type:   release

Запустите защищённый кластер.

Создайте сертификаты. Процедура, описанная в документации, для наших целей подойдёт. Я просто передаю команде certs добавочные имена DNS:

HOSTNAME="node.example.com node 192.168.33.10"
mkdir certs my-safe-directory
cockroach cert create-ca --certs-dir=certs --ca-key=my-safe-directory/ca.key
cockroach cert create-node $HOSTNAME --certs-dir=certs --ca-key=my-safe-directory/ca.key
openssl x509 -in certs/node.crt -text | grep "Subject Alternative Name" -A 1
cockroach cert create-client root --certs-dir=certs --ca-key=my-safe-directory/ca.key
[vagrant@node ~]$ openssl x509 -in certs/node.crt -text | grep "Subject Alternative Name" -A 1
        	X509v3 Subject Alternative Name:
            	DNS:node.example.com, DNS:node, IP Address:192.168.33.10

Запускаем Cockroach в безопасном режиме:

cockroach start --certs-dir=certs --store=node1 --listen-addr=node.example.com:26257 --http-addr=node.example.com:8080 --join=node.example.com:26257,node.example.com:26258,node.example.com:26259 --background
cockroach start --certs-dir=certs --store=node2 --listen-addr=node.example.com:26258 --http-addr=node.example.com:8081 --join=node.example.com:26257,node.example.com:26258,node.example.com:26259 --background
cockroach start --certs-dir=certs --store=node3 --listen-addr=node.example.com:26259 --http-addr=node.example.com:8082 --join=node.example.com:26257,node.example.com:26258,node.example.com:26259 --background

Инициируем кластер:

[vagrant@node ~]$ cockroach init --certs-dir=certs --host=node.example.com:26257
Cluster successfully initialized

Подключаемся к базе данных:

cockroach sql --certs-dir=certs --host=node.example.com

Активируем корпоративную лицензию. GSSAPI на момент написания статьи доступен только в корпоративной версии:

SET CLUSTER SETTING cluster.organization = 'Acme Company';
SET CLUSTER SETTING enterprise.license = 'xxxxxxxxxxxx';

Включаем GSSAPI — для всех, кроме привилегированного пользователя (root user). Он будет по-прежнему подключаться с помощью корневого сертификата:

SET cluster setting server.host_based_authentication.configuration = 'host all all all gss include_realm=0';

Создаём рядового пользователя и наделяем его правами:

CREATE USER pguser;
GRANT ALL ON DATABASE defaultdb TO pguser;
\q

Осталось только выполнить команду kinit от имени pguser. Если помните, мы уже это делали, но для завершения процесса шаг надо повторить:

kdestroy -A
kinit pguser
klist

И наконец, надо установить клиент psql, поскольку cockroach.CLI не поддерживает GSSAPI.

На мой взгляд, предпочтительно использовать psql-клиент версии 9.5, поскольку CockroachDB поддерживает именно этот проводной протокол. К сожалению, CentOS 7 поставляется с версией 9.2, и чтобы установить нужную нам версию, мы выполним шаги, описанные здесь.

Отключим postgresql в разделах [base] и [updates] файла /etc/yum.repos.d/CentOS-Base.repo командой exclude=postgresql*:

[base]
name=CentOS-$releasever - Base
mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=os&infra=$infra
#baseurl=http://mirror.centos.org/centos/$releasever/os/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
exclude=postgresql*
 
#released updates
[updates]
name=CentOS-$releasever - Updates
mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=updates&infra=$infra
#baseurl=http://mirror.centos.org/centos/$releasever/updates/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
exclude=postgresql*

Затем выполним инструкцию, приведённую по ссылке. Я выбираю версию 9.5.

yum install https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm -y
yum install postgresql95 -y

И наконец, подключаемся к CockroachDB как пользователь pguser:

psql "postgresql://node.example.com:26257/defaultdb?sslmode=require" -U pguser
psql (9.5.22, server 9.5.0)
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES128-GCM-SHA256, bits: 128, compression: off)
Type "help" for help.
 
defaultdb=>

А что если у вас много узлов, как это часто бывает в CockroachDB? Нужно добавить все узлы в список участников в файле ключей. Поскольку у меня только один узел — node.example.com — я укажу его и через IP-адрес, чтобы наглядно продемонстрировать следующий шаг:

Мой IP-адрес — 192.168.33.10. Команда ktpass для добавления субъектов будет иметь несколько иной вид:

ktpass -out ip.keytab -princ postgres/192.168.33.10@EXAMPLE.COM -mapUser pguser@EXAMPLE.COM -mapOp add -pType KRB5_NT_PRINCIPAL -crypto AES256-SHA1 -pass CRDB123?

Заметьте, я использую новый файл ключей — чтобы не перезаписать уже имеющийся. Можем использовать его как дополнение к существующему или объединить их позднее. Кроме того, я изменил -mapOp на add с set. Это важно, чтобы избежать замены текущей записи на новую. Вместо этого создаётся дополнение:


При выполнении команды setspn -l user будут отображаться обе записи:


Давайте проверим номер KVNO, поскольку мы вносили изменения в параметры субъекта:


Заметьте, номер KVNO увеличился.

Теперь скопируем файл ключей в host CockroachDB и проверим, всё ли там правильно:

[vagrant@node ~]$ klist -kt ip.keytab
Keytab name: FILE:ip.keytab
KVNO Timestamp       	Principal
---- ------------------- ------------------------------------------------------
   4 12/31/1969 19:00:00 postgres/192.168.33.10@EXAMPLE.COM

Значения KVNO совпадают:

[vagrant@node ~]$ kvno pguser@EXAMPLE.COM
pguser@EXAMPLE.COM: kvno = 4
[vagrant@node ~]$ kvno postgres/192.168.33.10@EXAMPLE.COM
postgres/192.168.33.10@EXAMPLE.COM: kvno = 4
[vagrant@node ~]$ kvno postgres/node.example.com@EXAMPLE.COM
postgres/node.example.com@EXAMPLE.COM: kvno = 3

Номер KVNO для узла node.example.com всё ещё равен 3, поскольку указывает на старый файл ключей — как и должен:

[vagrant@node ~]$ klist -kt ip.keytab
Keytab name: FILE:ip.keytab
KVNO Timestamp       	Principal
---- ------------------- ------------------------------------------------------
   4 12/31/1969 19:00:00 postgres/192.168.33.10@EXAMPLE.COM
[vagrant@node ~]$ klist -kt node.keytab
Keytab name: FILE:node.keytab
KVNO Timestamp       	Principal
---- ------------------- ------------------------------------------------------
   3 12/31/1969 19:00:00 postgres/node.example.com@EXAMPLE.COM

Давайте объединим два файла ключей и попробуем снова:

[vagrant@node ~]$ ktutil
ktutil:  read_kt ip.keytab
ktutil:  read_kt node.keytab
ktutil:  list
slot KVNO Principal
---- ---- ---------------------------------------------------------------------
   1	4   	postgres/192.168.33.10@EXAMPLE.COM
   2	3	postgres/node.example.com@EXAMPLE.COM
ktutil:  write_kt postgres.keytab
ktutil:  exit
[vagrant@node ~]$ klist -kt postgres.keytab
Keytab name: FILE:postgres.keytab
KVNO Timestamp       	Principal
---- ------------------- ------------------------------------------------------
   4 06/03/2020 14:21:20 postgres/192.168.33.10@EXAMPLE.COM
   3 06/03/2020 14:21:20 postgres/node.example.com@EXAMPLE.COM
chmod 600 postgres.keytab
export KRB5_KTNAME=postgres.keytab

Возможно, потребуется прокатный перезапуск, чтобы кластер перешёл на работу с новым файлом ключей:

vagrant@node ~]$ psql "postgresql://node.example.com:26257/defaultdb?sslmode=require" -U pguser
psql (9.5.22, server 9.5.0)
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES128-GCM-SHA256, bits: 128, compression: off)
Type "help" for help.
 
defaultdb=>

У нас и правда есть субъект с IP-адресом 192.168.33.10. Попробуем к нему подключиться:

[vagrant@node ~]$ psql "postgresql://192.168.33.10:26257/defaultdb?sslmode=require" -U pguser
psql: could not connect to server: Connection refused
    	Is the server running on host "192.168.33.10" and accepting
    	TCP/IP connections on port 26257?

Причина ошибки — в том, что узел был запущен с помощью команды --listen-addr=node.example.com:26257. Чтобы перезапустить его, я ввожу --listen-addr=192.168.33.10:26257.

Подключаемся ещё раз:

[vagrant@node ~]$ psql "postgresql://192.168.33.10:26257/defaultdb?sslmode=require" -U pguser
psql (9.5.22, server 9.5.0)
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES128-GCM-SHA256, bits: 128, compression: off)
Type "help" for help.
 
defaultdb=>

Получилось! Этот IP можно использовать и для балансировщика нагрузки.

Важное примечание


Использование режима sslmode=require сопряжено с риском атаки MITM (Man In The Middle). В связи с этим рекомендуется использовать значения verify-ca или verify-full.

verify-ca безопаснее в плане атак MITM, поскольку проверяет безопасность сервера через издателя сертификата:

psql "postgresql://node.example.com:26257/defaultdb?sslmode=verify-ca&sslrootcert=certs/ca.crt" -U pguser

verify-full в добавление к этому ещё и проверяет атрибут Common Name (стандартное имя) на совпадение с именем компьютера:

psql "postgresql://node.example.com:26257/defaultdb?sslmode=verify-full&sslrootcert=certs/ca.crt" -U pguser

Более подробно об этом рассказано в официальной документации.



НЛО прилетело и оставило здесь промокоды для читателей нашего блога:

15% на все тарифы VDS (кроме тарифа Прогрев) — HABRFIRSTVDS.

20% на выделенные серверы AMD Ryzen и Intel Core HABRFIRSTDEDIC.

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


  1. gecube
    07.01.2022 15:15
    +3

    . AD — коммерческий побратим Kerberos, предоставляемый компанией Microsoft.

    не надо такой самодеятельности. AD - это сильно больше, чем керберос...

    В остальном материал интересный и доказывающий, что CockroachDB готова к использованию в энтерпрайзе


    1. sshikov
      07.01.2022 15:47

      >AD — это сильно больше, чем керберос
      А Слава КПСС — это вообще не человек? (с) анекдот.

      Керберос — это вообще-то протокол, а не софт. Софт, и в случае windows тоже, это например сервис KDC. Который с AD вообще располагается где-то рядом, и использует оный для понятных целей хранения юзеров, групп и пр.


      1. gecube
        07.01.2022 16:03

        Керберос — это вообще-то протокол, а не софт.

        я в общем-то именно про это. Спецом посмотрел - в оригинальной статье вот такого:

        AD — коммерческий побратим Kerberos, предоставляемый компанией Microsoft.

        не писали. И правильно сделали.

        Который с AD вообще располагается где-то рядом, и использует оный для понятных целей хранения юзеров, групп и пр.

        все так. А еще есть вариант интеграции через LDAP или нативно с AD... Интересно, выигрывает ли керберос тут? К сожалению, у меня не хватает квалификации, но мне казалось, что интеграция через LDAP протокол удобнее для настройки и для поддержки


        1. sshikov
          07.01.2022 17:19

          >я в общем-то именно про это.
          Так я лишь усилил ваше утверждение. Это разные вещи. Хотя обычно и работают вместе (что вообще не удивительно ни разу). Близким аналогом AD в мире юникс является скажем FreeIPA, которая в общем-то тоже не керберос, а набор компонентов, включающих сервер каталогов (сюрприз) и керберос (но далеко не только их, а еще службу времени, например).


        1. creker
          07.01.2022 19:36

          Я если честно мало вообще систем знаю, которые керберос умеют. В основном все понимает LDAP и может быть еще AD. Керберос это все же протокол аутентификации, что достаточно для SSO, но маловато для интеграции с доменом. В большинстве случаев требуется синхронизация групп и учеток, для чего LDAP как раз создан. А заодно еще дает аутентификацию через биндинг, что и нивелириует необходимость реализовывать керберос.


  1. The_Kf
    08.01.2022 21:29

    > добавление хостов CockroachDB к файлу hosts сервера AD

    Зачем, если на контроллере домена у вас свой собственный DNS-сервис есть?