ウイルスチェックをGitHub ActionsとClamAVで自動化する

はじめに

e2infoではシステムリリースの準備として、ソースコードのウイルスチェックをしています。GitHubのリリースからzipをダウンロードして、手元のESETでスキャンして、検査ログを残す。という作業を手作業で実施していまいした。
リリースが月に数十回あるため、この作業の自動化を考えました。リリースを作ったらGitHub Actionsが動いてClamAVでチェックするフローに変更しました。

方針

  • 検査エンジンはESETからClamAVに置き換える(GitHub Actions上で動かせるので)
  • 結果結果はGitHubのリリースに貴録する
  • 各リポジトリにコピーせず、共通のワークフローとして作る

結果はリリースノートの末尾に追記し、検査ログをリリースに添付することにしました。

仕組み

共通リポジトリに on: workflow_call の本体を置き、各リポジトリには呼び出すだけのYAMLを置きます。前回のDB設計書の自動出力と同じ形です。

各リポジトリに置く .github/workflows/virus-scan.yml

name: virus-scan

on:
  release:
    types: [published]
  # 既存のタグを検査し直す場合の手動実行用。
  workflow_dispatch:
    inputs:
      tag:
        description: 検査するタグ(例:v1.2.3)
        required: true
        type: string
      write-release:
        description: 結果をリリースに書き込む
        type: boolean
        default: true

# リリースノートの編集とログの添付に必要。
permissions:
  contents: write

concurrency:
  group: virus-scan-${{ inputs.tag || github.event.release.tag_name }}
  cancel-in-progress: false

jobs:
  virus-scan:
    uses: your-org/shared-workflows/.github/workflows/virus-scan.yml@main
    with:
      tag: ${{ inputs.tag }}
      write-release: ${{ github.event_name == 'release' || inputs.write-release }}

本体でやっていることは次のとおりです。

  1. リリースの「Source code (zip)」と同じzipを gh api repos/<repo>/zipball/<tag> で取得して展開する
  2. 公式のDockerイメージ clamav/clamav を起動し、freshclam で定義ファイルを最新にする
  3. clamscan で検査する
  4. 結果をリリースノートに追記し、ログを添付する
  5. 検出したらジョブを失敗させる(releaseを作った人にGitHubから通知が届く)

検査の部分はこんな感じです。

      - name: ウイルスを検査する
        id: scan
        shell: bash
        run: |
          (略)
          status=0
          docker exec clamav clamscan --stdout --recursive --alert-exceeds-max=yes /scan | tee -a "$log" || status=$?
          echo "status=$status" >> "$GITHUB_OUTPUT"

zipのまま検査せずに展開しているのは、検出したときにどのファイルかをログに出したいからです。--alert-exceeds-max=yes は、サイズの上限を超えたファイルを検査しないまま通さず、検出として扱うオプションです。

リリースノートには、次のような表が追記されます。

結果

自社サービスのリリースで試したところ、ジョブ全体で56秒でした(約1,900ファイル、18MiB)。

処理時間
zipの取得・展開2秒
イメージの取得3秒
コンテナの起動1秒
定義ファイルの更新6秒
検査38秒

事前の見込みは数分だったので、思ったより速かったです。

検出の確認は、テスト用の検体(EICAR)でやっています。EICARのファイルと、EICARを入れたzipの両方が Eicar-Test-Signature FOUND で検出され、ジョブが失敗することを確認しました。

/scan/eicar.com: Eicar-Test-Signature FOUND
/scan/sub/archive.zip: Eicar-Test-Signature FOUND

AIに任せたこと、任せなかったこと

最初の壁打ち以外は、すべてAIまかせ

  • ワークフロー本体・セルフテスト・説明書の作成
  • Pull Requestの作成
  • Backlogの課題への記録
  • 全案件のPR作成、案件担当者へのバックログ課題作成・通知。

まとめ

成功した