Spring LDAP через SSL - PullRequest
       131

Spring LDAP через SSL

0 голосов
/ 29 апреля 2020

Я пытаюсь реализовать 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
...