Sysdigを用いたAzureパイプラインにおけるコンテナイメージスキャン

By 清水 孝郎 - SEPTEMBER 19, 2022

SHARE:

本文の内容は、2022年9月19日にEduardo Mínguezが投稿したブログ(https://sysdig.com/blog/container-image-scanning-for-azure-pipelines-with-sysdig/)を元に日本語に翻訳・再構成した内容となっております。

Sysdig Secureを使用して、Azure Pipelinesに脆弱性やバッドプラクティスがないかコンテナイメージをスキャンするのは、簡単なプロセスです。この記事では、その方法についてステップバイステップの例を説明していきます。



以下のProof of Contentでは、Azure Pipelinesで sysdig-cli-scanner を活用する方法が紹介されていました。可能ではありますが、Sysdigが公式にサポートしているわけではないので、ドキュメントを確認し、これらの手順を自分の環境に適応させることをお勧めします。
このブログ記事は、2022年4月以降に利用可能な脆弱性スキャナーに焦点を合わせています。レガシースキャナーをご利用の方は、公式ドキュメントをご参照ください。

こちらからパイプラインの定義に直行できます。


Sysdig Secureによるイメージ脆弱性スキャン

イメージスキャンにより、DevOpsチームは、コンテナが本番環境にデプロイされる前、またはイメージが任意のコンテナ・レジストリにプッシュされる前に、パイプラインの初期段階で既知の脆弱性を検出し、コンテナのビルド構成を検証することにより、セキュリティをシフトレフトさせることができます。これにより、問題の迅速な検出と修正、本番環境での脆弱性の回避、クレデンシャル漏洩の回避、本番環境へのデリバリー時間の短縮が可能になり、そのすべてがより安全な方法で行われます。

Sysdigのイメージスキャンプロセスは、ImageConfigのチェック(機密情報の漏洩など)、OSパッケージだけでなくサードパーティパッケージ(java、pythonなど)のチェックなど、さまざまなルールを含むようにカスタマイズ可能なポリシーに基づいています。

Sysdigの脆弱性スキャンは、スキャン手順を実行する場所によってイメージの分類が異なってきます。

  • パイプライン sysdig-cli-scanner ツールで実行されるランタイム段階(開発者のワークステーション内、CI/CDパイプライン内など)の前。
  • ランタイム:実行ノードでイメージが実行され、Sysdigエージェントによってスキャンが実行される場合

Sysdig vulnerabilities UI screenshot
Sysdig 脆弱性のUIスクリーンショット

本記事では、ベストプラクティスとして採用されているAzure Pipelinesを使ったパイプラインステップでのスキャンの実行方法について説明します。

コンテナイメージに対してスキャナーを実行するのは、 sysdig-cli-scanner  ツールに以下のようないくつかのフラグ(詳細は公式ドキュメントを参照)を付けて実行するのと同じくらい簡単です。

SECURE_API_TOKEN=<your-api-token> ./sysdig-cli-scanner --apiurl <sysdig-api-url> <image-name> --policy <my-policy>

イメージはツールを実行するホスト、ラップトップ、パイプラインを実行するコンテナ上でローカルにスキャンされ、スキャン結果のみがSysdig Secureのバックエンドに送信されます。

Azureパイプライン

Azure DevOpsは、コードリポジトリ、レポート、プロジェクト管理、自動ビルド、ラボ管理、テスト、およびリリース管理などのツールをチームに提供します。Azure パイプラインはAzure DevOpsバンドルのコンポーネントで、コードに対するテストの実行、gitリポジトリにコミットがプッシュされたときのコンテナイメージのビルド、コンテナイメージに対する脆弱性スキャンの実行など、CI/CDタスクの実行を自動化するものです。

脆弱性スキャンのためのAzure パイプライン

Azureパイプラインは、YAMLファイルに記述されたタスクの一括を定義し、イベントが発生したときに自動的に実行されます。イベントとは、通常、リンク先のリポジトリに新しいコミットやプルリクエストが発生したときのことです。

Azure pipeline example steps diagram
Azureパイプラインのステップ例図

これにより、自動的にビルドしてレジストリ(Azure Container Registryなど)にイメージをプッシュし、Kubernetesにデプロイすることができます。

このブログ記事の説明に使う例では、Azureパイプラインを起動するGitHubのリポジトリにコミットをプッシュしていきます。そして、パイプラインはプロジェクトをローカルイメージにビルドし、脆弱性をスキャンして、コンテナレジストリに公開します。

Azure pipeline and Sysdig scan procedure diagram
AzureパイプラインとSysdigのスキャン手順図

アプリケーションの例

具体的な例を示すために、GitHub リポジトリに保存されている、 8080/tcp  ポートをリッスンし、リクエストされたパスに基づいた文字列を返す簡単な golang アプリケーションを利用します。

package main
import (
    "fmt"
    "log"
    "net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "I love %s!\n", r.URL.Path[1:])
}
func main() {
    http.HandleFunc("/", handler)
    log.Fatal(http.ListenAndServe(":8080", nil))
}

このシンプルなプログラムを、ビルドしてコンテナ化します。

FROM golang:1.18-alpine as builder
WORKDIR /app
COPY . .
RUN go mod download
RUN go build -o /love
FROM alpine
COPY --from=builder /love /love
EXPOSE 8080
ENTRYPOINT [ "/love" ]

Azure パイプラインに GitHub リポジトリへのアクセス権を与える

Azure は GitHub リポジトリにアクセスし、プロジェクトのビルドとコンテナイメージの生成に必要なコードをダウンロードします。また、パイプラインを構成するタスクが含まれる azure-pipelines.yaml ファイルも同じリポジトリに格納されているので、それを取得することになります。

すでに Azure DevOps アカウントを取得し、プロジェクトを作成していると仮定して、次のステップに従って Azure Pipelines に GitHub リポジトリへのアクセスを許可します:



リポジトリが空の場合、空白の yaml ファイルから始まり、Azure Pipelines のエディターで修正するか、コードを直接 GitHub にプッシュする必要があります。

今回の場合、パイプラインはすでにGitHubのリポジトリにファイルとして作成されているので、そこに表示されているコードは、リポジトリに存在するものです。パイプラインの各ステップについては、後ほど詳しく説明します。

Azure Pipelines にコンテナイメージを公開するためのアクセス権を与える

今回は、コンテナイメージを公開するために quay.io を活用します。

すでに持っていると仮定して:

  • quay.ioのアカウント
  • そして ‘空の’ quay リポジトリ

必要なものは:
  •  ‘robot’ アカウントを作成する
  • そして、Azure パイプラインサービスへの接続を設定します。

では、行っていきます:


connection nameを忘れないようにしましょう。後でレジストリにアクセスする際に使用します。

Azure Pipeline のシークレット変数

 sysdig-cli-scanner  は、スキャン結果を Sysdig アカウントに送信できるようにするために Sysdig API トークンを必要とします。

これは Sysdig アカウントの Settings -> User profile セクションにあり(詳しくは Retrieve the Sysdig API Token を参照)、シークレット変数を用いて暗号化して保存するとよいでしょう。


Sysdig Secure でイメージスキャンするための Azure パイプライン YAML 定義

これですべてが揃ったので、パイプラインの定義を掘り下げましょう。基本的には、以下のようなワークフローになります。

  1. コンテナイメージをビルドし、ローカルに保存する
  2. 必要に応じて、 sysdig-cli-scanner ツールをダウンロードします。
  3. スキャンを実行する
  4. コンテナイメージをリモートレジストリにプッシュする

このワークフローでは、Azure パイプラインのキャッシュを利用して、バイナリ、データベース、コンテナイメージが利用可能な場合は、ダウンロードを回避することもできます。

それでは、パイプラインのさまざまなステップを見ていきましょう。

この例で使用するバージョンは、Azure agent version: ‘2.209.0’、Image: ubuntu-20.04 (Version: 20220828.1, Included Software: https://github.com/actions/runner-images/blob/ubuntu20/20220828.1/images/linux/Ubuntu2004-Readme.md), Runner Image Provisioner: 1.0.0-main-20220825-1 and Sysdig-cli-scanner version 1.2.6-rc (commit: 17bb64a) となります。
レガシースキャナーを使用する場合、パイプラインの定義が異なります。そのため、sysdiglabs/secure-inline-scan コンテナイメージを使用する必要があります。sysdiglabs/secure-inline-scan-example リポジトリで提供されている例を参照してください。


準備

ごく標準的な設定で、メインブランチにプッシュしたときにトリガーされ、デフォルトのコンテナイメージ(ubuntu-latest)が使われます。

trigger:
- main
pool:
  vmImage: ubuntu-latest

また、異なるステップで使用されるいくつかの変数を定義しています。

  • アセットが保存される  CACHE_FOLDER 
  • Sysdig Secure API のエンドポイント (SYSDIG_SECURE_ENDPOINT)
  • イメージの詳細 (SYSDIG_SECURE_ENDPOINT)
  • そして、以前に作成したコンテナレジストリへの接続名 (REGISTRY_CONNECTION)

variables:
  CACHE_FOLDER: $(Pipeline.Workspace)/cache/
  SYSDIG_SECURE_ENDPOINT: "https://eu1.app.sysdig.com"
  REGISTRY_HOST: "quay.io"
  IMAGE_NAME: "e_minguez/my-example-app"
  IMAGE_TAG: "latest"
  REGISTRY_CONNECTION: "quayio-e_minguez"

キャッシュ

バイナリやアセット(脆弱性データベースなど)のキャッシュを設定し、毎回ダウンロードするのを避けます。

- task: Cache@2
  inputs:
    key: |
      sysdig-cli-scanner-cache | "$(Agent.OS)" | "$(CACHE_FOLDER)/sysdig-cli-scanner" | "$(CACHE_FOLDER)/latest_version.txt" | "$(CACHE_FOLDER)/db/main.db.meta.json" | "$(CACHE_FOLDER)/scanner-cache/inlineScannerCache.db"
    restoreKeys: |
      sysdig-cli-scanner-cache | "$(Agent.OS)"
      sysdig-cli-scanner-cache
    path: $(CACHE_FOLDER)
  displayName: Cache sysdig-cli-scanner and databases

コンテナイメージのキャッシュを設定し、毎回ダウンロードするのを回避します。

- task: Cache@2
  displayName: Docker cache
  inputs:
    key: 'docker | "$(Agent.OS)" | cache'
    path: $(Pipeline.Workspace)/docker
    cacheHitVar: CACHE_RESTORED

コンテナイメージがあれば、キャッシュからロードします。

- script: |
    docker load -i $(Pipeline.Workspace)/docker/cache.tar
  displayName: Docker restore
  condition: and(not(canceled()), eq(variables.CACHE_RESTORED, 'true'))

ビルド

Docker@2タスクでコンテナイメージをビルドします。

- task: Docker@2
  inputs:
    command: 'build'
    Dockerfile: 'love/Containerfile'
    buildContext: 'love/'
    repository: $(REGISTRY_HOST)/$(IMAGE_NAME)
    tags: $(IMAGE_TAG)
    addPipelineData: false
    addBaseImageData: false

このビルドプロセスでは、1つのタグにつき1つのコンテナイメージを生成します。コミットハッシュやコミットタグのような異なるタグでコミットごとに複数のイメージをビルドすることもできますが、ここではシンプルにしています。

スキャン

必要な場合のみ、最新版の sysdig-cli-scanner バイナリをダウンロードします。

- script: |
    curl -sLO https://download.sysdig.com/scanning/sysdig-cli-scanner/latest_version.txt
    mkdir -p $(CACHE_FOLDER)/db/
    if [ ! -f $(CACHE_FOLDER)/latest_version.txt ] || [ $(cat ./latest_version.txt) != $(cat $(CACHE_FOLDER)/latest_version.txt) ]; then
      cp ./latest_version.txt $(CACHE_FOLDER)/latest_version.txt
      curl -sL -o $(CACHE_FOLDER)/sysdig-cli-scanner "https://download.sysdig.com/scanning/bin/sysdig-cli-scanner/$(cat $(CACHE_FOLDER)/latest_version.txt)/linux/amd64/sysdig-cli-scanner"
      chmod +x $(CACHE_FOLDER)/sysdig-cli-scanner
    else
      echo "sysdig-cli-scanner latest version already downloaded"
    fi
  displayName: Download the sysdig-cli-scanner if needed

スキャナーを実行します。これが本番!ご覧の通り、超簡単です。

- script: |
    $(CACHE_FOLDER)/sysdig-cli-scanner \
      --apiurl $(SYSDIG_SECURE_ENDPOINT) \
      --console-log \
      --dbpath=$(CACHE_FOLDER)/db/ \
      --cachepath=$(CACHE_FOLDER)/scanner-cache/ \
      docker://$(REGISTRY_HOST)/$(IMAGE_NAME):$(IMAGE_TAG) \
  displayName: Run the sysdig-cli-scanner
  env:
    SECURE_API_TOKEN: $(TOKEN)

ノート:sysdig-cli-scanner ツールで必要とされるように、シークレット変数 TOKEN を環境変数に変換しています。

最終タスク

コンテナイメージをリポジトリにプッシュします:

- task: Docker@2
  displayName: Push the container image
  inputs:
    containerRegistry: $(REGISTRY_CONNECTION)
    repository: $(IMAGE_NAME)
    command: push
    tags: $(IMAGE_TAG)
    addPipelineData: false
    addBaseImageData: false

今後のパイプラインの実行に使用するコンテナイメージを保存します:

- script: |
    mkdir -p $(Pipeline.Workspace)/docker
    docker save $(docker images -q) -o $(Pipeline.Workspace)/docker/cache.tar
  displayName: Docker save
  condition: and(not(canceled()), or(failed(), ne(variables.CACHE_RESTORED, 'true')))

パイプラインの実行

最後に、パイプラインを実行し、Azure で動作するのを確認する準備ができました!

GitHub リポジトリにコミットした後、Azure ウェブコンソールでパイプラインが動作しているのを確認できます。以下はパイプラインの結果です。

Successful Azure pipeline output screenshot
Azureパイプラインの出力成功画面

ご覧の通り、うまくいきました。スキャンはきちんと終了し、1秒で実行されました。

Sysdig Secureにおけるイメージスキャン結果

Sysdig Secureに戻り、これらの結果をさらに分析することができます。

レガシースキャナーの結果は若干異なりますので、詳しくは公式ドキュメントをご覧ください。


Sysdig vulnerability with 0 issues found screenshot
Sysdig脆弱性スキャン、問題発見0件のスクリーンショット

この基本的なアプリケーションでは脆弱性は発見されませんでしたが(やった!)、https://github.com/sysdiglabs/dummy-vuln-app などの別のアプリケーションを見ると、いくつか発見されていることが分かります。

Sysdig vulnerability scan with some issues found screenshot
Sysdig 脆弱性スキャンといくつかの問題点のスクリーンショット

Sysdig vulnerability scan UI showing some vulnerabilities found screenshot
Sysdigの脆弱性スキャンUIで、いくつかの脆弱性が発見されたスクリーンショット

すでに修正されている脆弱性や悪用可能な脆弱性をフィルタリングすることができるので、最も緊急性の高い脆弱性に絞って修正・更新することができます。

Sysdig vulnerability scan UI showing filtered vulnerabilities by the ones that has a fix already available screenshot
Sysdigの脆弱性スキャンのUIで、すでに修正が提供されている脆弱性でフィルタリングされた脆弱性スクリーンショット

脆弱性だけでなく、ベストプラクティスではないものも見ることができます:

Sysdig vulnerability scanning UI showing the list of failed policies
失敗したポリシーのリストを表示するSysdig脆弱性スキャンのUI

まとめ

Sysdigを使うと、Azure DevOps Pipelinesで作成したイメージを実に簡単なプロセスでスキャンすることができます。ローカルスキャン機能のおかげで、インフラストラクチャーを離れることなく私たちのイメージをスキャンすることができ、ローカルでビルドされたイメージもスキャンすることができます。

CI/CDパイプラインの早い段階で問題を検出することで、イメージスキャンは、DevOpsチームがセキュリティをシフトレフトさせ、本番環境への納期を改善し、本番環境でイメージを実行する信頼性を高めることを可能にします。


Azure MarketplaceでSysdig Secure DevOps Platformをご購入ください!

Sysdigは、Azure Marketplaceで簡単にご購入いただくことができます。

Azure MarketplaceのSysdig Secure DevOps Platformをご参照ください。