尝试使用 JNDI 和 GSSAPI 执行 LDAP 搜索时获取 javax.naming.CommunicationException:连接重置和 AD“事件 ID 1216”

尝试使用 JNDI 和 GSSAPI 执行 LDAP 搜索时获取 javax.naming.CommunicationException:连接重置和 AD“事件 ID 1216”

我正在尝试分析 Ldap 搜索期间出现异常/失败的原因。我正在使用 Active Directory 域控制器上的 JNDI 执行操作。

以下是我正在尝试做的事情的背景:

  1. 使用 SASL ( Kerberos authentication) 使用 JAAS ( KRB5LoginModule) 生成LoginContext
  2. 一旦登录成功,LoginContext实例就拥有经过身份验证的主体,该主体TGT在其PrivateCredentials
  3. 之后,我使用上述经过身份验证的 GSSAPI 生成 LdapContext Subject
  4. 一旦LdapContext生成,我就用它来执行 JNDI 操作(主要使用分页搜索)

  1. 到目前为止一切都很好并且 LdapContext 已正确生成
  2. Active Directory 域控制器设置的一些详细信息:
  3. TGT 的生命周期设置为1 hour
  4. 的生命周期服务票TGS)设置为10分钟(由于某些限制而必须这样做,但就是这样)

现在的情况是:

  • 使用LdapContext上面创建的,它开始使用查询域控制器,pagingcontrol并且事情在一定时间或一定数量的搜索中顺利进行(这样说是为了避免大家误以为这可能实际上涉及时间,只是认为这发生在(大约)固定间隔之后 - 这些间隔可能是时间或搜索

  • 当它在一定间隔后获取下一页时,搜索失败:

       Caused by: javax.naming.CommunicationException: Connection reset
          at com.sun.jndi.ldap.LdapCtx.getSearchReply(LdapCtx.java:1920) ~[?:1.8.0_73]
          at com.sun.jndi.ldap.AbstractLdapNamingEnumeration.getNextBatch(AbstractLdapNamingEnumeration.java:130) ~[?:1.8.0_73]
          at com.sun.jndi.ldap.AbstractLdapNamingEnumeration.hasMoreImpl(AbstractLdapNamingEnumeration.java:217) ~[?:1.8.0_73]
          at com.sun.jndi.ldap.AbstractLdapNamingEnumeration.hasMore(AbstractLdapNamingEnumeration.java:189) ~[?:1.8.0_73]
    
          ... 10 more
    
       Caused by: java.net.SocketException: Connection reset
          at java.net.SocketInputStream.read(SocketInputStream.java:209) ~[?:1.8.0_73]
          at java.net.SocketInputStream.read(SocketInputStream.java:141) ~[?:1.8.0_73]
          at java.io.BufferedInputStream.fill(BufferedInputStream.java:246) ~[?:1.8.0_73]
          at java.io.BufferedInputStream.read1(BufferedInputStream.java:286) ~[?:1.8.0_73]
          at java.io.BufferedInputStream.read(BufferedInputStream.java:345) ~[?:1.8.0_73]
          at com.sun.jndi.ldap.sasl.SaslInputStream.readFully(SaslInputStream.java:166) ~[?:1.8.0_73]
          at com.sun.jndi.ldap.sasl.SaslInputStream.fill(SaslInputStream.java:123) ~[?:1.8.0_73]
          at com.sun.jndi.ldap.sasl.SaslInputStream.read(SaslInputStream.java:90) ~[?:1.8.0_73]
          at com.sun.jndi.ldap.Connection.run(Connection.java:860) ~[?:1.8.0_73]
          at java.lang.Thread.run(Thread.java:745) ~[?:1.8.0_73]
    

同时,我在 Active Directory 域控制器上看到以下事件日志:EventId:2889

 Log Name:      Directory Service
 Source:        Microsoft-Windows-ActiveDirectory_DomainService
 Event ID:      2889
 Task Category: LDAP Interface
 Level:         Information
 Keywords:      Classic
 User:          ANONYMOUS LOGON
 Computer:      myad01.example.lab
 Description:The following client performed a SASL (Negotiate/Kerberos/NTLM/Digest) LDAP bind without
 requesting signing (integrity verification), or performed a simple bind over a clear text (non-
 SSL/TLS-encrypted) LDAP connection. 

 Client IP address: X.X.X.X:56260 
 Identity the client attempted to authenticate as:EXAMPLE\Administrator 
 Binding Type:0

我还看到了一个EventID为1216的日志,详细信息如下:

 Log Name:      Directory Service
 Source:        Microsoft-Windows-ActiveDirectory_DomainService
 Event ID:      1216
 Task Category: LDAP Interface
 Level:         Warning
 Keywords:      Classic
 User:          N/A
 Computer:      myad01.example.lab
 Description:Internal event: An LDAP client connection was closed because of an error. 

 Client IP:X.X.X.X:56244 

 Additional Data 
 Error value: 1236 The network connection was aborted by the local system. 
 Internal ID: c060420

我的理解是:每当(经过一段时间后)它去获取下一页时,ldap connection服务器都会使该页面无效(如事件 ID 所示1216) 因此我得到了CommunicationException。我的问题是为什么我在一定时间间隔后而不是立即得到这个?是不是因为kerberos和服务票证的有效期已经过了? 如果是这样,那么我应该如何设计来克服分页问题?因为,在收到通信异常后,如果我创建一个新的 LdapContext 并设置分页控件,我会按预期收到以下异常:

  javax.naming.OperationNotSupportedException: [LDAP: error code 12 - 00000057: LdapErr: DSID-0C090B0B, comment: Error processing control, data 0, v3839 ]
     at com.sun.jndi.ldap.LdapCtx.mapErrorCode(Unknown Source) ~[?:1.8.0_201]
     at com.sun.jndi.ldap.LdapCtx.processReturnCode(Unknown Source) ~[?:1.8.0_201]
     at com.sun.jndi.ldap.LdapCtx.processReturnCode(Unknown Source) ~[?:1.8.0_201]
     at com.sun.jndi.ldap.LdapCtx.searchAux(Unknown Source) ~[?:1.8.0_201]
     at com.sun.jndi.ldap.LdapCtx.c_search(Unknown Source) ~[?:1.8.0_201]
     at com.sun.jndi.toolkit.ctx.ComponentDirContext.p_search(Unknown Source) ~[?:1.8.0_201]
     at com.sun.jndi.toolkit.ctx.PartialCompositeDirContext.search(Unknown Source) ~[?:1.8.0_201]
     at javax.naming.directory.InitialDirContext.search(Unknown Source) ~[?:1.8.0_201]

对我来说,同时拥有 SASL(kerberos)身份验证支持和 GSSAPI 创建 LdapContext 支持非常重要。此外,分页也很重要,因为数据量巨大,而且我无法控制客户的环境,因此我们无法对票证有效性进行任何限制!

请向我提供关于如何进一步调试此问题的指示,并建议适当的方法或解决方法(对此感到抱歉,但无论如何都需要它)来解决此问题。

答案1

我的观察可能有助于某些人分析他们的问题。首先,Event ID:1216当客户端(如我的问题中提到的 JNDI,在这种情况下,客户端只是 LdapContext/DirContext)关闭其底层时,在 Active Directory 上生成Socket看看这个链接

LdapContext只不过是使用某些连接设置形成的连接,在客户端(例如:JNDI)和 LDAP 服务器(例如:Active Directory 目录服务)之间。当网络上任何两个实体之间存在连接时,它通常由客户端套接字和服务器端套接字的形成支持。对于 LdapContext 也是如此,LdapContext 有一个底层套接字。

在使用GSSAPI获取 LdapContext 实例时,底层套接字带有一个等于有效性服务票证的生存期AD DS 上存在的设置。一旦底层套接字的有效性/超时结束,套接字就会关闭。如果LdapContext尝试查询 AD DS,则会发生上述Caused by: javax.naming.CommunicationException: Connection reset异常并且通信失败。

由于 TGT 和 TGS 的生命周期/有效性设置在 AD DS 上,因此无法使用 GSSAPI 绕过它们。如果存在需要使用LdapContext更长时间的要求,那么唯一的出路就是增加相应票证的有效期。

相关内容