S3バケットへのファイルアップロードをClamAVでウィルススキャンする構成をCDKでサクッと作ってみる

2022.12.11

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

CX事業部Delivery部の新澤です。

S3バケットにファイルがアップロードされたタイミングでウィルススキャンツールのClamAVを用いてウィルススキャンを行う仕組みをCDKでサクッと作ってしまえるコンストラクトをConstruct Hubで見つけたので、使ってみました!

概要

S3バケットにファイルがアップロードされた際のPUTイベントでClamAVを実行するLambda関数を実行して、スキャン実行結果をSQSキューに投入します。

これらの仕組みを簡単に構築するためのCDKコンストラクト"cdk-serverless-clamscan"がConstruct Hubにて公開されていますので、活用してみます。

CDKの作成

まず、projenでCDKの雛形を作成します。

$ mkdir cdk-clamav & cd cdk-clamav
$ npx projen new awscdk-app-ts

次にprojen設定ファイル(.projenrc.js)を修正します。 修正箇所は、次の2箇所です。

  • CDKのバージョン
  • 依存ライブラリにcdk-serverless-clamscanを追加
const { awscdk } = require('projen');
const project = new awscdk.AwsCdkTypeScriptApp({
  cdkVersion: '2.54.0',
  defaultReleaseBranch: 'main',
  name: 'cdk-clamav',

  deps: ['cdk-serverless-clamscan@2.4.110'],                /* Runtime dependencies of this module. */
  // description: undefined,  /* The description is just a string that helps people understand the purpose of the package. */
  // devDeps: [],             /* Build dependencies for this module. */
  // packageName: undefined,  /* The "name" in package.json. */
});
project.synth();

修正したら、下記コマンドで反映させ、依存ライブラリをインストールすれば準備完了です。

$ npx projen
$ npm i

それでは早速CDKスタックを書いていきます。

今回作成した内容は以下3点です。

  • ファイルをアップロードするS3バケット
  • PUTイベントでClamAVのLambda関数を実行するコンストラクトServerlessClamscan
  • スキャン結果の送信先にSQSキューを指定(デフォルトはEventBridge)

src/main.ts

import { App, RemovalPolicy, Stack, StackProps } from 'aws-cdk-lib';
import { SqsDestination } from 'aws-cdk-lib/aws-lambda-destinations';
import { Bucket } from 'aws-cdk-lib/aws-s3';
import { Queue } from 'aws-cdk-lib/aws-sqs';
import { ServerlessClamscan } from 'cdk-serverless-clamscan';
import { Construct } from 'constructs';

export class MyStack extends Stack {
  constructor(scope: Construct, id: string, props: StackProps = {}) {
    super(scope, id, props);

    const bucket = new Bucket(this, 'MyBucket', {
      bucketName: `source-bucket-${this.account}`,
      removalPolicy: RemovalPolicy.DESTROY,
      autoDeleteObjects: true,
    });

    new ServerlessClamscan(this, 'ClamAV', {
      buckets: [ bucket ],
      onResult: new SqsDestination(
        new Queue(this, 'ClamavResultQueue', {
          queueName: 'clamav-result-queue',
        })
      ),
    });

  }
}

// for development, use account/region from cdk cli
const devEnv = {
  account: process.env.CDK_DEFAULT_ACCOUNT,
  region: process.env.CDK_DEFAULT_REGION,
};

const app = new App();

new MyStack(app, 'cdk-clamav-dev', { env: devEnv });
// new MyStack(app, 'cdk-clamav-prod', { env: prodEnv });

app.synth();

ServerlessClamscanには他にも色々なオプションがありますので、詳細はドキュメントで確認してみてください。

それではデプロイしてみます。

デプロイ開始すると、ClamAVのコンテナイメージがビルドされ、lambda関数にデプロイされます。

$ AWS_PROFILE=test npx projen deploy

動作確認

作成したS3バケットに適当なファイルをアップロードしてみます。

Lambda関数のログを確認すると、"status"に"CLEAN"と出力されており、ウィルスが検知されなかったことがわかります。

{
    "level": "INFO",
    "location": "lambda_handler:102",
    "message": {
        "source": "serverless-clamscan",
        "input_bucket": "source-bucket-xxxxxxxxxxxxx",
        "input_key": "sample.gif",
        "status": "CLEAN",
        "message": "Scanning /mnt/lambda/60c513eb-0a98-4fce-a1e2-4dbd6e803c90/sample.gif\n/mnt/lambda/60c513eb-0a98-4fce-a1e2-4dbd6e803c90/sample.gif: OK\n\n----------- SCAN SUMMARY -----------\nKnown viruses: 8645605\nEngine version: 0.103.7\nScanned directories: 1\nScanned files: 1\nInfected files: 0\nData scanned: 9.95 MB\nData read: 9.35 MB (ratio 1.06:1)\nTime: 23.786 sec (0 m 23 s)\nStart Date: 2022:12:10 14:12:56\nEnd Date:   2022:12:10 14:13:20\n"
    },
    "timestamp": "2022-12-10 14:13:20,392+0000",
    "service": "virus-scan",
    "cold_start": true,
    "function_name": "cdk-clamav-dev-ClamAVServerlessClamscan66D03349-xUDNzpvIWyd4",
    "function_memory_size": "10240",
    "function_arn": "arn:aws:lambda:ap-northeast-1:xxxxxxxxxxxxx:function:cdk-clamav-dev-ClamAVServerlessClamscan66D03349-xUDNzpvIWyd4",
    "function_request_id": "60c513eb-0a98-4fce-a1e2-4dbd6e803c90",
    "xray_trace_id": "1-63949386-75750e8a2134fca901ba62e3"
}

今度はウィルスチェックのテストファイルをアップロードしてみます。

ウィルスチェックツールがインストールされているPCでテストファイルをダウンロードすると、ツールが検知してしまいますので気をつけてください。(私も過去にうっかりやらかして情シス部門に通知が飛んでしまって迷惑かけた経験あります…)

なお、今回はCloud9環境を作成して作業しています。

$ curl -O http://www.eicar.org/download/eicarcom2.zip
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   308  100   308    0     0    391      0 --:--:-- --:--:-- --:--:--   391
$ ls -l
total 8
-rw-rw-r-- 1 ec2-user ec2-user 308 Dec 10 14:19 eicarcom2.zip

ダウンロードしたテストファイルをS3バケットにアップロードします。

$ aws s3 cp ./eicarcom2.zip s3://source-bucket-xxxxxxxxxxxxx
upload: ./eicarcom2.zip to s3://source-bucket-xxxxxxxxxxxxx/eicarcom2.zip
$ aws s3 ls s3://source-bucket-xxxxxxxxxxxxx/
2022-12-10 14:11:18    9804352 sample.gif
2022-12-10 14:20:36        308 eicarcom2.zip

先ほどと同様にログを確認してみると、今度は"status"に"INFECTED"と出力されており、"message"には検出したウィルスについて記載されているのがわかります。

{
    "level": "INFO",
    "location": "lambda_handler:102",
    "message": {
        "source": "serverless-clamscan",
        "input_bucket": "source-bucket-xxxxxxxxxxxxx",
        "input_key": "eicarcom2.zip",
        "status": "INFECTED",
        "message": "Scanning /mnt/lambda/d86246ae-522a-4017-8c00-7b59a5c9fe23/eicarcom2.zip\n/mnt/lambda/d86246ae-522a-4017-8c00-7b59a5c9fe23/eicarcom2.zip: Win.Test.EICAR_HDB-1 FOUND\n\n----------- SCAN SUMMARY -----------\nKnown viruses: 8645605\nEngine version: 0.103.7\nScanned directories: 1\nScanned files: 1\nInfected files: 1\nData scanned: 0.00 MB\nData read: 0.00 MB (ratio 0.00:1)\nTime: 25.635 sec (0 m 25 s)\nStart Date: 2022:12:10 14:20:39\nEnd Date:   2022:12:10 14:21:05\n"
    },
    "timestamp": "2022-12-10 14:21:05,746+0000",
    "service": "virus-scan",
    "cold_start": true,
    "function_name": "cdk-clamav-dev-ClamAVServerlessClamscan66D03349-xUDNzpvIWyd4",
    "function_memory_size": "10240",
    "function_arn": "arn:aws:lambda:ap-northeast-1:xxxxxxxxxxxxx:function:cdk-clamav-dev-ClamAVServerlessClamscan66D03349-xUDNzpvIWyd4",
    "function_request_id": "d86246ae-522a-4017-8c00-7b59a5c9fe23",
    "xray_trace_id": "1-639495b5-6794bcd3582a13c158c1b7a7"
}

また、上記の結果についてはCloudWatchのカスタムメトリクスにも出力されていますので、ウィルス検知時にアラートを通知するなど対応が可能になっています。

最後に

S3バケットのウィルスチェックを実行する環境が非常に簡単に構築できるコンストラクトをConstruct Hubで見つけて、とても感動しまして、アドベントカレンダー10日目として紹介させていただきました。

参考