AWS CDKを使ってTrend Micro Cloud One Container SecurityをEKSにセットアップしてみた その１

# 背景

## Trend Micro Cloud One Container Securityとは

Trend Micro Cloud One Container Securityとは、コンテナイメージのスキャンやポリシーベースのデプロイメントコントロール（例えば、イメージスキャンで脆弱性が見つかったイメージはデプロイできないなど）などの機能を兼ね備えたKubernetesに対するセキュリティソリューションです。

詳しくは[公式サイト](https://www.trendmicro.com/ja_jp/business/products/hybrid-cloud/cloud-one-container-image-security.html)をご覧ください。

![container_security_lifecycle.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1782759/176ea1cd-d8f6-f854-8f04-012c69e056dd.png)
画像は[公式ガイド](https://cloudone.trendmicro.com/docs/container-security/about/)より

## AWS CDKとは

AWS CDKとは、TypeScriptやPythonなど使い慣れたプログラミング言語を使用してクラウドアプリケーションリソースを定義するためのオープンソースのソフトウェア開発フレームワークです。

いわゆるInfrastructure as Code(IaC)ツールの一種ですね。

こちらも詳しくは[公式サイト](https://aws.amazon.com/jp/cdk/)をご覧ください。

![AppStacks.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1782759/61a1a0c9-4588-ba72-47cd-62f8641bc5db.png)
画像は[公式開発者ガイド](https://docs.aws.amazon.com/cdk/latest/guide/home.html)より


## なぜAWS CDKを使ってセットアップしたいのか

AWS CDKおよびInfrastructure as Codeのメリットは多岐に渡りますが、本記事では*セキュリティの向上*に着目したいと思います。Cloud One Container Securityは確かに素晴らしい製品ですが、万能ではありません。せっかくCloud One Container SecurityでEKS Clusterをセキュアにしても、他のところが雑だとそこが脆弱性になってしまいます。

- EKS Clusterが乗っているVPCレベルのセキュリティ（例えば、セキュリティグループの設定）が雑になっている
- API KEYや認証情報(例えば、Clusterに乗っているコンテナが参照するRDSのID/Pass)の管理が雑になっている

AWS CDKを使うことでなぜ上記のような「雑な設定」を防ぐことができるのか、解説を交えながら手順を説明します。

# Container SecurityにClusterを追加する

まず、Container Securityで管理する対象のEKS ClusterをAWS CDKで作成して、管理対象に追加します。

基本的には、公式ガイドの[Add a cluster](https://cloudone.trendmicro.com/docs/container-security/cluster-add/)に記載されている手順をAWS CDKベースで行なっていきます。

## 開発環境構築

まずは開発環境を作ります。ローカルPCに作ることもできますが、今回は[AWS Cloud9](https://aws.amazon.com/jp/cloud9/)を利用することにします。

ローカルPCからAWS CDKを実行する場合は、IAMのAccess Key IDやSecret Access Key IDの発行・管理がセキュリティ上の課題となります。
一方でCloud9の場合は、[AWS Managed Temporary Credentials](https://dev.classmethod.jp/articles/aws-cloud9-aws-managed-temporary-credentials/)もしくはEC2インスタンスに付与したIAMロールベースでの実行の利用が可能なので、Management Consoleへのログインをしっかり管理している前提（例えばMFAを必須にしているなど）であれば、セキュリティ上比較的安全に利用することができるかと思います。

### Cloud9インスタンスの作成

Management ConsoleからCloud9のインスタンスを作成します。

スペックはサイフの中身と相談だとは思いますが、AWS CDKでEKSのプロジェクトを扱う（≒Cloud9の中でdocker buildなどを行う）前提で、快適に開発するための最低スペックは以下のような感じかなと思います。

<dl>
   <dt>インスタンスタイプ</dt>
   <dd>m5.large (t3.smallでもいけなくないですがちょっと重いです。。)</dd>
　　<dt>プラットフォーム</dt>
   <dd>Amazon Linux2 (ほかでも問題ないですが、まあこれが無難かと。)</dd>
   <dt>EBSボリュームサイズ</dt>
   <dd>40GB (Cloud9のコンソールからは変更できないですが、EC2のコンソールから変更ください。デフォルトの10GBはすぐ枯渇します)</dd>
</dl>

### Cloud9へのミドルウェアのインストール

私はAWS CDKはTypeScriptで書きたいので、その前提でミドルウェアを整えていきます。何度も同じ作業をやってきたので、シェルスクリプト化しました。

```bash:setup_cdk.sh
#!/bin/bash

export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" # nvmのロード

nvm install stable #nodeの最新Stableのインストール
nvm alias default stable #デフォルトで↑のStable版を使うよう設定
npm update -g npm #npmの最新化
npm install -g yarn #yarnのインストール
echo export PATH=\$PATH:`yarn global bin` >> ~/.bashrc  # yarnのglobalディレクトリにpathを通す
source ~/.bashrc #上でやった設定の反映

yarn global add aws-cdk # CDK Cliのインストール
```

### CDKプロジェクトの作成

インストールしたAWS CDKのCliを使ってAWS CDKのプロジェクトを作成します。

```bash
$ mkdir cdk-container-security-sample
$ cd cdk-container-security-sample
$ cdk init --language=typescript # TypeScriptのCDKプロジェクトを作成する
```
![スクリーンショット 2021-07-23 10.15.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1782759/c80e402d-3025-5c5a-cdb5-029ff780dffd.png)

このようにプロジェクトが初期化されればOKです。

### CDK Bootstrap (AWSアカウント/リージョンごとに初めてAWS CDKを使用する場合のみ)

初めてAWS CDKを利用する場合、CDK Bootstrapという初期設定が必要です。CDKの実行に必要なS3バケットや関連するIAMロールなどが作成されます。

```bash
# 初期化したcdk-container-security-sampleディレクトリ内で実行
$ yarn run cdk bootsrtap
```

## Container Securityで管理するEKS Clusterを作成する

早速、`lib/cdk-container-security-sample-stack.ts`を編集して、Container Securityの監視対象にするAWS EKSのコンテナを作成します。ただVPCを作成してそこにEKSクラスタを作成するだけでも良いのですが、ついでにEKSに展開するコンテナ上のアプリケーションが利用する想定で、PostgreSQLのAWS RDSを同じVPCに作成します。

### 関連ライブラリの追加

```bash
$ yarn add @aws-cdk/aws-ec2 @aws-cdk/aws-eks @aws-cdk/aws-rds
```

### EKSを構築するスタックの作成

```typescript:lib/cdk-container-security-sample-stack.ts
import * as cdk from '@aws-cdk/core';
import * as ec2 from '@aws-cdk/aws-ec2';
import * as eks from '@aws-cdk/aws-eks';
import * as rds from '@aws-cdk/aws-rds';

/**
 * 使用するCDK Contextをマップするinterface
 */
interface CdkContainerSecuritySampleContext{
  /** CIDRプレフィックス*/
  cidrPrefix: string,
  /** EKSクラスタのノード数*/
  desiredCapacity: number,
  /** EKSクラスタのノードのインスタンスタイプ*/
  nodeInstanceType: string,
  /** EKSクラスタ名*/
  clusterName: string,
}

/**
 * CdkContainerSecuritySampleContextのデフォルト値
 */ 
const DEFAULT_CONTEXT: CdkContainerSecuritySampleContext = {
  cidrPrefix: '10.0',
  desiredCapacity: 2,
  nodeInstanceType: 'm5.large',
  clusterName: 'container-security-sample',
}


export class CdkContainerSecuritySampleStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);
    // CDK Contextのパース
    const context = this.parseContext();
    
    // EKSクラスタをデプロイするVPCの作成
    const vpc = new ec2.Vpc(this, 'cloudone-poc-vpc', {
      cidr: `${context.cidrPrefix}.0.0/16`,
      natGateways:1,
      subnetConfiguration: [
        {
          cidrMask: 24,
          name: 'Public1',
          subnetType: ec2.SubnetType.PUBLIC
        },
        {
          cidrMask: 24,
          name: 'Public2',
          subnetType: ec2.SubnetType.PUBLIC
        },
        {
          cidrMask: 24,
          name: 'Private1',
          subnetType: ec2.SubnetType.PRIVATE
        },
        {
          cidrMask: 24,
          name: 'Private2',
          subnetType: ec2.SubnetType.PRIVATE
        }
      ]
    });
    
    // EKSクラスタの作成
    const cluster = new eks.Cluster(this, 'container-security-cluster', {
      vpc,
      clusterName: context.clusterName,
      version: eks.KubernetesVersion.V1_20,
      defaultCapacity: context.desiredCapacity,
      defaultCapacityInstance: new ec2.InstanceType(context.nodeInstanceType)
    });
    
    
    /**
     * kubernetes-external-secrets(AWS Secrets Managerで管理されているSecretをKubernatesのSecretに連携するController)のインストール
     */ 
     
    // kubernetes-external-secretsで使用するサービスアカウントの作成
    const externalSecretServiceAccount = cluster.addServiceAccount("external-secret-sa", {name: `${context.clusterName}-sa`});
    
    // Helm経由でkubernetes-external-secretsのインストール
    // @see https://github.com/external-secrets/kubernetes-external-secrets/tree/master/charts/kubernetes-external-secrets
    const externalSecretHelm = new eks.HelmChart(this,'external-secret', {
      cluster,
      repository: "https://external-secrets.github.io/kubernetes-external-secrets",
      chart: "kubernetes-external-secrets",
      values: {
        securityContext:{
          fsGroup: 65534
        },
        serviceAccount:{
          create:false,
          name: externalSecretServiceAccount.serviceAccountName
        },
        env:{
          AWS_REGION: this.region
        }
      }
    });
    
    
    // EKS上のコンテナが利用するRDS(PostgreSQL)をプライベートサブネットへ作成
    const postgresRds = new rds.DatabaseInstance(this, 'postgres-container-security', {
      engine: rds.DatabaseInstanceEngine.postgres({version: rds.PostgresEngineVersion.VER_13_3}),
      vpc,
      vpcSubnets: {
        subnetType: ec2.SubnetType.PRIVATE
      }
    });
    
    // EKSクラスタからRDSへのデフォルトポートでの疎通を許可する
    postgresRds.connections.allowDefaultPortFrom(cluster);
    
    /**
     * kubernetes-external-secretsを使用してSecretManagerに格納されたRDSの接続情報を
     * KubernetesのSecretに連携する
     */ 
    
    // RDSの接続情報が格納されているSecret名
    const postgresRdsSecretInfo = postgresRds.secret;
    
    if(postgresRdsSecretInfo){
      // kubernetes-external-secretsのサービスアカウントに接続情報の読み取りを許可
      postgresRdsSecretInfo.grantRead(externalSecretServiceAccount);
      
      // kubernetes-external-secretsを使用して接続情報を連携するマニフェストの追加
      const postgresRdsSecretManifest = new eks.KubernetesManifest(this, 'rds-secret-manifest', {
        cluster,
        manifest:[{
          apiVersion: 'kubernetes-client.io/v1',
          kind: 'ExternalSecret',
          metadata: {
            name: 'postgres-rds-secret',
          },
          spec:{
            backendType: "secretsManager",
            data:[
              {
                key: postgresRdsSecretInfo.secretName,
                name: "dbcredential"
              }
            ]
          }
        }]
      });
      // このマニフェストがkubernetes-external-secretsのインストール後に実行されるよう依存関係を明示
      postgresRdsSecretManifest.node.addDependency(externalSecretHelm);
    }
  }
  
  /**
   * CDK Contextをパースして返す
   */ 
  private parseContext(): CdkContainerSecuritySampleContext{
    return {
      cidrPrefix: this.node.tryGetContext("CIDR_PREFIX") ?? DEFAULT_CONTEXT.cidrPrefix,
      desiredCapacity: this.node.tryGetContext("DESIRED_CAPACITY") ?? DEFAULT_CONTEXT.desiredCapacity,
      nodeInstanceType: this.node.tryGetContext("NODE_INSTANCE_TYPE") ?? DEFAULT_CONTEXT.nodeInstanceType,
      clusterName: this.node.tryGetContext("CLUSTER_NAME") ?? DEFAULT_CONTEXT.clusterName,
    }
  }
}

```

上記のようにソースコードを作成して、`cdk deploy`コマンドでリソースをデプロイします。

```bash
$ yarn run cdk deploy
```

また、いくつかContextを定義しているので、パラメータの上書きが可能です。例えば、VPCのCIDRプレフィックスを`10.2`に変更するには、

```bash
$ yarn run cdk deploy -c CIDR_PREFIX=10.2
```

のようにして実行します。

### AWS CDKによるセキュリティ向上ポイント

#### VPCセキュリテイ

VPC上にあるデータベースなどのリソースを保護するためには、ルートテーブルやセキュリティグループの設定・管理などの専門知識が必要です。例えば、本例で作成したRDSに関しては、

1. 作成したEKSクラスタからはPostgreSQLのデフォルトポート(5432)でアクセスさせたい
1. EKSクラスタからであっても5432以外からのアクセスは拒否したい
1. EKSクラスタ以外からのアクセスはいかなるポートであっても拒否したい

という要件を満たす設定が必要です。

もちろん、きちんと勉強した専門家であれば手動でこのような環境を構築・管理することは可能ですが、専門家も人間である以上は人為ミスを想定せねばならず、その防止や発見のためにかなりのコストを費やすことになります。

一方で、AWS CDKには`High Level Construct`もしくは`L2 Construct`という概念があります。
上記のソース例を見ていただくと分かる通り、ソースコード上でセキュリティグループやルートテーブルの設定を明示的に記述していません。ただ1行だけ、

```typescript
    // EKSクラスタからRDSへのデフォルトポートでの疎通を許可する
    postgresRds.connections.allowDefaultPortFrom(cluster);
```

と記述されているのみです。

実はAWS CDKでは、<b>上に挙げた３つの要件がこの1行だけで実現できます</b>。
以下は、上記のソースによって作成されたセキュリティグループ（のうち１つ）のInbound ruleです。

![スクリーンショット 2021-07-23 11.55.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1782759/0d97add2-918a-30f2-5c1e-69ef53f30fef.png)


このようにAWS CDKではセキュリティグループやルートテーブルなどの設定を<b>抽象化</b>し、`allowDefaultPortFrom`のような目的別に応じたメソッドを提供してくれます。このような工夫により、

- 個別に手動設定することによる人為ミスの防止
- 担当者の理解不足・知識不足による雑な設定（全ポートを解放してしまうなど）の防止
- レビューの単純化（ルートテーブル/セキュリティグループの設定を１つ１つレビューするより`allowDefaultPortFrom`が使われていることを
チェックする方がはるかに単純で楽。）

などを図ることが可能です。


#### 認証情報の管理

AWS CDKやKubernetesなど、いわゆるInfrastructure as Codeがセキュリティに与えるメリットは多くありますが、一方でよくある失敗例は<b>認証情報の流出</b>かと思います。具体的には、
*DBへの接続情報(ID/Pass)をソースコードや設定ファイルにハードコードしたままGitHubにpushして流出させてしまった*
のような事態を防止しなければいけません。

ソース例で使用している`@aws-cdk/aws-rds`の`DatabaseInstance`は、<b>デフォルトで認証情報をAWS Secrets Managerに格納してくれます</b>。

![スクリーンショット 2021-07-23 13.14.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1782759/4607fc92-8893-c58c-fd53-c1d16cace696.png)

今回はこのようにAWS Secrets Managerに格納された認証情報をEKS上のコンテナから参照するため、EKSクラスタ上に[Kubernetes External Secrets](https://github.com/external-secrets/kubernetes-external-secrets)というコントローラをインストールして、Secrets Manager上の情報をKubernetes上の[Secret](https://kubernetes.io/ja/docs/concepts/configuration/secret/)として参照できるように設定します。

AWS CDKでは`@aws-cdk/aws-eks`ライブラリを用いてクラスタへのHeml Chartのインストールやマニフェストの適用が可能なので、以下のようにインストールします。

```typescript
    // Helm経由でkubernetes-external-secretsのインストール
    // @see https://github.com/external-secrets/kubernetes-external-secrets/tree/master/charts/kubernetes-external-secrets
    const externalSecretHelm = new eks.HelmChart(this,'external-secret', {
      cluster,
      repository: "https://external-secrets.github.io/kubernetes-external-secrets",
      chart: "kubernetes-external-secrets",
      values: {
        securityContext:{
          fsGroup: 65534
        },
        serviceAccount:{
          create:false,
          name: externalSecretServiceAccount.serviceAccountName
        },
        env:{
          AWS_REGION: this.region
        }
      }
    });
```
インストールしたコントローラを用いて、Secrets Manager上に格納された認証情報を連携します。

```typescript
      // kubernetes-external-secretsのサービスアカウントに接続情報の読み取りを許可
      postgresRdsSecretInfo.grantRead(externalSecretServiceAccount);
      
      // kubernetes-external-secretsを使用して接続情報を連携するマニフェストの追加
      const postgresRdsSecretManifest = new eks.KubernetesManifest(this, 'rds-secret-manifest', {
        cluster,
        manifest:[{
          apiVersion: 'kubernetes-client.io/v1',
          kind: 'ExternalSecret',
          metadata: {
            name: 'postgres-rds-secret',
          },
          spec:{
            backendType: "secretsManager",
            data:[
              {
                key: postgresRdsSecretInfo.secretName,
                name: "dbcredential"
              }
            ]
          }
        }]
      });
      // このマニフェストがkubernetes-external-secretsのインストール後に実行されるよう依存関係を明示
      postgresRdsSecretManifest.node.addDependency(externalSecretHelm);
```

利用する側は以下の通り、Secretとしてマウントできます。

```yaml
apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: myapp
    volumeMounts:
    - name: rdsinfo
      mountPath: "/etc/secret"
      readOnly: true
  volumes:
  - name: rdsinfo
    secret:
      secretName: postgres-rds-secret
      items:
      - key: dbcredential
        path: rds-info
```

このように、これからEKSに乗るであろうコンテナの定義も含め、発行されたRDSの認証情報を一切ソースコードに残すことなくRDSへの接続を定義することが可能です。

## policy-based deployment controllerをインストールする

では、[公式ガイドライン](https://cloudone.trendmicro.com/docs/container-security/cluster-add/#install-the-policy-based-deployment-controller)に則り、作成したクラスタをContainer Security配下に登録します。

### クラスタ定義の追加

ガイドラインの通り、Cloud OneのコンソールからContainer Securityを選択し、クラスタを追加します。

![スクリーンショット 2021-07-23 13.51.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1782759/a517d021-1254-cac4-5762-ca307f59546f.png)

すると以下のような指示が表示されます。

![スクリーンショット 2021-07-23 13.53.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1782759/12a21167-a8da-f255-ba05-64f47010fe35.png)


ここでは`helm`コマンドを使ってクラスタに`policy-based deployment controller`をインストールするよう指示されていますが、今回はここもAWS CDKで行います。

### API Keyを~~AWS Secrets Manager~~AWS Systems Managerのパラメータストアに登録する

上の画面ショットで黒塗りした箇所に書かれているAPI Keyは、先ほどのRDSの認証情報と同じく流出させてはいけないセキュリティ情報です。間違ってもソースコードや設定ファイルにハードコードしないよう、~~AWS Security Managerに登録しましょう~~。

と、書きたいところだったのですが、実は数時間ハマった末、今回のユースケースではAWS Security Managerでは上手くいかないことが分かりました（後述）
なので、今回はAWS Systems Managerのパラメータストアに登録して、そこからCloudFormationのパラメータとして読み込む形にしたいと思います。

以下のように登録しました。

![スクリーンショット 2021-07-23 20.38.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1782759/30cd071b-fec6-f572-d3c6-230d8c913dcc.png)


### policy-based deployment controllerをインストールするためのConstructを作成する

そのままStackに記述しても良かったのですが、少々ソースが長くなってきたのと、このコントローラのインストールは他のクラスタでも使い回すであろうことを鑑み、別のConstructに切り出しました。

基本的には、先にスクリーンショットを貼った`Add Cluster`完了時の画面に書いてある`helm install`のコマンドを`@aws-cdk/aws-eks`の`HelmChart`で定義するよう書き換えただけです。

```typescript:lib/helm-policybased-deployment-controller.ts
import * as cdk from '@aws-cdk/core';
import * as eks from '@aws-cdk/aws-eks';

/**
 * PolicyBasedDeploymentControllerのプロパティ
 */ 
export interface PolicyBasedDeploymentControllerProps{
    /** インストール対象のEKSクラスタ*/
    cluster: eks.ICluster,
    /** Cloud Oneが発行したAPI Key*/
    apiKey: cdk.SecretValue
}

/**
 * Trend Micro Cloud One Container SecurityのPolicy-based Deployment ControllerをインストールするHelmを適用するConstruct
 * @see https://cloudone.trendmicro.com/docs/container-security/cluster-add/#install-the-policy-based-deployment-controller
 */ 
export class PolicyBasedDeploymentController extends cdk.Construct{
    constructor(scope: cdk.Construct, id:string, props: PolicyBasedDeploymentControllerProps){
        super(scope, id);
        
        const {cluster, apiKey} = props;
        new eks.HelmChart(this, "policybased-deployment", {
            cluster,
            release: "trendmicro",
            namespace: "trendmicro-system",
            createNamespace: true,
            chart: "https://github.com/trendmicro/cloudone-container-security-helm/archive/master.tar.gz",
            values: {
                cloudOne: {
                    apiKey
                }
            } 
        })
    }
}
```

### Stackにpolicy-based deployment controllerのインストールを追加する。

あとは、先ほど作成した`lib/cdk-container-security-sample-stack.ts`から呼び出すだけです。

長くなるので主な変更箇所のみ示します

```typescript:lib/cdk-container-security-sample-stack.ts
import * as cdk from '@aws-cdk/core';
import * as ec2 from '@aws-cdk/aws-ec2';
import * as eks from '@aws-cdk/aws-eks';
import * as rds from '@aws-cdk/aws-rds';

//追加
import {PolicyBasedDeploymentController} from './helm-policybased-deployment-controller';

//中略

export class CdkContainerSecuritySampleStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    //だいぶ中略
    
    //Cloud One Container Securityの導入
    new PolicyBasedDeploymentController(this, "cloudone", {
      cluster,
      apiKey: cdk.SecretValue.cfnParameter(new cdk.CfnParameter(this, 'CloudOneApiKey', {type: "AWS::SSM::Parameter::Value<String>", noEcho: true}))
    });
  }
  
  //中略
}
```

先ほども少し触れましたが、今回はCloudFormationのパラメータを使用して、Systems ManagerのパラメータストアからAPI Keyを参照することにしました。ということで、デプロイ時のコマンドが少し変わります。

```bash
$ yarn cdk deploy --parameters CloudOneApiKey=/cloudone/apikey/samplecluster
```

では、早速事項して結果を確認しましょう。

## 結果確認

`cdk deploy`コマンドを打ってしばらく待つと、先ほど作成したEKS Clusterに`trendmicro-system`という`namespace`が作成され、その中にいくつかのPodが作成されます。

![スクリーンショット 2021-07-23 20.30.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1782759/896f160e-777e-4d1c-7d78-0a8977367a10.png)

全てのPodのステータスが`Running`となっているので正しく稼働していそうです。

また、CloudOneのコンソールを確認すると、

![スクリーンショット 2021-07-23 20.32.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1782759/ffaa7479-345e-76ba-e904-4ef17070eba9.png)

このように`Last Evaluation`の時間が更新され、クラスタが認識されたことが分かります。

# まとめと今後について

ここまでで、

- AWS CDKを用いてセキュアにAWS EKSクラスタとそこから参照するRDSを作成し
- 同じくAWS CDKを用いて作成したEKSクラスタをCloud One Container Securityの監視対象にする

ことができました。
[ガイド](https://cloudone.trendmicro.com/docs/container-security/policy-create/#define-a-policy-for-a-cluster)に記載の通り、ここからはCloud Oneのコンソールから「ポリシー」を定義して、クラスタに対して様々な監視やガードレールの敷設を行うことができます。VPCレベルでのガードレールの敷設はAWS Configなどからも可能ですが、Kubernetesのレイヤーに対してガードレールが設置できるのは非常にありがたいですね。

![スクリーンショット 2021-07-23 21.42.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1782759/18c89d7b-1a56-da34-c569-09a954c26658.png)


本来、AWS ECR上のイメージに対してイメージスキャンを実行して、Container Securityのポリシーと連動して脆弱性のあるイメージのデプロイを防ぐための[Deep Security SmartCheck](https://cloudone.trendmicro.com/docs/container-security/sc-about/)のセットアップについてAWS CDKで行う予定だったのですが、記事が長くなりそうなので一度ここで区切らせて頂こうかと思います。

また数日後にそちらのやり方についても公開できればと思います。

また、ソースの全体像についてはSmartCheckのセットアップを完了したのち、GitHubで公開しようと思います。

# Appendix

## EKS Clusterの片づけについて

今回作成したEKS Clusterおよびその周りのVPC,RDSを稼働させ続けているとそれなりの利用料が発生します。

Infrastructure as Codeのメリットの１つは、
*(後で同じ環境が再現できるので)一度作った環境を躊躇なく消せる*
ことです。

クラウド破産しないためにも、検証が終わったら

```bash
$ yarn run cdk destory 
``` 

で環境を削除しておくことを忘れないようにしましょう。


## API Keyの管理にAWS SecretsManagerを使用するを断念した理由

本来はパラメータストアではなくSecretManagerにAPI Keyを登録して、

```typescript
    //Cloud One Container Securityの導入
    //注意：失敗します
    new PolicyBasedDeploymentController(this, "cloudone", {
      cluster,
      apiKey: cdk.SecretValue.secretsManager('cloudone-secret',{jsonField:"SAMPLE_API_KEY"})
    });
```

のように呼び出した方が`cdk deploy`のコマンドを変えることもなく、API Keyの管理自体も暗号化されていてスマートだと思ったのですが、うまく行きませんでした。

`@aws-cdk/aws-eks`の`HelmChart`コンストラクトを使用すると、Cloud Formationレベルでは`カスタムリソース`が作成されます。そして、`cdk.SecretValue.secretsManager`を用いてSecretsManagerから値を参照すると、CloudFormationレベルでは[動的参照](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/dynamic-references.html#dynamic-references-secretsmanager)が用いられます。

残念なことに、[動的参照](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/dynamic-references.html#dynamic-references-secretsmanager)のリファレンスに記載の通り、


> ssm-secure や secretsmanager などの安全な値の動的な参照は、現在カスタムリソースではサポートされていません。

ということで2021/07現在、`HelmChart`のプロパティに対してSecretManagerの値は解決できないようです。

今回は苦肉の策として、

- ソースコードや設定ファイル上にAPI Keyをハードコードしなくて済む
- `cdk deploy`などのコマンド実行時にAPI Keyそのものを入力しなくて済む

を最低限満たす方法として、CloudFormationパラメータ+パラメータストアという手段を採用しました。