GitHub Actionsにおけるイメージスキャン

By 清水 孝郎 - SEPTEMBER 27, 2022

SHARE:

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

Sysdig Secureを使って、GitHub Actionsのコンテナイメージに脆弱性やバッドプラクティスがないかスキャンするのは、簡単な作業です。この記事では、その方法をステップバイステップで例示しています。


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

こちらからパイプラインの定義を参照できます。


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

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

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

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

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

今回は、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のバックエンドに送信されます。

GitHub Actionsにおける脆弱性スキャン

GitHub Actionsは、Gitリポジトリ上でソフトウェア開発タスクを直接自動化し、さまざまなイベントをトリガーとした強力なCI/CD(継続的インテグレーション/継続的デリバリーまたはデプロイ)ワークフローを作成できるようにするものです。GitHub Marketplace には、最も一般的なタスクに対応した既存のアクションが多数用意されています。また、ワークフローをカスタマイズして独自のアクションを作成することも可能です。

イメージスキャンは、開発プロセスの早い段階でセキュリティを導入することで、CI/CDワークフローにおいて重要なステップとなっています(セキュリティ・シフト・レフト)。このワークフローでは、コンテナイメージをビルドし、sysdig-cli-scannerツールを使ってローカルでイメージをスキャンします。スキャン結果はSysdigに送信されます。また、スキャンに失敗すると、ワークフローは中断され、レジストリにイメージがアップロードされないようになります。



GitHub Actionを作成する

この例で使用するバージョンは以下の通りです。:

レガシースキャナを使用する場合、パイプラインの定義が異なります。 sysdiglabs/[email protected] actionが必要となります。
sysdiglabs/secure-inline-scan-example リポジトリで提供される例を参照してください。


前提条件

Sysdig Image Scanningを稼働させるための要件は簡単です。

  • GitHub リポジトリと管理者権限(Actions を有効にし、Secrets を管理するために必要)。
  • スキャン結果を収集するためのSysdig Secureアカウント。お持ちでない場合は、無料トライアルをリクエストしてください。
  • ビルド可能なコンテナDockerfileこの例をフォークして使用することもできますが、自分のコンテナを使用する方がより楽しいでしょう!
準備ができたら、次に進みましょう!

GitHub Actions をリポジトリで有効にする

もしリポジトリの Actions を有効にしていないのなら、まずはそれを確認しましょう。リポジトリの設定に移動し、Actions セクションを探します
(https://github.com/<user>/<repo>/settings/actions).

そこで、すべてのアクションと再利用可能なワークフローを許可するオプションが選択されていることを確認します:



アクションが有効になると、リポジトリのメインページの上部ナビゲーションバーに、このようなアクションタブが表示されます。



リポジトリのsecrets

レジストリパスワードや API トークンのような機密データについては、 repository Settings -> Secrets -> Actions -> “New repository secret” で、リポジトリのsecretsを作成することをおすすめします。


この例では、3つのリポジトリシークレットを使用します:

  • REGISTRY_USER自明です。
  • REGISTRY_PASSWORDわかりますよね。
  • SECURE_API_TOKENSysdig APIへのクエリーとスキャン結果の送信に必要な、Sysdig APIトークンです。取得方法については、公式ドキュメントを参照してください。
これらの変数は、ワークフロー内で ${{ secrets.VARIABLE_NAME }},という構文を使って、

 ${{ secrets.REGISTRY_PASSWORD }}などと参照されます。



Githubでイメージスキャンのワークフローを設定する

GitHub Actionsは、CI/CDソフトウェアのワークフローをGithubリポジトリ上でそのまま自動化することができる機能です。Actionsから名前をとっており、自動化されたタスクが組み合わされてワークフローが作成されます。そこで、私たちの旅は、新しいワークフローを作成し、設定することから始まります。

Actionsタブから、Skip this and set up a workflow yourselfのリンクを選択します。


すると、このようなデフォルトのワークフローが表示されます:






GitHub Actions のワークフローは、リポジトリ内の  .github/workflows/ ディレクトリにある YAML ファイルで定義します。つまり、Actions の UI を介する必要がないのです。 .github/workflows/my-shiny-workflow.yml をリポジトリに追加し、変更をコミットしてプッシュするだけで、キーボードから手を離さずに新しいワークフローを作成することができるのです。GitHub Actions の詳しい使い方は、公式ドキュメントを参照ください。ワークフローの例全体は sysdiglabs/secure-inline-scan-examples リポジトリで見ることができます。


デフォルトの main.yml を  build-scan-and-push.yaml のような適切な名前に変更し、組み込みエディタでワークフローの YAML を編集して次のステップを使用するようにしましょう:

env:
    SYSDIG_SECURE_ENDPOINT: "https://eu1.app.sysdig.com"
    REGISTRY_HOST: "quay.io"
    IMAGE_NAME: "mytestimage"
    IMAGE_TAG: "my-tag"
    DOCKERFILE_CONTEXT: "github/"
name: Container build, scan and push
on: [push, pull_request]

jobs:
  build-scan-and-push:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout
      uses: actions/checkout@v2

    - name: Set up Docker Buildx
      uses: docker/setup-buildx-action@v2

    - name: Build and save
      uses: docker/build-push-action@v3
      with:
        context: ${{ env.DOCKERFILE_CONTEXT }}
        tags: ${{ env.REGISTRY_HOST }}/${{ secrets.REGISTRY_USER }}/${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }}
        load: true

スキャナ結果が送信されるAPIエンドポイント(これについては公式ドキュメントを参照してください)や、イメージがプッシュされるレジストリの詳細など、後で使用するパラメータを含む環境変数を最初に定義しています。

次に、「Container build, scan and push」というワークフローを定義します(これは、リポジトリのActions UIに表示される名前です)。このワークフローは、リポジトリでプッシュやプルのリクエストが発生するたびに起動し、GitHub がホストする ubuntu-latest ランナー上で実行される job_id build-scan-and-push で定義し、一連のステップを順次実行するようにしました。もちろん、トリガーには多くの種類があります。複数のジョブを並行して実行したり、ジョブ間の依存関係やデータフローを定義することができます。さらに、self-hosted runners,など、様々な種類のワーカーで実行することもできます。

それでは、最初の3つのステップを確認しましょう:

  • 最初のステップ Checkoutでは、https://github.com/actions/checkout にある  actions/checkout@v2 というactionを使用して、リポジトリのコードをランナーで利用できるようにします。
  • 2つ目の“Set up Docker Buildx” は、公式Dockerアクションの docker/setup-buildx-action@v2 を使用して、コンテナイメージをビルドする環境を準備します(この特定のアクションに関する詳細については、https://github.com/docker/setup-buildx-action を参照してください)。
  • 3つ目の “Build and saveは、公式Dockerアクションの docker/build-push-action@v3 を使ってコンテナイメージをビルドし、それをローカルに保存するものです。これはまだレジストリにイメージをプッシュしていません。スキャンが成功すれば、後でプッシュされます(詳しくはhttps://github.com/docker/build-push-action を参照してください)。事前に設定した環境変数(たとえば、 ${{ env.DOCKERFILE_CONTEXT }})と、シークレット(たとえば、 ${{ secrets.REGISTRY_USER }})が使用されます。

Setup cache”  “Download sysdig-cli-scanner if needed” はとりあえずスキップして、イメージのスキャンにフォーカスして、追加してみましょう:

    - name: Scan the image using sysdig-cli-scanner
      env:
        SECURE_API_TOKEN: ${{ secrets.SECURE_API_TOKEN }}
      run: |
        ${GITHUB_WORKSPACE}/cache/sysdig-cli-scanner \
          --apiurl ${SYSDIG_SECURE_ENDPOINT} \
          docker://${REGISTRY_HOST}/${{ secrets.REGISTRY_USER }}/${IMAGE_NAME}:${IMAGE_TAG} \
          --console-log \
          --dbpath=${GITHUB_WORKSPACE}/cache/db/ \
          --cachepath=${GITHUB_WORKSPACE}/cache/scanner-cache/

これは単純なコマンド実行で、sysdig-cli-scannerバイナリを実行してイメージをスキャンしています。パラメータを詳しく見てみましょう:

  • --apiurl ${SYSDIG_SECURE_ENDPOINT}スキャン結果の送信先、およびデータベースのダウンロード先となるSysdig APIのエンドポイントです。
  • docker://${REGISTRY_HOST}/${{ secrets.REGISTRY_USER }}/${IMAGE_NAME}:${IMAGE_TAG} 前のステップでビルドされ、コンテナキャッシュにローカルに保存されたコンテナイメージです。
  • –dbpath=${GITHUB_WORKSPACE}/cache/db/ : 脆弱性データベースを格納するパスです(詳細は後述)。
  • --cachepath=${GITHUB_WORKSPACE}/cache/scanner-cache/ : スキャナキャッシュが格納されているパスです(詳細は後述)。
  • また、 ${{ secrets.SECURE_API_TOKEN }} のシークレットを環境変数に変換し、 sysdig-cli-scanner バイナリから利用できるようにします。

最後に、スキャンが成功した場合に、コンテナイメージをレジストリにプッシュしてみましょう:

    -  name: Login to the registry
       uses: docker/login-action@v2
       with:
        registry: ${{ env.REGISTRY_HOST }}
        username: ${{ secrets.REGISTRY_USER }}
        password: ${{ secrets.REGISTRY_PASSWORD }}
    - name: Push
      uses: docker/build-push-action@v3
      with:
        context: ${{ env.DOCKERFILE_CONTEXT }}
        push: true
        tags: ${{ env.REGISTRY_HOST }}/${{ secrets.REGISTRY_USER }}/${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }}


この2つのステップは簡単です。最初のものはレジストリにログインし、2番目のものは、前にビルドされたコンテナイメージを適切なレジストリにプッシュするだけです。

キャッシュ

sysdig-cli-scannerのバイナリはどこに保存されているのだろうかと思う人がいるでしょう。その答えは、初回にSysdigからダウンロードされ、その後、そのバイナリの新しいバージョンが利用可能にならない限りキャッシュされる、というものです。

どうしてそうなるのでしょうか?見てみましょう:

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

このステップは、 sysdig-cli-scannerの最新バージョンをチェックする単なるbashスクリプトです。もし、すでに環境にあるものと異なる場合は、ダウンロードされます(データベースフォルダが存在しない場合は、作成もします)。

さて、しかし、すべてのワークフローの実行はステートレスであり、どのようにそのファイルを保存するのでしょうか?答えは、GitHub の cache actionを使うことです。キャッシュは、ライブラリの依存関係やアセットなどのものを保存するために利用できるスペースです。

この例では、そのスペースを活用して、Sysdig-cli-scanner のバイナリだけでなく、脆弱性データベースやその他のスキャナーのアセットも保存することにします。

    - name: Setup cache
      uses: actions/cache@v3
      with:
        path: cache
        key: ${{ runner.os }}-cache-${{ hashFiles('**/sysdig-cli-scanner', '**/latest_version.txt', '**/db/main.db.meta.json', '**/scanner-cache/inlineScannerCache.db') }}
        restore-keys: ${{ runner.os }}-cache-

このアクションは、キャッシュからデータを収集するためにダウンロードステップの前に実行する必要があり、ワークフローの実行結果としてアセットを保存するために、ワークフローの最後に自動的に実行されます。

 key  restore-keys の設定は、何か変更があった場合にキャッシュを無効化し、保存・復元したいアセットのみを保存するために使用します。このアクションの詳細については、公式ドキュメントを参照してください。

sysdiglabs/secure-inline-scan-examples リポジトリで、ワークフロー例の全体を見ることができます。


イメージスキャンはGithubで:ライト、カメラ、アクション!

リポジトリにDockerfileがあり、有効なSecure APIトークンがある場合、作成したワークフローのコミットにより、ワークフローが実行され、イメージがビルドされ、スキャンされるはずです。

repoのActionsセクションに移動して、Workflowの実行結果を確認することができます:



また、ワークフローの実行をクリックすると、各ステップの詳細なビューやログなどを取得することができます。



”Post” ステップはワークフローに含まれていませんが、代わりに “Post Setup Cache” を含む、アセットをキャッシュに保存するステップが自動的に実行されていることがわかります。



解析結果は、Sysdig Secureアカウントの Vulnerability -> Pipeline に投稿されます。



成功! 脆弱性は発見されず、イメージはレジストリにプッシュして公開され、アセットは次の実行のためにキャッシュに自動的に保存され、時間の節約と不要なダウンロードの回避につながります。

このスキャン例では、デフォルトの”Sysdig Best Practices”ポリシーを使用しましたが(ログで確認できます)、お客様がチェックしたいポリシーを作成しカスタマイズすることも可能です。脆弱性ポリシーだけでなく、イメージのベストプラクティスなど、ポリシーの作成・カスタマイズの方法については、公式ドキュメントをご覧ください。

ポリシーが失敗したためにスキャンが失敗した場合、ワークフローは停止し、イメージはレジストリにプッシュされません(これは良いアイデアです)ここで見ることができます。


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




すでに修正されているもの、および/または、悪用可能なものをフィルタリングし、修正または更新が最も急がれるものに焦点を当てることができます。



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



まとめ

GitHub Actions は、GitHub リポジトリ上で CI/CD パイプラインを直接自動化するための強力なツールです。Sysdigコンテナのイメージスキャン機能をワークフローに組み込むことは簡単で、イメージの脆弱性をスキャンし、ビルド時にベストプラクティスを適用することで、レジストリ内の従来のイメージスキャンよりもいくつかの利点を提供することができます。

  • CI/CDパイプラインにイメージスキャンを実装することは、脆弱性が発見された場合、そのイメージが公開されるのを一切防ぐことを意味します。
  • 解析はランナー内でインライン(ローカル)に行われるため、ビルドされた環境の外を含め、イメージはどこにも送信されません。解析の際、イメージからはメタデータ情報のみが抽出され、実際のコンテンツは抽出されません。
  • 解析で得られたメタデータは、新たな脆弱性が発見されたり、ポリシーが変更されたりしても、警告のためにイメージを新たにスキャンする必要がなく、後で再評価することができます。ビルドを破棄したい場合も、ビルド内で新たなスキャンを起動させる必要があります。
  • Sysdig Secureは、様々なコンテナのコンプライアンス基準(NIST 800-190PCIなど)を実施・遵守するためのポリシーをすぐに利用できるようにします。
Sysdig Secureのイメージスキャンは、ほとんどのCI/CDパイプライン・ツールとシームレスに統合することが可能です。

まだSysdig Secureをイメージスキャンに利用していない方は、今すぐデモをご依頼ください。


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

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

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