Step 3 - SonarQube
- - 5 min read
After the Pipeline has been created and tested we will add another Task to verify the source code and check for possible security issues, leveraging the tool SonarQube by Sonar.
Goals
The goals of this step are:
Install and configure SonarQube
Add a Task to scan our source code for vulnerabilities
Verify the results.
SonarQube
SonarQube by Sonar helps developers to deliver clean code. With the integration into our CICD pipeline it will detect issues and reports them back to the developers. The results will be shown in a dashboard. We will install the Community version of SonarQube, which is enough for our showcase.
SonarQube Installation
To install SonarQube I have prepared a Helm Chart that I use with GitOps when I deploy a new lab environment. Feel free to use it. It simply calls the Chart that is provided by Sonar. In addition, it creates a Job that changes the default administrator password to a different one.
The values file is good to go, the only item you must change is the route in the very first line. Also, if you prefer not to deploy any plugins (for example, the German language pack), you can remove the appropriate line.
Before you run the Helm, you need to provide a Sealed Secret (or manually create a Secret) like the following:
kind: Secret
apiVersion: v1
metadata:
name: credentials
namespace: sonarqube
data:
adminpass: <your base64 password string>
type: Opaque
This password will be used by the Job "change-admin-password" and will configure a new password for the user "admin"
Once everything is installed (this will take several minutes), you can access SonarQube using the URL you defined in the values file.
SonarQube Create Token
Our Pipeline will talk to SonarQube and request a scan of the source code. To be able to do this, we need to create a Token in SonarQube and store it as a Secret in our ci namespace.
Click on "My Account" (Upper right corner) > Security and create a new token:
Copy the token and create the following Secret:
kind: Secret
apiVersion: v1
metadata:
name: globex-ui-sonarqube-secret
namespace: ci
stringData:
token: <your SonarQube Token> (1)
type: Opaque
1 | The generated Token NOT base64 encrypted (I am using stringData in this case) |
Modify the Pipeline
Now it is time to bring the SonarQube scan task into our Pipeline. This requires some modifications to existing objects and the creation of some new ones.
Modify the TriggerBinding.
Add the following lines to globex-ui TriggerBinding
- name: sonarqubeHostUrl value: http://sonarqube.apps.ocp.aws.ispworld.at/ (1)
1 The URL of SonarQube So, it will look like this:
apiVersion: triggers.tekton.dev/v1alpha1 kind: TriggerBinding metadata: name: globex-ui namespace: ci spec: params: - name: tlsVerify value: 'false' - name: gitRepoHost value: github.com - name: sonarqubeHostUrl value: https://sonarqube.apps.ocp.aws.ispworld.at/ (1)
1 The URL of SonarQube Modify the TriggerTemplate
Add the following lines:
spec: params: (1) - description: Sonarqube host url name: sonarqubeHostUrl ... resourcetemplates: - apiVersion: tekton.dev/v1beta1 spec: params: (2) - name: SONARQUBE_HOST_URL value: $(tt.params.sonarqubeHostUrl) - name: SONARQUBE_PROJECT_KEY value: globex-ui (3) - name: SONARQUBE_PROJECT_SECRET value: globex-ui-sonarqube-secret (4) ...
1 Parameters provided by the TriggerBinding. 2 Parameters provided to the Pipeline. 3 Project that will be created in SonarQube. 4 Secret of the SonarQube token. The result should look like the following:
apiVersion: triggers.tekton.dev/v1alpha1 kind: TriggerTemplate metadata: name: app-globex-ui-template namespace: ci spec: params: - description: The git repository URL. name: gitrepositoryurl - description: The repository name for this PullRequest. name: fullname - description: The git branch for this PR. name: io.openshift.build.commit.ref - description: the specific commit SHA. name: io.openshift.build.commit.id - description: The date at which the commit was made name: io.openshift.build.commit.date - description: The commit message name: io.openshift.build.commit.message - description: The name of the github user handle that made the commit name: io.openshift.build.commit.author - description: The host name of the git repo name: gitRepoHost - description: Enable image repository TLS certification verification. name: tlsVerify - description: Extra parameters passed for the push command when pushing images. name: build_extra_args - description: Target image repository name name: imageRepo - description: Sonarqube host url name: sonarqubeHostUrl resourcetemplates: - apiVersion: tekton.dev/v1beta1 kind: PipelineRun metadata: generateName: secure-supply-chain- spec: params: - name: REPO_HOST value: $(tt.params.gitRepoHost) - name: GIT_REPO value: $(tt.params.gitrepositoryurl) - name: TLSVERIFY value: $(tt.params.tlsVerify) - name: BUILD_EXTRA_ARGS value: $(tt.params.build_extra_args) - name: IMAGE_REPO value: $(tt.params.imageRepo) - name: IMAGE_TAG value: >- $(tt.params.io.openshift.build.commit.ref)-$(tt.params.io.openshift.build.commit.id) - name: COMMIT_SHA value: $(tt.params.io.openshift.build.commit.id) - name: GIT_REF value: $(tt.params.io.openshift.build.commit.ref) - name: COMMIT_DATE value: $(tt.params.io.openshift.build.commit.date) - name: COMMIT_AUTHOR value: $(tt.params.io.openshift.build.commit.author) - name: COMMIT_MESSAGE value: $(tt.params.io.openshift.build.commit.message) - name: SONARQUBE_HOST_URL value: $(tt.params.sonarqubeHostUrl) - name: SONARQUBE_PROJECT_KEY value: globex-ui - name: SONARQUBE_PROJECT_SECRET value: globex-ui-sonarqube-secret pipelineRef: name: secure-supply-chain serviceAccountName: pipeline workspaces: - name: shared-data volumeClaimTemplate: metadata: creationTimestamp: null spec: accessModes: - ReadWriteOnce resources: requests: storage: 3Gi status: {}
Create the Task scan-source. This task will use the pulled source code and uses SonarQube to let it scan our code.
apiVersion: tekton.dev/v1beta1 kind: Task metadata: name: scan-code namespace: ci spec: description: >- Source code scan using sonar-scanner and SonarQube. params: - default: 'docker.io/sonarsource/sonar-scanner-cli:latest' (1) name: scanImage type: string - default: 'https://sonarqube-sonarqube.myplaceholder.com/' (2) name: sonarqubeHostUrl type: string - default: object-detection-rest name: sonarqubeProjectKey type: string - default: object-detection-rest-sonarqube-secret name: sonarqubeProjectSecret type: string - default: 'true' name: verbose type: string steps: - env: - name: SONAR_TOKEN_WEB_UI (3) valueFrom: secretKeyRef: key: token name: $(params.sonarqubeProjectSecret) (4) image: $(params.scanImage) name: scan-code resources: {} script: > (5) set -x echo $(ls -a) sonar-scanner -X -Dsonar.projectKey=$(params.sonarqubeProjectKey) -Dsonar.sources=./ -Dsonar.host.url=$(params.sonarqubeHostUrl) -Dsonar.login=$SONAR_TOKEN_WEB_UI workingDir: /workspace/repository workspaces: (6) - name: repository
1 Image containing SonarQube command line tool. The cluster must be able to connect to docker.io. 2 Default parameters for this Task that might be overwritten. 3 The Secret with the token. 4 Parameter as set by the PipelineRun which gets the value from the TriggerTemplate. 5 Script that is executed to scan the source code. 6 The workspace where we can find the source code. Update your Pipeline and add the following task:
spec:
params:
...
- name: SONARQUBE_HOST_URL
type: string
- name: SONARQUBE_PROJECT_KEY
type: string
- name: SONARQUBE_PROJECT_SECRET
type: string
tasks:
...
- name: scan-source
params: (1)
- name: sonarqubeHostUrl
value: $(params.SONARQUBE_HOST_URL)
- name: sonarqubeProjectKey
value: $(params.SONARQUBE_PROJECT_KEY)
- name: sonarqubeProjectSecret
value: $(params.SONARQUBE_PROJECT_SECRET)
runAfter: (2)
- pull-source-code
taskRef: (3)
kind: Task
name: scan-code
workspaces: (4)
- name: repository
workspace: shared-data
1 | Parameters that shall be provided for the Task. |
2 | The task should run AFTER the source has been pulled … which makes sense. |
3 | Reference to the Task we created above. |
4 | Workspace shared-data where the source code was pulled from the previous Task. |
The full pipeline objects now look like the following:
The Pipeline now has a second task:
Execute the Pipeline
Let’s update the README.md of our source code again to trigger another PipelineRun. After the code has been pulled it should now perform the second task and scan the quality of the source code.
You can monitor the progress of the PipelineRun again:
Once the PipelineRun executed both tasks successfully, we can check SonarQube.
The project globex-ui has been created which shows the results of our scan:
Summary
We have now added a Task to our Pipeline that performs a code analysis of our source code. The results are shown in SonarQube and the developers can react accordingly.
Copyright © 2020 - 2024 Toni Schmidbauer & Thomas Jungbauer