В этом посте раскрываются основы интеграции СУБД 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.
Требования:
- Контроллер домена Active Directory.
- ОС 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.