Secure Sonarqube instance with Cloudflare Access

Hi,

We are setting up a new SQ instance that will be exposed on Internet, and we would like to have a first authentication in front of SQ webapp, to protect it in case of a vulnerability.
Cloudflare Access is a nice solution for this that we already use for other sites, it acts as an auth proxy with SSO in front of the site to protect.

It works well for users, but we need to allow access for the Sonar scanners that will run in our CI/CD pipelines.
I had considered authorizing the IP ranges of the runners of our devops platforms, but the IP ranges from Github runners is endless, with millions of IPs to whitelist, it is not exploitable.

It would therefore be necessary to go for another form of authentication. I had thought of passing a specific http header to be able to authenticate the sonar scanners with Cloudflare, but it seems this is not supported by the scanners. This question remained unanswered: node.js - Adding extra headers in sonarqube webhook - Stack Overflow

Other options would be to use cloudflared to create a tunnel between the sonar scanner and Cloudflare but it’s a bit complex and probably require at least the scanner to run inside a specific Docker image.

Do you have any suggestion about how we could achieve this?

Thanks,
Maxime

Hey there.

You might want to add your voice here:

The scanne itself runs on Java (and/or .NET Framework if you use the Scanner for .NET). It is possible to pass proxy information through the scanner via the SONAR_SCANNER_OPTS environment variable.

SONAR_SCANNER_OPTS="-Dhttp.proxyHost=proxy.mycompany.com -Dhttp.proxyPort=8080 -Dhttp.proxyUser=someUserName -Dhttp.proxyPassword=somePassword"

Maybe this is helpful?

1 Like

Thanks Colin, this is definitely interesting. The ticket raised by Ken is completely aligned with my need.
SONAR_SCANNER_OPTS could indeed be an option to pass the required parameters but the ones related to http proxy that you shared won’t cover our need (http proxy is used to provide access to internet from private network).
Such parameter could work though:
SONAR_SCANNER_OPTS=“-Dhttp.extraHeader=CF-Access-Client-Id: -Dhttp.extraHeader=CF-Access-Client-Secret:”

This would be quite straightforward to implement, adding specific headers in http client framework is a 1 liner. i can open a PR if the source code is available (did not check).

Hey @emex

You’re welcome to raise a PR – however keep this in mind:

Please be aware that we are not actively looking for feature contributions. The truth is that it’s extremely difficult for someone outside SonarSource to comply with our roadmap and expectations. Therefore, we typically only accept minor cosmetic changes and typo fixes.

With that in mind, if you would like to submit a code contribution, please create a pull request for this repository. Please explain your motives to contribute this change: what problem you are trying to fix, what improvement you are trying to make.

And, we’ve seen some more technical PRs accepted lately, so I don’t want to discourage you.

1 Like

So it is currently not possible to post to a SQ instance running behind CloudFlare?
I am having the exact same issue and using the CF-Access-XXXX headers without any success; wasted hours on this.

Colin - We would really like to see this functionality, I’d be willing to open a PR, but I can see a few possible ways to approach this, and I would like to avoid opening a PR that would directly be rejected.

I am unable to follow the process documented here Contributing to SonarQube to open a Jira ticket, as it appears that people outside of SonarSource no longer have access to view/create tickets.

What is the appropriate way to collaborate with the SS team to align on an approach before opening a PR?

We had the same problem and we approached it and solved it using the following steps.

  1. Configure WARP to access Cloudflare protected instance:
    We created a COMPOSITE Github actions that runs directly in the GH actions specific worker machine (not inside docker) as follows:
name: 'Configure Cloudflare WARP'
description: 'Configure Cloudflare WARP for accessing protected CF instances'
runs:
  using: "composite"
  steps:
    - run: echo "${{ github.action_path }}" >> $GITHUB_PATH
      shell: bash
    - name: install warp-cli
      shell: bash
      run: |
        curl https://pkg.cloudflareclient.com/pubkey.gpg | sudo gpg --yes --dearmor --output /usr/share/keyrings/cloudflare-warp-archive-keyring.gpg
        echo 'deb [arch=amd64 signed-by=/usr/share/keyrings/cloudflare-warp-archive-keyring.gpg] https://pkg.cloudflareclient.com/ focal main' | sudo tee /etc/apt/sources.list.d/cloudflare-client.list
        sudo apt update
        sudo apt install cloudflare-warp -y
    - name: create mdm.xml file
      shell: bash
      run: |
        cat > mdm.xml <<EOF
        <dict>
          <key>organization</key>
          <string>your_organization</string>
          <key>auth_client_id</key>
          <string>${CLOUDFLARE_ACCESS_CLIENT_ID}</string>
          <key>auth_client_secret</key>
          <string>${CLOUDFLARE_ACCESS_CLIENT_SECRET}</string>
        </dict>
        EOF
        sudo mv mdm.xml /var/lib/cloudflare-warp/
    - name: init connection
      shell: bash
      run: warp-cli --accept-tos connect
    - name: wait for connection
      shell: bash
      run: |
        echo "Waiting for connection..."
        until warp-cli --accept-tos account | grep -q "Team"; do
          sleep 1
        done
        echo "Warp is now connected!"
  1. Create a COMPOSITE action that downloads SonarQube scanner cli and performs the analysis as following
name: 'SonarQube Analysis action'
description: 'Install sonar-scanner and perform analysis'
inputs:
  propertyFile:
    description: 'Path to sonar-project.properties file'
    required: false
    default: 'sonar-project.properties'
  projectVersion:
    description: 'Project version'
    required: false
    default: 'test_version'
runs:
  using: "composite"
  steps:
    - name: Install SonarQube
      shell: bash
      run: |
        wget https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-5.0.1.3006-linux.zip
        unzip sonar-scanner-cli-5.0.1.3006-linux.zip
        sudo mv sonar-scanner-5.0.1.3006-linux /opt/
        ln -s /opt/sonar-scanner-5.0.1.3006-linux/bin/sonar-scanner /usr/local/bin/sonar-scanner
    - name: Run SonarQube analysis
      shell: bash
      run: |
        sonar-scanner -Dproject.settings=${{ inputs.propertyFile }} -Dsonar.host.url=${SONARQUBE_URL} -Dsonar.login=${SONARQUBE_API_TOKEN} -Dsonar.projectVersion=${{ inputs.projectVersion }}

Therefore by using sequentially those 2 actions we bypassed the problem.

      - name: Configure Cloudflare WARP
        uses: action1
        env:
          CLOUDFLARE_ACCESS_CLIENT_ID: *******
          CLOUDFLARE_ACCESS_CLIENT_SECRET: ******
      - name: SonarQube analysis
        uses: action2
        env:
          SONARQUBE_URL: ******
          SONARQUBE_API_TOKEN:******
        with:
          projectVersion: test

We didn’t use one of the existing marketplace actions that run SonarQube because those were running inside a container in which case the first step had no effect when running inside container. That’s why the 2 custom GH actions were composite eunning directly in tha bash shell of the coresponding runner

More on custom GH actions and the types of them here

2 Likes