极狐GitLab 集成 sonarqube 实践指南

背景

场景

  • 极狐GitLab旗舰版已经具备扫描能力,但是某些场景下扫描能力偏弱,所以希望集成第三方扫描工具作为补充。但是,单纯的通过Runner调用的方式,只是松散的集成,无法真正形成「深度集成」的体验。
  • 若可以「在极狐GitLab的漏洞报告中展现第三方扫描工具的扫描结果」,那么,对于集成的体验则会提升很多。本文即针对此目的进行展开说明。

外部扫描器选型 SonarQube-CE

  • 以非常流行的扫描工具SonarQube社区版为例加以说明。

快速入门

直接在.gitlab-ci.yml文件中填入如下内容,触发扫描,即可在漏洞报告中查看结果。

yaml 复制代码
variables:
  sonar_host_url: http://1.13.160.207:9000
  sonar_login: 333f3410ce3e575d559329e8f3d0a5d4ec8a499d

sonarqube:
  artifacts:
    reports:
      sast:
      - gl-sast-report.json
  script:
  - "/scan.sh"
  image:
     name: satomic/sonarscanner:v6

以下仓库参考

使用方法

参考dvwa仓库中的配置方法。

SonarQube-CE部署

  • 公网环境中已经部署好SonarQube,可以忽略部署过程,继续往下。
  • 若为私网环境,参考下文部署章节。

环境变量配置

在project级别中,settings - CI/CD - Variables中添加如下2个变量指向SonarQube服务。

  • sonar_host_url: http://1.13.160.207:9000
  • sonar_login: 333f3410ce3e575d559329e8f3d0a5d4ec8a499d

.gitlab-ci.yml配置

在待扫描仓库中增加.gitlab-ci.yml配置文件,如下,不建议直接把敏感信息如快速入门那样配置在ci文件中。

yaml 复制代码
sonarqube:
  artifacts:
    reports:
      sast:
      - gl-sast-report.json
  script:
  - "/scan.sh"
  image:
     name: satomic/sonarscanner:v6

然后进行扫描即可得到扫描结果。

报告总览

特定issue展现

文件定位

SonarQube说明

SonarQube原理

docker方式部署 SonarQube-CE 7.6

根据文章How to get the sonar-report.json file created with sonarqube?How to get sonar-report.json file to display sonar issues at gerrit level itself中所言,从7.7版本开始,不支持在scanner端导出json格式的报告,因此部署支持的旧版本中的最后一个版本,即7.6版。

PgSql

参考Docker 安装SonarQube 步骤以及遇到的坑进行部署,创建工作目录

arduino 复制代码
mkdir -p /home/sonar/postgres/postgresql
mkdir -p /home/sonar/postgres/data

创建网络

lua 复制代码
docker network create sonarqube-network

部署pg

ini 复制代码
docker run --name postgres -d -p 5432:5432 --network sonarqube-network \
-v /home/sonar/postgres/postgresql:/var/lib/postgresql \
-v /home/sonar/postgres/data:/var/lib/postgresql/data \
-v /etc/localtime:/etc/localtime:ro \
-e POSTGRES_USER=sonar \
-e POSTGRES_PASSWORD=sonar \
-e POSTGRES_DB=sonar \
-e TZ=Asia/Shanghai \
--restart always \
--privileged=true \
--network-alias postgres \
postgres

SonarQube

创建工作目录

bash 复制代码
mkdir -p /data/sonarqube_dir

修改系统参数

bash 复制代码
echo "vm.max_map_count=262144" > /etc/sysctl.conf
sysctl -p

运行测试容器

arduino 复制代码
docker run -d --name sonartest sonarqube:7.6-community

拷贝必须文件到本地,并修改权限为777

bash 复制代码
docker cp sonartest:/opt/sonarqube/conf /data/sonarqube_dir
docker cp sonartest:/opt/sonarqube/data /data/sonarqube_dir
docker cp sonartest:/opt/sonarqube/logs /data/sonarqube_dir
docker cp sonartest:/opt/sonarqube/extensions /data/sonarqube_dir

chmod -R 777 /data/sonarqube_dir/

删除容器

arduino 复制代码
docker stop sonartest
docker rm sonartest

启动SonarQube,其中SONARQUBE_JDBC_URL的IP地址需要修改为实际PgSql数据库的IP

ini 复制代码
 docker run -itd --name sonar -p 9000:9000 \
 -e ALLOW_EMPTY_PASSWORD=yes \
 -e SONARQUBE_DATABASE_USER=sonar \
 -e SONARQUBE_DATABASE_NAME=sonar \
 -e SONARQUBE_DATABASE_PASSWORD=sonar \
 -e SONARQUBE_JDBC_URL="jdbc:postgresql://192.168.1.4:5432/sonar" \
 --privileged=true \
 --network sonarqube-network \
 --restart always \
 -v /data/sonarqube_dir/logs:/opt/sonarqube/logs \
 -v /data/sonarqube_dir/conf:/opt/sonarqube/conf \
 -v /data/sonarqube_dir/data:/opt/sonarqube/data \
 -v /data/sonarqube_dir/extensions:/opt/sonarqube/extensions\
 sonarqube:7.6-community

生成token

登录SonarQube UI地址( http://IP:9000 ),默认用户名密码为admin/admin,在My Account - Security中生成Token。

部署使用sonar-scanner

参考SonarScanner下载最新版的扫描器

解压后,进入bin目录,执行如下命令即可完成扫描,扫描完成后可以去SonarQube中查看扫描结果。

ini 复制代码
./sonar-scanner \
-Dsonar.host.url=http://1.13.160.207:9000 \
-Dsonar.login=333f3410ce3e575d559329e8f3d0a5d4ec8a499d \
-Dsonar.projectKey=my:test \
-Dsonar.sources=/path_to_codes

如果想要在本地生成json格式报告,则增加如下参数

ini 复制代码
-Dsonar.report.export.path=report.json \
-Dsonar.analysis.mode=preview

扫描器制作

json 格式转换

基于以上内容,关键是把扫描结果转化为极狐GitLab旗舰版所识别的json格式。

json 样本

SonarQube扫描结果issue样本

python 复制代码
{
    "key": "AX-Gc4tIjhpt-OIbVVk0",
    "component": "my:fuck:fuck/dvwa/includes/DBMS/MySQL.php",
    "line": 70,
    "startLine": 70,
    "endLine": 70,
    "message": "Extract this nested ternary operation into an independent statement.",
    "severity": "MAJOR",
    "rule": "php:S3358",
    "status": "OPEN",
    "isNew": False,
    "creationDate": "2022-03-14T11:22:52+0800"
}

极狐GitLab报告展现issue样本

json 复制代码
{
  "category": "test",
  "message": "这个问题不怎么严重",
  "cve": "python-webhook/MicroService/Service.py:960662f9bd521d32692b07bd8d5b10538924c23c37cec891847f40e436c5c2f:B104",
  "severity": "Medium",
  "confidence": "Medium",
  "scanner": {
    "id": "test",
    "name": "test"
  },
  "location": {
    "file": "python-webhook/MicroService/Service.py",
    "start_line": 26,
    "end_line": 28
  },
  "identifiers": [
    {
      "type": "bandit_test_id",
      "name": "Bandit Test ID B104",
      "value": "B104",
      "url": "https://bandit.readthedocs.io/en/latest/plugins/b104_hardcoded_bind_all_interfaces.htl"
    }
  ]
}

转换器 converter.py

转码程序采用python,编写converter.py文件,内容如下:

kotlin 复制代码
# coding=utf-8
# Copyright 2022 Xuefeng Yin, All Rights Reserved

from datetime import datetime
import json
import hashlib

f = open(".scannerwork/report.json", "r")

report = json.loads(f.read())
issues = report.get("issues")


# {u'INFO': 50, u'BLOCKER': 3, u'MAJOR': 5724, u'CRITICAL': 1089, u'MINOR': 1103}
severitys_mapper = {
    "INFO": "info",
    "BLOCKER":"Unknown",
    "MAJOR":"High",
    "CRITICAL":"Critical",
    "MINOR":"Low",
}


# = issue.get("")
def conv(issue):

    component = issue.get("component")
    startLine = issue.get("startLine")
    endLine = issue.get("endLine")
    message = issue.get("message")
    severity = issue.get("severity")
    rule = issue.get("rule")

  	# "": ,
    ret = {
        "category": "sast",
        "message": message,
        "cve": "",
        "severity": severitys_mapper.get(severity, "Unknown"),
        "confidence": severitys_mapper.get(severity, "Unknown"),
        "scanner": {
            "id": "sonarqube",
            "name": "sonarqube"
        },
        "location": {
            "file": component.split(":")[-1],
            "start_line": startLine,
            "end_line": endLine
        },
        "identifiers": [
            {
                "type": rule,
                "name": rule,
                "value": rule,
                "url": ""
            }
        ]
    }

    id = hashlib.sha256(json.dumps(ret, sort_keys=True)).hexdigest()
    ret["id"] = id

    return ret



dateTimeObj = datetime.now()
timeStr = dateTimeObj.strftime("%Y-%m-%dT%H:%M:%S")

gl_sast_report = {
  "version": "3.0.0",
  "vulnerabilities": [],
  "remediations": [],
  "scan": {
    "scanner": {
      "id": "sonarqube",
      "name": "SonarQube",
      "url": "https://docs.sonarqube.org/",
      "vendor": {
        "name": "GitLab"
      },
      "version": "1.7.0"
    },
    "type": "sast",
    "start_time": timeStr,
    "end_time": timeStr,
    "status": "success"
  }
}



for i, issue in enumerate(issues[:]):
    #print("Issue No. %s ---------------------" % i)
    #print("SonarQube: %s" % issue)
    issue_gitlab = conv(issue)
    #print("GitLab: %s" % issue_gitlab)
    gl_sast_report["vulnerabilities"].append(issue_gitlab)


gl_sast_report_file = open("gl-sast-report.json", "w")
gl_sast_report_file.write(json.dumps(gl_sast_report, indent=4, sort_keys=True))
gl_sast_report_file.close()

scan.sh

基于环境变量进行扫描

bash 复制代码
pwd
/sonar-scanner-4.7.0.2747-linux/bin/sonar-scanner -Dsonar.host.url=$sonar_host_url -Dsonar.login=$sonar_login -Dsonar.projectKey=my:test -Dsonar.sources=. -Dsonar.report.export.path=report.json -Dsonar.analysis.mode=preview
ls -l
ls -l .scannerwork
python /sonar-scanner-4.7.0.2747-linux/bin/converter.py
ls -l gl-sast-report.json

Dockerfile

  • 基于ubuntu:18.04制作扫描器
  • 默认没有python环境,安装python环境
  • 拷贝提前下载好的sonar-scanner到镜像内
  • 设定默认工作目录
  • 复制转换器
sql 复制代码
from ubuntu:18.04
run apt update -y
run apt install python -y
add sonar-scanner-4.7.0.2747-linux.tar .
workdir ./sonar-scanner-4.7.0.2747-linux/bin
add converter.py .
add scan.sh /

构建与推送

制作好的镜像已经推送到DockerHub中,采用前文使用方法中所说的内容即可实现扫描。

bash 复制代码
docker build -t satomic/sonarscanner:v6 .
docker push satomic/sonarscanner:v6

Findbugs支持

安装插件

Server端安装Findbugs插件

到SonarQube的容器内部

bash 复制代码
docker exec -it sonar bash

在插件路径中下载findbugs的jar包

bash 复制代码
cd /opt/sonarqube/extensions/plugins
wget https://github.com/spotbugs/sonarfindbugs/releases/download/3.10.0/sonar-findbugs-plugin-3.10.0.jar

然后重启sonar即可

复制代码
docker restart sonar

然后到sonar的UI上检查是否安装成功,出现如下画面即表示可以。

配置Java默认规则

在sonar UI上进行如下配置,这样Java的默认规则就是Findbugs了

Java maven 扫描器制作

因为扫描Java项目需要代码经过编译,所以需要对扫描器配置maven扫描能力。理论上,通过在Dockerfile中添加安装maven包即可,但是失败,因此直接在前述步骤构建出的镜像中操作完后commit出镜像。操作步骤如下:

安装软件包列表

python 复制代码
apt install openjdk-11-jdk-headless -y
apt install maven -y
apt install git
apt install wget
apt install unzip
apt install zip
apt install vim
apt install tree

commit命令

bash 复制代码
docker commit 100fd2c54f0a satomic/sonarscanner:v7-mvn

此时,考虑到参数暴露的问题,仅仅把如下4个参数内置到扫描器内部,因此修改scan.sh文件如下

  • sonar.host.url SonarQube Server地址
  • sonar.login token
  • sonar.report.export.path 本地report.json路径
  • sonar.analysis.mode=preview 本地预览模式
bash 复制代码
pwd
/sonar-scanner-4.7.0.2747-linux/bin/sonar-scanner -X -Dsonar.host.url=$sonar_host_url -Dsonar.login=$sonar_login -Dsonar.report.export.path=report.json -Dsonar.analysis.mode=preview
ls -l
ls -l .scannerwork
python /sonar-scanner-4.7.0.2747-linux/bin/converter.py
ls -l gl-sast-report.json

再更新如上的scan.sh文件,所以Dockerfile更新为

csharp 复制代码
from satomic/sonarscanner:v7-mvn
add scan.sh /

构建出最新镜像

bash 复制代码
docker build -t satomic/sonarscanner:v9-mvn .
docker push satomic/sonarscanner:v9-mvn

使用

.gitlab-ci.yml更新

为了支持Findbug的触发,需要使用如下配置

bash 复制代码
# 正常情况下应该配置在settings设置的环境变量中,而不是如此明文暴露
variables:
  sonar_host_url: http://1.13.160.207:9000
  sonar_login: 333f3410ce3e575d559329e8f3d0a5d4ec8a499d

sonarqube:
  artifacts:
    reports:
      sast:
      - gl-sast-report.json
  script:
  # 通过在此动态生成 sonar-project.properties 配置,而无需侵入源码仓库
  # 同同时可以提高自由度
  - echo -e "sonar.projectKey=JavaProj\nsonar.projectName=JavaProj\nsonar.projectVersion=1.0\nsonar.sourceEncoding=UTF-8\nsonar.language=java\nsonar.sources=.\nsonar.java.binaries=./target/classes\nsonar.language=java\nsonar.ce.javaOpts=-Xmx2560m -Xms853m -XX:+HeapDumpOnOutOfMemoryError" > sonar-project.properties
  # 检查生成的配置是否正常
  - cat sonar-project.properties
  # 创建构建物目录
  - mkdir -p target/classes
  # 此命令是否执行成功依赖mvn配置的合理性,需要实际仓库的研发介入,配合配置的可用性,默认可访问官方mvn库的外网环境问题不大,内网则不太可能编译成功
  - mvn compile
  # 查看编译产物
  - tree target/classes
  # 执行sonar扫描
  - /scan.sh
  # 查看报告生成大小
  - ls -l .scannerwork/report.json
  image:
     name: satomic/sonarscanner:v9-mvn

扫描结果

参考

问题与风险

  • 从SonarQube 7.7版本开始不支持在scanner端直接生成json报告。
  • 7.7之前的版本的报告中也未包含特别详细的信息。

文章转自《极狐GitLab 集成 sonarqube 实践指南》

相关推荐
不念霉运1 天前
2025 Gitee vs. GitLab:全面对比与选择指南
gitee·gitlab
水瓶_bxt1 天前
创建 GitLab Runner 使用CICD自动化部署容器
eureka·自动化·gitlab
黑心的奥利奥3 天前
Docker配置Gitlab-runner实现自动化容器化部署前端项目
docker·自动化·gitlab
wuzuyu3653 天前
在腾讯云上安装gitlab
云计算·gitlab·腾讯云
xiaodaiwang3 天前
OpenEuler 22.03 系统上安装配置gitlab runner
gitlab
TimberWill4 天前
gitlab私服搭建
gitlab
中东大鹅4 天前
访问 gitlab 跳转 0.0.0.0
gitlab
guygg884 天前
配置本地git到gitlab并推送
git·gitlab
大A崛起5 天前
Gitlab-CI实现组件自动推送
ci/cd·gitlab·github
越来越无动于衷5 天前
GitLab 社区版 10.8.4 安装、汉化与使用教程
gitlab