マルチアカウント・マルチリージョンのSecurityHubをまとめて無効化してみた

AWS
AWS
この記事は約26分で読めます。

検証でSecurityHubの有効化などを試していましたが、どのアカウントのどのリージョンでSecurityHubが有効化されているか分からなくなったので、まとめてSecurityHubを無効化してみました。

構成図です。

任意のアカウントでLambdaを作成し、そのLambdaから指定したアカウントの全リージョンSecurityHubを無効化します。
実行する環境がLambdaである必要もないので、IAMロールの信頼ポリシーなどを変更すれば、ローカルのスクリプトからでも実行可能と思います。

リソースの説明

Lambda

指定したアカウント・全リージョンのSecurityHubを無効化します。
SecurityHub管理者アカウントにより管理されているメンバーアカウントの関連付け解除は行わないので、無効化するSecurityHubはメンバーアカウントでないことを前提としています。

コードは以下。

import boto3

ec2 = boto3.client('ec2')
sts_connection = boto3.client('sts')

def lambda_handler(event, context):
    print(event)
# 全リージョンをリストで取得
    regions = list(map(lambda x: x['RegionName'], ec2.describe_regions()['Regions']))
#  アカウントごとに繰り返し
    for account in event["accounts"]:
        print("=={} Start==".format(account))
# アカウントごとにSecurityHubを無効化する権限を持つIAMロールにAssumeRole
        acct_b = sts_connection.assume_role(
            RoleArn="arn:aws:iam::" + account + ":role/DisableSecurityHubRole",
            RoleSessionName="lambda"
        )

        ACCESS_KEY = acct_b['Credentials']['AccessKeyId']
        SECRET_KEY = acct_b['Credentials']['SecretAccessKey']
        SESSION_TOKEN = acct_b['Credentials']['SessionToken']
# 全リージョンで繰り返し
        for region in regions:
            try:
                print("--{}--".format(region))
                securityhub = boto3.client(
                    'securityhub',
                    region_name=region,
                    aws_access_key_id=ACCESS_KEY,
                    aws_secret_access_key=SECRET_KEY,
                    aws_session_token=SESSION_TOKEN,
                )
# SecurityHubを無効化
                response = securityhub.disable_security_hub()
                print(response)
                print("SecurityHubを無効化しました")
            except securityhub.exceptions.ResourceNotFoundException as e:
                print("SecurityHubが有効化されていません")
                print(e)
            except Exception as e:
                print(e)

        print("=={} End==".format(account))

上記のように、指定したアカウントごとに存在するIAMロールにAssumeRoleした上で、そのアカウントのリージョンごとにSecurityHubの無効化を繰り返しています。

なお、Lambdaの実行時間はデフォルトのタイムアウト時間である3秒では当然終わらないので、5分にしています。アカウント数によっては最大の15分でも終わらないと思うので、その際はLambdaではなくローカルでスクリプトを実行してもいいでしょう。

IAMロール

LambdaのIAMロールと、そのIAMロールがAssumeRoleするIAMロールがあります。
LambdaのIAMロールは通常のLambdaのIAMロールに付与される権限(AWSLambdaExecute)と、全リージョンを取得するための権限、もう一つのIAMロールにAssumeRoleする権限を持っています。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:*"
            ],
            "Resource": "arn:aws:logs:*:*:*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:GetObject",
                "s3:PutObject"
            ],
            "Resource": "arn:aws:s3:::*"
        }
    ]
}
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": "ec2:DescribeRegions",
            "Resource": "*",
            "Effect": "Allow"
        }
    ]
}
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Condition": {
                "StringEquals": {
                    "aws:ResourceOrgID": "o-xxx"
                }
            },
            "Action": "sts:AssumeRole",
            "Resource": "*",
            "Effect": "Allow"
        }
    ]
}

今回無効化するSecurityHubのアカウントは同一組織に所属するので、AssumeRole先が同一組織に所属する場合のみAssumeRoleを許可する内容にしています。

もう一つのIAMロールは、SecurityHubを無効化する権限を持っており、

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": "securityhub:DisableSecurityHub",
            "Resource": "arn:aws:securityhub:*:xxx:hub/default",
            "Effect": "Allow"
        }
    ]
}

信頼ポリシーでLambdaのIAMロールのみ許可しています。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::xxx:role/DisableSecurityHubLambdaRole"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

リソースの展開方法

Lambdaは一つのアカウントだけで作成し、IAMロールはSecurityHubを無効化するアカウントごとに作成します。
私は、LambdaはCloudFormation Stackで、各アカウントに必要なIAMロールはCloudFormation Stack と StackSet で作成しました。

Lambda

以下内容のCloudFormationテンプレートでスタックを作成しました。

AWSTemplateFormatVersion: 2010-09-09
Description: Create Lambda to disable SecurityHub

Parameters:
  LambdaName:
    Type: String
    Default: DisableSecurityHubLambda
  OrgId:
    Type: String
    Description: Organization ID to which the Lambda that disables the SecurityHub belongs
    Default: o-xxx

Resources:
  Lambda:
    Type: AWS::Lambda::Function
    Properties:
      Code:
        ZipFile: |
          import boto3

          ec2 = boto3.client('ec2')
          sts_connection = boto3.client('sts')

          def lambda_handler(event, context):
              print(event)
              regions = list(map(lambda x: x['RegionName'], ec2.describe_regions()['Regions']))

              for account in event["accounts"]:
                  print("=={} Start==".format(account))
                  acct_b = sts_connection.assume_role(
                      RoleArn="arn:aws:iam::" + account + ":role/DisableSecurityHubRole",
                      RoleSessionName="lambda"
                  )

                  ACCESS_KEY = acct_b['Credentials']['AccessKeyId']
                  SECRET_KEY = acct_b['Credentials']['SecretAccessKey']
                  SESSION_TOKEN = acct_b['Credentials']['SessionToken']

                  for region in regions:
                      try:
                          print("--{}--".format(region))
                          securityhub = boto3.client(
                              'securityhub',
                              region_name=region,
                              aws_access_key_id=ACCESS_KEY,
                              aws_secret_access_key=SECRET_KEY,
                              aws_session_token=SESSION_TOKEN,
                          )
                          response = securityhub.disable_security_hub()
                          print(response)
                          print("SecurityHubを無効化しました")
                      except securityhub.exceptions.ResourceNotFoundException as e:
                          print("SecurityHubが有効化されていません")
                          print(e)
                      except Exception as e:
                          print(e)

                  print("=={} End==".format(account))
      Role: !GetAtt LambdaRole.Arn
      Runtime: python3.9
      Handler: index.lambda_handler
      FunctionName: !Ref LambdaName
      MemorySize: 512
      Timeout: 300

  LambdaRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub ${LambdaName}Role
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AWSLambdaExecute
  RolePolicyDescribeRegions:
    Type: AWS::IAM::Policy
    Properties:
      PolicyName: DescribeRegions
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action: ec2:DescribeRegions
            Resource: "*"
      Roles:
        - !Ref LambdaRole
  RolePolicyAssumeRole:
    Type: AWS::IAM::Policy
    Properties:
      PolicyName: AssumeRole
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action: sts:AssumeRole
            Resource: "*"
            Condition:
              StringEquals:
                aws:ResourceOrgID: !Ref OrgId
      Roles:
        - !Ref LambdaRole

IAMロール

以下テンプレートをCloudFormation StackSetにより各アカウントに作成しました。

AWSTemplateFormatVersion: 2010-09-09
Description: Create an IAM role with permissions for the account that will disable the SecurityHub

Parameters:
  RoleName:
    Type: String
    Description: IAM role name created by this stack
    Default: DisableSecurityHubRole
  LambdaRoleName:
    Type: String
    Description: IAM role name for Lambda to disable SecurityHub
    Default: DisableSecurityHubLambdaRole
  LambdaExecutionAccountId:
    Type: String
    Description: Account ID under which the Lambda to disable the SecurityHub will run

Resources:
  DisableSecurityHubRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Ref RoleName
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              AWS:
                - !Sub arn:aws:iam::${LambdaExecutionAccountId}:role/${LambdaRoleName}
            Action:
              - sts:AssumeRole
  RolePolicies:
    Type: "AWS::IAM::Policy"
    Properties:
      PolicyName: DisableSecurityHub
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Action: securityhub:DisableSecurityHub
            Resource: !Sub arn:aws:securityhub:*:${AWS::AccountId}:hub/default
      Roles:
        - !Ref DisableSecurityHubRole

組織管理アカウントで組織に対してStackSetをデプロイする場合、組織管理アカウントにはスタックがデプロイされません。
そのため、組織管理アカウントのSecurityHubを無効化したい時は、組織管理アカウントにてCloudFormation Stackも作成します。

実行してみた

Lambdaを呼び出したりLambdaでTest実行することで実行できます。
Lambdaに渡すイベントは以下の形式です。

{
  "accounts": [
    "xxx",
    "xxx"
  ]
}

私は組織内のアカウント全てに対して行いたかったので、まずAWS CLIで組織内のアカウントID全てを取得しました。

$ aws organizations list-accounts --query "Accounts[].Id"     
[
    "xxx",
   (省略)
    "xxx"
]

なお、上記コマンドは組織管理アカウントでのみ実行できます。
上記で得たアカウントのリストをLambdaのテストイベントに保存し、Test実行しました。

ログの一部を抜粋します。

==xxx Start==
--eu-north-1--
SecurityHubが有効化されていません
An error occurred (ResourceNotFoundException) when calling the DisableSecurityHub operation: Account is not subscribed to Security Hub
--ap-south-1--
SecurityHubが有効化されていません
An error occurred (ResourceNotFoundException) when calling the DisableSecurityHub operation: Account is not subscribed to Security Hub
--eu-west-3--
SecurityHubが有効化されていません
An error occurred (ResourceNotFoundException) when calling the DisableSecurityHub operation: Account is not subscribed to Security Hub
--eu-west-2--
SecurityHubが有効化されていません
An error occurred (ResourceNotFoundException) when calling the DisableSecurityHub operation: Account is not subscribed to Security Hub
--eu-west-1--
SecurityHubが有効化されていません
An error occurred (ResourceNotFoundException) when calling the DisableSecurityHub operation: Account is not subscribed to Security Hub
--ap-northeast-3--
SecurityHubが有効化されていません
An error occurred (ResourceNotFoundException) when calling the DisableSecurityHub operation: Account is not subscribed to Security Hub
--ap-northeast-2--
SecurityHubが有効化されていません
An error occurred (ResourceNotFoundException) when calling the DisableSecurityHub operation: Account is not subscribed to Security Hub
--ap-northeast-1--
{'ResponseMetadata': {'RequestId': '810273cd-345d-4432-9cb5-9099697899e1', 'HTTPStatusCode': 200, 'HTTPHeaders': {'date': 'Sun, 12 Jun 2022 00:22:27 GMT', 'content-type': 'application/json', 'content-length': '2', 'connection': 'keep-alive', 'x-amzn-requestid': '810273cd-345d-4432-9cb5-9099697899e1', 'access-control-allow-origin': '*', 'access-control-allow-headers': 'Authorization,Date,X-Amz-Date,X-Amz-Security-Token,X-Amz-Target,content-type,x-amz-content-sha256,x-amz-user-agent,x-amzn-platform-id,x-amzn-trace-id', 'x-amz-apigw-id': 'TlS2dEv3tjMFrng=', 'access-control-allow-methods': 'GET,POST,OPTIONS,PUT,PATCH,DELETE', 'access-control-expose-headers': 'x-amzn-errortype,x-amzn-requestid,x-amzn-errormessage,x-amzn-trace-id,x-amz-apigw-id,date', 'x-amzn-trace-id': 'Root=1-62a531c2-4e2b1ffd1a3e667f33ad95b1', 'access-control-max-age': '86400'}, 'RetryAttempts': 0}}
SecurityHubを無効化しました
--sa-east-1--
SecurityHubが有効化されていません
An error occurred (ResourceNotFoundException) when calling the DisableSecurityHub operation: Account is not subscribed to Security Hub
--ca-central-1--
SecurityHubが有効化されていません
An error occurred (ResourceNotFoundException) when calling the DisableSecurityHub operation: Account is not subscribed to Security Hub
--ap-southeast-1--
SecurityHubが有効化されていません
An error occurred (ResourceNotFoundException) when calling the DisableSecurityHub operation: Account is not subscribed to Security Hub
--ap-southeast-2--
{'ResponseMetadata': {'RequestId': '7af45799-548c-456a-b60f-26a97a5453ca', 'HTTPStatusCode': 200, 'HTTPHeaders': {'date': 'Sun, 12 Jun 2022 00:22:30 GMT', 'content-type': 'application/json', 'content-length': '2', 'connection': 'keep-alive', 'x-amzn-requestid': '7af45799-548c-456a-b60f-26a97a5453ca', 'access-control-allow-origin': '*', 'access-control-allow-headers': 'Authorization,Date,X-Amz-Date,X-Amz-Security-Token,X-Amz-Target,content-type,x-amz-content-sha256,x-amz-user-agent,x-amzn-platform-id,x-amzn-trace-id', 'x-amz-apigw-id': 'TlS3AFJASwMF_Ag=', 'access-control-allow-methods': 'GET,POST,OPTIONS,PUT,PATCH,DELETE', 'access-control-expose-headers': 'x-amzn-errortype,x-amzn-requestid,x-amzn-errormessage,x-amzn-trace-id,x-amz-apigw-id,date', 'x-amzn-trace-id': 'Root=1-62a531c6-11324fec6a46d7c10d38177a', 'access-control-max-age': '86400'}, 'RetryAttempts': 0}}
SecurityHubを無効化しました
--eu-central-1--
SecurityHubが有効化されていません
An error occurred (ResourceNotFoundException) when calling the DisableSecurityHub operation: Account is not subscribed to Security Hub
--us-east-1--
SecurityHubが有効化されていません
An error occurred (ResourceNotFoundException) when calling the DisableSecurityHub operation: Account is not subscribed to Security Hub
--us-east-2--
SecurityHubが有効化されていません
An error occurred (ResourceNotFoundException) when calling the DisableSecurityHub operation: Account is not subscribed to Security Hub
--us-west-1--
SecurityHubが有効化されていません
An error occurred (ResourceNotFoundException) when calling the DisableSecurityHub operation: Account is not subscribed to Security Hub
--us-west-2--
SecurityHubが有効化されていません
An error occurred (ResourceNotFoundException) when calling the DisableSecurityHub operation: Account is not subscribed to Security Hub
==xxx End==

上記のように、アカウントごとに全リージョンでSecurityHubが無効化されます。
特定のリージョンだけ無効化したい場合は、Lambdaのソースコードを変更すればOKです。

タイトルとURLをコピーしました