当前位置:首页>java>GitLab CI/CD 全流程实操:代码静态扫描、Docker 构建、镜像安全扫描与部署(安全合规篇)

GitLab CI/CD 全流程实操:代码静态扫描、Docker 构建、镜像安全扫描与部署(安全合规篇)

  • 2026-10-21 13:27:54
GitLab CI/CD 全流程实操:代码静态扫描、Docker 构建、镜像安全扫描与部署(安全合规篇)
运维工作中,CI构建需要对代码进行扫描,确保代码规范和漏洞不会影响应用服务正常运行,这篇文章介绍gitlab CICD 配置静态代码扫描和镜像扫描方法,助力高质量代码生成,同时满足安全合规要求。

一、构建部署逻辑
文章基于上篇Gitlab CICD流水线搭建,实现下面5个步骤(侧重安全扫描过程)
阶段1:代码安全扫描(使用Semgrep,可自定义扫描规则)
阶段2:安装Python/Flask依赖,验证依赖可用性
阶段3:构建Docker镜像
阶段4:扫描Docker镜像安全漏洞(trivy工具)
阶段5:本地停止旧容器,启动新构建的Docker镜像
整体构建部署逻辑:使用两个runner 做执行端,构建使用的配置和项目代码全在项目仓库里;代码提交自动触发流水线作业,代码被执行器为docke的runner拉取做Semgrep扫描,结果有生成日志和扫描报告文件,扫描通过后,在执行器为shell的runner上(后面步骤都在这个runner执行),安装构建依赖包,实施构建docker操作,接着进行docker镜像扫描,最后是本地部署,展示结果。

二、GitLabCICD 配置

👉 1、注册GitLab Runner

GitLab Runner 注册执行器为docker,是因为gitlab内置的代码扫描工具Semgrep需要使用它,可以在同一个节点上注册多个runner,方法和结果如下图

👉 2、安装镜像扫描工具(gitlab内置的镜像扫描工具就是基于trivy命令封装的)

# 下载 trivy 命令并安装curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/binsu - gitlab-runner -c "trivy --version"# 拉取最新的漏洞库trivy image mirror.gcr.io/aquasec/trivy-db:2

👉 3、提前拉取代码扫描镜像文件

# 提前拉取镜像,避免第一次构建拉取时间过长docker pull registry.gitlab.com/security-products/semgrep:6docker pull registry.gitlab.com/security-products/container-scanning:8docker images #查看拉取情况

👉 4、配置代码和ci工具文件

代码app.py文件,设定几个代码bug
from flask import Flask# 新增:导入request用于获取用户输入(为XSS漏洞做准备)from flask import requestapp = Flask(__name__)# Bug 1:硬编码敏感API密钥(Semgrep高风险漏洞,关键字「secret」「key」触发扫描)# 符合Semgrep内置规则 python.security.secrets.hardcoded-api-keyHARDCODED_SECRET_KEY = "flask_secret_123456_unsafe"  # 硬编码敏感数据app.config['SECRET_KEY'] = HARDCODED_SECRET_KEY  # 有效使用,确保Semgrep能识别@app.route('/')def hello():    # 后续测试可修改此内容,验证流程闭环    return "Hello Flask! 容器部署成功~ v2 "@app.route('/greet')def greet():    user_name = request.args.get('name', 'Guest')    return f"Hello, {user_name}! 欢迎访问~" if __name__ == '__main__':    # Bug 2:Flask开启调试模式(Semgrep中风险漏洞)    # 符合Semgrep内置规则 python.flask.security.debug-mode.enabled    app.run(host='0.0.0.0', port=5000, debug=True)
CI文件 .gitlab-ci.yml
# ======================================================# GitLab CI/CD 配置文件# 用途:Python Flask应用的安全扫描与部署流水线# 包含阶段:代码扫描 → 依赖安装 → 镜像构建 → 镜像扫描 → 部署# ======================================================# ==================== 模板引入 ====================# 引入GitLab官方安全扫描模板,简化配置include:  # SAST:静态应用安全测试,自动扫描代码中的安全漏洞  - template: Security/SAST.gitlab-ci.yml  # 容器扫描:扫描Docker镜像中的安全漏洞  - template: Security/Container-Scanning.gitlab-ci.yml# ==================== 流水线阶段定义 ====================# 定义全流程五个阶段,按顺序执行stages:  - code-scan    # 阶段1:代码安全扫描(使用Semgrep)  - install      # 阶段2:安装Python/Flask依赖,验证依赖可用性  - build        # 阶段3:验证应用语法,构建Docker镜像  - image-scan   # 阶段4:扫描Docker镜像安全漏洞  - deploy       # 阶段5:本地停止旧容器,启动新构建的Docker镜像# ==================== 阶段1:代码安全扫描 ====================# 使用GitLab SAST功能进行静态代码安全分析sast:  stage: code-scan        # 绑定到code-scan阶段  tags:    - scanning            # 使用带有scanning标签的Runner执行  # SAST扫描配置变量  variables:    # 指定使用Semgrep作为默认分析器    SAST_DEFAULT_ANALYZERS: "semgrep"    # 使用官方安全规则集:p/security(通用安全)+ p/python(Python专用)    SAST_SEMGREP_RULESETS: "p/security,p/python"    # 排除不需要扫描的目录    SAST_EXCLUDED_PATHS: "vendor/, node_modules/, .git/"    # 引入自定义Semgrep规则文件(关键:增强扫描能力)    SAST_SEMGREP_ADDITIONAL_RULES: "./semgrep-rules/custom-python-rules.yml"  # 扫描前执行的脚本,主要用于调试和验证  before_script:    # 查看项目目录结构,确认文件存在    - echo "=== 列出Docker容器内项目目录文件 ==="    - ls -la /builds/root/app-demo-src/    # 确认自定义规则文件存在    - echo "=== 列出semgrep-rules文件夹内文件 ==="    - ls -la /builds/root/app-demo-src/semgrep-rules/    # 验证自定义规则文件是否存在    - echo "=== 验证自定义规则文件是否存在 ==="    - test -f /builds/root/app-demo-src/semgrep-rules/custom-python-rules.yml && echo "自定义规则文件存在" || echo "自定义规则文件不存在"    # 新增:手动执行Semgrep扫描,用于调试和详细日志输出    - echo "=== 手动执行Semgrep原生扫描 ==="    - semgrep scan --config /builds/root/app-demo-src/semgrep-rules/custom-python-rules.yml --verbose /builds/root/app-demo-src/app.py# ==================== 阶段2:安装依赖 ====================# 安装Python和Flask项目所需的依赖包install_flask_deps:  stage: install          # 绑定到install阶段  tags:    - docker              # 使用带有docker标签的Runner执行  # 缓存配置:缓存pip下载的包,提升后续运行效率  cache:    paths:      - ~/.cache/pip/  # 主要执行脚本  script:    # 升级pip到最新版本    - echo "===== 开始升级pip ====="    - pip3 install --upgrade pip --user    # 安装requirements.txt中指定的所有依赖    - echo "===== 开始安装Flask项目依赖 ====="    - pip3 install --no-cache-dir -r requirements.txt --user    # 验证Flask依赖是否安装成功    - echo "===== 验证依赖安装结果 ====="    - python3 -c "import flask; print('Flask依赖安装成功,版本:', flask.__version__)"  # 工件(artifacts)配置:保存安装成功的依赖信息  artifacts:    paths:      - requirements.txt       # 保存依赖列表文件    when: on_success          # 仅当任务成功时保存    expire_in: 1 hour         # 工件保存1小时后过期# ==================== 阶段3:构建镜像 ====================# 构建应用的Docker镜像build_flask_docker:  stage: build              # 绑定到build阶段  tags: [docker]           # 使用带有docker标签的Runner执行  dependencies: [install_flask_deps]  # 依赖install_flask_deps阶段成功完成  script:    # 验证Python代码语法正确性    - echo "===== 验证 app.py 语法正确性 ====="    - python3 -m py_compile app.py  # 语法检查,报错则终止CI    # 构建Docker镜像并标记为latest    - echo "===== 构建本地 Docker 镜像(flask-app:latest) ====="    - docker build -t flask-app:latest .    # 查看构建成功的镜像信息    - echo "===== 本地镜像构建成功,查看镜像信息 ====="    - docker images | grep flask-app  # 打印本地镜像信息  # 不允许失败:如果构建失败,整个流水线应该停止  allow_failure: false# ==================== 阶段4:镜像安全扫描 ====================# 使用Trivy扫描Docker镜像中的安全漏洞container_scanning:  stage: image-scan         # 绑定到image-scan阶段  tags: [docker]           # 使用带有docker标签的Runner执行  dependencies: [build_flask_docker]  # 依赖build_flask_docker阶段成功完成  # 不允许失败:如果扫描失败,整个流水线应该停止  allow_failure: false  script:    # 验证环境工具版本    - echo "===== 1. 验证环境与 Docker 守护进程 ====="    - docker --version     # 验证Docker客户端可用    - trivy --version      # 验证Trivy工具可用    # 执行容器安全扫描,生成GitLab标准格式的JSON报告    - echo "===== 2. 生成漏洞扫描报告(GitLab 标准格式) ====="    - trivy image --skip-version-check --skip-db-update --severity CRITICAL,HIGH --format json -o gl-container-scanning-report.json flask-app:latest    # 生成软件物料清单(SBOM),CycloneDX格式    - echo "===== 3. 生成 SBOM 报告(CycloneDX 格式) ====="    - trivy image --skip-version-check --skip-db-update --format cyclonedx -o gl-sbom-flask-app.cdx flask-app:latest    # 验证报告文件已正确生成    - echo "===== 4. 验证报告文件已生成 ====="    - ls -l# ==================== 阶段5:部署应用 ====================# 在本地部署Flask应用deploy_flask_local:  stage: deploy             # 绑定到deploy阶段  tags: [docker]           # 使用带有docker标签的Runner执行  dependencies: [build_flask_docker]  # 依赖build_flask_docker阶段成功完成  # 限制部署条件:仅在master或main分支执行  only:    - main  script:    # 定义容器配置变量    - echo "===== 开始本地部署Flask应用 ====="    - CONTAINER_NAME="flask-container"  # 容器名称    - PORT_MAPPING="5000:5000"          # 端口映射:主机端口:容器端口    # 停止并删除已存在的同名容器(避免端口冲突)    - echo "===== 停止并删除已存在的同名容器(避免端口占用/冲突) ====="    - docker stop ${CONTAINER_NAME} || true  # 如果容器不存在,忽略错误    - docker rm ${CONTAINER_NAME} || true    # 如果容器不存在,忽略错误    # 使用构建的镜像启动新容器    - echo "===== 本地启动Docker容器 ====="    - docker run -d --name ${CONTAINER_NAME} -p ${PORT_MAPPING} flask-app:latest    # 验证容器运行状态    - echo "===== 验证容器运行状态 ====="    - docker ps | grep ${CONTAINER_NAME}  # 查找运行中的容器    # 打印访问地址信息    - echo "===== 打印应用访问地址 ====="    - LOCAL_IP=$(hostname -I | awk '{print $1}')  # 获取本机局域网IP    - echo "======================================"    - echo "Flask应用本地部署成功!"    - echo "本地访问地址:http://localhost:5000"    - echo "局域网访问地址:http://${LOCAL_IP}:5000"    - echo "容器名称:${CONTAINER_NAME}"    - echo "镜像名称:flask-app:latest"
代码扫描规则的文件 semgrep-rules/custom-python-rules.yml(可根据实际情况自定义)
rules:- id: insecure-eval-use  patterns:  - pattern: eval($VAR)  - pattern-not: eval("...")  fix: secure_eval($VAR)  message: Calling 'eval' with user input  languages: [python]  severity: MEDIUM- id: custom-flask-hardcoded-secret  pattern: HARDCODED_SECRET_KEY = "flask_secret_123456_unsafe"  message: "自定义规则:检测到Flask硬编码敏感密钥,存在信息泄露风险(对应Bandit B105)"  severity: LOW  languages: [python]  metadata:    category: security    cwe: "CWE-798: Use of Hard-coded Credentials"    bandit_id: B105# 规则3:自定义Flask绑定所有接口扫描(已包含pattern,符合schema规范)- id: custom-flask-bind-all-interfaces  pattern: app.run(host='0.0.0.0', port=5000, debug=True)  message: "自定义规则:检测到Flask绑定所有网络接口,可能导致未授权访问(对应Bandit B104)"  severity: MEDIUM  languages: [python]  metadata:    category: security    cwe: "CWE-605: Multiple Binds to the Same Port"    bandit_id: B104
构建镜像Dockerfile文件
# 基础镜像FROM python:3.9-slim# 工作目录WORKDIR /app# 复制依赖文件并安装COPY requirements.txt .RUN pip install --no-cache-dir -r requirements.txt# 复制应用源码COPY app.py .# 暴露端口EXPOSE 5000# 启动命令CMD ["python", "app.py"]
确保依赖的命令和配置文件验证正常后,可触发流水线

三、触发构建
Gitlab CICD 5个阶段可以在流水看到分成5个作业执行,下面是具体过程截图
代码扫描日志,正常扫描到代码bug
浏览器验证:浏览器展示是app.py的代码信息
流水线构建的结果,有日志和扫描报告,可实现问题追溯、复盘、定位和持续优化
结果说明:设定的几个bug是可以配置是否拦截中断构建过程的,这里为了测试bug扫描的过程,设定允许失败继续构建的,代码bug不影响构建和结果,只是有潜在漏洞风险。
附镜像扫描报告

四、总结
  1. 左移安全,提前拦截风险,大幅降低线上整改成本
  2. 全链路层层验证,提升部署成功率,减少线上故障
  3. 全程自动化,减少人工干预,提升研发部署效率
  4. 标准化流程,保障环境一致性,解决「本地能跑,线上不能跑」的痛点
  5. 可追溯、可复盘,便于问题定位和持续优化
核心优点是 全链路、自动化、标准化、安全化,既实现了从代码到容器的全流程验证和部署,又提前拦截了安全风险和运行故障,大幅提升了部署成功率和研发效率,同时保障了环境一致性和业务连续性。
以上,看到这里,觉得还不错,顺手点个赞吧,谢谢你看我的文章,下次见。

最新文章

随机文章