Active Directory authentication with two domain controllers in SonarQube on Docker

Hey there,

I am trying to utilize both Active Directory controllers in SonarQube.
https://docs.sonarqube.org/latest/instance-administration/delegated-auth/#header-6
This document explains how to set it up step by step and it worked for me…however I’ve moved my SonarQube installation onto Docker and I can’t find any configuration example which would let me still use it. Could you help me out with this one?

The version I use is: 9.4.0.54424, basically what’s in the Docker Hub linked to sonarqube:9.4.0-developer

Hi,

Welcome to the community!

If I compare the documented envvars with the property names in $SONARQUBE-HOME/conf/sonar.properties the translation seems to be

  • upper case
  • change dots to underscores

Have you tried this?

 
HTH,
Ann

Let’s consider a configuration example like this for LDAP:

sonar.security.realm=LDAP

# Set to true when connecting to a LDAP server using a case-insensitive setup.
sonar.authenticator.downcase=true

# List the different servers
ldap.servers=server1,server2

# URL of the LDAP server. Note that if you are using ldaps, then you should install the server certificate into the Java truststore.
ldap.server1.url=ldaps://ads01.example.com:636
ldap.server2.url=ldaps://ads02.example.com:636

# Bind DN is the username of an LDAP user to connect (or bind) with. Leave this blank for anonymous access to the LDAP directory (optional)
ldap.server1.bindDn=CN=citools,OU=Execution,OU=COMPANYUSERS,DC=example,DC=com
ldap.server2.bindDn=CN=citools,OU=Execution,OU=COMPANYUSERS,DC=example,DC=com

# Bind Password is the password of the user to connect with. Leave this blank for anonymous access to the LDAP directory (optional)
ldap.server1.bindPassword=hahafunnypassword
ldap.server2.bindPassword=hahafunnypassword

# Possible values: simple | CRAM-MD5 | DIGEST-MD5 | GSSAPI See http://java.sun.com/products/jndi/tutorial/ldap/security/auth.html (default: simple)
ldap.server1.authentication=simple
ldap.server2.authentication=simple
#ldap.authentication=simple

# See :
#   * http://java.sun.com/products/jndi/tutorial/ldap/security/digest.html
#   * http://java.sun.com/products/jndi/tutorial/ldap/security/crammd5.html
# (optional)
# ldap.realm=example.org

# Context factory class (optional)
# ldap.contextFactoryClass=com.sun.jndi.ldap.LdapCtxFactory

# Enable usage of StartTLS (default : false)
#ldap.StartTLS=false
ldap.server1.StartTLS=false
ldap.server2.StartTLS=false
#ldap.StartTLS=false

# Follow or not referrals. See http://docs.oracle.com/javase/jndi/tutorial/ldap/referral/jndi.html (default: true)
ldap.server1.followReferrals=true
ldap.server2.followReferrals=true
#ldap.followReferrals=true

# USER MAPPING

# Distinguished Name (DN) of the root node in LDAP from which to search for users (mandatory)
ldap.server1.user.baseDn=dc=example,dc=com
ldap.server2.user.baseDn=dc=example,dc=com

# LDAP user request. (default: (&(objectClass=inetOrgPerson)(uid={login})) )
ldap.server1.user.request=(&(objectClass=user)(sAMAccountName={login})(memberOf:1.2.840.113556.1.4.1941:=CN=sonarqube_users,OU=Groups,OU=COMPANYUSERS,DC=example,DC=com))
ldap.server2.user.request=(&(objectClass=user)(sAMAccountName={login})(memberOf:1.2.840.113556.1.4.1941:=CN=sonarqube_users,OU=Groups,OU=COMPANYUSERS,DC=example,DC=com))

# Attribute in LDAP defining the user’s real name. (default: cn)
#ldap.user.realNameAttribute=cn
ldap.server1.user.realNameAttribute=cn
ldap.server2.user.realNameAttribute=cn

# Attribute in LDAP defining the user’s email. (default: mail)
#ldap.user.emailAttribute=mail
ldap.server1.user.emailAttribute=mail
ldap.server2.user.emailAttribute=mail

# GROUP MAPPING

# Distinguished Name (DN) of the root node in LDAP from which to search for groups. (optional, default: empty)
ldap.server1.group.baseDn=DC=example,DC=com
ldap.server2.group.baseDn=DC=example,DC=com

# LDAP group request (default: (&(objectClass=groupOfUniqueNames)(uniqueMember={dn})) )
ldap.server1.group.request=(&(objectClass=group)(member:1.2.840.113556.1.4.1941:={dn})(|(sAMAccountName=sonarqube_users)(sAMAccountName=ci_admins)))
ldap.server2.group.request=(&(objectClass=group)(member:1.2.840.113556.1.4.1941:={dn})(|(sAMAccountName=sonarqube_users)(sAMAccountName=ci_admins)))

# Property used to specifiy the attribute to be used for returning the list of user groups in the compatibility mode. (default: cn)
# ldap.group.idAttribute=sAMAccountName
ldap.server1.group.idAttribute=sAMAccountName
ldap.server2.group.idAttribute=sAMAccountName

Does your message mean it should be like this in Docker?

LDAP_SERVERS=server1,server2
LDAP_SERVER1_URL=ldaps://ads01.example.com:636
LDAP_SERVER2_URL=ldaps://ads02.example.com:636
LDAP_SERVER1_BINDDN=CN=citools,OU=Execution,OU=COMPANYUSERS,DC=example,DC=com
LDAP_SERVER2_BINDDN=CN=citools,OU=Execution,OU=COMPANYUSERS,DC=example,DC=com
LDAP_SERVER1_BINDPASSWORD=hahafunnypassword
LDAP_SERVER2_BINDPASSWORD=hahafunnypassword
LDAP_SERVER1_STARTTLS=false
LDAP_SERVER2_STARTTLS=false
LDAP_SERVER1_FOLLOWREFERRALS=true
LDAP_SERVER2_FOLLOWREFERRALS=true
LDAP_SERVER1_USER_BASEDN=dc=example,dc=com
LDAP_SERVER2_USER_BASEDN=dc=example,dc=com
LDAP_SERVER1_USER_REQUEST=(&(objectClass=user)(sAMAccountName={login})(memberOf:1.2.840.113556.1.4.1941:=CN=sonarqube_users,OU=Groups,OU=COMPANYUSERS,DC=example,DC=com))
LDAP_SERVER2_USER_REQUEST=(&(objectClass=user)(sAMAccountName={login})(memberOf:1.2.840.113556.1.4.1941:=CN=sonarqube_users,OU=Groups,OU=COMPANYUSERS,DC=example,DC=com))
LDAP_SERVER1_USER_REALNAMEATTRIBUTE=cn
LDAP_SERVER2_USER_REALNAMEATTRIBUTE=cn
LDAP_SERVER1_USER_EMAILATTRIBUTE=mail
LDAP_SERVER2_USER_REALNAMEATTRIBUTE=mail
LDAP_SERVER1_GROUP_BASEDN=DC=example,DC=com
LDAP_SERVER2_GROUP_BASEDN=DC=example,DC=com
LDAP_SERVER1_GROUP_REQUEST=(&(objectClass=group)(member:1.2.840.113556.1.4.1941:={dn})(|(sAMAccountName=sonarqube_users)(sAMAccountName=ci_admins)))
LDAP_SERVER2_GROUP_REQUEST=(&(objectClass=group)(member:1.2.840.113556.1.4.1941:={dn})(|(sAMAccountName=sonarqube_users)(sAMAccountName=ci_admins)))
LDAP_SERVER1_GROUP_IDATTRIBUTE=sAMAccountName
LDAP_SERVER2_GROUP_IDATTRIBUTE=sAMAccountName

Having some environmental variables does not mean Docker container will use it, that’s a sense of my question.

Hi,

Yes, that’s my guess. But I’ve flagged this for more expert attention.

 
Ann

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.

I have to say that I am impressed, because it actually works:

2022.05.26 00:01:37 INFO  web[][o.s.a.l.LdapContextFactory] Test LDAP connection on ldaps://ads01.example.com:636: OK
2022.05.26 00:01:37 INFO  web[][o.s.a.l.LdapContextFactory] Test LDAP connection on ldaps://ads02.example.com:636: OK
2022.05.26 00:01:37 INFO  web[][org.sonar.INFO] Security realm started

I suggest you to add the information about it in the documentation, because it definitely lacks it. You can also add a template for easy docker-compose deployment, because honestly if you just fill the values, that’s all needed to actually deploy SonarQube and start working with it.

This configuration sample will work with any Active Directory domain where you use two domain controllers with a connection over LDAP.

docker-compose.yml

version: '3.9'

services:
  sonarqube:
    container_name: sonarqube
    depends_on:
      - postgres
    image: sonarqube:9.4-developer
    restart: unless-stopped
    hostname: sonarqube
    env_file:
      - ./.env
    ports:
      - '127.0.0.1:9000:9000'
    networks:
      - sonarqube-network
    volumes:
      - 'sonarqube-data:/opt/sonarqube/data'
      - 'sonarqube-extensions:/opt/sonarqube/extensions'
      - 'sonarqube-logs:/opt/sonarqube/logs'
      - './cacerts:/etc/ssl/certs/java/cacerts' #you MUST create your own Java certificate store and place your AD CS CA certificate, otherwise your connections will fail!
    environment:
      - SONAR_JDBC_URL=${SONAR_JDBC_URL}
      - SONAR_JDBC_USERNAME=${SONAR_JDBC_USERNAME}
      - SONAR_JDBC_PASSWORD=${SONAR_JDBC_PASSWORD}
      - SONAR_WEB_HOST=${SONAR_WEB_HOST}
      - SONAR_WEB_PORT=${SONAR_WEB_PORT}
      - SONAR_WEB_HTTP_MAXTHREADS=${SONAR_WEB_HTTP_MAXTHREADS}
      - SONAR_WEB_HTTP_MINTHREADS=${SONAR_WEB_HTTP_MINTHREADS}
      - SONAR_WEB_HTTP_ACCEPTCOUNT=${SONAR_WEB_HTTP_ACCEPTCOUNT}
      - SONAR_WEB_HTTP_KEEPALIVETIMEOUT=${SONAR_WEB_HTTP_KEEPALIVETIMEOUT}
      - SONAR_AUTH_JWTBASE64HS256SECRET=${SONAR_AUTH_JWTBASE64HS256SECRET}
      - SONAR_WEB_SESSIONTIMEOUTINMINUTES=${SONAR_WEB_SESSIONTIMEOUTINMINUTES}
      - SONAR_WEB_SYSTEMPASSCODE=${SONAR_WEB_SYSTEMPASSCODE}
      - SONAR_WEB_SSO_ENABLE=${SONAR_WEB_SSO_ENABLE}
      - SONAR_WEB_SSO_LOGINHEADER=${SONAR_WEB_SSO_LOGINHEADER}
      - SONAR_WEB_SSO_NAMEHEADER=${SONAR_WEB_SSO_NAMEHEADER}
      - SONAR_WEB_SSO_EMAILHEADER=${SONAR_WEB_SSO_EMAILHEADER}
      - SONAR_WEB_SSO_GROUPSHEADER=${SONAR_WEB_SSO_GROUPSHEADER}
      - SONAR_WEB_SSO_REFRESHINTERVALINMINUTES=${SONAR_WEB_SSO_REFRESHINTERVALINMINUTES}
      - SONAR_SECURITY_REALM=${SONAR_SECURITY_REALM}
      - SONAR_AUTHENTICATOR_DOWNCASE=${SONAR_AUTHENTICATOR_DOWNCASE}
      - LDAP_SERVERS=${LDAP_SERVERS}
      - LDAP_SERVER1_URL=${LDAP_SERVER1_URL}
      - LDAP_SERVER2_URL=${LDAP_SERVER2_URL}
      - LDAP_SERVER1_BINDDN=${LDAP_SERVER1_BINDDN}
      - LDAP_SERVER2_BINDDN=${LDAP_SERVER2_BINDDN}
      - LDAP_SERVER1_BINDPASSWORD=${LDAP_SERVER1_BINDPASSWORD}
      - LDAP_SERVER2_BINDPASSWORD=${LDAP_SERVER2_BINDPASSWORD}
      - LDAP_SERVER1_AUTHENTICATION=${LDAP_SERVER1_AUTHENTICATION}
      - LDAP_SERVER2_AUTHENTICATION=${LDAP_SERVER2_AUTHENTICATION}
      - LDAP_SERVER1_REALM=${LDAP_SERVER1_REALM}
      - LDAP_SERVER2_REALM=${LDAP_SERVER2_REALM}
      - LDAP_SERVER1_CONTEXTFACTORYCLASS=${LDAP_SERVER1_CONTEXTFACTORYCLASS}
      - LDAP_SERVER2_CONTEXTFACTORYCLASS=${LDAP_SERVER2_CONTEXTFACTORYCLASS}
      - LDAP_SERVER1_STARTTLS=${LDAP_SERVER1_STARTTLS}
      - LDAP_SERVER2_STARTTLS=${LDAP_SERVER2_STARTTLS}
      - LDAP_SERVER1_FOLLOWREFERRALS=${LDAP_SERVER1_FOLLOWREFERRALS}
      - LDAP_SERVER2_FOLLOWREFERRALS=${LDAP_SERVER2_FOLLOWREFERRALS}
      - LDAP_SERVER1_USER_BASEDN=${LDAP_SERVER1_USER_BASEDN}
      - LDAP_SERVER2_USER_BASEDN=${LDAP_SERVER2_USER_BASEDN}
      - LDAP_SERVER1_USER_REQUEST=${LDAP_SERVER1_USER_REQUEST}
      - LDAP_SERVER2_USER_REQUEST=${LDAP_SERVER2_USER_REQUEST}
      - LDAP_SERVER1_USER_REALNAMEATTRIBUTE=${LDAP_SERVER1_USER_REALNAMEATTRIBUTE}
      - LDAP_SERVER2_USER_REALNAMEATTRIBUTE=${LDAP_SERVER2_USER_REALNAMEATTRIBUTE}
      - LDAP_SERVER1_USER_EMAILATTRIBUTE=${LDAP_SERVER1_USER_EMAILATTRIBUTE}
      - LDAP_SERVER2_USER_EMAILATTRIBUTE=${LDAP_SERVER2_USER_EMAILATTRIBUTE}
      - LDAP_SERVER1_GROUP_BASEDN=${LDAP_SERVER1_GROUP_BASEDN}
      - LDAP_SERVER2_GROUP_BASEDN=${LDAP_SERVER2_GROUP_BASEDN}
      - LDAP_SERVER1_GROUP_REQUEST=${LDAP_SERVER1_GROUP_REQUEST}
      - LDAP_SERVER2_GROUP_REQUEST=${LDAP_SERVER2_GROUP_REQUEST}
      - LDAP_SERVER1_GROUP_IDATTRIBUTE=${LDAP_SERVER1_GROUP_IDATTRIBUTE}
      - LDAP_SERVER2_GROUP_IDATTRIBUTE=${LDAP_SERVER2_GROUP_IDATTRIBUTE}
      - SONAR_CE_JAVAADDITIONALOPTS=${SONAR_CE_JAVAADDITIONALOPTS}
      - SONAR_UPDATECENTER_ACTIVATE=${SONAR_UPDATECENTER_ACTIVATE}
      - HTTP_PROXYHOST=${HTTP_PROXYHOST}
      - HTTP_PROXYPORT=${HTTP_PROXYPORT}
      - HTTPS_PROXYHOST=${HTTPS_PROXYHOST}
      - HTTPS_PROXYPORT=${HTTPS_PROXYPORT}
      - HTTP_PROXYUSER=${HTTP_PROXYUSER}
      - HTTP_PROXYPASSWORD=${HTTP_PROXYPASSWORD}
      - HTTP_NONPROXYHOSTS=${HTTP_NONPROXYHOSTS}
      - SONAR_LOG_LEVEL=${SONAR_LOG_LEVEL}
      - SONAR_LOG_LEVEL_APP=${SONAR_LOG_LEVEL_APP}
      - SONAR_LOG_LEVEL_WEB=${SONAR_LOG_LEVEL_WEB}
      - SONAR_LOG_LEVEL_CE=${SONAR_LOG_LEVEL_CE}
      - SONAR_LOG_LEVEL_ES=${SONAR_LOG_LEVEL_ES}
      - SONAR_LOG_MAXFILES=${SONAR_LOG_MAXFILES}
      - SONAR_WEB_ACCESSLOGS_ENABLE=${SONAR_WEB_ACCESSLOGS_ENABLE}
      - SONAR_NOTIFICATIONS_DELAY=${SONAR_NOTIFICATIONS_DELAY}
      - SONAR_TELEMETRY_ENABLE=${SONAR_TELEMETRY_ENABLE}
    ulimits:
      nofile:
        soft: 131072
        hard: 131072
      nproc:
        soft: 8192
        hard: 8192
    stop_grace_period: 60s
  postgres:
    container_name: postgres
    image: postgres:13.6-bullseye
    restart: unless-stopped
    hostname: postgres-sonarqube
    env_file: 
      - ./.env
    networks:
      - sonarqube-network
    volumes:
      - postgresql-data:/var/lib/postgresql/data:rw
      - /etc/localtime:/etc/localtime:ro
    environment:
      - POSTGRES_USER=${POSTGRES_USER}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
      - POSTGRES_DB=${POSTGRES_DB}
volumes:
  sonarqube-data:
  sonarqube-extensions:
  sonarqube-logs:
  postgresql-data:
networks:
  sonarqube-network:

.env

#Postgres
POSTGRES_USER=sonarqube
POSTGRES_PASSWORD=hahafunnydatabasepassword
POSTGRES_DB=sonarqube

#Sonarqube
SONAR_JDBC_URL=jdbc:postgresql://postgres/sonarqube
SONAR_JDBC_USERNAME=sonarqube
SONAR_JDBC_PASSWORD=hahafunnydatabasepassword
SONAR_WEB_JAVAOPTS=
SONAR_WEB_HOST=0.0.0.0
SONAR_WEB_CONTEXT=
SONAR_WEB_PORT=9000
SONAR_WEB_HTTP_MAXTHREADS=50
SONAR_WEB_HTTP_MINTHREADS=5
SONAR_WEB_HTTP_ACCEPTCOUNT=25
SONAR_WEB_HTTP_KEEPALIVETIMEOUT=60000
SONAR_AUTH_JWTBASE64HS256SECRET=
SONAR_WEB_SESSIONTIMEOUTINMINUTES=4320
SONAR_WEB_SYSTEMPASSCODE=
SONAR_WEB_SSO_ENABLE=false
SONAR_WEB_SSO_LOGINHEADER=X-Forwarded-Login
SONAR_WEB_SSO_NAMEHEADER=X-Forwarded-Name
SONAR_WEB_SSO_EMAILHEADER=X-Forwarded-Email
SONAR_WEB_SSO_GROUPSHEADER=X-Forwarded-Groups
SONAR_WEB_SSO_REFRESHINTERVALINMINUTES=5

SONAR_SECURITY_REALM=LDAP
SONAR_AUTHENTICATOR_DOWNCASE=true

LDAP_SERVERS=server1,server2
LDAP_SERVER1_URL=ldaps://ads01.example.com:636
LDAP_SERVER2_URL=ldaps://ads02.example.com:636
LDAP_SERVER1_BINDDN=CN=citools,OU=Execution,OU=COMPANYUSERS,DC=example,DC=com
LDAP_SERVER2_BINDDN=CN=citools,OU=Execution,OU=COMPANYUSERS,DC=example,DC=com
LDAP_SERVER1_BINDPASSWORD=hahafunnypassword
LDAP_SERVER2_BINDPASSWORD=hahafunnypassword
LDAP_SERVER1_AUTHENTICATION=simple
LDAP_SERVER2_AUTHENTICATION=simple
LDAP_SERVER1_REALM=example.com
LDAP_SERVER2_REALM=example.com
LDAP_SERVER1_CONTEXTFACTORYCLASS=com.sun.jndi.ldap.LdapCtxFactory
LDAP_SERVER2_CONTEXTFACTORYCLASS=com.sun.jndi.ldap.LdapCtxFactory
LDAP_SERVER1_STARTTLS=false
LDAP_SERVER2_STARTTLS=false
LDAP_SERVER1_FOLLOWREFERRALS=true
LDAP_SERVER2_FOLLOWREFERRALS=true

LDAP_SERVER1_USER_BASEDN=dc=example,dc=com
LDAP_SERVER2_USER_BASEDN=dc=example,dc=com
LDAP_SERVER1_USER_REQUEST=(&(objectClass=user)(sAMAccountName={login})(memberOf:1.2.840.113556.1.4.1941:=CN=sonarqube_users,OU=Groups,OU=COMPANYUSERS,DC=example,DC=com))
LDAP_SERVER2_USER_REQUEST=(&(objectClass=user)(sAMAccountName={login})(memberOf:1.2.840.113556.1.4.1941:=CN=sonarqube_users,OU=Groups,OU=COMPANYUSERS,DC=example,DC=com))
LDAP_SERVER1_USER_REALNAMEATTRIBUTE=cn
LDAP_SERVER2_USER_REALNAMEATTRIBUTE=cn
LDAP_SERVER1_USER_EMAILATTRIBUTE=mail
LDAP_SERVER2_USER_EMAILATTRIBUTE=mail
LDAP_SERVER1_GROUP_BASEDN=DC=example,DC=com
LDAP_SERVER2_GROUP_BASEDN=DC=example,DC=com
LDAP_SERVER1_GROUP_REQUEST=(&(objectClass=group)(member:1.2.840.113556.1.4.1941:={dn})(|(sAMAccountName=sonarqube_users)(sAMAccountName=ci_admins)))
LDAP_SERVER2_GROUP_REQUEST=(&(objectClass=group)(member:1.2.840.113556.1.4.1941:={dn})(|(sAMAccountName=sonarqube_users)(sAMAccountName=ci_admins)))
LDAP_SERVER1_GROUP_IDATTRIBUTE=sAMAccountName
LDAP_SERVER2_GROUP_IDATTRIBUTE=sAMAccountName
SONAR_CE_JAVAADDITIONALOPTS=
SONAR_UPDATECENTER_ACTIVATE=true
HTTP_PROXYHOST=
HTTP_PROXYPORT=
HTTPS_PROXYHOST=
HTTPS_PROXYPORT=
HTTP_PROXYUSER=
HTTP_PROXYPASSWORD=
HTTP_NONPROXYHOSTS=
SONAR_LOG_LEVEL=INFO
SONAR_LOG_LEVEL_APP=INFO
SONAR_LOG_LEVEL_WEB=INFO
SONAR_LOG_LEVEL_CE=INFO
SONAR_LOG_LEVEL_ES=INFO
SONAR_LOG_MAXFILES=7
SONAR_WEB_ACCESSLOGS_ENABLE=true
SONAR_NOTIFICATIONS_DELAY=60
SONAR_TELEMETRY_ENABLE=false
1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.