Создание keytab-файла, содержащего несколько разных Kerberos Principal

Разные службы и приложения, работающие в Linux и использующие аутентификацию Kerberos, например в связке с контроллерами домена Active Directory (AD), используют для механизмов этой самой аутентификации специальный keytab-файл, который содержит хеши пароля доменной учётной записи пользователя, с которым ассоциирована та или иная служба в Linux-системе (как с Kerberos Principal). И вполне типичной и распространённой практикой является создание отдельного keytab-файла для каждого принципала Kerberos. То есть, как правило, прослеживается такая логика: отдельная учётная запись служебного пользователя в AD, для которого зарегистрировано конкретное имя службы ServicePrincipalName (SPN) => отдельный keytab-файл, сопоставимый с записью SPN в AD.

А теперь представьте ситуацию, когда, например, на веб-сервере Apache настроена и успешно работает Kerberos-аутентификация с помощью keytab-файла, содержащего имя принципала Kerberos, равное HTTP/web.holding.com@HOLDING.COM, сопоставимое с SPN HTTP/web.holding.com в свойствах некоторой учётной записи служебного пользователя в домене AD (примеры такой настройки мы рассматривали ранее: здесь, здесь и здесь). В таком случае аутентификация Kerberos будет успешно работать только при обращении к веб-сайту Apache по имени http://web.holding.com. Однако, в один прекрасный момент возникает необходимость использовать другое имя FQDN сервера в URL, так как в Apache создан дополнительный виртуальный хост, а в DNS создана A-запись, имеющая имя этого виртуального хоста и ведущая к IP адресу этого же веб-сервера. Например, пусть это будет имя web2.holding.com.
В этом случае, если мы попытаемся использовать старый keytab-файл файл для настройки конфигурации нового виртуального хоста Apache, то при попытке обращения к URL http://web2.holding.com, клиенты получат от веб-сервера ошибку 500 Internal Server Error.

В логе при этом будут регистрироваться события типа:

# tail -f /var/log/apache2/error.log

...
[auth_kerb:error] [pid 11006] [client 10.1.1.2:3059] gss_acquire_cred() failed: Unspecified GSS failure.  Minor code may provide more information (, No key table entry found matching HTTP/web2.holding.com@)
...

И это справедливо, ведь имеющийся у нас на веб-сервер keytab-файл содержит записи только для принципала HTTP/web.holding.com, и никакого HTTP/web2.holding.com там нет и в помине.

# klist -K -e -t -k /etc/apache2/web.keytab

Keytab name: FILE:/etc/apache2/web.keytab
KVNO Timestamp           Principal
---- ------------------- ------------------------------------------------------
   4 01/01/1970 03:00:00 HTTP/web.holding.com@HOLDING.COM (des-cbc-crc) (0x62...)
   4 01/01/1970 03:00:00 HTTP/web.holding.com@HOLDING.COM (des-cbc-md5) (0x62...)
   4 01/01/1970 03:00:00 HTTP/web.holding.com@HOLDING.COM (arcfour-hmac) (0xa9...)
   4 01/01/1970 03:00:00 HTTP/web.holding.com@HOLDING.COM (aes256-cts-hmac-sha1-96) (0x7c...)
   4 01/01/1970 03:00:00 HTTP/web.holding.com@HOLDING.COM (aes128-cts-hmac-sha1-96) (0x55...)

Как же быть в таком случае? Можно по классической схеме сделать в домене AD новую учётную запись служебного пользователя, зарегистрировать для неё SPN HTTP/web2.holding.com и сгенерировать отдельный keytab-файл.
С точки зрения информационной безопасности - это более правильный подход, то есть отдельное веб-приложение = отдельный служебный пользователь в домене.
Но как быть, если на сервере много небольших однотипных веб-сайтов, которым нужны разные имена, а плодить в домене AD под каждый такой сайт учётную запись не очень хочется.
В этом случае нам на помощь придут небольшие советы, которые я подсмотрел в статье
Ricky's Hodgepodge - Two Tips about Kerberos.

Итак, имеем задачу - получить комплексный keytab-файл, содержащий в себе некоторое множество разных имён Kerberos Principal, сопряжённых с записями ServicePrincipalName в свойствах одной конкретной учётной записи служебного пользователя в AD. Приступим.

 

Подготовка учётной записи пользователя в AD

Для начала, необходимо обеспечить наличие всех нужных SPN-записей в свойствах учётной записи служебного пользователя в домене AD. В нашем примере это будет пользователь DOM\web-srv-user, для которого ранее на нашем веб-сервере Apache уже была настроена аутентификация Kerberos по имени сайта web.holding.com. Посмотрим текущее состояние SPN-записей для этого пользователя на Windows-системе (для просмотра SPN достаточно прав рядового пользователя домена):

setspn -L DOM\web-srv-user

Зарегистрирован ServicePrincipalNames для CN=web-srv-user,OU=Service Users,OU=KOM,DC=holding,DC=com:
        HTTP/web.holding.com

Зарегистрируем для пользователя новую SPN-запись, которая будут содержать имя нужных нам дополнительной службы, например HTTP/web2.holding.com, следующим образом (для изменения SPN требуются права администратора домена):

setspn -A HTTP/web2.holding.com DOM\web-srv-user
setspn -L DOM\web-srv-user
Зарегистрирован ServicePrincipalNames для CN=web-srv-user,OU=Service Users,OU=KOM,DC=holding,DC=com: HTTP/web2.holding.com HTTP/web.holding.com

Теперь можно переходить с манипуляциями с keytab-файлом.

 

Генерация и обновление keytab-файла на контроллере домена AD

Для начала, напомню о том, как мы создавали исходный keytab-файл, уже имеющийся на нашем веб-сервере Apache.
Для настроенной учетной записи был сгенерирован keytab-файл на контроллере домена AD (в нашем случае на базе Windows Server 2012 R2) с помощью утилиты ktpass в следующем порядке:

ktpass –princ {тип службы}/{FQDN имя узла службы}@{FQDN домена в верхнем регистре} –mapuser {имя сервисного пользователя AD} -pass "{пароль сервисного пользователя AD}" -crypto All -ptype KRB5_NT_PRINCIPAL –out {полный путь к создаваемому keytab-файлу}

В нашем примере команда выглядела так:

ktpass -princ HTTP/web.holding.com@HOLDING.COM -mapuser DOM\web-srv-user -pass "Str0nGPQs$w0rd" -crypto All -ptype KRB5_NT_PRINCIPAL -out C:\Temp\web.keytab

В результате выполнения команды мы увидим исчерпывающую информацию о том, что попало в keytab-файл:

Targeting domain controller: DC01.holding.com
Successfully mapped HTTP/web.holding.com to web-srv-user.
Password successfully set!
Key created.
Key created.
Key created.
Key created.
Key created.
Output keytab to C:\Temp\web.keytab:
Keytab version: 0x502
keysize 84 HTTP/web.holding.com@HOLDING.COM ptype 1 (KRB5_NT_PRINCIPAL) vno 4 etype 0x1 (DES-CBC-CRC) keylength 8 (0x62...)
keysize 84 HTTP/web.holding.com@HOLDING.COM ptype 1 (KRB5_NT_PRINCIPAL) vno 4 etype 0x3 (DES-CBC-MD5) keylength 8 (0x62...)
keysize 92 HTTP/web.holding.com@HOLDING.COM ptype 1 (KRB5_NT_PRINCIPAL) vno 4 etype 0x17 (RC4-HMAC) keylength 16 (0xa9...)
keysize 108 HTTP/web.holding.com@HOLDING.COM ptype 1 (KRB5_NT_PRINCIPAL) vno 4 etype 0x12 (AES256-SHA1) keylength 32 (0x7c...)
keysize 92 HTTP/web.holding.com@HOLDING.COM ptype 1 (KRB5_NT_PRINCIPAL) vno 4 etype 0x11 (AES128-SHA1) keylength 16 (0x55...)

При выполнении утилиты ktpass в указанном порядке значение номера Key Version Number (KVNO) будет обновлено в свойствах учётной записи в AD (атрибут msDS-KeyVersionNumber), с последующей записью номера в keytab-файл. image

А все keytab-файлы, которые генерировались ранее для этой учётной записи (где номер KVNO меньше текущего) станут недействительными.

***

Здесь хочу немного отвлечься и сделать небольшую ремарку о том, как посмотреть содержимое keytab-файла на Windows. Стандартных инструментов в составе Windows для этой задачи я найти не смог (возможно плохо искал). Однако если в вашей Windows-системе установлен стандартный клиент Java (JRE), то в его составе есть утилита klist.exe, которая работает с опциями, схожими с теми, что используются в утилите klist под Linux. Например, чтобы получить полную информацию о содержимом нашего keytab-файла нужно будет вызвать эту утилиту следующим образом:

"C:\Program Files\Java\jre7\bin\klist.exe" -K -e -t -k "C:\Temp\web.keytab"

Key tab: C:\Temp\web.keytab, 5 entries found.

[1] Service principal: HTTP/web.holding.com@HOLDING.COM
         KVNO: 4
         Key type: 1
         Key: 0x62...
         Time stamp: Jan 01, 1970 03:00
[2] Service principal: HTTP/web.holding.com@HOLDING.COM
         KVNO: 4
         Key type: 3
         Key: 0x62...
         Time stamp: Jan 01, 1970 03:00
[3] Service principal: HTTP/web.holding.com@HOLDING.COM
         KVNO: 4
         Key type: 23
         Key: 0xa9d...
         Time stamp: Jan 01, 1970 03:00
[4] Service principal: HTTP/web.holding.com@HOLDING.COM
         KVNO: 4
         Key type: 18
         Key: 0x7c...
         Time stamp: Jan 01, 1970 03:00
[5] Service principal: HTTP/web.holding.com@HOLDING.COM
         KVNO: 4
         Key type: 17
         Key: 0x55...
         Time stamp: Jan 01, 1970 03:00

***

Теперь мы подошли к самому интересному. Второе и последующие имена нужные нам имена принципалов Kerberos добавляем в уже имеющийся keytab-файл с применением дополнительных параметров (-in, -setupn, -setpass) утилиты ktpass, которые позволят сохранить текущий номер KVNO в файле и AD:

ktpass -princ HTTP/web2.holding.com@HOLDING.COM -mapuser DOM\web-srv-user -pass "Str0nGPQs$w0rd" -crypto All -ptype KRB5_NT_PRINCIPAL -in C:\Temp\web.keytab -out C:\Temp\web_refreshed.keytab -setupn -setpass

Из вывода утилиты будет понятно, что номер keytab-файла, и KVNO остались неизменными:

Keytab version: 0x502
keysize 84 HTTP/web.holding.com@HOLDING.COM ptype 1 (KRB5_NT_PRINCIPAL) vno 4 etype 0x1 (DES-CBC-CRC) keylength 8 (0x62...)
keysize 84 HTTP/web.holding.com@HOLDING.COM ptype 1 (KRB5_NT_PRINCIPAL) vno 4 etype 0x3 (DES-CBC-MD5) keylength 8 (0x62...)
keysize 92 HTTP/web.holding.com@HOLDING.COM ptype 1 (KRB5_NT_PRINCIPAL) vno 4 etype 0x17 (RC4-HMAC) keylength 16 (0xa9d...)
keysize 108 HTTP/web.holding.com@HOLDING.COM ptype 1 (KRB5_NT_PRINCIPAL) vno 4 etype 0x12 (AES256-SHA1) keylength 32 (0x7c...)
keysize 92 HTTP/web.holding.com@HOLDING.COM ptype 1 (KRB5_NT_PRINCIPAL) vno 4 etype 0x11 (AES128-SHA1) keylength 16 (0x55...)
Targeting domain controller: DC01.holding.com
Successfully mapped HTTP/web2.holding.com to web-srv-user.
Key created.
Key created.
Key created.
Key created.
Key created.
Output keytab to C:\Temp\web_refreshed.keytab:
Keytab version: 0x502
keysize 84 HTTP/web.holding.com@HOLDING.COM ptype 1 (KRB5_NT_PRINCIPAL) vno 4 etype 0x1 (DES-CBC-CRC) keylength 8 (0x62...)
keysize 84 HTTP/web.holding.com@HOLDING.COM ptype 1 (KRB5_NT_PRINCIPAL) vno 4 etype 0x3 (DES-CBC-MD5) keylength 8 (0x62...)
keysize 92 HTTP/web.holding.com@HOLDING.COM ptype 1 (KRB5_NT_PRINCIPAL) vno 4 etype 0x17 (RC4-HMAC) keylength 16 (0xa9d...)
keysize 108 HTTP/web.holding.com@HOLDING.COM ptype 1 (KRB5_NT_PRINCIPAL) vno 4 etype 0x12 (AES256-SHA1) keylength 32 (0x7c...)
keysize 92 HTTP/web.holding.com@HOLDING.COM ptype 1 (KRB5_NT_PRINCIPAL) vno 4 etype 0x11 (AES128-SHA1) keylength 16 (0x55...)
keysize 83 HTTP/web2.holding.com@HOLDING.COM ptype 1 (KRB5_NT_PRINCIPAL) vno 4 etype 0x1 (DES-CBC-CRC) keylength 8 (0xa1...)
keysize 83 HTTP/web2.holding.com@HOLDING.COM ptype 1 (KRB5_NT_PRINCIPAL) vno 4 etype 0x3 (DES-CBC-MD5) keylength 8 (0xa1...)
keysize 91 HTTP/web2.holding.com@HOLDING.COM ptype 1 (KRB5_NT_PRINCIPAL) vno 4 etype 0x17 (RC4-HMAC) keylength 16 (0xa9...)
keysize 107 HTTP/web2.holding.com@HOLDING.COM ptype 1 (KRB5_NT_PRINCIPAL) vno 4 etype 0x12 (AES256-SHA1) keylength 32 (0xbe...)
keysize 91 HTTP/web2.holding.com@HOLDING.COM ptype 1 (KRB5_NT_PRINCIPAL) vno 4 etype 0x11 (AES128-SHA1) keylength 16 (0x1e...)

Ещё раз заглянем для убедительности в новый keytab-файл web_refreshed.keytab с помощью утилиты klist:

"c:\Program Files\Java\jre7\bin\klist.exe" -K -e -t -k "C:\Temp\web_refreshed.keytab"

Key tab: C:\Temp\web_refreshed.keytab, 10 entries found.

...
[6] Service principal: HTTP/web2.holding.com@HOLDING.COM
         KVNO: 4
         Key type: 1
         Key: 0xa1...
         Time stamp: Jan 01, 1970 03:00
[7] Service principal: HTTP/web2.holding.com@HOLDING.COM
         KVNO: 4
         Key type: 3
         Key: 0xa1d...
         Time stamp: Jan 01, 1970 03:00
[8] Service principal: HTTP/web2.holding.com@HOLDING.COM
         KVNO: 4
         Key type: 23
         Key: 0xa9d...
         Time stamp: Jan 01, 1970 03:00
[9] Service principal: HTTP/web2.holding.com@HOLDING.COM
         KVNO: 4
         Key type: 18
         Key: 0xb...
         Time stamp: Jan 01, 1970 03:00
[10] Service principal: HTTP/web2.holding.com@HOLDING.COM
         KVNO: 4
         Key type: 17
         Key: 0x1e...
         Time stamp: Jan 01, 1970 03:00

И действительно, мы видим, что в файле появилось 5 новых записей, а номер KVNO при этом у всех записей остался прежним (в нашем примере это 4). Заменим keytab-файл на веб-сервере Apache и убедимся в том, что теперь с Kerberos аутентификацией успешно работает и основное имя и альтернативное. Таким образом мы можем добавить в keytab-файл столько имён принципалов сколько необходимо, при условии, что все эти имена в виде SPN-записей есть в свойствах учётной записи соответствующего доменного пользователя в атрибуте servicePrincipalName. При проверках с Windows-клиентов перед проверкой не забываем очищать локальный клиентский кэш билетов Kerberos командой (выполнять в контексте того пользователя, от имени которого делается проверка доступности веб-сайтов на Windows-системе):

klist purge

 

Обновление keytab-файла на Linux-сервере

Есть ещё одна хитрость, которая позволит нам менять содержимое keytab-файла непосредственно на Linux-сервере без манипуляций по обновлению файла на контроллере домена и копированию его туда-сюда по сети. Если нам известен пароль от учётной записи сервисного пользователя (а он нам конечно известен:)) и текущий номер KVNO (подсмотреть его можно как в keytab-файле, так и в AD), то с помощью утилиты ktutil, мы сможем добавить в существующий keytab-файл нужную нам дополнительную запись с новым именем принципала Kerberos. Для этого войдём в режим интерактивного взаимодействия утилиты ktutil: 

# ktutil

Выполним команду list, которая покажет, какими данными оперирует утилита (пока там пусто): 

ktutil:  list

slot KVNO Principal
---- ---- ---------------------------------------------------------------------

Выполним команду чтения содержимого из нашего keytab-файла (read_kt), который мы хотим использовать, как исходный, затем команду list: 

ktutil:  read_kt /etc/apache2/web.keytab
ktutil:  list

slot KVNO Principal
---- ---- ---------------------------------------------------------------------
   1    4 HTTP/web.holding.com@HOLDING.COM
   2    4 HTTP/web.holding.com@HOLDING.COM
   3    4 HTTP/web.holding.com@HOLDING.COM
   4    4 HTTP/web.holding.com@HOLDING.COM
   5    4 HTTP/web.holding.com@HOLDING.COM

Следующей командой add_entry добавим в массив данных, с которыми оперирует утилита, нужную нам дополнительную запись принципала Kerberos. При этом нужно будет указать имя принципала Kerberos (ключ -p), текущий номер KVNO (ключ -k) и тип шифрования (ключ -e). Правильный формат типов шифрования можно посмотреть в самом keytab-файле утилитой klist с ключом -e, как было показано ранее. После ввода команды будет запрошен пароль пользователя, с которым связан данный принципал Kerberos (у нас это DOM\web-srv-user). 

ktutil:  add_entry -password -p HTTP/web2.holding.com@HOLDING.COM -k 4 -e aes128-cts-hmac-sha1-96
Password for HTTP/web2.holding.com@HOLDING.COM: {введём пароль сервисного пользователя AD}

Таким образом мы можем добавить аналогичные записи для всех необходимых типов шифрования: 

ktutil:  add_entry -password -p HTTP/web2.holding.com@HOLDING.COM -k 4 -e aes256-cts-hmac-sha1-96
...
ktutil:  add_entry -password -p HTTP/web2.holding.com@HOLDING.COM -k 4 -e arcfour-hmac
...
ktutil:  add_entry -password -p HTTP/web2.holding.com@HOLDING.COM -k 4 -e des-cbc-md5
...
ktutil:  add_entry -password -p HTTP/web2.holding.com@HOLDING.COM -k 4 -e des-cbc-crc

По завершению добавления записей командой write_kt сохраняем получившийся массив данных в новый keytab-файл и выходим из интерактивного режима работы утилиты: 

ktutil:  write_kt /etc/apache2/web_refreshed.keytab
ktutil:  exit

Разумеется, для того, чтобы обновлённый на Linux-системе keytab-файл работал, нужно обеспечить наличие соответствующей дополнительной SPN-записей в свойствах учётной записи в домене AD. Осталось подключить обновлённый keytab-файл к конфигурации Apache и проверить результат.

***

В качестве заключения хочется напомнить о паре простых правил безопасности, про которые всегда стоит помнить при работе с keytab-файлами:

  • Не оставлять keytab-файлы во всяких временных каталогах после операций над этими файлами и всевозможных тестов. Keytab-файл должен лежать только в том месте, где он реально используется.
  • Жёстко ограничивать на Linux-системе доступ к keytab-файлу, оставляя право на чтение лишь тем Linux-пользователям, от имени которых работает служба использующая для Kerberos-аутентификации данный keytab-файл.

 

Дополнительные источники информации:

Добавить комментарий