株式会社ネットワールドのエンジニアがお届けする技術情報ブログです。
各製品のエキスパートたちが旬なトピックをご紹介します。

GitとCI/CDに関する知識ゼロのSEが、GitLabでコンテナスキャンを実装するだけ

皆様こんにちは。SEの小池と申します。

前回GitLabのコンテナレジストリにイメージを登録する話を記載したので、今回はそれに関連してGitLabのAuto DevOpsの1つであるコンテナスキャンを実装します。
なお、前回のブログの内容と重複する部分がございます。
前回のブログの続きでご覧いただいている方は、適宜読み飛ばしていただければと存じます。

本記事の対象の方

  • GitLabのAuto DevOpsでコンテナスキャンを使ってみたい方。

今回のブログのゴール

このブログのゴールはこちらです。

今回のゴール
  • GitLabのAuto DevOpsを使ってコンテナスキャンを実装する。

このブログをお読みいただくにあたっての事前ご連絡事項

  • 本記事はオンプレミス版 (Self-Managed) のGitLab Enterprise Edition 14.10.4-ee (Ultimate) における仕様をベースに記載しております。それ以外のエディションやバージョンではこの記事に記載の通りではない可能性がございます。
  • 本記事ではGitLabのAuto DevOps 及び そのコンテナスキャンがどういったものなのかの説明は記載しておりません。これについては恐れ入りますがGitLab Docs (こちら) をご参照ください。
  • 本記事のGitLabのGUIは日本語にローカライズした状態で掲載しております。それ以外の言語をご利用の方は適宜読み替えてください。

事前準備

GitLabの Auto DevOps でコンテナスキャンを使うにあたっては以下いずれかのRunnnerが必要になります。

  • エクゼキューターが docker のRunner
  • エクゼキューターが kubernetes のRunner
  • エクゼキューターが shell 且つ Docker Engine がインストール済のRunner

お手元のGitLab環境にこれを満たすRunnerがない場合は、GitLab Docsや弊社の過去のブログをご参照いただき、事前にご準備をお願い致します。
Runnerのインストールに関するGitLab Docs : Install GitLab Runner | GitLab
弊社の技術ブログ : GitとCI/CDに関する知識ゼロのSEが、GitLabでRunner (Docker) を登録するだけの話

また、今回ご紹介する手順はGitLabのコンテナレジストリを有効にする必要があります。
オンプレミス版 (Self-Managed) の場合に限りデフォルトでは有効になっていない場合があります。
その場合はGitLab Docs もしくは 弊社の技術ブログをご参照いただき、コンテナレジストリを有効にしてください。
コンテナレジストリに関するGitLab Docs : GitLab Container Registry | GitLab
弊社の技術ブログ : GitとCI/CDに関する知識ゼロのSEが、GitLabでCI/CDパイプラインを使ってコンテナレジストリにイメージを登録する話

コンテナスキャンの実装

Step1 : CI/CDパイプラインを使って任意のイメージをビルドする

まずはスキャン対象となるイメージを、CI/CDパイプラインを使ってビルドする処理を作成します。

このブログでは例として簡易なイメージをビルドしてみます。
既にお手元のGitLabで、スキャンしたいイメージをCI/CDパイプラインでビルドしていらっしゃる場合は、イメージ名とタグの設定だけこの章の設定値に変更してください。
(Auto DevOpsのコンテナスキャンではデフォルトだとスキャン対象のイメージ名とタグが変数を利用した固定値です。カスタムのイメージ名とタグを使用すると、このブログに書いた手順だとスキャンが失敗してしまいます。)

まず、今回の検証のために空のプロジェクト (Blank Project) を作成します。
空のプロジェクト (Blank Project) の作成方法についてはこちらのブログをご参照ください。

作成した空のプロジェクト (Blank Project) のmainのリポジトリのルートに以下2つのファイルを作成します。

  • Dockerfile
  • .gitlab-ci.yml

Dockerfileの内容はご自由に設定なさってください。
以下に (超シンプルな) サンプルを載せますので、こちらをコピーしていただいても大丈夫です。

FROM ubuntu:18.04
RUN apt-get install

.gitlab-ci.ymlの内容のサンプルは以下の通りです。
そのままコピーしていただいても、アレンジしていただいても、どちらでも大丈夫です。
ただし、アレンジなさる場合でも、ビルド時のイメージ名とタグの設定は以下のサンプルの設定値を用いてください。

stages:
  - build

Build-SmapleJob:
  stage: build
  image: docker:20.10.17
  services:
    - docker:20.10.17-dind
  variables:
    # ビルドイメージの変数作成
    IMAGE: $CI_REGISTRY_IMAGE/$CI_COMMIT_REF_SLUG:$CI_COMMIT_SHA
  script:
    # ビルド
    - docker build --tag $IMAGE .

変更をコミットし、mainブランチにDockerfile.gitlab-ci.ymlをマージしてください。
なおこの際、マージリクエストの作成は任意です。

この時点でパイプラインが成功しているかどうかを確認します。
対象プロジェクトで [CI/CD] > [パイプライン] を開きます。
最新のパイプラインのステータスが緑色で [成功] と表示されていることを確認します。

以上がスキャン対象となるイメージのビルド処理の作成でした。

Step2 : ビルドしたイメージをAuto DevOpsを使ってスキャンする

前の章では、CI/CDパイプラインでイメージをビルドする処理を作成しました。
この章では、そのイメージに対してGitLabのAuto DevOpsを使ってコンテナスキャンを実行します。

リポジトリに追加した.gitlab-ci.ymlファイルを使って、コンテナスキャンを有効にします。
既存の.gitlab-ci.ymlに、testステージを追加します。
(以下のサンプルの場合は3行目を追加しています。)

stages:
  - build
  - test

Build-SmapleJob:
  stage: build
  image: docker:20.10.17
  services:
    - docker:20.10.17-dind
  variables:
    # ビルドイメージの変数作成
    IMAGE: $CI_REGISTRY_IMAGE/$CI_COMMIT_REF_SLUG:$CI_COMMIT_SHA
  script:
    # ビルド
    - docker build --tag $IMAGE .

次に同じく.gitlab-ci.ymlに対して、キーワードincludeを使ってコンテナスキャンのテンプレートを指定します。
(以下のサンプルの場合は最後の2行を追加しています。)

stages:
  - build
  - test

Build-SmapleJob:
  stage: build
  image: docker:20.10.17
  services:
    - docker:20.10.17-dind
  variables:
    # ビルドイメージの変数作成
    IMAGE: $CI_REGISTRY_IMAGE/$CI_COMMIT_REF_SLUG:$CI_COMMIT_SHA
  script:
    # ビルド
    - docker build --tag $IMAGE .

include:
   - template: Security/Container-Scanning.gitlab-ci.yml

変更をコミットし、mainブランチに.gitlab-ci.ymlをマージしてください。
なおこの際、マージリクエストの作成は任意です。

実はコミットの時点でスキャンは実行されています。
対象プロジェクトで [CI/CD] > [パイプライン] を開きます。
最新のパイプラインのステータスが緑色で [成功] となっていることを確認しつつ、その [成功] ボタンをクリックします。

BuildTestの2つのステージがあります。
Testステージにあるcontainer_scanningジョブが正常終了していること (緑色のチェックマークが表示されていること) を確認します 。

同ページの [セキュリティ] タブが存在することを確認します。

なお、脆弱性が0件だった場合でも、コンテナスキャンが正常に完了した場合は [セキュリティ] タブは出現します。もし [セキュリティ] タブが存在しなかった場合は、container_scanningジョブ名をクリックし、ログをご確認ください。

以上がビルドしたイメージをAuto DevOpsを使ってスキャンする手順でした。

コンテナスキャンの結果確認

コンテナスキャンの結果はいくつかのメニューから確認することができます。

CI/CDパイプラインの結果から

CI/CDパイプラインの結果から簡単にコンテナスキャンの結果を確認することができます。
まず、CI/CDパイプラインの結果一覧において、コンテナスキャンを実行したパイプラインの行の右側の点が3つ連なっているボタンから、スキャン結果のアーティファクトをjson形式でダウンロードすることができます。

また、スキャンのジョブが実行されたCI/CDパイプラインの詳細画面から [セキュリティ] タブを開くことでも確認ができます。

この画面では任意の脆弱性をDismiss (無視) することも可能です。

他にも、実際のスキャンジョブのログから検知結果を確認することができます。
CI/CDパイプラインの詳細画面でcontainer_scanningジョブをクリックすると、下図のようにジョブのログが確認できます。この中にコンテナスキャンのログも載っています。

脆弱性レポートから

対象のプロジェクトの [セキュリティとコンプライアンス] > [Vulnerability report] から、今回のスキャン結果を確認することができます。
この画面では、今回のコンテナスキャン (及び 依存関係のスキャン) の結果だけでなく、SASTやDASTなど他のAuto DevOpsを用いて検出した脆弱性を一括で閲覧することができます。

コンテナスキャンの結果だけを表示したい場合は、下図の通り [Tool] > [コンテナ―スキャン] を選択します。

他、この画面では検知した脆弱性のステータスを変更することができます。

マージリクエスト画面から

マージリクエストを作成している場合は、その画面の履歴からも確認できます。
下図のように、マージリクエストの履歴内にスキャン結果が自動で表示され、[展開] をクリックするとその画面上で一覧を確認することができます。

また、マージリクエストの画面からコンテナスキャン結果のアーティファクトをjson形式でダウンロードできます。

【余談】筆者のトライアンドエラー

ブログを書くにあたり筆者が遭遇したエラーの回避策について記載致します。
同じエラーが出た際の参考になれば幸いです。

変数CI_REGISTRY_IMAGEが参照できないよエラー

GitLabのコンテナレジストリが無効のまま、CI/CDパイプラインでコンテナスキャンを実行しようとして出たエラーです。

[ERROR] [2022-07-21 01:34:03 +0000] []  ▶  Environment variable `CI_REGISTRY_IMAGE` was not found and is required for execution

Auto DevOpsのコンテナスキャンは、デフォルトではスキャン対象のイメージを変数CI_REGISTRY_IMAGEを使って特定しているのですが、コンテナレジストリを有効にしないとそもそもこの変数自体が使えない仕様なので出たエラーです。
コンテナレジストリを有効にするとこのエラーは解消されました。

Dockerのソケットファイルにアクセスできないよエラー

このエラーはコンテナスキャンを実装したCI/CDパイプラインをコンテナで起動しているRunnerで実行した際に出たエラーです。

[ERROR] [2022-07-20 08:51:33 +0000] [] ▶ 2022-07-20T08:51:33.989Z FATAL scan error: unable to initialize a scanner: unable to initialize a docker scanner: <エラー件数> errors occurred:
unable to inspect the image (<コンテナレジストリURL>:<ポート番号>/<GroupとPJのID>/<ブランチ名>:<変数で指定された40文字>): Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?

Runnerでdockerコマンドを使うにあたり、いくつかの方法があります。
参考 : Use Docker to build Docker images | GitLab Docs

当方の検証環境は上記の参考サイトのうち、Runner (エクゼキューターdocker) をコンテナで起動させて、ソケットファイルをバインドする方法を設定・・・しているつもりだったのですが、実はソケットファイルのバインドを忘れていたために発生したエラーです。
そのため、Runnerの設定ファイルconfig.tomlvolumesに以下を追記したところ、解消致しました。

    volumes = ["/var/run/docker.sock:/var/run/docker.sock"]

以下に設定例の画面を載せますのでご参考までに。

Dockerのソケットファイルのパーミッションが足りないよエラー

このエラーは、先述したエラーの対応後にCI/CDパイプラインを試行した際に出たエラーです。

[ERROR] [2022-07-20 08:13:15 +0000] [] ▶ 2022-07-20T08:13:15.363Z FATAL scan error: unable to initialize a scanner: unable to initialize a docker scanner: <エラー個数> errors occurred:
unable to inspect the image (<コンテナレジストリURL>:<ポート番号>/<GroupとPJのID>/<ブランチ名>:<変数で指定された40文字>): Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get "http://%2Fvar%2Frun%2Fdocker.sock/v1.24/images/<コンテナレジストリURL>:<ポート番号>/<GroupとPJのID>/<ブランチ名>:<変数で指定された40文字>/json": dial unix /var/run/docker.sock: connect: permission denied

当方の検証環境はRunner (エクゼキューターdocker) をコンテナで起動させていて、CI/CDのジョブでdockerコマンドを実行するためにDockerホストのソケットファイルをバインドする方法 (Docker in Dockerじゃない方法) を使っているのですが、どうもこのケースでコンテナスキャンのアナライザーを使おうとすると出るエラーっぽいです。。。

一応回避策として、以下いずれかが挙げられる模様です。

  • ユーザーgitlab-runnerをグループdockerに入れる。
  • /var/run/docker.sockのパーミッションを666に変更する。
参考 : Permission denied when executing docker commands from Gitlab CI/CD pipeline (#3492) · Issues · GitLab.org / gitlab-runner · GitLab

ただ、上記の回避策のうち、当方の環境では1つ目では回避できなかったので、2つ目の方法で回避致しました。

最後に

この度はGitもCI/CDもよくわかっていないど素人SEによるGitLab検証ブログをお読みいただき、誠にありがとうございます。
このブログの目標は以下のとおりでしたが、皆さまはいかがでしたでしょうか。


今回のゴール
  • GitLabのAuto DevOpsを使ってコンテナスキャンを実装する。

以前Auto DevOpsを使ったSASTの実装方法を記載致しましたが、今回のコンテナスキャンも実装がとても容易です。
また、マージリクエストにおいてコミットと同時にCI/CDパイプラインでコンテナスキャンを実行することで、ターゲットブランチにマージする前に脆弱性の有無を確認することができます。
もしCI/CDパイプラインでイメージをビルドする処理を入れていらっしゃる方は、Auto DevOpsのコンテナスキャンもお試しいただければと存じます。


GitLabに関するお問い合わせは、以下のフォームからお願い致します。
GitLab製品 お問い合わせ

今までのGitLabの検証ブログはこちらです。
GitLab カテゴリーの記事一覧 - ネットワールド らぼ

GitLab操作デモ動画 (基本編) を作ってみました。(音声の録音は自宅でiPhoneのボイスメモ使うという超低クオリティですが…。)
つたない内容ではありますが、ご興味がおありでしたら是非ご視聴いただければと存じます。


www.youtube.com