79574140

Date: 2025-04-14 23:01:45
Score: 0.5
Natty:
Report link

OK, his was not straightforward to diagnose nor fix, and I really had to receive some pointers from this SonarSource Community topic (credits to @ganncamp).

There are multiple factors that led here.

Factors that are SonarQube-specific:

SonarQube Server's LDAP support is not designed to connect multiple servers in a failover mode.

(...)

Authentication will be tried on each server, in the order they are listed in the configurations until one succeeds.

source: https://docs.sonarsource.com/sonarqube-server/2025.1/instance-administration/authentication/ldap/#multiple-servers

What's it designed for then? They probably meant to provide access using heterogenous LDAP servers. Consider multiple firms or branches each with their own LDAP directory using the same SonarQube instance.

To address this use case in a multi-server LDAP setup, the SonarQube users table contains an external_login and an external_identity_provider field, which together must be unique in the whole table. In a single-server LDAP setup, external_identity_provider is always 'sonarqube'. In a multi-server LDAP setup, the field reflects the LDAP server the user was authenticated against the first time they logged in. For example: "LDAP_foobar". (See linked documentation above.) Now our two John Does can be told apart:

login external_login external_identity_provider
john_doe john_doe LDAP_foobar
john_doe1234 john_doe LDAP_yeehaw

Also, since the SonarQube users table had an original "login" field (which is unique of course), they had to work around that unique constraint by adding a random number sequence to the username. Since the login field is probably not used for external users anymore, this is just for backwards compatibility, I guess.

ldap.url=ldaps://foo.bar.local:636
ldap.foo.bindDn=CN=foo,OU=orgunit,DC=bar,DC=local

...then the certificate SAN should contain a bar.local DNS field, otherwise the query fails and produces a (debug-level) message in web.log:

2025.04.11 18:43:18 DEBUG web[b7a70ba3-0e9a-4685-a1ad-c2a30e919e64][o.s.a.l.LdapSearch] More result might be forthcoming if the referral is followed
javax.naming.PartialResultException: null
    at java.naming/com.sun.jndi.ldap.AbstractLdapNamingEnumeration.hasMoreImpl(AbstractLdapNamingEnumeration.java:237)
    at java.naming/com.sun.jndi.ldap.AbstractLdapNamingEnumeration.hasMore(AbstractLdapNamingEnumeration.java:189)
    at org.sonar.auth.ldap.LdapSearch.hasMore(LdapSearch.java:156)
    at org.sonar.auth.ldap.LdapSearch.findUnique(LdapSearch.java:146)
    at org.sonar.auth.ldap.DefaultLdapUsersProvider.getUserDetails(DefaultLdapUsersProvider.java:78)
    at org.sonar.auth.ldap.DefaultLdapUsersProvider.doGetUserDetails(DefaultLdapUsersProvider.java:58)
    at org.sonar.server.authentication.LdapCredentialsAuthentication.doAuthenticate(LdapCredentialsAuthentication.java:92)
    at org.sonar.server.authentication.LdapCredentialsAuthentication.authenticate(LdapCredentialsAuthentication.java:74)
    at org.sonar.server.authentication.CredentialsAuthentication.lambda$authenticate$0(CredentialsAuthentication.java:71)
    at java.base/java.util.Optional.or(Optional.java:313)
    at org.sonar.server.authentication.CredentialsAuthentication.authenticate(CredentialsAuthentication.java:71)
    at org.sonar.server.authentication.CredentialsAuthentication.authenticate(CredentialsAuthentication.java:57)
    at org.sonar.server.authentication.ws.LoginAction.authenticate(LoginAction.java:116)
    at org.sonar.server.authentication.ws.LoginAction.doFilter(LoginAction.java:95)
    ...
Caused by: javax.naming.CommunicationException: simple bind failed: bar.local:636
    at java.naming/com.sun.jndi.ldap.LdapReferralContext.<init>(LdapReferralContext.java:96)
    at java.naming/com.sun.jndi.ldap.LdapReferralException.getReferralContext(LdapReferralException.java:151)
    at java.naming/com.sun.jndi.ldap.AbstractLdapNamingEnumeration.hasMoreReferrals(AbstractLdapNamingEnumeration.java:326)
    at java.naming/com.sun.jndi.ldap.AbstractLdapNamingEnumeration.hasMoreImpl(AbstractLdapNamingEnumeration.java:227)
    ... 68 common frames omitted
Caused by: javax.net.ssl.SSLHandshakeException: No subject alternative DNS name matching bar.local found.
    at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:131)
    at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:383)
    at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:326)
    at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:321)
    at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.checkServerCerts(CertificateMessage.java:654)
    at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.onCertificate(CertificateMessage.java:473)
    at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.consume(CertificateMessage.java:369)
    at java.base/sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:396)
    at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:480)
    at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:458)
    at java.base/sun.security.ssl.TransportContext.dispatch(TransportContext.java:206)
    at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:172)
    at java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1510)
    at java.base/sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1425)
    at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:455)
    at java.base/sun.security.ssl.SSLSocketImpl.ensureNegotiated(SSLSocketImpl.java:925)
    at java.base/sun.security.ssl.SSLSocketImpl$AppOutputStream.write(SSLSocketImpl.java:1295)
    at java.base/java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:81)
    at java.base/java.io.BufferedOutputStream.flush(BufferedOutputStream.java:142)
    at java.naming/com.sun.jndi.ldap.Connection.writeRequest(Connection.java:418)
    at java.naming/com.sun.jndi.ldap.Connection.writeRequest(Connection.java:391)
    at java.naming/com.sun.jndi.ldap.LdapClient.ldapBind(LdapClient.java:359)
    at java.naming/com.sun.jndi.ldap.LdapClient.authenticate(LdapClient.java:214)
    at java.naming/com.sun.jndi.ldap.LdapCtx.connect(LdapCtx.java:2896)
    at java.naming/com.sun.jndi.ldap.LdapCtx.<init>(LdapCtx.java:348)
    at java.naming/com.sun.jndi.ldap.LdapCtxFactory.getLdapCtxFromUrl(LdapCtxFactory.java:229)
    at java.naming/com.sun.jndi.ldap.LdapCtxFactory.getUsingURL(LdapCtxFactory.java:189)
    at java.naming/com.sun.jndi.ldap.LdapCtxFactory.getLdapCtxInstance(LdapCtxFactory.java:152)
    at java.naming/com.sun.jndi.url.ldap.ldapURLContextFactory.getObjectInstance(ldapURLContextFactory.java:52)
    at java.naming/javax.naming.spi.NamingManager.getURLObject(NamingManager.java:625)
    at java.naming/javax.naming.spi.NamingManager.processURL(NamingManager.java:402)
    at java.naming/javax.naming.spi.NamingManager.processURLAddrs(NamingManager.java:382)
    at java.naming/javax.naming.spi.NamingManager.getObjectInstance(NamingManager.java:354)
    at java.naming/com.sun.jndi.ldap.LdapReferralContext.<init>(LdapReferralContext.java:119)
    ... 71 common frames omitted
Caused by: java.security.cert.CertificateException: No subject alternative DNS name matching bar.local found.
    at java.base/sun.security.util.HostnameChecker.matchDNS(HostnameChecker.java:212)
    at java.base/sun.security.util.HostnameChecker.match(HostnameChecker.java:103)
    at java.base/sun.security.ssl.X509TrustManagerImpl.checkIdentity(X509TrustManagerImpl.java:471)
    at java.base/sun.security.ssl.X509TrustManagerImpl.checkIdentity(X509TrustManagerImpl.java:418)
    at java.base/sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:238)
    at java.base/sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:132)
    at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.checkServerCerts(CertificateMessage.java:638)
    ... 100 common frames omitted

The tricky part is: the same rigorous SAN-checking does not happen on server startup, when SonarQube checks connectivity to all configured LDAP servers. Even if the TLS certificate is imperfect, it will log:

2025.04.14 21:42:54 INFO  web[][o.s.a.l.LdapContextFactory] Test LDAP connection on ldaps://foo.bar.local:636: OK

Factors and events related to our specific setup and situation:

Not totally sure about how it all played down, but the results were as follows:

login email external_login external_identity_provider
john_doe [email protected] john_doe LDAP_foobar
john_doe1234 [email protected] john_doe LDAP_yeehaw

How we managed to fix the situation:

UPDATE users SET external_identity_provider = 'sonarqube' WHERE external_identity_provider LIKE 'LDAP_%';
Reasons:
  • Long answer (-1):
  • Has code block (-0.5):
  • Contains question mark (0.5):
  • User mentioned (1): @ganncamp
  • Self-answer (0.5):
Posted by: Attila Csipak