写点什么

极狐 GitLab 集成 sonarqube 实践指南

作者:极狐GitLab
  • 2023-12-26
    上海
  • 本文字数:5989 字

    阅读完需:约 20 分钟

极狐GitLab 集成 sonarqube 实践指南

背景

场景


  • 极狐 GitLab 旗舰版已经具备扫描能力,但是某些场景下扫描能力偏弱,所以希望集成第三方扫描工具作为补充。但是,单纯的通过 Runner 调用的方式,只是松散的集成,无法真正形成「深度集成」的体验。

  • 若可以「在极狐 GitLab 的漏洞报告中展现第三方扫描工具的扫描结果」,那么,对于集成的体验则会提升很多。本文即针对此目的进行展开说明。


外部扫描器选型 SonarQube-CE


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


快速入门


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


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
复制代码


以下仓库参考


  • Java: https://jihudemo.online/demo/standard-demo/hello-order/hairou-app-downsteam/-/security/vulnerability_report

  • cpp: https://jihudemo.online/demo/standard-demo/hello-order/cpp-example/-/security/vulnerability_report

  • Python: https://jihudemo.online/mumutech/dev-backend/bigdata/funny-toolkits/-/security/vulnerability_report

  • PHP: https://jihudemo.online/mumutech/dev-backend/dvwa/-/security/vulnerability_report


使用方法


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

SonarQube-CE 部署


  • 公网环境中已经部署好 SonarQube,可以忽略部署过程,继续往下。

  • 若为私网环境,参考下文部署章节。

环境变量配置


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

  • sonar_host_urlhttp://1.13.160.207:9000

  • sonar_login333f3410ce3e575d559329e8f3d0a5d4ec8a499d

.gitlab-ci.yml配置


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

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

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

报告总览

特定 issue 展现 image

文件定位 image


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 步骤以及遇到的坑进行部署,

创建工作目录


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


创建网络

docker network create sonarqube-network
复制代码

部署 pg

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

创建工作目录

mkdir -p /data/sonarqube_dir
复制代码

修改系统参数

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

运行测试容器

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

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

docker cp sonartest:/opt/sonarqube/conf /data/sonarqube_dirdocker cp sonartest:/opt/sonarqube/data /data/sonarqube_dirdocker cp sonartest:/opt/sonarqube/logs /data/sonarqube_dirdocker cp sonartest:/opt/sonarqube/extensions /data/sonarqube_dir
chmod -R 777 /data/sonarqube_dir/
复制代码

删除容器

docker stop sonartestdocker rm sonartest
复制代码

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

 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 中查看扫描结果。

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

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

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


扫描器制作

json 格式转换

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

json 样本

SonarQube 扫描结果 issue 样本

{    "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 样本

{  "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文件,内容如下:

# coding=utf-8# Copyright 2022 Xuefeng Yin, All Rights Reserved
from datetime import datetimeimport jsonimport 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

基于环境变量进行扫描

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=previewls -lls -l .scannerworkpython /sonar-scanner-4.7.0.2747-linux/bin/converter.pyls -l gl-sast-report.json
复制代码


Dockerfile

  • 基于ubuntu:18.04制作扫描器

  • 默认没有 python 环境,安装 python 环境

  • 拷贝提前下载好的 sonar-scanner 到镜像内

  • 设定默认工作目录

  • 复制转换器

from ubuntu:18.04run apt update -yrun apt install python -yadd sonar-scanner-4.7.0.2747-linux.tar .workdir ./sonar-scanner-4.7.0.2747-linux/binadd converter.py .add scan.sh /
复制代码

构建与推送

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

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


Findbugs 支持

安装插件

Server 端安装 Findbugs 插件

到 SonarQube 的容器内部

docker exec -it sonar bash
复制代码

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

cd /opt/sonarqube/extensions/pluginswget 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 出镜像。操作步骤如下:

安装软件包列表

apt install openjdk-11-jdk-headless -yapt install maven -yapt install gitapt install wgetapt install unzipapt install zipapt install vimapt install tree
复制代码

commit 命令

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 本地预览模式

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=previewls -lls -l .scannerworkpython /sonar-scanner-4.7.0.2747-linux/bin/converter.pyls -l gl-sast-report.json
复制代码

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

from satomic/sonarscanner:v7-mvnadd scan.sh /
复制代码

构建出最新镜像

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


使用

.gitlab-ci.yml更新

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

# 正常情况下应该配置在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

关注

开源开放,人人贡献 2021-05-19 加入

开放式一体化DevOps平台,助力行业高速协同增长!

评论

发布
暂无评论
极狐GitLab 集成 sonarqube 实践指南_极狐GitLab_InfoQ写作社区