В чем разница между GSS API и SSPI API при использовании Kerberos с делегированием?
В чем разница между GSS API и SSPI API при использовании Kerberos с делегированием?
У меня есть промежуточное ПО, работающее на Java-коде на сервере Tomcat. По промежуточного слоя аутентифицирует пользователя с помощью Kerberos (GSS API). Если в заголовке авторизации отсутствует токен Kerberos, промежуточное ПО возвращает 401 и присоединяет заголовок ответа WWW-Authenticate:Negotiate для инициализации аутентификации SPNEGO.
Проверка входящего билета на обслуживание с помощью GSSContext.acceptSecContext работает нормально.
Однако у меня есть некоторые проблемы с делегированием.
Как указывает название "middleware", моя java-служба должна вызывать серверную службу с использованием аутентификации Kerberos с исходным участником-пользователем. Для этого я реализовал механизм делегирования Kerberos Java GSS API. Кроме того, AD был настроен правильно, и tomcat работает как служба с определенной учетной записью службы.
Чтобы протестировать эту реализацию, я написал тестовый клиент Java, использующий GSS API, чтобы получить билет для промежуточного программного обеспечения. Запуск тестового клиента Java с правами администратора или получение перенаправляемого билета с помощью kinit -f комбинация клиента и промежуточного программного обеспечения работает нормально: клиент получает билет, промежуточное ПО принимает билет, GSSContext.getCredDelegState() возвращает true, используя GSSContext.getDelegCred() промежуточное ПО получает учетные данные для делегирования, и логин на бэкэнде работает нормально.
Кроме того, я протестировал реализацию промежуточного программного обеспечения с помощью браузеров и небольшого тестового клиента C#. Оба используют SPNEGO. В этом случае авторизация тоже работает. Я получаю сообщение о том, что аутентификация прошла успешно, и получаю участника-пользователя. Используя браузеры или мой тестовый клиент C#, я получаю следующую отладочную печать в промежуточном программном обеспечении:
Debug is true storeKey true useTicketCache false useKeyTab true doNotPrompt false ticketCache is null isInitiator false KeyTab is D:/app/Tomcat_9019_SSO/conf/tomcat.keytab refreshKrb5Config is true principal is HTTP/SERVICE.MYDOMAIN.NET@MYDOMAIN.NET tryFirstPass is false useFirstPass is false storePass is false clearPass is false
Refreshing Kerberos configuration
Java config name: C:\Windows\kerb5.ini
Loading krb5 profile at C:\Windows\kerb5.ini
Loaded from Java config
>>> KdcAccessibility: reset
principal is HTTP/SERVICE.MYDOMAIN.NET@MYDOMAIN.NET
Will use keytab
Commit Succeeded
2020-03-18 06:36:50.254 INFO .e.s.a.t.a.KerberosCheckAuthTicketAction [TC~3~c80e3d5b-3] : Starting check of incoming Kerberos service ticket.
Search Subject for SPNEGO ACCEPT cred (<<DEF>>, sun.security.jgss.spnego.SpNegoCredElement)
Search Subject for Kerberos V5 ACCEPT cred (<<DEF>>, sun.security.jgss.krb5.Krb5AcceptCredential)
Found KeyTab D:\app\Tomcat_9019_SSO\conf\tomcat.keytab for HTTP/SERVICE.MYDOMAIN.NET@MYDOMAIN.NET
Found KeyTab D:\app\Tomcat_9019_SSO\conf\tomcat.keytab for HTTP/SERVICE.MYDOMAIN.NET@MYDOMAIN.NET
Search Subject for Kerberos V5 ACCEPT cred (<<DEF>>, sun.security.jgss.krb5.Krb5AcceptCredential)
Found KeyTab D:\app\Tomcat_9019_SSO\conf\tomcat.keytab for HTTP/SERVICE.MYDOMAIN.NET@MYDOMAIN.NET
Found KeyTab D:\app\Tomcat_9019_SSO\conf\tomcat.keytab for HTTP/SERVICE.MYDOMAIN.NET@MYDOMAIN.NET
Entered Krb5Context.acceptSecContext with state=STATE_NEW
Looking for keys for: HTTP/SERVICE.MYDOMAIN.NET@MYDOMAIN.NET
Added key: 23version: 0
>>> EType: sun.security.krb5.internal.crypto.ArcFourHmacEType
Using builtin default etypes for permitted_enctypes
default etypes for permitted_enctypes: 18 17 20 19 16 23.
>>> EType: sun.security.krb5.internal.crypto.ArcFourHmacEType
MemoryCache: add 1584509810/000627/5EBDF35F49476E365F32DE53C3CAFA81C4730A13D881ECA15E9F43023F99A80B/CLIENTUSERD@MYDOMAIN.NET to CLIENTUSERD@MYDOMAIN.NET|HTTP/SERVICE.MYDOMAIN.NET@MYDOMAIN.NET
>>> KrbApReq: authenticate succeed.
Krb5Context setting peerSeqNumber to: 947381056
>>> EType: sun.security.krb5.internal.crypto.ArcFourHmacEType
Krb5Context setting mySeqNumber to: 214468704
>>> Constrained deleg from GSSCaller{UNKNOWN}
Debug is true storeKey true useTicketCache false useKeyTab true doNotPrompt false ticketCache is null isInitiator true KeyTab is D:/app/Tomcat_9019_SSO/conf/tomcat.keytab refreshKrb5Config is false principal is HTTP/SERVICE.MYDOMAIN.NET@MYDOMAIN.NET tryFirstPass is false useFirstPass is false storePass is false clearPass is false
Looking for keys for: HTTP/SERVICE.MYDOMAIN.NET@MYDOMAIN.NET
Added key: 23version: 0
Looking for keys for: HTTP/SERVICE.MYDOMAIN.NET@MYDOMAIN.NET
Added key: 23version: 0
default etypes for default_tkt_enctypes: 23 18 17.
>>> KrbAsReq creating message
>>> KrbKdcReq send: kdc=kb01.mydomain.net UDP:88, timeout=30000, number of retries =3, #bytes=174
>>> KDCCommunication: kdc=kb01.mydomain.net UDP:88, timeout=30000,Attempt =1, #bytes=174
>>> KrbKdcReq send: #bytes read=175
>>>Pre-Authentication Data:
PA-DATA type = 11
PA-ETYPE-INFO etype = 23, salt =
>>>Pre-Authentication Data:
PA-DATA type = 19
PA-ETYPE-INFO2 etype = 23, salt = null, s2kparams = null
>>>Pre-Authentication Data:
PA-DATA type = 2
PA-ENC-TIMESTAMP
>>>Pre-Authentication Data:
PA-DATA type = 16
>>>Pre-Authentication Data:
PA-DATA type = 15
>>> KdcAccessibility: remove kb01.mydomain.net
>>> KDCRep: init() encoding tag is 126 req type is 11
>>>KRBError:
sTime is Wed Mar 18 06:36:50 CET 2020 1584509810000
suSec is 765149
error code is 25
error Message is Additional pre-authentication required
sname is krbtgt/MYDOMAIN.NET@MYDOMAIN.NET
eData provided.
msgType is 30
>>>Pre-Authentication Data:
PA-DATA type = 11
PA-ETYPE-INFO etype = 23, salt =
>>>Pre-Authentication Data:
PA-DATA type = 19
PA-ETYPE-INFO2 etype = 23, salt = null, s2kparams = null
>>>Pre-Authentication Data:
PA-DATA type = 2
PA-ENC-TIMESTAMP
>>>Pre-Authentication Data:
PA-DATA type = 16
>>>Pre-Authentication Data:
PA-DATA type = 15
KrbAsReqBuilder: PREAUTH FAILED/REQ, re-send AS-REQ
default etypes for default_tkt_enctypes: 23 18 17.
Looking for keys for: HTTP/SERVICE.MYDOMAIN.NET@MYDOMAIN.NET
Added key: 23version: 0
Looking for keys for: HTTP/SERVICE.MYDOMAIN.NET@MYDOMAIN.NET
Added key: 23version: 0
default etypes for default_tkt_enctypes: 23 18 17.
>>> EType: sun.security.krb5.internal.crypto.ArcFourHmacEType
>>> KrbAsReq creating message
>>> KrbKdcReq send: kdc=kb01.mydomain.net UDP:88, timeout=30000, number of retries =3, #bytes=253
>>> KDCCommunication: kdc=kb01.mydomain.net UDP:88, timeout=30000,Attempt =1, #bytes=253
>>> KrbKdcReq send: #bytes read=90
>>> KrbKdcReq send: kdc=kb01.mydomain.net TCP:88, timeout=30000, number of retries =3, #bytes=253
>>> KDCCommunication: kdc=kb01.mydomain.net TCP:88, timeout=30000,Attempt =1, #bytes=253
>>>DEBUG: TCPClient reading 2154 bytes
>>> KrbKdcReq send: #bytes read=2154
>>> KdcAccessibility: remove kb01.mydomain.net
Looking for keys for: HTTP/SERVICE.MYDOMAIN.NET@MYDOMAIN.NET
Added key: 23version: 0
>>> EType: sun.security.krb5.internal.crypto.ArcFourHmacEType
[Krb5LoginModule] authentication failed
Message stream modified (41)
Используя клиент Java, я получаю эту отладочную печать в промежуточном программном обеспечении:
Debug is true storeKey true useTicketCache false useKeyTab true doNotPrompt false ticketCache is null isInitiator false KeyTab is D:/app/Tomcat_9019_SSO/conf/tomcat.keytab refreshKrb5Config is true principal is HTTP/SERVICE.MYDOMAIN.NET@MYDOMAIN.NET tryFirstPass is false useFirstPass is false storePass is false clearPass is false
Refreshing Kerberos configuration
Java config name: C:\Windows\kerb5.ini
Loading krb5 profile at C:\Windows\kerb5.ini
Loaded from Java config
>>> KdcAccessibility: reset
principal is HTTP/SERVICE.MYDOMAIN.NET@MYDOMAIN.NET
Will use keytab
Commit Succeeded
2020-03-18 06:47:41.029 INFO .e.s.a.t.a.KerberosCheckAuthTicketAction [TC~9~c80e3d5b-9] : Starting check of incoming Kerberos service ticket.
Search Subject for SPNEGO ACCEPT cred (<<DEF>>, sun.security.jgss.spnego.SpNegoCredElement)
Search Subject for Kerberos V5 ACCEPT cred (<<DEF>>, sun.security.jgss.krb5.Krb5AcceptCredential)
Found KeyTab D:\app\Tomcat_9019_SSO\conf\tomcat.keytab for HTTP/SERVICE.MYDOMAIN.NET@MYDOMAIN.NET
Found KeyTab D:\app\Tomcat_9019_SSO\conf\tomcat.keytab for HTTP/SERVICE.MYDOMAIN.NET@MYDOMAIN.NET
Search Subject for Kerberos V5 ACCEPT cred (<<DEF>>, sun.security.jgss.krb5.Krb5AcceptCredential)
Found KeyTab D:\app\Tomcat_9019_SSO\conf\tomcat.keytab for HTTP/SERVICE.MYDOMAIN.NET@MYDOMAIN.NET
Found KeyTab D:\app\Tomcat_9019_SSO\conf\tomcat.keytab for HTTP/SERVICE.MYDOMAIN.NET@MYDOMAIN.NET
Entered Krb5Context.acceptSecContext with state=STATE_NEW
Looking for keys for: HTTP/SERVICE.MYDOMAIN.NET@MYDOMAIN.NET
Added key: 23version: 0
>>> EType: sun.security.krb5.internal.crypto.ArcFourHmacEType
Using builtin default etypes for permitted_enctypes
default etypes for permitted_enctypes: 18 17 20 19 16 23.
>>> EType: sun.security.krb5.internal.crypto.ArcFourHmacEType
MemoryCache: add 1584510459/567826/FDE0027391B8BF26BF807FF04E5FD5F7CE38794A3264EB298BB36F736B2CF050/CLIENTUSERD@MYDOMAIN.NET to CLIENTUSERD@MYDOMAIN.NET|HTTP/SERVICE.MYDOMAIN.NET@MYDOMAIN.NET
>>> KrbApReq: authenticate succeed.
>>> EType: sun.security.krb5.internal.crypto.ArcFourHmacEType
>>>Delegated Creds have pname=CLIENTUSERD@MYDOMAIN.NET sname=krbtgt/MYDOMAIN.NET@MYDOMAIN.NET authtime=20200318054735Z starttime=20200318054739Z endtime=20200318154735ZrenewTill=null
Krb5Context setting peerSeqNumber to: 99984043
>>> EType: sun.security.krb5.internal.crypto.ArcFourHmacEType
Krb5Context setting mySeqNumber to: 161819208
Однако основная проблема здесь в том, что в случае клиента Java делегирование работает, а в случае браузеров и клиента C# делегирование не работает. Обратите внимание, что браузеры настроены на добавление домена в белый список для делегирования.
Дополнительная информация: Я настроил ограниченное делегирование. Tomcat с промежуточным программным обеспечением работает на сервере Windows 2016 как служба с учетной записью службы AD.
Я сравнил сервисные билеты, которые были отправлены промежуточному программному обеспечению:
Java (с возможностью пересылки), делегирование работает: 10980 байт
C# (делегирование не работает): 8572 байт
Браузер (делегирование не работает): 8572 байт
Для сравнения я использовал kinit без опции -f, чтобы получить tgt, который нельзя пересылать, и измерил размер:
Java (не пересылается, делегирование не работает): 8174 байта
Кстати, это вызывает ту же ошибку.
2 ответа
Вот ответ на мой собственный вопрос.
Я понял, что домен, в котором я работаю, имеет 3 контроллера домена: в моем промежуточном программном обеспечении krb5.ini был настроен первый:
[realms]
MYDOMAIN.NET = {
kdc = d01.mydomain.net
admin_server = d01.mydomain.net
default_domain = MYDOMAIN.NET
}
У моего тестового клиента Java была следующая строка кода
System.setProperty("java.security.krb5.kdc", d01.mydomain.net);
Однако мой клиентский компьютер использовал d02.mydomain.net, что я видел с помощью команды "klist".
#2> Client: CLIENTUSERD @ MYDOMAIN.NET
Server: HTTP/SERVICE.MYDOMAIN.NET @ MYDOMAIN.NET
KerbTicket Encryption Type: RSADSI RC4-HMAC(NT)
Ticket Flags 0x40a00000 -> forwardable renewable pre_authent
Start Time: 3/24/2020 5:33:39 (local)
End Time: 3/24/2020 15:33:39 (local)
Renew Time: 3/31/2020 5:33:39 (local)
Session Key Type: RSADSI RC4-HMAC(NT)
Cache Flags: 0
Kdc Called: d02.mydomain.net
По некоторым причинам - которые сейчас мне неясны - делегирование не работает, если неправильные контроллеры домена должны работать вместе. Если я изменю контроллер домена на d03.mydomain.net в промежуточном программном обеспечении, делегирование будет работать нормально. Однако прием билетов отлично работает в любых комбинациях. Возможно, время контроллеров домена не синхронизировано.
Как у вас настроено делегирование для учетной записи службы промежуточного слоя в AD? Без ограничений или ограничений?
Попробуйте взять копию билетов, отправленных как java-клиентами, так и C#, и сравните их размеры. Они должны быть примерно одинаковыми, однако если один значительно больше, то более крупный содержит делегированный TGT.
Основываясь на журналах, похоже, что клиент Java отправляет неограниченный билет (делегированный TGT, перенаправленный AKA) и просто использует его.
Мы не можем видеть, что содержит билет C#, но промежуточное ПО пытается связаться с KDC сразу после получения, что, кажется, указывает на то, что оно может пытаться обменять его на делегированный билет, но терпит неудачу, потому что промежуточное ПО не может получить свой собственный билет первый. Это немного странное предположение, потому что журналы так или иначе не указывают, почему запускается AS-REQ.
Вы можете рассмотреть возможность трассировки сети между промежуточным программным обеспечением и KDC, чтобы увидеть, что он отправляет в случае C#.