[Перевод] Настройка CockroachDB с помощью Active Directory

В этом посте раскрываются основы интеграции СУБД 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.

Читайте так же:

  • Ежедневный вебинар для маркетологов «Как находить в Дзене новых клиентов»Ежедневный вебинар для маркетологов «Как находить в Дзене новых клиентов» Яндекс.Дзен — находка маркетолога. Каждый день Дзен открывает более 20 миллионов пользователей, и каждый из них может стат для бизнеса клиентом. Сотни компаний, включая ПИК, Skillbox, Альфа-Банк, Philips уже используют контент-маркетинг в Дзене, чтобы рассказать […]
  • [Перевод] Фрагменты кода со StackOverflow, которые разработчики часто копируют вместе с ошибками[Перевод] Фрагменты кода со StackOverflow, которые разработчики часто копируют вместе с ошибками Инспекция кода с прицелом на безопасность – то, чем я занимаюсь изо дня в день уже тринадцать с половиной лет. За это время я просмотрел несколько сотен кодовых баз и не раз имел дело с криптографическим кодом. В большинстве случаев в криптографическом коде, который я проверял, […]
  • Требования к системе управления контентом сайтаТребования к системе управления контентом сайта Таким образом, вы знаете, что вам нужно использовать CMS для вашего издательского сайта. Будь то новостной сайт, подписка на онлайн-газету или просто портал информационных статей. Использование CMS здесь, очевидно. Не является проблемой. Самый большой драйвер на новостных и статейных […]
  • Сбер приобрел платформу бизнес-коммуникаций JivoСбер приобрел платформу бизнес-коммуникаций Jivo Сбер объявил о завершении сделки по покупке коммуникационной платформы Jivo (ООО «Живой Сайт») – банк получил 100% компании. Основатели платформы продолжат работать в компании: Тимур Валишев — в роли генерального директора, Николай Иванников – технического директора. Jivo — […]