cdk-nag入門


## はじめに

CDK でパワーをかけずにセキュリティチェックしたい！って時ありますよね。
cdk-nag を使用することで、簡単にセキュリティスキャンができますので、使用方法などを簡単にまとめておきます。

## cdk-nag とは

一言でいうと、CDK のリソースセキュリティ・コンプライアンスをチェックできるツールです。
セキュリティ・コンプライアンスのルールが記載されたルールを選択し、そのルールに準拠しているかを確認することができます。
また、準拠していない場合、デプロイを停止することができます。

現在は、次のルールセットがあります。

- AWS Solutions
- HIPAA Security
- NIST 800-53 rev 4
- NIST 800-53 rev 5
- PCI DSS 3.2.1

特にセキュリティの要件などなければ`AWS Solutions`ルールを使用するのがよいかと思います。

## 使い方

### 前提条件

- `typescript`の言語の使用方法になります。
- `AWS Solutions`ルールに焦点を当てた使用方法になります。

### 準備

次のコマンドで CDK アプリを作成します。

```shell
mkdir cdk-nag-confirm
cd cdk-nag-confirm
cdk init app --language typescript
```

次のコマンドで cdk-nag パッケージをインストールします。

```shell
npm install -D cdk-nag
```

`lib/cdk-nag-confirm-stack.ts`のコメントアウトを外します。

```typescript:lib/cdk-nag-confirm-stack.ts
    const queue = new sqs.Queue(this, "CdkNagConfirmQueue", {
      visibilityTimeout: cdk.Duration.seconds(300),
    });
```

### cdk-nag の導入

`bin/cdk-nag-confirm.ts`の内容を以下の内容に置き換え、`AWS Solutions`ルールに準拠しているか確認するようにします。

```typescript:bin/cdk-nag-confirm.ts
import "source-map-support/register";
import * as cdk from "aws-cdk-lib";
import { AwsSolutionsChecks } from "cdk-nag";
import { CdkNagConfirmStack } from "../lib/cdk-nag-confirm-stack";

const app = new cdk.App();
cdk.Aspects.of(app).add(new AwsSolutionsChecks());
new CdkNagConfirmStack(app, "CdkNagConfirmStack", {});
```

### ルールに沿っているかの確認

次のコマンドを実行し、ルールに沿っているか確認します。

```shell
cdk synth
```

エラーが 2 つ出力されました。

```shell
[Error at /CdkNagConfirmStack/CdkNagConfirmQueue/Resource] AwsSolutions-SQS3: The SQS queue is not used as a dead-letter queue (DLQ) and does not have a DLQ enabled.

[Error at /CdkNagConfirmStack/CdkNagConfirmQueue/Resource] AwsSolutions-SQS4: The SQS queue does not require requests to use SSL.
```

メッセージ内容を確認すると、DLQ を有効にしていないことと
SQS のリクエストに、SSL を強制していないので出力されているようです。

このエラーを修正および抑制していきます。

なお、`cdk.out/AwsSolutions-CdkNagConfirmStack-NagReport.csv`の方にもコンソールと同様の内容が出力されます。

### エラーの修正

SQS のリクエストに SSL を強制するように修正し、`cdk synth`を再実行します。

```diff_typescript:cdk-nag-confirm-stack.ts
    const queue = new sqs.Queue(this, "CdkNagConfirmQueue", {
      visibilityTimeout: cdk.Duration.seconds(300),
+       enforceSSL: true,
    });
```

SSL のエラーが出力されなくなったことが確認できました。

```shell
[Error at /CdkNagConfirmStack/CdkNagConfirmQueue/Resource] AwsSolutions-SQS3: The SQS queue is not used as a dead-letter queue (DLQ) and does not have a DLQ enabled.
```

### エラーの抑制

ルールはあくまでベストプラクティス集のため、特定のルールについては抑制したい場合があると思います。
この場合、サプレスすることによって特定のルールを抑制することができます。
以下は DLQ を抑制する記載方法になります。

```diff_typescript:lib/cdk-nag-confirm-stack.ts
import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
import * as sqs from "aws-cdk-lib/aws-sqs";
+ import { NagSuppressions } from "cdk-nag";

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

    const queue = new sqs.Queue(this, "CdkNagConfirmQueue", {
      visibilityTimeout: cdk.Duration.seconds(300),
      enforceSSL: true,
    });
+     NagSuppressions.addResourceSuppressions(queue, [
+       {
+         id: "AwsSolutions-SQS3",
+         reason: "Demonstrate a resource level suppression.",
+       },
+     ]);
  }
}

```

サプレスに、対象の`ルールID`を渡し、抑制の理由を記述します。
これで再度`cdk synth`を実行すると、エラーが解消され、CloudFormation テンプレートが
出力されるようになるはずです。

### cdk-nag のテストの記載方法

エラーや警告が 1 つもないかを確認するようにテストコードを記述することで、ルールに準拠しているかをテストすることができます。
以下は、テストコードの例になります。

```typescript:test-cdk-nag.ts
import { Annotations, Match } from "aws-cdk-lib/assertions";
import { App, Aspects, Stack } from "aws-cdk-lib";
import { CdkNagConfirmStack } from "../lib/cdk-nag-confirm-stack";
import { AwsSolutionsChecks } from "cdk-nag";

describe("cdk-nag Aws Solutions Pack", () => {
  let stack: Stack;
  let app: App;
  beforeAll(() => {
    app = new App();
    stack = new CdkNagConfirmStack(app, "test-stack");

    Aspects.of(stack).add(new AwsSolutionsChecks());
  });

  test("No unsuppressed Warnings", () => {
    const Warnings = Annotations.fromStack(stack).findWarning(
      "*",
      Match.stringLikeRegexp("AwsSolutions-.*")
    );
    expect(Warnings).toHaveLength(0);
  });

  test("No unsuppressed Errors", () => {
    const errors = Annotations.fromStack(stack).findError(
      "*",
      Match.stringLikeRegexp("AwsSolutions-.*")
    );
    expect(errors).toHaveLength(0);
  });
});
```

## さいごに

cdk-nag はすぐに導入でき、体力をかけずにセキュリティ・コンプライアンス要件をチェックできるので、積極的に使っていきたいと思います！

## 参照

- [AWS Cloud Development Kit と cdk-nag でアプリケーションのセキュリティとコンプライアンスを管理する](https://aws.amazon.com/jp/blogs/news/manage-application-security-and-compliance-with-the-aws-cloud-development-kit-and-cdk-nag/)
- [CDK Aspects を利用してベストプラクティスに従ったインフラストラクチャを構築する](https://aws.amazon.com/jp/blogs/news/align-with-best-practices-while-creating-infrastructure-using-cdk-aspects/#:~:text=CDK%20Aspects%20%E3%81%AF%E3%80%81%E9%96%8B%E7%99%BA%E8%80%85,%E3%81%99%E3%82%8B%E3%81%9F%E3%82%81%E3%81%AE%E6%96%B9%E6%B3%95%E3%81%A7%E3%81%99%E3%80%82)