Я пытаюсь реализовать Spring ldap с несколькими арендаторами, где количество серверов ldap неизвестно. Клиент должен вставить свою информацию LDAP и сертификат в форму, и я должен создать безопасное соединение TLS. Мне удалось настроить обычный LDAP без SSL, но у меня проблемы с настройкой SSL для правильной работы.
Это мой тестовый сервер:
docker run \
--rm \
-p 389:389 \
-p 636:636 \
--hostname local.dev \
--name ldap-local \
-e LDAP_ORGANISATION="Local" \
-e LDAP_DOMAIN=local.dev \
-e LDAP_BASE_DN="dc=local,dc=dev" \
-e LDAP_LOG_LEVEL=-1 \
-e LDAP_READONLY_USER=true \
-e LDAP_READONLY_USER_USERNAME=milos \
-e LDAP_READONLY_USER_PASSWORD=milos1234 \
-v /home/devel/script/ldap/keys:/container/service/slapd/assets/certs \
-e LDAP_TLS_CRT_FILENAME=ldap.crt \
-e LDAP_TLS_KEY_FILENAME=ldap.key \
-e LDAP_TLS_CA_CRT_FILENAME=jatheon.crt \
-e LDAP_TLS_VERIFY_CLIENT=try \
osixia/openldap:1.2.4 --copy-service --loglevel trace
ключей смонтированная папка содержит самозаверяющий сертификат, созданный с помощью:
openssl req -newkey rsa:1024 -x509 -sha256 -days 3650 -nodes -out ldap.crt -keyout ldap.key
Я использую Spring LdapTemplate для проверки соединения:
final LdapTemplate template = new LdapTemplate(contextFactory.buildLdapContext(connection));
template.getContextSource().getContext(connection.getUserDN(), connection.getAdminPassword());
buildLdapContext (Connection):
public LdapContextSource buildLdapContext(final LdapConnection connection) {
final LdapContextSource context = new LdapContextSource();
context.setBase(connection.getBaseDN());
context.setUrl(connection.getConnectionUrl());
context.setUserDn(connection.getUserDN());
context.setPassword(connection.getAdminPassword());
if(connection.hasCertificate()) {
final DefaultTlsDirContextAuthenticationStrategy authenticationStrategy = new DefaultTlsDirContextAuthenticationStrategy();
authenticationStrategy.setHostnameVerifier((hostname, session) -> hostname.equals(connection.getHost()));
authenticationStrategy.setSslSocketFactory(ldapSslSocketFactoryBuilder.buildSslSocketFactory(connection));
authenticationStrategy.setShutdownTlsGracefully(true);
context.setAuthenticationStrategy(authenticationStrategy);
}
context.afterPropertiesSet();
return context;
}
buildSslSocketFactory:
public SSLSocketFactory buildSslSocketFactory(final LdapConnection connection) {
try {
final KeyStore store = buildKeyStore(connection);
final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(store, null);
final SSLContext ctx = SSLContext.getInstance("TLSv1.2");
ctx.init(kmf.getKeyManagers(), new TrustManager[] {new DummyTrustManager()}, null);
return ctx.getSocketFactory();
} catch(Exception e) {
throw new LdapException(e.getMessage(), e);
}
}
private KeyStore buildKeyStore(final LdapConnection ldapConnection) {
try {
// Load in-memory keystore
final KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
keystore.load(null);
// Decode certificate
byte[] decoded = Base64.decodeBase64(ldapConnection.getSslCertificate()
.replaceAll(BEGIN_CERT, "")
.replaceAll(END_CERT, "")
.trim().getBytes(StandardCharsets.UTF_8));
// Load certificate
CertificateFactory certificateFactory = CertificateFactory.getInstance("x.509");
Certificate cert = certificateFactory.generateCertificate(new ByteArrayInputStream(decoded));
keystore.setCertificateEntry(ldapConnection.getConnectionUrl(), cert);
return keystore;
} catch(Exception e) {
log.error(e.getMessage(), e);
throw new LdapException(e.getMessage(), e);
}
}
Я использую DummyTrustManager для включения самоподписанных сертификатов:
public class DummyTrustManager implements X509TrustManager {
@Override
public void checkClientTrusted(final X509Certificate[] chain, final String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(final X509Certificate[] chain, final String authType) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
}
Это работает, но по какой-то причине это работает для любого случайного сертификата ie. Я могу разместить любой действующий сертификат, и он будет работать. Это журнал OpenLDAP:
5ea9a99d connection_read(12): unable to get TLS client DN, error=49 id=1000
5ea9a99d conn=1000 fd=12 TLS established tls_ssf=256 ssf=256
Если я изменю -e LDAP_TLS_VERIFY_CLIENT = требование запрос не выполнен:
TLS: can't accept: No certificate was found..
5ea9aaee connection_read(12): TLS accept failure error=-1 id=1000, closing