Ошибка справочника продолжения LDAP в результатах поиска из Active Directory при использовании аутентификации GSSAPI в Java

ОБНОВЛЕНИЕ: на основании комментария от @ Michael-O ниже, это кажется правильным способом решения этой проблемы, если поставщик LDAP JNDI или реализация SASL канонизируют имя хоста, выполнив пересылку, затем обратный поиск DNS перед выдачей в запросе билета службы KRN. Я попытаюсь обратиться к списку безопасности Open JDK и посмотреть, не появятся ли оттуда какие-либо ответы.

Я пытаюсь выполнить рекурсивный поиск LDAP по корневому DN на сервере Active Directory, используя сеанс, который аутентифицируется через GSSAPI с использованием Subject из Kerberos LoginContext.

Я могу успешно подключиться к серверу с URL ldap://dc1.example.com. Для InitidalDirContext java.naming.referral установлено значение follow.

Когда я выполняю поиск (&(objectClass=user)([email protected])) по корневому DN dc=example,dc=com, я получаю один SearchResult:

CN=Sample User,OU=ExampleUsers,DC=example,DC=com

И несколько ссылок на продолжение:

ldap://example.com/CN=Configuration,DC=example,DC=com
ldap://ForestDnsZones.example.com/DC=ForestDnsZones,DC=example,DC=com
ldap://DomainDnsZones.example.com/DC=DomainDnsZones,DC=example,DC=com

Я могу нормально перебирать SearchResult, но как только я встречаю продолжение, я получаю PartialResultsException. Я проверил DNS, и все указанные выше имена хостов разрешаются правильно. Исключение, которое я получаю, выглядит так:

javax.naming.PartialResultException 
  [Root exception is javax.naming.AuthenticationException: GSSAPI 
    [Root exception is javax.security.sasl.SaslException: GSS initiate failed 
      [Caused by GSSException: No valid credentials provided 
        (Mechanism level: Server not found in Kerberos database (7))]]].

Если посмотреть на трассировку Kerberos, эта ошибка имеет смысл. При попытке следить за продолжением библиотека LDAP пытается выполнить привязку к ldap://example.com. Поскольку мы используем GSSAPI для аутентификации, это вызывает запрос билета службы для ldap/example.com. Ответ, который я вижу в журнале:

>>>KRBError:
     sTime is Thu Aug 21 14:27:20 EDT 2014 0000000000000
     suSec is 414575
     error code is 7
     error Message is Server not found in Kerberos database
     realm is EXAMPLE.COM
     sname is ldap/example.com
     msgType is 30

Я проверил Active Directory и убедился, что ни на одном из контроллеров домена нет атрибута servicePrincipalName со значением ldap/example.com. Я попытался вручную добавить имя участника-службы для ldap/example.com в учетную запись компьютера контроллера домена SAVANT-DC1. Это работает временно, но Active Directory, кажется, автоматически очищает запись SPN через пару минут.

Похоже, решение было бы сделать одно из

  1. Получите Active Directory, чтобы возвращать продолжения, содержащие имя контроллера домена вместо домена. Мы знаем, что можем получить билеты на обслуживание для SPN в форме ldap/dc1.example.com.
  2. Каким-то образом продолжение карты на конце java для перенаправления ldap://example.com на ldap://dc1.example.com

Я не могу понять, как это сделать (1).

Я пробовал сделать (2), используя Пример ручной обработки переходов по JNDI в качестве руководства. Я переключил свойство java.naming.referral на throw и написал собственный обработчик перехода, который вручную переопределяет свойство java.naming.provider.url в контексте перехода. Однако LdapReferralException.getReferralContext(), похоже, игнорирует свойство среды java.naming.provider.url. Глядя на код OpenJDK на LdapReferralContext.java, похоже, подтверждает это (строка 105).

Вот где я: я не могу перехватывать и управлять ссылками на стороне Java, потому что они рассматриваются как черный ящик JNDI API. Я не могу вручную создать имя участника-службы LDAP на стороне AD, потому что оно не будет оставаться постоянным в каталоге. Что еще мне не хватает?


Вот код, который я запускаю

import java.io.File;
import java.security.PrivilegedExceptionAction;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.ReferralException;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag;
import javax.security.auth.login.Configuration;
import javax.security.auth.login.LoginContext;

public class LdapContinuationDemoAction implements PrivilegedExceptionAction<Object> {
  private final String ldapUrl;
  private final String ldapDn;
  private final String username;

  public static void main(String[] argv) {
    try {
      String username = "[email protected]";
      String password = "Password1";
      String ldapUrl  = "ldap://dc1.example.com";
      String searchDn = "dc=example,dc=com";
      String pwd      = System.getProperty("user.dir");
      String krb5Conf = new File(pwd, "krb5.conf").getAbsolutePath();

      System.setProperty("java.security.krb5.conf", krb5Conf);
      System.setProperty("sun.security.krb5.debug", "true");

      // Login to the domain via Kerberos
      LoginContext loginCtx = new LoginContext("doesn't matter", null,
        getUsernamePasswordHandler(username, password),
        getKrb5Configuration());

      System.out.println("********************************");
      System.out.println("      KRB5 Login");
      System.out.println("********************************");
      loginCtx.login();

      // Execute the LDAP search as the user logged in above
      LdapContinuationDemoAction action = new LdapContinuationDemoAction(ldapUrl,
        searchDn, username);

      Subject.doAs(loginCtx.getSubject(), action);
    } catch( Exception e) {
      System.out.println();
      System.out.println("*** ERROR: " + e);
    }
  }

  private LdapContinuationDemoAction(String ldapUrl, String ldapDn,
    String username) {
    this.ldapUrl  = ldapUrl;
    this.ldapDn   = ldapDn;
    this.username = username;
  }

  // Perform a recursive LDAP search for a user principal and print the results
  @Override
  public Object run() throws Exception {
    System.out.println("********************************");
    System.out.println("      LDAP Login");
    System.out.println("********************************");

    //Setup the directory context environment
    Properties dirCtxProps = new Properties();
    dirCtxProps.put(Context.INITIAL_CONTEXT_FACTORY,      "com.sun.jndi.ldap.LdapCtxFactory");
    dirCtxProps.put(Context.PROVIDER_URL,                 this.ldapUrl);
    dirCtxProps.put(Context.SECURITY_AUTHENTICATION,      "GSSAPI");
    dirCtxProps.put("java.naming.ldap.attributes.binary", "objectSID");
    dirCtxProps.put(Context.REFERRAL,                     "follow");

    DirContext dirCtx = new InitialDirContext(dirCtxProps);

    // enable recursive searching
    SearchControls ctrls = new SearchControls();
    ctrls.setSearchScope(SearchControls.SUBTREE_SCOPE);

    // do the search
    NamingEnumeration<SearchResult> results = dirCtx.search(this.ldapDn,
      "(&(objectClass=user)(userPrincipalName={0}))",
      new Object[] { this.username }, ctrls);

    System.out.println("********************************");
    System.out.println("      LDAP User Info");
    System.out.println("********************************");
    int resultNum = 0;

    while (results.hasMore()) {
      resultNum++;

      Attributes userAttr = results.next().getAttributes();

      System.out.println("ldap result " + resultNum + ": User DN: "
        + userAttr.get("distinguishedName").get());
      System.out.println();
    }


    return null;
  }

  // JAAS callback handler for username and password Kerberos authn
  private static CallbackHandler getUsernamePasswordHandler(
    final String username, final String password) {

    final CallbackHandler handler = new CallbackHandler() {
      @Override
      public void handle(final Callback[] callback) {
        for (int i = 0; i < callback.length; i++) {
          if (callback[i] instanceof NameCallback) {
            final NameCallback nameCallback = (NameCallback) callback[i];
            nameCallback.setName(username);
          } else if (callback[i] instanceof PasswordCallback) {
            final PasswordCallback passCallback = (PasswordCallback) callback[i];
            passCallback.setPassword(password.toCharArray());
          } else {
            System.err.println("Unsupported Callback: "
              + callback[i].getClass().getName());
          }
        }
      }
    };

    return handler;
  }

  // dynamically build a Kerberos JAAS configuration so we don't need a login.conf
  private static Configuration getKrb5Configuration() {
    return new Configuration() {

      @Override
      public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
        Map<String, String> options = new HashMap<String, String>();
        options.put("client", "true");
        return new AppConfigurationEntry[] {
          new AppConfigurationEntry(
            "com.sun.security.auth.module.Krb5LoginModule",
            LoginModuleControlFlag.REQUIRED, options)
        };
      }
    };
  }

}

Вот мой krb5.conf:

[libdefaults]
  default_realm = EXAMPLE.COM

[realms]
  EXAMPLE.COM = {
    kdc = dc1.example.com
    default_domain = example.com
  }

[domain_realm]
  .example.com = EXAMPLE.COM
  example.com = EXAMPLE.COM

Вот вывод из приведенного выше кода

********************************
      KRB5 Login
********************************
Config name: C:\src\scratch\krb5\krb5.conf
>>> KdcAccessibility: reset
Using builtin default etypes for default_tkt_enctypes
default etypes for default_tkt_enctypes: 18 17 16 23 1 3.
>>> KrbAsReq creating message
>>> KrbKdcReq send: kdc=dc1.example.com UDP:88, timeout=30000, number of retries =3, #bytes=158
>>> KDCCommunication: kdc=dc1.example.com UDP:88, timeout=30000,Attempt =1, #bytes=158
>>> KrbKdcReq send: #bytes read=227
>>>Pre-Authentication Data:
     PA-DATA type = 19
     PA-ETYPE-INFO2 etype = 18, salt = EXAMPLE.COMexample_user, s2kparams = null
     PA-ETYPE-INFO2 etype = 23, salt = null, s2kparams = null
     PA-ETYPE-INFO2 etype = 3, salt = EXAMPLE.COMexample_user, 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 dc1.example.com
>>> KDCRep: init() encoding tag is 126 req type is 11
>>>KRBError:
     sTime is Thu Aug 21 16:35:42 EDT 2014 0000000000000
     suSec is 659371
     error code is 25
     error Message is Additional pre-authentication required
     realm is EXAMPLE.COM
     sname is krbtgt/EXAMPLE.COM
     eData provided.
     msgType is 30
>>>Pre-Authentication Data:
     PA-DATA type = 19
     PA-ETYPE-INFO2 etype = 18, salt = EXAMPLE.COMexample_user, s2kparams = null
     PA-ETYPE-INFO2 etype = 23, salt = null, s2kparams = null
     PA-ETYPE-INFO2 etype = 3, salt = EXAMPLE.COMexample_user, 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
Using builtin default etypes for default_tkt_enctypes
default etypes for default_tkt_enctypes: 18 17 16 23 1 3.
Using builtin default etypes for default_tkt_enctypes
default etypes for default_tkt_enctypes: 18 17 16 23 1 3.
>>> EType: sun.security.krb5.internal.crypto.Aes256CtsHmacSha1EType
>>> KrbAsReq creating message
>>> KrbKdcReq send: kdc=dc1.example.com UDP:88, timeout=30000, number of retries =3, #bytes=240
>>> KDCCommunication: kdc=dc1.example.com UDP:88, timeout=30000,Attempt =1, #bytes=240
>>> KrbKdcReq send: #bytes read=1425
>>> KdcAccessibility: remove dc1.example.com
>>> EType: sun.security.krb5.internal.crypto.Aes256CtsHmacSha1EType
>>> KrbAsRep cons in KrbAsReq.getReply example_user
********************************
      LDAP Login
********************************
Found ticket for [email protected] to go to krbtgt/[email protected] expiring on Fri Aug 22 02:35:42 EDT 2014
Entered Krb5Context.initSecContext with state=STATE_NEW
Found ticket for [email protected] to go to krbtgt/[email protected] expiring on Fri Aug 22 02:35:42 EDT 2014
Service ticket not found in the subject
>>> Credentials acquireServiceCreds: same realm
Using builtin default etypes for default_tgs_enctypes
default etypes for default_tgs_enctypes: 18 17 16 23 1 3.
>>> CksumType: sun.security.krb5.internal.crypto.RsaMd5CksumType
>>> EType: sun.security.krb5.internal.crypto.ArcFourHmacEType
>>> KrbKdcReq send: kdc=dc1.example.com UDP:88, timeout=30000, number of retries =3, #bytes=1392
>>> KDCCommunication: kdc=dc1.example.com UDP:88, timeout=30000,Attempt =1, #bytes=1392
>>> KrbKdcReq send: #bytes read=1398
>>> KdcAccessibility: remove dc1.example.com
>>> EType: sun.security.krb5.internal.crypto.ArcFourHmacEType
>>> KrbApReq: APOptions are 00000000 00000000 00000000 00000000
>>> EType: sun.security.krb5.internal.crypto.Aes256CtsHmacSha1EType
Krb5Context setting mySeqNumber to: 774790609
Krb5Context setting peerSeqNumber to: 0
Created InitSecContextToken:
0000: 01 00 6E 00 00 00 00 00   00 00 A0 03 02 01 05 A1  ..n..)0..%......
0010: 03 02 01 0E A2 00 00 00   00 00 00 00 00 A3 82 04  ................
0020: 00 00 00 00 00 00 00 00   2D A0 03 02 01 05 A1 0E  5a..10..-.......
0030: 1B 0C 55 54 42 53 41 56   2E 4C 4F 43 41 4C A2 2A  ..EXAMPLE.COM.*
0040: 30 28 A0 03 02 01 00 A1   21 30 1F 1B 04 6C 64 61  0(......!0...lda
0050: 70 1B 17 73 61 76 61 6E   74 2D 64 63 31 2E 75 74  p..dc1.ut
0060: 62 73 61 76 2E 6C 6F 63   61 6C A3 82 03 E8 30 82  bsav.local....0.
0070: 03 E4 A0 03 02 01 12 A1   03 02 01 08 A2 82 03 D6  ................
---8<--- Snipping a bunch of binary

Krb5Context.unwrap: token=[05 04 01 ff 00 0c 00 0c 00 00 00 00 2e 2e 5d d1 f5 d2 e8 21 c1 23 92 20 61 f4 77 a8 07 a0 00 00 ]
Krb5Context.unwrap: data=[07 a0 00 00 ]
Krb5Context.wrap: data=[01 01 00 00 ]
Krb5Context.wrap: token=[05 04 00 ff 00 0c 00 00 00 00 00 00 2e 2e 5d d1 00 00 00 00 00 00 00 00 fa b6 79 67 ce db 58 d2 ]
********************************
      LDAP User Info
********************************
ldap result 1: User DN: CN=Sample User,OU=ExampleUsers,DC=example,DC=com

Found ticket for [email protected] to go to krbtgt/[email protected] expiring on Fri Aug 22 02:35:42 EDT 2014
Entered Krb5Context.initSecContext with state=STATE_NEW
Found ticket for [email protected] to go to krbtgt/[email protected] expiring on Fri Aug 22 02:35:42 EDT 2014
Found ticket for [email protected] to go to ldap/[email protected] expiring on Fri Aug 22 02:35:42 EDT 2014
Service ticket not found in the subject
>>> Credentials acquireServiceCreds: same realm
Using builtin default etypes for default_tgs_enctypes
default etypes for default_tgs_enctypes: 18 17 16 23 1 3.
>>> CksumType: sun.security.krb5.internal.crypto.RsaMd5CksumType
>>> EType: sun.security.krb5.internal.crypto.ArcFourHmacEType
>>> KrbKdcReq send: kdc=dc1.example.com UDP:88, timeout=30000, number of retries =3, #bytes=1381
>>> KDCCommunication: kdc=dc1.example.com UDP:88, timeout=30000,Attempt =1, #bytes=1381
>>> KrbKdcReq send: #bytes read=94
>>> KdcAccessibility: remove dc1.example.com
>>> KDCRep: init() encoding tag is 126 req type is 13
>>>KRBError:
     sTime is Thu Aug 21 16:35:46 EDT 2014 0000000000000
     suSec is 918178
     error code is 7
     error Message is Server not found in Kerberos database
     realm is EXAMPLE.COM
     sname is ldap/example.com
     msgType is 30
KrbException: Server not found in Kerberos database (7)
    at sun.security.krb5.KrbTgsRep.<init>(KrbTgsRep.java:70)
    at sun.security.krb5.KrbTgsReq.getReply(KrbTgsReq.java:192)
    at sun.security.krb5.KrbTgsReq.sendAndGetCreds(KrbTgsReq.java:203)
    at sun.security.krb5.internal.CredentialsUtil.serviceCreds(CredentialsUtil.java:311)
    at sun.security.krb5.internal.CredentialsUtil.acquireServiceCreds(CredentialsUtil.java:115)
    at sun.security.krb5.Credentials.acquireServiceCreds(Credentials.java:442)
    at sun.security.jgss.krb5.Krb5Context.initSecContext(Krb5Context.java:641)
    at sun.security.jgss.GSSContextImpl.initSecContext(GSSContextImpl.java:248)
    at sun.security.jgss.GSSContextImpl.initSecContext(GSSContextImpl.java:179)
    at com.sun.security.sasl.gsskerb.GssKrb5Client.evaluateChallenge(GssKrb5Client.java:193)
    at com.sun.jndi.ldap.sasl.LdapSasl.saslBind(LdapSasl.java:123)
    at com.sun.jndi.ldap.LdapClient.authenticate(LdapClient.java:232)
    at com.sun.jndi.ldap.LdapCtx.connect(LdapCtx.java:2740)
    at com.sun.jndi.ldap.LdapCtx.<init>(LdapCtx.java:316)
    at com.sun.jndi.ldap.LdapCtxFactory.getUsingURL(LdapCtxFactory.java:193)
    at com.sun.jndi.ldap.LdapCtxFactory.getLdapCtxInstance(LdapCtxFactory.java:152)
    at com.sun.jndi.url.ldap.ldapURLContextFactory.getObjectInstance(ldapURLContextFactory.java:52)
    at javax.naming.spi.NamingManager.getURLObject(NamingManager.java:601)
    at javax.naming.spi.NamingManager.processURL(NamingManager.java:381)
    at javax.naming.spi.NamingManager.processURLAddrs(NamingManager.java:361)
    at javax.naming.spi.NamingManager.getObjectInstance(NamingManager.java:333)
    at com.sun.jndi.ldap.LdapReferralContext.<init>(LdapReferralContext.java:111)
    at com.sun.jndi.ldap.LdapReferralException.getReferralContext(LdapReferralException.java:150)
    at com.sun.jndi.ldap.LdapNamingEnumeration.hasMoreReferrals(LdapNamingEnumeration.java:357)
    at com.sun.jndi.ldap.LdapNamingEnumeration.hasMoreImpl(LdapNamingEnumeration.java:226)
    at com.sun.jndi.ldap.LdapNamingEnumeration.hasMore(LdapNamingEnumeration.java:189)
    at LdapContinuationDemoAction.run(LdapContinuationDemoAction.java:123)
    at java.security.AccessController.doPrivileged(Native Method)
    at javax.security.auth.Subject.doAs(Subject.java:415)
    at LdapContinuationDemoAction.main(LdapContinuationDemoAction.java:52)
Caused by: KrbException: Identifier doesn't match expected value (906)
    at sun.security.krb5.internal.KDCRep.init(KDCRep.java:143)
    at sun.security.krb5.internal.TGSRep.init(TGSRep.java:66)
    at sun.security.krb5.internal.TGSRep.<init>(TGSRep.java:61)
    at sun.security.krb5.KrbTgsRep.<init>(KrbTgsRep.java:55)
    ... 29 more

*** ERROR: java.security.PrivilegedActionException: javax.naming.PartialResultException [Root exception is javax.naming.AuthenticationException: GSSAPI [Root exception is javax.security.sasl.SaslException: GSS initiate failed [Caused by GSSException: No valid credentials provided (Mechanism level: Server not found in Kerberos database (7))]]]

person Dominic A.    schedule 21.08.2014    source источник
comment
Это не похоже на проблему Java, а похоже на проблему администратора Windows AD, поскольку вы не можете постоянно устанавливать SPN ?!   -  person Michael-O    schedule 28.08.2014
comment
@ Michael-O - Я не уверен, что установка имени участника-службы вручную - это жизнеспособный выбор в долгосрочной перспективе, поскольку имена участников-служб должны быть уникальными, а к доменному имени может быть привязано несколько контроллеров домена. Например, если было два контроллера домена для домена example.com: dc1.example.com и dc2.example.com. Я мог бы поставить ldap/example.com под dc1. Однако, когда мой клиент разрешает ldap://example.com, из-за циклического перебора DNS запрос на соединение может быть обслужен dc2. Поскольку dc2 не владеет SPN, он не сможет проверить билет Kerberos.   -  person Dominic A.    schedule 03.09.2014
comment
Где реально можно решить проблему? Вы связались со списком рассылки. Я наткнулся на это на работе и теперь точно знаю, что это не проблема SASL. Прямой и обратный поиск (не) всегда являются правильным решением. Я опубликую анализ этого и теоретическое решение.   -  person Michael-O    schedule 11.03.2016


Ответы (1)


Вы не можете и не должны регистрировать SPN с каноническим именем области. В этом случае имена участников-служб должны быть привязаны к конкретной машине. Если вы действительно хотите использовать ldap://example.com, убедитесь, что обратный DNS выполняется до создания SPN. MIT Kerberos, Heimdal и JGSS по умолчанию будут выполнять обратный поиск в DNS, а SSPI - нет, так что это нереально.

Лучшим решением было бы вместо указания имени хоста использовать DNS SRV для определения местоположения контроллера домена, а затем выполнить привязку. Поэтому измените свой URL к ldap:///DC=example,DC=com.

Изменить (2016-03-14): Спустя более 1,5 лет я сам наткнулся на это на работе и провел некоторое исследование с инструментами Windows, Wireshark и документацией Microsoft по этой теме. Некоторые из моих предыдущих заявлений нужно отменить, некоторые обновить. Вот объяснение, также задокументированное в моем Tomcat SPNEGO / AD Authenticator:

Изменить (2021-06-06): для тех, кто все еще страдает от этого, используйте мой Локатор DNS Active Directory, и все готово.

person Michael-O    schedule 03.09.2014
comment
Спасибо, @ Michael-O, я подозревал, что попытка обойти это путем определения нового SPN была неправильным тактом. Однако я не контролирую URL-адрес, к которому я привязываюсь в этом случае. Сначала я привязываюсь к ldap://dc1.example.com и получаю обратно рефералов, которые выглядят как ldap://example.com/CN=Configuration,DC=example,DC=com. - person Dominic A.; 04.09.2014
comment
Можете ли вы выполнить поиск CNAME для example.com. В качестве альтернативы вы должны переключиться на использование GC вместо простого LDAP. Используйте порт 3268. - person Michael-O; 04.09.2014
comment
К сожалению, нет возможности перехватить URL-адрес перехода и преобразовать его в каноническое имя между моментом возврата ссылки и моментом, когда библиотека LDAP пытается его разрешить. Все это связано с тем, как провайдер Java LDAP обрабатывает отсылки. GC - хороший момент. К сожалению, порт глобального каталога отсутствует в списке одобренных и одобренных портов для нашего приложения, поэтому нам не разрешен к нему доступ. Однако это хорошая идея для следующего раунда. - person Dominic A.; 04.09.2014
comment
Если все так плотно, вы, вероятно, заблудились. Попробуйте эти варианты и отчет. - person Michael-O; 04.09.2014
comment
Попадание в порт GC определенно работает. Наша работа заключается в том, чтобы просто указать несколько DN, отличных от корневых, инициировать поиск от каждого по очереди и объединить результаты. В качестве альтернативы мы можем поймать ReferralException, которые выдаются при поиске из корневого DN, и игнорировать их. Меня больше всего беспокоит, почему контроллер AD возвращает URL-адреса, для которых не существует SPN? Контроллер домена прекрасно знает, что ldap/example.com SPN не существует, так зачем же возвращать URL-адреса в from ldap://example.com, для которых будет невозможно получить билет? Каждый домен, с которым я тестировал, демонстрировал такое поведение. - person Dominic A.; 04.09.2014
comment
Ответ на ваши вопросы прост: 1) LDAP не заботится об аутентификации, потому что это полностью обрабатывается SASL. Он не может знать, что такого канонического SPN не существует. 2) возвращается DNS-имя области, поскольку возвращать доменное имя DC небезопасно. Со временем он может измениться, поскольку Windows динамически регистрирует серверы LDAP с помощью DNS. example.com гарантированно работает из-за циклического перебора DNS. Включите обратное разрешение DNS в вашем krb.conf, и JGSS должен разрешить реальное имя DNS до создания SPN. - person Michael-O; 04.09.2014
comment
Я установил и dns_canonicalize_hostname = true, и rdns = true в krb5.ini под [libdefaults]. Я также проверил, работает как прямой, так и обратный DNS, например: nslookup example.com возвращает 10.1.1.13, 10.1.1.14; nslookup 10.1.1.13 возвращает dc-01.example.com; nslookup 10.1.1.14 возвращает dc-02.example.com. Однако GSS по-прежнему запрашивает билет для ldap/example.com при отслеживании перехода, и я все еще вижу ту же ошибку. Может быть, поставщик LDAP JNDI должен быть расширен опцией канонизации имен хостов перед переходом по ссылкам? - person Dominic A.; 08.09.2014
comment
@DominicA., То, что вы можете установить эти параметры, не означает, что они соблюдаются JGSS. Эти реквизиты доступны для MIT Kerberos. Кажется, у вас все в порядке с настройкой DNS. Что вы можете сделать сейчас, так это связаться со списком рассылки security-dev @ openjdk. Вейджун Ван довольно быстро отвечает на такие вопросы. Вот создание контекст GSS. - person Michael-O; 08.09.2014