Skip to content

Deploying SonarQube In My Self-Hosted Environment With SAML Authentication

Overview

If you are a software developer of any level, finding tools which help you improve your craft can be a major win. One such tool that I have been using for a long time is SonarQube. SonarQube is a server and associated build/IDE plugins to analyze your code for security issues, bad practices, and "smells". Not only does it identify these issues, but it also explains why they are issues and helps you to become a better developer in almost any languages it supports. Using SonarQube has helped me to be come a much better developer over the years with minimal friction and higher productivity.

Self-Host Or Use The Free Cloud Services?

The choice of using the free SonarCloud or self-hosting is up to you. I like to have my services private and under my own control, so this post will describe how I am deploying SonarQube on my container host. Both ways are helpful and productive.

Getting Started

As with most other things on my blog related to self-hosting, it starts with Podman and Quadlets. Refer to my earlier post about Podman & Traefik so that some of the context around how I set up TLS and reverse HTTP proxy make sense.

This is the Quadlet file I use for SonarQube:

ini
[Unit]
Wants=traefik.service
Requires=backend-network.service
Requires=frontend-network.service
Requires=postgresql.service
After=network-online.target
Requires=network-online.target

[Container]
ContainerName=sonarqube
Image=docker.io/library/sonarqube:community
PodmanArgs=--memory=3G --cpus=3
SecurityLabelType=container_runtime_t
Environment=SONAR_JDBC_USERNAME=dbuser
Environment=SONAR_JDBC_URL=jdbc:postgresql://postgresql:5432/sonarqube
Environment=SONAR_JDBC_PASSWORD=**REDACTED**
Environment=SONAR_JDBC_MAXACTIVE=10
Environment=SONAR_JDBC_MAXIDLE=5
Environment=SONAR_JDBC_MINIDLE=2
HealthCmd="curl -ks -o /dev/null http://localhost:9000"
HealthInterval=3s
HealthRetries=5
HealthStartPeriod=15s
HealthTimeout=3s
Network=backend
Network=frontend
Label="traefik.enable=true"
Label="traefik.docker.network=frontend"
Label="traefik.http.routers.sonarqube.rule=Host(`sonarqube.example.com`)"
Label="traefik.http.routers.sonarqube.entrypoints=https"
Label="traefik.http.routers.sonarqube.service=sonarqube-http"
Label="traefik.http.routers.sonarqube.tls.certresolver=traefiktls"
Label="traefik.http.routers.sonarqube.middlewares=secure-headers@file"
Label="traefik.http.services.sonarqube-http.loadbalancer.server.port=9000"

[Service]
Restart=always

[Install]
WantedBy=default.target

Since I already have an instance of PostgreSQL running in my container environment, I just create a new database in that server and connect to it from SonarQube. I also expose SonarQube using Traefik the same as all of my other web-based services. There ARE some additional considerations for SonarQube which I will detail below:

  1. SonarQube uses ElasticSearch, so on a Linux system you need to ensure that various kernel and ULIMIT parameters are set to allow it to run properly

    bash
    sysctl -w vm.max_map_count=524288
    sysctl -w fs.file-max=131072
    ulimit -n 131072
    ulimit -u 8192
    1. These settings need to be configured such that they are applied on system startup
  2. ElasticSearch requires that SECCOMP is enabled in the Linux kernel as described HERE

  3. ElasticSearch also requires write access to /tmp as described HERE

Adding A Custom Script Mapper To Keycloak

I already customize my Keycloak container instance to install my own them which matches the rest of my domain. I added some customization to my container build to also add a custom script mapper which is needed for SonarQube to display users properly.

Mapper Script

Here's my mapper script (in JavaScript) which combines the first and last name into a single field. I named this file full-name-mapper.js

javascript
var user = keycloakSession.getContext().getUser();
var fullName = user.getFirstName() + " " + user.getLastName()
exports = fullName

You also need a keycloak-scripts.json which looks like this:

json
{
    "saml-mappers": [
        {
            "name": "Full Name Mapper",
            "fileName": "full-name-mapper.js",
            "description": "Combine firstName and lastName in a single field"
        }
    ]
}

And both of these files need to be put into a JAR file with the following structure:

<root>
  - full-name-mapper.js
  - META-INF/
    - keycloak-scripts.json

This JAR is then included when you build your customized Keycloak container as shown below:

Dockerfile
FROM quay.io/keycloak/keycloak:26.5 AS builder

# Enable health and metrics support
ENV KC_HEALTH_ENABLED=true
ENV KC_METRICS_ENABLED=true
ENV KC_DB=postgres

# Configure a database vendor
ENV KC_PROXY_HEADERS=xforwarded
ENV KC_HTTP_ENABLED=true
ENV KC_DB=postgres
ENV KC_FEATURES=preview

COPY mappers.jar /opt/keycloak/providers/mappers.jar    ## Mappers added here!

WORKDIR /opt/keycloak
RUN /opt/keycloak/bin/kc.sh build

FROM quay.io/keycloak/keycloak:26.5
COPY --from=builder /opt/keycloak/ /opt/keycloak/
COPY . /opt/keycloak/themes/example

# change these values to point to a running postgres instance
ENV KC_HTTP_ENABLED=true
ENV KC_DB=postgres
ENV KC_DB_URL=jdbc:postgresql://postgresql:5432/keycloak
ENTRYPOINT ["/opt/keycloak/bin/kc.sh"]

Create A Keycloak Group For sonar-administrators

  1. Log on to your Keycloak instance and ensure you can manage the Realm you want to use for SonarQube.
  2. Navigate to Groups and create a new group named sonarqube-administratorsCreate SAML Client
  3. Add one or more users to the sonarqube-administrators group Create SAML Client

Adding A SAML Client To Keycloak

  1. Log on to your Keycloak instance and ensure you can manage the Realm you want to use for SonarQube.
  2. Go to Clients and create a new client for SAML Create SAML Client
  3. Fill in the name and client ID for the SAML Client Create SAML Client
  4. Define the base, home, and valid redirect URLs for the client; then click SaveCreate SAML Client
  5. Once the client is created, navigate to the Client Scopes tab and open the sonarqube-dedicated scope so that we can set up the mappers Create SAML Client
  6. Add mappers for the fields needed by SonarQube by clicking on Add Mapper and selecting By ConfigurationCreate SAML Client
  7. The first mapper should use the custom script mapper we created above Create SAML ClientCreate SAML Client
  8. Create a mapper for the Keycloak groups Create SAML ClientCreate SAML Client
  9. Create the Login and E-Mail Mappers using the User Property mapper under By ConfigurationCreate SAML ClientCreate SAML ClientCreate SAML Client

Configuring SAML Mappings For SonarQube

The documentation which SonarQube provides for integrating Keycloak via SAML is a little difficult to understand, so I will attempt to clarify it here for you.

  1. Open the SonarQube web interface and set up the initial administrator (or use the admin credentials you have already configured)
  2. Navigate to Administration -> General settings -> Authentication and then click on SAML and Create ConfigurationConfigure SAML
  3. In the pop-over, configure the following options
    • Application ID: sonarqube - This must match the client ID configured in Keycloak
    • Provider Name: Keycloak - This is an arbitrary identify which should be descriptive
    • Provider ID: https://keycloak.example.com/realms/example - This must match the URL provided by Keycloak's SAML Configuration and should follow the example provided here
    • SAML login url: https://keycloak.example.com/realms/example/protocol/saml - This must match the URL provided by Keycloak's SAML Configuration and should follow the example provided here
    • Identity provider certificate: Leave Blank - Encryption and signing are not explained in this document (YET!)
    • SAML user login attribute: login - MUST match the name of the Login mapper configured in Keycloak
    • SAML user name attribute: name - MUST match the Name mapper configured in Keycloak
    • SAML user email attribute: email - MUST match the E-Mail mapper configured in Keycloak
    • SAML group attribute: groups - MUST match the Groups mapper configured in Keycloak
    • Leave the remaining items blank as this document does not (YET!) cover signing and encrypting SAML assertions
  4. Click on Save configuration
  5. Test your SAML configuration Configure SAML
  6. After authenticating via Keycloak, you should see a report like the following: Configure SAML
  7. Enable SAML Authentication Configure SAML

Summary

That's it! At this point, you can use your SonarQube server for analyzing your development projects. This includes the ability to set up scanning tokens. Additionally, any users you assign to the sonarqube-administrators group in Keycloak will have administrator privileges in SonarQube. You can set up additional groups and use those group memberships for project permissions in SonarQube as well.

Best of luck and I wish you happy and productive development!

Updated at: