Tuesday, May 10, 2016

Немного о трёхглавых собаках

Нашёл недавно довольно интересную штуку:

Положим, у нас есть сайт, использующий "Windows Integrated Authentication" - то есть Kerberos или NTLM. И допустим, что юзер, пытающийся на него зайти - относится к другому, недоверяемому домену, или же он локальный юзер на доменном компе, а то и вовсе на стэнд-элоне. Так или иначе, попытка автоматического, прозрачного для юзера логина проваливается. Что произойдёт?

Ответ: зависит от браузера. Firefox просто выдаст сообщение об ошибке. А вот Chrome или IE поступят по другому - выкинут стандартное окошко username & password. А самое интересное, что если username указать в полной форме - к примеру, me@mydomain.com, - то они пошлют запросы DNS-серверу на две записи SRV, стремясь обнаружить керберосовский центр раздачи ключей (KDC) для указанного домена:
_kerberos._tcp.Default-First-Site-Name._sites.dc._msdcs.mydomain.com
_kerberos._tcp.dc._msdcs.mydomain.com

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

Кстати, и клиент Remote Desktop в Win7 тоже так умеет.

Почему это так интересно? А потому, что означает, вопреки популярному убеждению, что Kerberos можно использовать в Интернете - а не только в корпоративных сеточках.
(Апдейт: не так всё здорово, см. внизу *).

Допустим, у нас есть сайт где-то в облаке, без всякой связи с сетью домена. Допустим, он вовсе не на Windows - например, это бегущий под линуксом Tomcat. Тогда всё, что нам надо сделать:
1. Создать какой-нибудь аккаунт в домене и сгенерить для него файл keytab - аналог пароля.
2. Связать URL сайта с этим аккаунтом, определив SPN.
3. Залить файл keytab на сервер и прописать его в конфигурации - сервер будет им юзерские билетики расшифровывать.
4. Открыть 88-й порт (TCP и UDP) на доменном контроллере в Интернет - наиболее спорный пункт. На мой взгляд, достаточно безопасный вариант - построить read-only domain controller, разместить его в DMZ, запретить ему держать у себя пароли и разрешить разговаривать с обычным DC. Тогда он превращается в прокси, обвал которого домену никак не вредит.
Если же это никак неприемлемо, остаётся вариант с VPN - что, конечно, странно, но может и кому-то и подойти, учитывая, что вся система предназначена всё же для людей из определённой организации.
5. Прописать публичный адрес этого RODC в DNS - под SRV-record _kerberos._tcp.dc._msdcs.mydomain.com.

Всё. Любой юзер домена, вооружённый IE или Хромом, может логиниться на сайт. Возможно, также и юзеры других доменов, у которых есть отношения доверия с доменом сайта - не проверял.

Достоинства:

  • Пароли не проходят через сайт ни на каком этапе.
  • Взаимная аутентификация - без нужды в SSL. Ответ сервера на предъявленный билетик доказывает, что он был способен его расшифровать - что сам сервер легитимен.
  • Сайт работает полностью автономно от доменной сети.
  • URL сайта может быть любым, никак с доменом не связаным.
  • В случае взлома сайта выписка любых новых билетов для него прекращается моментально, отключением ассоциированного с ним аккаунта, а уже выписанные билеты протухают по дефолту за 10 часов - но этот срок можно уменьшить и до 10-ти минут. 
  • В случае взлома юзера - тем более, баним его аккаунт и конец делу. Время жизни выданного ему TGT с дефолтовых 10-ти часов также можно урезать. Сравните с процессом отмены сертификата!
  • Проще в настройке, чем SAML, оAuth, OpenID и прочее с того же куста, и намного прозрачнее для серверной аппликации.
  • При этом позволяет передавать не только username, но и информацию о группах - микрософтовская имплементация Кербероса присобачивает к билетику поле под названием PAC - оно содержит список групп в Active Directory, к которым относится юзер (причём, что особенно прелестно, не обязательно напрямую - nesting groups там сидят также). Неприятное ограничение в том, что имена групп в PAC не значатся - только их длинные номера SID. Но построив на сервере mapping из SID в имена, можно справиться и с этим, после чего разным группам пораздавать разные права.
    Существует библиотека на Java, умеющая извлекать SID-ы групп из PAC - может быть полезна для всевозможных серверов JavaEE.


Недостатки:

  • Необходимость доступности контроллера домена в Интернете, пусть даже и read-only и лишь парой керберосовских портов. Я большой проблемы с этим не вижу, но возможно, что люди поумнее таки да. (Апдейт: таки есть проблема, см. внизу *).
  • Сами билетики шифрованные, но некоторые поля в пакете Kerberos передаются открытым текстом - например, юзернейм. С трафиком между сидящим вне доменной сети юзером и KDC особо делать нечего (разве что VPN), между юзером и сайтом - включить HTTPS. Лишний слой шифрования не помешает.
  • Отсутствует logout - нет способа сказать сайту, что юзерская сессия закончилась и дальнейшие запросы с тем же билетом приниматься не должны. Неидеальный способ для юзера состоит в том, чтобы закрыть полностью браузер - лишь в заново запущенном браузере процесс логина повторится сначала. Впрочем, с клиентскими сертификатами та же проблема.
  • Как уже сказано, не самый удобный способ передавать информацию о группах - тут ADFS куда круче, конечно, к тому же позволяет передавать массу других атрибутов. Тут же, если дополнительная информация из AD необходима - воленс-ноленс приходится либо запрашивать по LDAP, либо переходить на ADFS или аналог.
  • Какой-либо provisioning юзеров исключается - создавать или убивать их можно лишь через AD. Не через сайт.
  • Использовать таким образом иные методы аутентификации, кроме пароля - проблемно, несмотря на том, что в Windows в целом поддерживается интеграция сертификатов и Кербероса (см. PKINIT).
  • Привязка к конкретным браузерам.


Вот такие пироги с собачками. Для полноценного сайта, предназначенного для общей публики, это, конечно, не решение - но если задача в том, чтобы корпоративный сайтик из серверной комнаты куда-нибудь на Амазон перенести, то Kerberos тут вполне в тему, на мой взгляд.


* Апдейт: не так всё просто, оказывается. Между получением адресов контроллеров от DNS и запросом билетиков Kerberos есть ещё один этап - "LDAP ping". Это запрос по Connectionless LDAP (CLDAP) к домейн-контроллеру, адрес которого получен от DNS, по UDP-порту 389. Его цель - обнаружить ближайший контроллер для дальнейших запросов. Контроллер, получивший "ping", смотрит, с какого адреса он пришёл, и смотрит к какому сабнету можно его отнести (а сабнеты в AD относятся к тому или иному "сайту", в каждом "сайте" как минимум один свой контроллер). Соответственно, он сообщает клиенту, от какого контроллера тому требовать билетики.
Ответ кэшируется, так что при перезапуске браузера "LDAP ping" будет послан необязательно.

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