Amazon Web Services ブログ

AWS Cloud Development Kit と cdk-nag でアプリケーションのセキュリティとコンプライアンスを管理する

この記事は Arun Donti によって寄稿された Manage application security and compliance with the AWS Cloud Development Kit and cdk-nag (記事公開日: 2022 年 5 月 25 日) を翻訳したものです。

Infrastructure as Code (IaC) は、クラウド・アプリケーションの重要な要素です。開発者は、様々な静的解析 (SAST) ツールを利用して、セキュリティやコンプライアンスの問題を特定し、アプリケーションを本番環境にリリースする前に、これらの問題を早期に軽減することができます。さらに、静的解析ツールは、開発者がセキュリティレビュー中にコンプライアンスを検証するために役立つレポートメカニズムを提供してくれます。

cdk-nag は、AWS Cloud Development Kit (AWS CDK) アプリケーションに直接統合し、静的解析ツールと同様の検出およびレポートメカニズムを提供します。

本投稿では、AWS CDK アプリケーションに cdk-nag を統合して、継続的なフィードバックを提供し、アプリケーションをベストプラクティスな形に整えていく方法を紹介します。

cdk-nag の概要

cdk-nag (cfn_nag により影響) は、与えられたスコープ内のコンストラクトの状態が、与えられたルール群に準拠しているかどうかを検証します。さらに、cdk-nag は、ルール抑制とコンプライアンスレポートシステムを提供します。cdk-nag は、AWS CDK Aspects を拡張することにより、コンストラクトを検証します。もし、AWS CDK Aspects の仕組みについての詳細に興味がある場合、この投稿をチェックしてみてください。

cdk-nag には、アプリケーションを検証するためのいくつかのルールセット(NagPacks)が含まれています。この投稿の時点では、cdk-nagは、AWS SolutionsHIPAA SecurityNIST 800-53 rev 4NIST 800-53 rev 5、およびPCI DSS 3.2.1 のNagPacks を含んでいます。あなたは、異なる NagPacks を選択し、与えられたスコープに必要なものだけを適用することができます。

cdk-nag のルールは、警告またはエラーのいずれかを指定できます。警告エラーの両方は、コンソールおよびコンプライアンスレポートに表示されます。抑制されていないエラーのみが、cdk deploy コマンドによるアプリケーションのデプロイを防いでくれます。

どのルールが各 NagPacks に実装されているかは、GitHub リポジトリの Rules Documentation で確認することができます。

手順

本手順では、最小限の AWS CDK v2 アプリケーションをセットアップし、アプリケーションに NagPack を適用する方法、ルールを抑制する方法、調査結果のレポートを表示する方法について説明します。 cdk-nag は Python 、 TypeScript 、 Java、 .NET による AWS CDKアプリケーションをサポートしていますが、本手順ではTypeScriptを使用します。

前提

本手順では、以下の前提条件を満たしている必要があります。

  • AWS CDK をローカルにインストールし、使用した経験があること。

ベースとなる AWS CDK アプリケーションの作成

このセクションでは、Amazon Simple Storage Service (Amazon S3) のバケットを使った、シンプルな AWS CDK v2 アプリケーションを作成します。もし、AWS CDK の使用に慣れていない場合は、オープンソースの GitHub リポジトリを参照し、AWS CDK のインストールとセットアップの方法を学習することをおすすめします。

  1. 以下のコマンドを実行し、AWS CDK アプリケーションを作成します。
mkdir CdkTest
cd CdkTest
cdk init app --language typescript
  1. lib/cdk_test-stack.ts を、以下の内容に置き換えます。
import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { Bucket } from 'aws-cdk-lib/aws-s3';

export class CdkTestStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);
    const bucket = new Bucket(this, 'Bucket')
  }
}
  1. 以下のコマンドを実行して、依存関係のインストール、および、サンプルアプリケーションの合成を行います。
npm install
npx cdk synth

ターミナルコンソールの画面と cdk.out/CdkTestStack.template.json の両方に、S3 バケットを持つ AWS CloudFormation テンプレートが表示されます。

アプリケーションに NagPack を適用する

このセクションでは、cdk-nag をインストールし、AwsSolutions NagPack をアプリケーションに含め、結果を表示します。

  1. 以下のコマンドを実行し、AWS CDK アプリケーションを作成します。
npm install cdk-nag
  1. bin/cdk_test.ts を、以下の内容に置き換えます。
#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import { CdkTestStack } from '../lib/cdk_test-stack';
import { AwsSolutionsChecks } from 'cdk-nag'
import { Aspects } from 'aws-cdk-lib';

const app = new cdk.App();
// Add the cdk-nag AwsSolutions Pack with extra verbose logging enabled.
Aspects.of(app).add(new AwsSolutionsChecks({ verbose: true }))
new CdkTestStack(app, 'CdkTestStack', {});
  1. 以下のコマンドを実行して出力結果を表示し、コンプライアンスレポートを作成します。
npx cdk synth

出力結果は、以下のようになるはずです(注:SSEはServer-side encryptionの略です)。

[Error at /CdkTestStack/Bucket/Resource] AwsSolutions-S1: The S3 Bucket has server access logs disabled. The bucket should have server access logging enabled to provide detailed records for the requests that are made to the bucket.

[Error at /CdkTestStack/Bucket/Resource] AwsSolutions-S2: The S3 Bucket does not have public access restricted and blocked. The bucket should have public access restricted and blocked to prevent unauthorized access.

[Error at /CdkTestStack/Bucket/Resource] AwsSolutions-S3: The S3 Bucket does not default encryption enabled. The bucket should minimally have SSE enabled to help protect data-at-rest.

[Error at /CdkTestStack/Bucket/Resource] AwsSolutions-S10: The S3 Bucket does not require requests to use SSL. You can use HTTPS (TLS) to help prevent potential attackers from eavesdropping on or manipulating network traffic using person-in-the-middle or similar attacks. You should allow only encrypted connections over HTTPS (TLS) using the aws:SecureTransport condition on Amazon S3 bucket policies.

Found errors

AwsSolutions NagPack をアプリケーションに適用すると、コンソールに複数のエラーAwsSolutions-S1AwsSolutions-S2AwsSolutions-S3AwsSolutions-S10)が表示されます。さらに、cdk.out/AwsSolutions-CdkTestStack-NagReport.csv にも、同様にエラー内容が出力されます。

Rule ID,Resource ID,Compliance,Exception Reason,Rule Level,Rule Info
"AwsSolutions-S1","CdkTestStack/Bucket/Resource","Non-Compliant","N/A","Error","The S3 Bucket has server access logs disabled."
"AwsSolutions-S2","CdkTestStack/Bucket/Resource","Non-Compliant","N/A","Error","The S3 Bucket does not have public access restricted and blocked."
"AwsSolutions-S3","CdkTestStack/Bucket/Resource","Non-Compliant","N/A","Error","The S3 Bucket does not default encryption enabled."
"AwsSolutions-S5","CdkTestStack/Bucket/Resource","Compliant","N/A","Error","The S3 static website bucket either has an open world bucket policy or does not use a CloudFront Origin Access Identity (OAI) in the bucket policy for limited getObject and/or putObject permissions."
"AwsSolutions-S10","CdkTestStack/Bucket/Resource","Non-Compliant","N/A","Error","The S3 Bucket does not require requests to use SSL."

エラーの修正と抑制

ここでは、AwsSolutions-S10 エラーの修復、Stackレベルの AwsSolutions-S1 エラーの抑制、Resource レベルの AwsSolutions-S2 エラーの抑制、AwsSolutions-S3 エラーの修復を行わず、結果を表示します。

  1. lib/cdk_test-stack.ts を、以下の内容に置き換えます。
import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { Bucket } from 'aws-cdk-lib/aws-s3';
import { NagSuppressions } from 'cdk-nag'

export class CdkTestStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);
    // The local scope 'this' is the Stack. 
    NagSuppressions.addStackSuppressions(this, [
      {
        id: 'AwsSolutions-S1',
        reason: 'Demonstrate a stack level suppression.'
      },
    ])
    // Remediating AwsSolutions-S10 by enforcing SSL on the bucket.
    const bucket = new Bucket(this, 'Bucket', { enforceSSL: true })
    NagSuppressions.addResourceSuppressions(bucket, [
      {
        id: 'AwsSolutions-S2',
        reason: 'Demonstrate a resource level suppression.'
      },
    ])
  }
}
  1. cdk synth コマンドを再度実行します。
npx cdk synth

以下のように出力されるはずです。

[Error at /CdkTestStack/Bucket/Resource] AwsSolutions-S3: The S3 Bucket does not default encryption enabled. The bucket should minimally have SSE enabled to help protect data-at-rest.

Found errors

cdk.out/AwsSolutions-CdkTestStack-NagReport.csv には、ルールの準拠、非準拠、抑制の詳細が記載されています。

Rule ID,Resource ID,Compliance,Exception Reason,Rule Level,Rule Info
"AwsSolutions-S1","CdkTestStack/Bucket/Resource","Suppressed","Demonstrate a stack level suppression.","Error","The S3 Bucket has server access logs disabled."
"AwsSolutions-S2","CdkTestStack/Bucket/Resource","Suppressed","Demonstrate a resource level suppression.","Error","The S3 Bucket does not have public access restricted and blocked."
"AwsSolutions-S3","CdkTestStack/Bucket/Resource","Non-Compliant","N/A","Error","The S3 Bucket does not default encryption enabled."
"AwsSolutions-S5","CdkTestStack/Bucket/Resource","Compliant","N/A","Error","The S3 static website bucket either has an open world bucket policy or does not use a CloudFront Origin Access Identity (OAI) in the bucket policy for limited getObject and/or putObject permissions."
"AwsSolutions-S10","CdkTestStack/Bucket/Resource","Compliant","N/A","Error","The S3 Bucket does not require requests to use SSL."

さらに、結果として出力された cdk.out/CdkTestStack.template.json には、cdk-nag の抑制データが含まれていることに注目してください。抑制データがリソースに含まれるため、アプリケーションに適用されなかったルールの透明性が確保されます。

{
  "Metadata": {
    "cdk_nag": {
      "rules_to_suppress": [
        {
          "id": "AwsSolutions-S1",
          "reason": "Demonstrate a stack level suppression."
        }
      ]
    }
  },
  "Resources": {
    "BucketDEB6E181": {
      "Type": "AWS::S3::Bucket",
      "UpdateReplacePolicy": "Retain",
      "DeletionPolicy": "Retain",
      "Metadata": {
        "aws:cdk:path": "CdkTestStack/Bucket/Resource",
        "cdk_nag": {
          "rules_to_suppress": [
            {
              "id": "AwsSolutions-S2",
              "reason": "Demonstrate a resource level suppression."
            }
          ]
        }
      }
    },
  ...
  },
  ...
}

手順の振り返り

このセクションでは、アプリケーションに NagPack を適用する方法、警告エラーを修正/抑制する方法、およびコンプライアンスレポートを確認する方法を学びました。レポートと抑制システムは、組織内の開発チームとセキュリティチームが協力して、潜在的なセキュリティ/コンプライアンス問題を特定し、軽減するためのメカニズムを提供します。セキュリティは、開発者が自分のアプリケーションに適用すべき NagPacks を選択することができます。そして、開発者はフィードバックを得ながら、問題を迅速に修正することができます。セキュリティはコンプライアンスを検証するために、レポートを使用することができます。さらに、開発者とセキュリティ担当は、抑制化を使用して、従わないと判断した例外的なルールを、透過的に文書化するために、相互に協力することができます。

高度な使用法と追加情報

このセクションでは、cdk-nag を使用するためのいくつかの高度なオプションについて、簡単に説明します。

AWS CDK Assertions Library を使用した単体テスト

AWS CDK Assertion Library のアノテーションサブモジュールは、アプリケーションのユニットテストに NagPack を統合することで、AWS 認証情報なしで cdk-nag警告エラーをチェックすることができます。AWS CDK アサーションモジュールの詳細については、この投稿をお読みください。以下は、TypeScript の AWS CDK アプリケーションとユニットテスト用の Jest でのアサーションを使用する例です。

import { Annotations, Match } from 'aws-cdk-lib/assertions';
import { App, Aspects, Stack } from 'aws-cdk-lib';
import { AwsSolutionsChecks } from 'cdk-nag';
import { CdkTestStack } from '../lib/cdk_test-stack';

describe('cdk-nag AwsSolutions Pack', () => {
  let stack: Stack;
  let app: App;
  // In this case we can use beforeAll() over beforeEach() since our tests 
  // do not modify the state of the application 
  beforeAll(() => {
    // GIVEN
    app = new App();
    stack = new CdkTestStack(app, 'test');

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

  // THEN
  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);
  });
});

さらに、多くのテストフレームワークは Watch 機能を備えています。これは、プロジェクト内のファイルが変更されたときに、すべてのテストを再実行するバックグラウンド処理で、高速なフィードバックが得られます。例えば、JavaScript/Typescript で AWS CDK を記述した場合、Jest CLI の watch コマンドを使用することができます。Jest watch はファイルの変更を検出すると、変更されたファイルに関連するユニットテストの実行を試みます。この仕組みを利用して、AWS CDK アプリケーションに変更を加える際に、cdk-nag 関連のテストを自動的に実行することができます。

CDK Watch

本番環境ではない環境で開発する場合、高速なフィードバックを得るために AWS CDK Watchを NagPack と一緒に使うことを検討してください。AWS CDK Watch は、ファイルに変更を保存するたびに、コードの合成を試みてから変更をデプロイします。 Aspects はコードの合成中に実行されます。したがって、アプリケーションに適用された NagPack は、保存時にも実行されます。手順の内容と同様に、すべての未対応エラーはデプロイを防止し、すべてのメッセージはコンソールに出力され、すべてのコンプライアンスレポートが生成されます。 AWS CDK Watch の詳細については、こちらの投稿をご覧ください。

まとめ

本投稿では、AWS CDK アプリケーションで cdk-nag を使用する方法を学びました。あなたのアプリケーションで cdk-nag を使用する方法の詳細については、 GitHub Repository の README をチェックしてください。独自のルールや NagPacks を作成する方法を学びたい場合は、開発者向けドキュメントをチェックしてください。このリポジトリはオープンソースであり、コミュニティの貢献とフィードバックを歓迎します。

翻訳はプロフェッショナルサービスの水流が担当しました。原文はこちらです。