Docker 安全检查实用程序的实现方法和示例

Docker 安全检查实用程序的实现方法和示例
嘿哈布尔!

在今天的现实中,由于容器化在开发过程中的作用越来越大,确保与容器相关的各个阶段和实体的安全问题并不是最后的问题。 手动执行检查是一项艰巨的任务,因此最好至少采取初始步骤来自动化此过程。

在本文中,我将分享用于实施多个 Docker 安全实用程序的现成脚本,以及有关如何设置小型演示台来测试此过程的说明。 您可以使用这些材料来试验如何组织测试 Dockerfile 图像和指令的安全性的过程。 很明显,每个人的开发和实施基础设施都不同,因此下面我将给出几种可能的选择。

安全检查实用程序

有大量不同的帮助应用程序和脚本可以对 Docker 基础设施的各个方面执行检查。 其中一些已经在之前的文章中描述过(https://habr.com/ru/company/swordfish_security/blog/518758/#docker-security),在本文中,我想重点关注其中三个,它们涵盖了在开发过程中构建的 Docker 镜像的大部分安全要求。 此外,我还将展示如何将这三个实用程序组合到一个管道中以执行安全检查的示例。

哈多林特
https://github.com/hadolint/hadolint

一个相当简单的控制台实用程序,有助于首先评估 Dockerfile 指令的正确性和安全性(例如,仅使用允许的图像注册表或使用 sudo)。

Docker 安全检查实用程序的实现方法和示例

多克
https://github.com/goodwithtech/dockle

一个控制台实用程序,用于图像(或保存的图像 tarball),通过分析图像的层和配置来检查特定图像的正确性和安全性 - 创建了哪些用户,正在使用哪些指令,安装了哪些卷,是否存在空白密码等。 e. 虽然检查次数不是很多,但基于自己的检查和建议 CIS(互联网安全中心)基准 对于码头工人。
Docker 安全检查实用程序的实现方法和示例

特里维
https://github.com/aquasecurity/trivy

此实用程序旨在发现两种类型的漏洞 - 操作系统构建问题(支持 Alpine、RedHat (EL)、CentOS、Debian GNU、Ubuntu)和依赖项问题(Gemfile.lock、Pipfile.lock、composer.lock、package-lock .json、纱线锁、货物锁)。 Trivy既可以扫描仓库中的镜像,也可以扫描本地镜像,也可以基于传输过来的带有Docker镜像的.tar文件进行扫描。

Docker 安全检查实用程序的实现方法和示例

实用程序实施选项

为了在孤立的条件下试用所描述的应用程序,我将提供安装所有实用程序的说明作为简化过程的一部分。

主要思想是演示如何对开发期间创建的 Dockerfile 和 Docker 映像实施自动内容检查。

验证本身包括以下步骤:

  1. 使用 linter 实用程序检查 Dockerfile 指令的正确性和安全性 哈多林特
  2. 检查最终和中间图像的正确性和安全性 - 一个实用程序 多克
  3. 检查基础映像中的常见漏洞 (CVE) 和一些依赖项 - 通过实用程序 特里维

在本文的后面,我将提供三个选项来实现这些步骤:
第一个是使用 GitLab 的示例配置 CI / CD 管道(并描述了引发测试实例的过程)。
第二种是使用 shell 脚本。
第三个是构建 Docker 镜像以扫描 Docker 镜像。
您可以选择最适合您的选项,将其转移到您的基础架构并使其适应您的需求。

所有必要的文件和附加说明也在存储库中: https://github.com/Swordfish-Security/docker_cicd

GitLab CI/CD 集成

在第一个选项中,我们将以 GitLab 存储库系统为例,了解如何实施安全检查。 在这里,我们将完成这些步骤,看看如何从头开始使用 GitLab 设置测试环境,创建扫描进程并运行实用程序来测试测试 Dockerfile 和随机图像 - JuiceShop 应用程序。

安装 GitLab
1.安装Docker:

sudo apt-get update && sudo apt-get install docker.io

2.将当前用户添加到docker组,这样就可以在不使用sudo的情况下使用docker:

sudo addgroup <username> docker

3.找到你的IP:

ip addr

4.在容器中安装并运行GitLab,将hostname中的IP地址替换成你自己的:

docker run --detach 
--hostname 192.168.1.112 
--publish 443:443 --publish 80:80 
--name gitlab 
--restart always 
--volume /srv/gitlab/config:/etc/gitlab 
--volume /srv/gitlab/logs:/var/log/gitlab 
--volume /srv/gitlab/data:/var/opt/gitlab 
gitlab/gitlab-ce:latest

我们正在等待 GitLab 完成所有必要的安装程序(您可以通过日志文件的输出来跟踪该过程:docker logs -f gitlab)。

5.在浏览器中打开你的本地IP,看到一个页面提供修改root用户的密码:
Docker 安全检查实用程序的实现方法和示例
设置新密码并转到 GitLab。

6.创建一个新项目,例如cicd-test并用一个启动文件初始化它 README.md:
Docker 安全检查实用程序的实现方法和示例
7. 现在我们需要安装 GitLab Runner:一个将根据请求运行所有必要操作的代理。
下载最新版本(在本例中,在 Linux 64 位下):

sudo curl -L --output /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64

8. 使其可执行:

sudo chmod +x /usr/local/bin/gitlab-runner

9.为Runner添加OS用户并启动服务:

sudo useradd --comment 'GitLab Runner' --create-home gitlab-runner --shell /bin/bash
sudo gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner
sudo gitlab-runner start

它应该看起来像这样:

local@osboxes:~$ sudo gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner
Runtime platform arch=amd64 os=linux pid=8438 revision=0e5417a3 version=12.0.1
local@osboxes:~$ sudo gitlab-runner start
Runtime platform arch=amd64 os=linux pid=8518 revision=0e5417a3 version=12.0.1

10. 现在我们注册 Runner 以便它可以与我们的 GitLab 实例进行交互。
为此,打开 Settings-CI/CD 页面 (http://OUR_IP_ADDRESS/root/cicd-test/-/settings/ci_cd) 并在 Runners 选项卡上找到 URL 和 Registration token:
Docker 安全检查实用程序的实现方法和示例
11. 通过替换 URL 和注册令牌注册 Runner:

sudo gitlab-runner register 
--non-interactive 
--url "http://<URL>/" 
--registration-token "<Registration Token>" 
--executor "docker" 
--docker-privileged 
--docker-image alpine:latest 
--description "docker-runner" 
--tag-list "docker,privileged" 
--run-untagged="true" 
--locked="false" 
--access-level="not_protected"

结果,我们得到了一个现成的工作 GitLab,我们需要在其中添加指令来启动我们的实用程序。 在此演示中,我们没有应用程序构建和容器化步骤,但在真实环境中,它们将在扫描步骤之前生成图像和 Dockerfile 以供分析。

管道配置

1.将文件添加到存储库 我的docker文件.df (这是我们要测试的测试Dockerfile)和GitLab CI/CD进程配置文件 .gitlab-cicd.yml,其中列出了扫描仪的说明(请注意文件名中的点)。

.yaml 配置文件包含运行三个实用程序(Hadolint、Dockle 和 Trivy)的说明,这些实用程序将解析选定的 Dockerfile 和 DOCKERFILE 变量中指定的图像。 所有必要的文件都可以从存储库中获取: https://github.com/Swordfish-Security/docker_cicd/

摘录自 我的docker文件.df (这是一个带有一组任意指令的抽象文件,只是为了演示该实用程序的工作原理)。 文件直接链接: 我的docker文件.df

mydockerfile.df 的内容

FROM amd64/node:10.16.0-alpine@sha256:f59303fb3248e5d992586c76cc83e1d3700f641cbcd7c0067bc7ad5bb2e5b489 AS tsbuild
COPY package.json .
COPY yarn.lock .
RUN yarn install
COPY lib lib
COPY tsconfig.json tsconfig.json
COPY tsconfig.app.json tsconfig.app.json
RUN yarn build
FROM amd64/ubuntu:18.04@sha256:eb70667a801686f914408558660da753cde27192cd036148e58258819b927395
LABEL maintainer="Rhys Arkins <[email protected]>"
LABEL name="renovate"
...
COPY php.ini /usr/local/etc/php/php.ini
RUN cp -a /tmp/piik/* /var/www/html/
RUN rm -rf /tmp/piwik
RUN chown -R www-data /var/www/html
ADD piwik-cli-setup /piwik-cli-setup
ADD reset.php /var/www/html/
## ENTRYPOINT ##
ADD entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
USER root

配置 YAML 如下所示(文件本身可以从此处的直接链接获取: .gitlab-ci.yml):

.gitlab-ci.yml 的内容

variables:
    DOCKER_HOST: "tcp://docker:2375/"
    DOCKERFILE: "mydockerfile.df" # name of the Dockerfile to analyse   
    DOCKERIMAGE: "bkimminich/juice-shop" # name of the Docker image to analyse
    # DOCKERIMAGE: "knqyf263/cve-2018-11235" # test Docker image with several CRITICAL CVE
    SHOWSTOPPER_PRIORITY: "CRITICAL" # what level of criticality will fail Trivy job
    TRIVYCACHE: "$CI_PROJECT_DIR/.cache" # where to cache Trivy database of vulnerabilities for faster reuse
    ARTIFACT_FOLDER: "$CI_PROJECT_DIR"
 
services:
    - docker:dind # to be able to build docker images inside the Runner
 
stages:
    - scan
    - report
    - publish
 
HadoLint:
    # Basic lint analysis of Dockerfile instructions
    stage: scan
    image: docker:git
 
    after_script:
    - cat $ARTIFACT_FOLDER/hadolint_results.json
 
    script:
    - export VERSION=$(wget -q -O - https://api.github.com/repos/hadolint/hadolint/releases/latest | grep '"tag_name":' | sed -E 's/.*"v([^"]+)".*/1/')
    - wget https://github.com/hadolint/hadolint/releases/download/v${VERSION}/hadolint-Linux-x86_64 && chmod +x hadolint-Linux-x86_64
     
    # NB: hadolint will always exit with 0 exit code
    - ./hadolint-Linux-x86_64 -f json $DOCKERFILE > $ARTIFACT_FOLDER/hadolint_results.json || exit 0
 
    artifacts:
        when: always # return artifacts even after job failure       
        paths:
        - $ARTIFACT_FOLDER/hadolint_results.json
 
Dockle:
    # Analysing best practices about docker image (users permissions, instructions followed when image was built, etc.)
    stage: scan   
    image: docker:git
 
    after_script:
    - cat $ARTIFACT_FOLDER/dockle_results.json
 
    script:
    - export VERSION=$(wget -q -O - https://api.github.com/repos/goodwithtech/dockle/releases/latest | grep '"tag_name":' | sed -E 's/.*"v([^"]+)".*/1/')
    - wget https://github.com/goodwithtech/dockle/releases/download/v${VERSION}/dockle_${VERSION}_Linux-64bit.tar.gz && tar zxf dockle_${VERSION}_Linux-64bit.tar.gz
    - ./dockle --exit-code 1 -f json --output $ARTIFACT_FOLDER/dockle_results.json $DOCKERIMAGE   
     
    artifacts:
        when: always # return artifacts even after job failure       
        paths:
        - $ARTIFACT_FOLDER/dockle_results.json
 
Trivy:
    # Analysing docker image and package dependencies against several CVE bases
    stage: scan   
    image: docker:git
 
    script:
    # getting the latest Trivy
    - apk add rpm
    - export VERSION=$(wget -q -O - https://api.github.com/repos/knqyf263/trivy/releases/latest | grep '"tag_name":' | sed -E 's/.*"v([^"]+)".*/1/')
    - wget https://github.com/knqyf263/trivy/releases/download/v${VERSION}/trivy_${VERSION}_Linux-64bit.tar.gz && tar zxf trivy_${VERSION}_Linux-64bit.tar.gz
     
    # displaying all vulnerabilities w/o failing the build
    - ./trivy -d --cache-dir $TRIVYCACHE -f json -o $ARTIFACT_FOLDER/trivy_results.json --exit-code 0 $DOCKERIMAGE    
    
    # write vulnerabilities info to stdout in human readable format (reading pure json is not fun, eh?). You can remove this if you don't need this.
    - ./trivy -d --cache-dir $TRIVYCACHE --exit-code 0 $DOCKERIMAGE    
 
    # failing the build if the SHOWSTOPPER priority is found
    - ./trivy -d --cache-dir $TRIVYCACHE --exit-code 1 --severity $SHOWSTOPPER_PRIORITY --quiet $DOCKERIMAGE
         
    artifacts:
        when: always # return artifacts even after job failure
        paths:
        - $ARTIFACT_FOLDER/trivy_results.json
 
    cache:
        paths:
        - .cache
 
Report:
    # combining tools outputs into one HTML
    stage: report
    when: always
    image: python:3.5
     
    script:
    - mkdir json
    - cp $ARTIFACT_FOLDER/*.json ./json/
    - pip install json2html
    - wget https://raw.githubusercontent.com/shad0wrunner/docker_cicd/master/convert_json_results.py
    - python ./convert_json_results.py
     
    artifacts:
        paths:
        - results.html

如有必要,您还可以将保存的图像扫描为 .tar 存档(但是,您需要更改 YAML 文件中实用程序的输入参数)

注意:Trivy 需要安装 и 混帐. 否则,它会在扫描基于 RedHat 的图像和获取漏洞数据库更新时产生错误。

2. 将文件添加到仓库后,根据我们配置文件中的说明,GitLab 会自动启动构建和扫描过程。 在 CI/CD → Pipelines 选项卡上,可以看到指令的进度。

因此,我们有四个任务。 其中三个直接参与扫描,最后一个(报告)从分散的文件中收集扫描结果的简单报告。
Docker 安全检查实用程序的实现方法和示例
默认情况下,如果在图像或依赖项中发现严重漏洞,Trivy 将停止执行。 同时,Hadolint在执行代码中总是返回Success,因为它的执行总是有备注,导致构建停止。

根据您的具体要求,您可以配置一个退出代码,以便这些实用程序在检测到某个严重程度的问题时也停止构建过程。 在我们的例子中,只有当 Trivy 检测到我们在 SHOWSTOPPER 变量中指定的严重程度的漏洞时,构建才会停止 .gitlab-ci.yml.
Docker 安全检查实用程序的实现方法和示例

每个实用程序的运行结果可以在每个扫描任务的日志中查看,直接在工件部分的 json 文件中查看,或者在简单的 HTML 报告中查看(更多内容见下文):
Docker 安全检查实用程序的实现方法和示例

3. 为了以更易于阅读的形式呈现效用报告,使用一个小的 Python 脚本将三个 json 文件转换为一个包含缺陷表的 HTML 文件。
此脚本由单独的报告任务启动,其最终工件是带有报告的 HTML 文件。 脚本源也在存储库中,可以根据您的需要、颜色等进行调整。
Docker 安全检查实用程序的实现方法和示例

外壳脚本

第二个选项适用于需要检查不在 CI/CD 系统内的 Docker 镜像,或者需要所有指令以一种可以直接在主机上执行的形式的情况。 这个选项包含在一个现成的 shell 脚本中,可以在干净的虚拟(甚至真实)机器上运行。 该脚本遵循与上面的 gitlab-runner 相同的指令。

要使脚本成功运行,必须在系统上安装 Docker,并且当前用户必须在 docker 组中。

脚本本身可以在这里找到: docker_sec_check.sh

在文件的开头,变量指定应该扫描哪个图像以及什么严重的缺陷会导致 Trivy 实用程序退出并显示指定的错误代码。

在脚本执行期间,所有实用程序将被下载到该目录 docker_工具, 他们的工作成果 - 在目录中 docker_tools/json,带有报告的 HTML 将在文件中 结果.html.

示例脚本输出

~/docker_cicd$ ./docker_sec_check.sh

[+] Setting environment variables
[+] Installing required packages
[+] Preparing necessary directories
[+] Fetching sample Dockerfile
2020-10-20 10:40:00 (45.3 MB/s) - ‘Dockerfile’ saved [8071/8071]
[+] Pulling image to scan
latest: Pulling from bkimminich/juice-shop
[+] Running Hadolint
...
Dockerfile:205 DL3015 Avoid additional packages by specifying `--no-install-recommends`
Dockerfile:248 DL3002 Last USER should not be root
...
[+] Running Dockle
...
WARN    - DKL-DI-0006: Avoid latest tag
        * Avoid 'latest' tag
INFO    - CIS-DI-0005: Enable Content trust for Docker
        * export DOCKER_CONTENT_TRUST=1 before docker pull/build
...
[+] Running Trivy
juice-shop/frontend/package-lock.json
=====================================
Total: 3 (UNKNOWN: 0, LOW: 1, MEDIUM: 0, HIGH: 2, CRITICAL: 0)

+---------------------+------------------+----------+---------+-------------------------+
|       LIBRARY       | VULNERABILITY ID | SEVERITY | VERSION |             TITLE       |
+---------------------+------------------+----------+---------+-------------------------+
| object-path         | CVE-2020-15256   | HIGH     | 0.11.4  | Prototype pollution in  |
|                     |                  |          |         | object-path             |
+---------------------+------------------+          +---------+-------------------------+
| tree-kill           | CVE-2019-15599   |          | 1.2.2   | Code Injection          |
+---------------------+------------------+----------+---------+-------------------------+
| webpack-subresource | CVE-2020-15262   | LOW      | 1.4.1   | Unprotected dynamically |
|                     |                  |          |         | loaded chunks           |
+---------------------+------------------+----------+---------+-------------------------+

juice-shop/package-lock.json
============================
Total: 20 (UNKNOWN: 0, LOW: 1, MEDIUM: 6, HIGH: 8, CRITICAL: 5)

...

juice-shop/package-lock.json
============================
Total: 5 (CRITICAL: 5)

...
[+] Removing left-overs
[+] Making the output look pretty
[+] Converting JSON results
[+] Writing results HTML
[+] Clean exit ============================================================
[+] Everything is done. Find the resulting HTML report in results.html

包含所有实用程序的 Docker 映像

作为第三种选择,我编译了两个简单的 Dockerfile 来创建带有安全实用程序的映像。 一个 Dockerfile 将帮助构建一个集合来扫描存储库中的图像,第二个 (Dockerfile_tar) 将构建一个集合来扫描带有图像的 tar 文件。

1. 我们从存储库中获取适当的 Docker 文件和脚本 https://github.com/Swordfish-Security/docker_cicd/tree/master/Dockerfile.
2.运行它进行组装:

docker build -t dscan:image -f docker_security.df .

3. 构建完成后,从镜像中创建一个容器。 同时,我们将我们感兴趣的图像名称传递给 DOCKERIMAGE 环境变量,并将我们要分析的 Dockerfile 从我们的机器挂载到文件中 /码头文件 (请注意,此文件的绝对路径是必需的):

docker run --rm -v $(pwd)/results:/results -v $(pwd)/docker_security.df:/Dockerfile -e DOCKERIMAGE="bkimminich/juice-shop" dscan:image


[+] Setting environment variables
[+] Running Hadolint
/Dockerfile:3 DL3006 Always tag the version of an image explicitly
[+] Running Dockle
WARN    - DKL-DI-0006: Avoid latest tag
        * Avoid 'latest' tag
INFO    - CIS-DI-0005: Enable Content trust for Docker
        * export DOCKER_CONTENT_TRUST=1 before docker pull/build
INFO    - CIS-DI-0006: Add HEALTHCHECK instruction to the container image
        * not found HEALTHCHECK statement
INFO    - DKL-LI-0003: Only put necessary files
        * unnecessary file : juice-shop/node_modules/sqlite3/Dockerfile
        * unnecessary file : juice-shop/node_modules/sqlite3/tools/docker/architecture/linux-arm64/Dockerfile
        * unnecessary file : juice-shop/node_modules/sqlite3/tools/docker/architecture/linux-arm/Dockerfile
[+] Running Trivy
...
juice-shop/package-lock.json
============================
Total: 20 (UNKNOWN: 0, LOW: 1, MEDIUM: 6, HIGH: 8, CRITICAL: 5)
...
[+] Making the output look pretty
[+] Starting the main module ============================================================
[+] Converting JSON results
[+] Writing results HTML
[+] Clean exit ============================================================
[+] Everything is done. Find the resulting HTML report in results.html

结果

我们只介绍了一组基本的 Docker 工件扫描实用程序,我认为它非常有效地涵盖了图像安全要求的很大一部分。 还有许多其他付费和免费工具可以执行相同的检查、绘制漂亮的报告或纯粹在控制台模式下工作、涵盖容器管理系统等。这些工具的概述以及如何集成它们可能稍后会出现。

本文中描述的工具集的积极方面是,它们都是基于开源构建的,您可以对它们和其他类似工具进行试验,以找到完全适合您的要求和基础架构功能的工具。 当然,应该研究所有发现的漏洞在特定条件下的适用性,但这是未来一篇大文章的主题。

我希望这些说明、脚本和实用程序对您有所帮助,并成为在容器化领域创建更安全基础架构的起点。

来源: habr.com

添加评论