Skip to content
This repository was archived by the owner on May 6, 2021. It is now read-only.

Commit

Permalink
LDAP-53 Add support for StartTLS
Browse files Browse the repository at this point in the history
  • Loading branch information
Godin committed Oct 11, 2016
1 parent 56c03dc commit 5a0c89f
Show file tree
Hide file tree
Showing 9 changed files with 160 additions and 1 deletion.
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,26 @@ SonarQube LDAP Plugin
[![SonarQube.Com Quality Gate status](https://sonarqube.com/api/badges/gate?key=org.sonarsource.ldap%3Asonar-ldap-plugin)](https://sonarqube.com/overview?id=org.sonarsource.ldap%3Asonar-ldap-plugin)

For more, see [the docs](http://docs.sonarqube.org/display/PLUG/LDAP+Plugin)


## Example

You can check this plugin in action using Docker as described below.

Build plugin:

mvn clean package

Generate certificates:

./docker/gen-certs.sh

Build containers (SonarQube and OpenLDAP servers):

docker-compose build

Start containers:

docker-compose up

To access SonarQube use LDAP user `tester` with password `test`.
31 changes: 31 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
version: "2"

services:
sonarqube-server:
build:
context: .
dockerfile: docker/Dockerfile.sonarqube
networks:
- sonarqube-network
ports:
- "9000:9000"
environment:
- SONARQUBE_WEB_JVM_OPTS=-Djavax.net.ssl.keyStore=/root/keystore -Djavax.net.ssl.keyStorePassword=changeit

ldap-server:
build:
context: .
dockerfile: docker/Dockerfile.ldap
networks:
- sonarqube-network
environment:
- HOSTNAME=ldap-server
- LDAP_TLS_CRT_FILENAME=my-cert.crt
- LDAP_TLS_KEY_FILENAME=my-cert.key
- LDAP_TLS_CA_CRT_FILENAME=my-ca.crt
- LDAP_TLS_ENFORCE=true
- LDAP_TLS_VERIFY_CLIENT=demand

networks:
sonarqube-network:
driver: bridge
1 change: 1 addition & 0 deletions docker/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/certs
7 changes: 7 additions & 0 deletions docker/Dockerfile.ldap
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
FROM osixia/openldap

COPY docker/tester.ldif /container/service/slapd/assets/config/bootstrap/ldif/tester.ldif

COPY docker/certs/server.crt /container/service/slapd/assets/certs/my-cert.crt
COPY docker/certs/server.key /container/service/slapd/assets/certs/my-cert.key
COPY docker/certs/ca.crt /container/service/slapd/assets/certs/my-ca.crt
15 changes: 15 additions & 0 deletions docker/Dockerfile.sonarqube
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
FROM sonarqube:lts-alpine

COPY docker/sonar.properties /opt/sonarqube/conf/sonar.properties
COPY sonar-ldap-plugin/target/sonar-ldap-plugin-*-SNAPSHOT.jar /opt/sonarqube/extensions/plugins/

COPY docker/certs/ca.crt /root/ca.crt
COPY docker/certs/client.p12 /root/client.p12

RUN keytool -import -trustcacerts -alias my-ca -file /root/ca.crt -keystore /etc/ssl/certs/java/cacerts -storepass changeit -noprompt

RUN keytool -importkeystore \
-deststorepass changeit -destkeypass changeit -destkeystore /root/keystore \
-srckeystore /root/client.p12 -srcstoretype PKCS12 -srcstorepass pass

RUN keytool -list -v -keystore /root/keystore -storepass changeit
22 changes: 22 additions & 0 deletions docker/gen-certs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/bash

set -e

cd docker
mkdir certs
cd certs

openssl genrsa -out ca.key 4096
openssl req -x509 -new -nodes -key ca.key -days 9131 -out ca.crt -subj "/C=CH/ST=Geneva/L=Geneva/O=Example/CN=example.org"

openssl genrsa -out server.key 4096
openssl req -new -key server.key -out server.csr -subj "/C=CH/ST=Geneva/L=Geneva/O=Example/CN=ldap-server"
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 9131

openssl genrsa -out client.key 4096
openssl req -new -key client.key -out client.csr -subj "/C=CH/ST=Geneva/L=Geneva/O=Example/CN=sonarqube-server"
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days 9131

cat client.crt ca.crt > cert-chain.txt
openssl pkcs12 -export -inkey client.key -in cert-chain.txt -out client.p12 -password pass:pass
rm cert-chain.txt
20 changes: 20 additions & 0 deletions docker/sonar.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# LDAP configuration

# General Configuration
sonar.security.realm=LDAP

ldap.url=ldap://ldap-server:389
ldap.StartTLS=true

ldap.bindDn=cn=admin,dc=example,dc=org
ldap.bindPassword=admin

# User Configuration
ldap.user.baseDn=dc=example,dc=org
ldap.user.request=(&(objectClass=inetOrgPerson)(uid={login}))
ldap.user.realNameAttribute=cn
ldap.user.emailAttribute=mail

# Group Configuration
#ldap.group.baseDn=ou=groups,dc=example,dc=org
#ldap.group.request=(&(objectClass=posixGroup)(memberUid={uid}))
9 changes: 9 additions & 0 deletions docker/tester.ldif
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
dn: uid=tester,dc=example,dc=org
changetype: add
uid: tester
cn: Tester
sn: Tester
objectClass: top
objectClass: inetOrgPerson
userPassword: test
mail: [email protected]
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
package org.sonar.plugins.ldap;

import javax.annotation.Nullable;
import java.io.IOException;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.InitialDirContext;
Expand All @@ -31,6 +32,8 @@
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Properties;
import javax.naming.ldap.StartTlsRequest;
import javax.naming.ldap.StartTlsResponse;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.config.Settings;
import org.sonar.api.utils.log.Logger;
Expand Down Expand Up @@ -62,6 +65,7 @@ public class LdapContextFactory {
private static final String SASL_REALM_PROPERTY = "java.naming.security.sasl.realm";

private final String providerUrl;
private final boolean startTLS;
private final String authentication;
private final String factory;
private final String username;
Expand All @@ -73,6 +77,7 @@ public LdapContextFactory(Settings settings, String settingsPrefix, String ldapU
this.factory = StringUtils.defaultString(settings.getString(settingsPrefix + ".contextFactoryClass"), DEFAULT_FACTORY);
this.realm = settings.getString(settingsPrefix + ".realm");
this.providerUrl = ldapUrl;
this.startTLS = settings.getBoolean(settingsPrefix + ".StartTLS");
this.username = settings.getString(settingsPrefix + ".bindDn");
this.password = settings.getString(settingsPrefix + ".bindPassword");
}
Expand All @@ -97,7 +102,33 @@ public InitialDirContext createUserContext(String principal, String credentials)
}

private InitialDirContext createInitialDirContext(String principal, String credentials, boolean pooling) throws NamingException {
return new InitialLdapContext(getEnvironment(principal, credentials, pooling), null);
final InitialLdapContext ctx;
if (startTLS) {
// Note that pooling is not enabled for such connections, because "Stop TLS" is not performed.
Properties env = new Properties();
env.put(Context.INITIAL_CONTEXT_FACTORY, factory);
env.put(Context.PROVIDER_URL, providerUrl);
env.put(Context.REFERRAL, DEFAULT_REFERRAL);
// At this point env should not contain properties SECURITY_AUTHENTICATION, SECURITY_PRINCIPAL and SECURITY_CREDENTIALS to avoid "bind" operation prior to StartTLS:
ctx = new InitialLdapContext(env, null);
// http://docs.oracle.com/javase/jndi/tutorial/ldap/ext/starttls.html
StartTlsResponse tls = (StartTlsResponse) ctx.extendedOperation(new StartTlsRequest());
try {
tls.negotiate();
} catch (IOException e) {
NamingException ex = new NamingException("StartTLS failed");
ex.initCause(e);
throw ex;
}
// Explicitly initiate "bind" operation:
ctx.addToEnvironment(Context.SECURITY_AUTHENTICATION, authentication);
ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, principal);
ctx.addToEnvironment(Context.SECURITY_CREDENTIALS, credentials);
ctx.reconnect(null);
} else {
ctx = new InitialLdapContext(getEnvironment(principal, credentials, pooling), null);
}
return ctx;
}

private InitialDirContext createInitialDirContextUsingGssapi(String principal, String credentials) throws NamingException {
Expand Down

0 comments on commit 5a0c89f

Please sign in to comment.