头图

背景

场景

  • 极狐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

以下仓库参考

使用方法

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

SonarQube-CE部署

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

环境变量配置

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

  • sonar_host_urlhttp://1.13.160.207:9000
  • sonar_login333f3410ce3e575d559329e8f3d0a5d4ec8a499d

配置

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

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

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

报告总览
image

特定issue展现
image

文件定位
image

SonarQube说明

SonarQube原理

image

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/postgresql
mkdir -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.conf
sysctl -p

运行测试容器

docker run -d --name sonartest sonarqube:7.6-community

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

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/

删除容器

docker stop sonartest
docker 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 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

基于环境变量进行扫描

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到镜像内
  • 设定默认工作目录
  • 复制转换器
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中,采用前文使用方法中所说的内容即可实现扫描。

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/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上检查是否安装成功,出现如下画面即表示可以。
image

配置Java默认规则

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

Java maven 扫描器制作

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

安装软件包列表

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命令

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=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更新为

from satomic/sonarscanner:v7-mvn
add 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

扫描结果

参考

image

问题与风险

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

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


极狐GitLab
64 声望36 粉丝

极狐(GitLab) 以“核心开放”为原则,面向中国市场,提供开箱即用的开放式一体化安全DevOps平台——极狐GitLab。通过业界领先的优先级管理、安全、风险和合规性功能,实现产品、开发、QA、安全和运维团队间的高效协同...