AWS CDK のカスタムリソースを使って単一のアカウントで Inspector を有効化してみた

AWS CDK のカスタムリソースを使って単一のアカウントで Inspector を有効化してみた

Clock Icon2025.01.12

こんにちは、製造ビジネステクノロジー部の若槻です。

Amazon Inspector は、EC2 インスタンスや ECR コンテナイメージ、Lambda 関数をスキャンして、ソフトウェア脆弱性やネットワーク露出などのセキュリティ問題を自動検出するサービスです。

https://docs.aws.amazon.com/ja_jp/inspector/latest/user/what-is-inspector.html

この Inspector を使う場合は明示的に有効化操作を行う必要がありますが、対応する CloudFormation リソースが無いため、有効化操作を IaC 化したい場合はカスタムリソースを使うことになります。

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/template-custom-resources.html

今回は、カスタムリソースを使って単一アカウントの Inspector を有効化する構成を AWS CDK で実装してみました。

なお、本記事での「Inspector」は Inspector v2 を指している前提とします。

https://dev.classmethod.jp/articles/difference-from-classic-and-v2/

やってみた

CDK コード

Inspector をカスタムリソースで有効化する CDK コードです。

lib/construct/inspector.ts
import { Construct } from 'constructs';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as cr from 'aws-cdk-lib/custom-resources';

export class InspectorConstruct extends Construct {
  constructor(scope: Construct, id: string) {
    super(scope, id);

    new cr.AwsCustomResource(
      this,
      'InspectorV2Enabler',
      {
        /**
         * Inspector2 の enable API を呼び出して Inspector2 を有効化する
         * @see https://docs.aws.amazon.com/inspector/v2/APIReference/API_Enable.html
         */
        onCreate: {
          service: 'Inspector2',
          action: 'enable',
          parameters: {
            resourceTypes: ['EC2', 'ECR', 'LAMBDA', 'LAMBDA_CODE'],
          },
          physicalResourceId: cr.PhysicalResourceId.of('InspectorV2Enabler'),
        },
        policy: cr.AwsCustomResourcePolicy.fromStatements([
          new iam.PolicyStatement({
            actions: [
              'inspector2:Enable',

              /**
               * アカウントに対して Inspector が使用するサービスリンクロールを作成するための権限
               * @see https://docs.aws.amazon.com/ja_jp/inspector/latest/user/using-service-linked-roles.html
               * @see https://docs.aws.amazon.com/ja_jp/inspector/latest/user/slr-permissions-agentless.html
               */
              'iam:CreateServiceLinkedRole',
            ],
            resources: ['*'],
          }),
        ]),

        /**
         * カスタムリソースから inspector2 の disable API を呼び出しても
         * Inspector2 が無効化されない挙動となったため、onDelete による設定無効化は実装していない。
         * 無効化したい場合は手動で実施する。
         * @see https://docs.aws.amazon.com/inspector/v2/APIReference/API_Disable.html
         */
      }
    );
  }
}

動作確認

Inspector の有効化状況を確認します。この時点では無効化されています。

$ aws inspector2 batch-get-account-status --account-ids ${AWS_ACCOUNT_ID}
{
    "accounts": [
        {
            "accountId": "XXXXXXXXXXXX",
            "resourceState": {
                "ec2": {
                    "status": "DISABLED"
                },
                "ecr": {
                    "status": "DISABLED"
                },
                "lambda": {
                    "status": "DISABLED"
                },
                "lambdaCode": {
                    "status": "DISABLED"
                }
            },
            "state": {
                "status": "DISABLED"
            }
        }
    ],
    "failedAccounts": []
}

サービスリンクロール AWSServiceRoleForAmazonInspector2 および AWSServiceRoleForAmazonInspector2Agentless はこの時点で作成されていません。これらのロールは Inspector 有効化時に存在していなければ自動作成されます。

$ aws iam get-role --role-name AWSServiceRoleForAmazonInspector2

An error occurred (NoSuchEntity) when calling the GetRole operation: The role with name AWSServiceRoleForAmazonInspector2 cannot be found.

$ aws iam get-role --role-name AWSServiceRoleForAmazonInspector2Agentless

An error occurred (NoSuchEntity) when calling the GetRole operation: The role with name AWSServiceRoleForAmazonInspector2Agentless cannot be found.

前述の CDK コードを使用してカスタムリソースを作成するデプロイを行います。デプロイが正常に完了したらに設定状況を確認します。

Inspector が有効化されています。

$ aws inspector2 batch-get-account-status --account-ids ${AWS_ACCOUNT_ID}
{
    "accounts": [
        {
            "accountId": "XXXXXXXXXXXX",
            "resourceState": {
                "ec2": {
                    "status": "ENABLED"
                },
                "ecr": {
                    "status": "ENABLED"
                },
                "lambda": {
                    "status": "ENABLED"
                },
                "lambdaCode": {
                    "status": "ENABLED"
                }
            },
            "state": {
                "status": "ENABLED"
            }
        }
    ],
    "failedAccounts": []
}

サービスリンクロールも作成されています。

$ aws iam get-role --role-name AWSServiceRoleForAmazonInspector2
{
    "Role": {
        "Path": "/aws-service-role/inspector2.amazonaws.com/",
        "RoleName": "AWSServiceRoleForAmazonInspector2",
        "RoleId": "AROAUL6WVPY2FUAOM2SNU",
        "Arn": "arn:aws:iam::XXXXXXXXXXXX:role/aws-service-role/inspector2.amazonaws.com/AWSServiceRoleForAmazonInspector2",
        "CreateDate": "2025-01-12T06:35:08+00:00",
        "AssumeRolePolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Effect": "Allow",
                    "Principal": {
                        "Service": "inspector2.amazonaws.com"
                    },
                    "Action": "sts:AssumeRole"
                }
            ]
        },
        "Description": "Allowing Inspector to call AWS services on behalf of customers",
        "MaxSessionDuration": 3600,
        "RoleLastUsed": {}
    }
}

$ aws iam get-role --role-name AWSServiceRoleForAmazonInspector2Agentless
{
    "Role": {
        "Path": "/aws-service-role/agentless.inspector2.amazonaws.com/",
        "RoleName": "AWSServiceRoleForAmazonInspector2Agentless",
        "RoleId": "AROAUL6WVPY2O2WBCZJ7Y",
        "Arn": "arn:aws:iam::XXXXXXXXXXXX:role/aws-service-role/agentless.inspector2.amazonaws.com/AWSServiceRoleForAmazonInspector2Agentless",
        "CreateDate": "2025-01-12T06:35:08+00:00",
        "AssumeRolePolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Effect": "Allow",
                    "Principal": {
                        "Service": "agentless.inspector2.amazonaws.com"
                    },
                    "Action": "sts:AssumeRole"
                }
            ]
        },
        "Description": "Allowing Inspector to call AWS services on behalf of customers",
        "MaxSessionDuration": 3600,
        "RoleLastUsed": {}
    }
}

補足

カスタムリソースから Inspector の無効化はできない?

当初 Inspector の無効化も可能な下記の CDK コードを試しましたが、onDelete を実装しても Inspector が無効化されませんでした。

lib/construct/inspector.ts
import { Construct } from 'constructs';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as cr from 'aws-cdk-lib/custom-resources';

export class InspectorConstruct extends Construct {
  constructor(scope: Construct, id: string) {
    super(scope, id);

    new cr.AwsCustomResource(this, 'InspectorV2Enabler', {
      onCreate: {
        service: 'Inspector2',
        action: 'enable',
        parameters: {
          resourceTypes: ['EC2', 'ECR', 'LAMBDA', 'LAMBDA_CODE'],
        },
        physicalResourceId: cr.PhysicalResourceId.of('InspectorV2Enabler'),
      },
      // カスタムリソースから Inspector の無効化はできなかった
      onDelete: {
        service: 'Inspector2',
        action: 'disable',
        physicalResourceId: cr.PhysicalResourceId.of('InspectorV2Enabler'),
      },
      policy: cr.AwsCustomResourcePolicy.fromStatements([
        new iam.PolicyStatement({
          actions: [
            'inspector2:Enable',
            'inspector2:Disable',
            'iam:CreateServiceLinkedRole',
          ],
          resources: ['*'],
        }),
      ]),
    });
  }
}

一応 parameters でアカウント ID を指定してみましたが、挙動は変わらず。


          parameters: {
            accountIds: [cdk.Aws.ACCOUNT_ID],
          },

policy 権限不足ではないのかデプロイ自体は失敗しません。また手動で無効化は可能でした。環境起因の可能性もありますが、もう少し詳細な調査が必要そうです。もちろん無効化まで IaC 化する必要の無い場合もあるかと思うので、運用要件と照らし合わせて検討する必要があります。

Inspector 無効化前にサービスリンクロールは削除できない

当然ですが Inspector が有効化されている間はサービスリンクロールが必須となるので、Inspector 無効化前に削除することはできません。試すとエラーとなります。

複数アカウント環境での Inspector 有効化

AWS Organizations などで複数アカウントを管理しているなどでデプロイ実行環境と異なるアカウントの Inspector を有効化したい場合は enable API の accountIds パラメーターを指定する必要があります。

https://docs.aws.amazon.com/inspector/v2/APIReference/API_Enable.html

今回はデプロイ実行環境の単一アカウントのみを対象としているため、accountIds パラメーターは指定していません。

おわりに

カスタムリソースを使って単一アカウントの Inspector を有効化する構成を AWS CDK で実装してみました。

セキュリティサービスの利用は設定系が多いため、CloudFormtion および CDK での IaC 化に際してはカスタムリソースの利用が宿命となります。留意するようにしましょう。

以上

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.