「セキュアで堅牢なAWSアカウント」を実現する CloudFormationテンプレート - ④アクセスキーのローテーションと削除

# はじめに

AWSにはアカウントやリソースへの脅威検知に対応した、**AWS IAM Access Analyzer**, **AWS Security Hub**, **Amazon Detective**, **Amazon Inspector**, **Amazon GuardDuty**, **AWS CloudTrail**, **AWS Config** などのサービスが用意されています。

また、**AWS Security Hub** では、[**CIS AWS Foundations Standard**](https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-cis.html) , [**Payment Card Industry Data Security Standard**](https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-pcidss.html) , [**AWS Foundational Security Best Practices Standard**](https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp.html) などのセキュリティ標準が公開されており、このガイドラインは、**AWSアカウントをセキュアに保つために必要なAWSのセキュリティ設定**を集めた**ベストプラクティス集**として活用できます。

本記事では、アカウントやリソースへの**脅威検知が可能なAWSサービスを有効化**するとともに、上記のセキュリティ標準に限りなく準拠することで、セキュアで堅牢なAWSアカウントを実現します。また、これらを**お手軽に実現できるCloudFormationテンプレートを公開**しています。

+ [**aws-cloudformation-templates/security - GitHub**](https://github.com/eijikominami/aws-cloudformation-templates/blob/master/security/)

このCloudFormationテンプレートの実行はこちらから。

[![cloudformation-launch-stack](https://raw.githubusercontent.com/eijikominami/aws-cloudformation-templates/master/images/cloudformation-launch-stack.png)](https://console.aws.amazon.com/cloudformation/home?region=ap-northeast-1#/stacks/create/review?stackName=DefaultSecuritySettings&templateURL=https://eijikominami.s3-ap-northeast-1.amazonaws.com/aws-cloudformation-templates/security/template.yaml) 

![](https://raw.githubusercontent.com/eijikominami/aws-cloudformation-templates/master/images/architecture-default-security-settings.png)

# アクセスキーのローテーション

**アクセスキー** とは、IAM ユーザーまたは ルートユーザー の [**長期的な認証情報**](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_credentials_access-keys.html)を指し、AWS SDK や AWS API を実行する際にこのアクセスキーを指定することで、このアクセスキーに許可された権限を実行することができます。したがって、このアクセスキーが外部に流出した場合には、 **第三者がアカウントへの永続的アクセスを取得する可能性** があります。そこで、**CIS AWS Foundations Benchmark** および **AWS Foundational Security Best Practices Standard** では、

| セキュリティ標準 | No. | 内容 |
| --- | --- | --- |
| CIS AWS Foundations Standard | 1.3 | **90 日間以上使用されていない認証情報は無効にします** |
| CIS AWS Foundations Standard | 1.4 | **アクセスキーは 90 日ごとに更新します** |
| AWS Foundational Security Best Practices Standard | IAM.3 | **IAM ユーザーのアクセスキーは 90 日ごとに更新する必要があります。** |

というガイドラインを規定しており、アクセスキーの定期的なローテーションによって、意図せぬアクセスのリスクを低減するように求めています。

上記ガイドラインに準拠するためには、`AWS Config` を用いて、**定期的に現在の状態の確認** を行う必要があります。また、上記基準を満たしていないことが判明した場合は、**当該のアクセスキーを無効化、もしくは削除** する必要があります。

## 1. AWS Configの有効化

`AWS Config` を設定して、上記ガイドラインに準拠しているかどうかを定期的に確認します。[**「セキュアで堅牢なAWSアカウント」を実現する CloudFormationテンプレート - ①サービスの有効化**](https://qiita.com/drafts/53f3a1d83df31981ad86)も合わせてご覧ください。

### サービスにリンクされたロールの作成

``AWS Config`` で使用する ``Service-Linked Role`` を作成します。この ``Service-Linked Role`` は、 **AWSリソースへの読み込み権限** と **S3への書き込み権限** を ``Config`` に、また、**IAMとSystemManagerへの書き込み権限** を ``Config Remediation`` にそれぞれ付与します。

```yaml
Resources:
  ServiceLinkedRoleForConfig:
    Type: AWS::IAM::ServiceLinkedRole
    UpdateReplacePolicy: Retain
    DeletionPolicy: Retain
    Properties: 
      AWSServiceName: config.amazonaws.com
      Description: A service-linked role required for AWS Config to access your resources.
  ServiceLinkedRoleForConfigRemediation:
    Type: AWS::IAM::ServiceLinkedRole
    UpdateReplacePolicy: Retain
    DeletionPolicy: Retain
    Properties: 
      AWSServiceName: remediation.config.amazonaws.com 
      Description: A service-linked role required for AWS Config Remediation to access your resources.
```

### AWS Configの有効化

``AWS Config`` の ``DeliveryChannel`` と ``ConfigurationRecorder`` を作成します。

```yaml
Resources:
  ConfigDeliveryChannel:
    Type: AWS::Config::DeliveryChannel
    Properties:
      Name: default
      S3BucketName: !Ref S3ForConfig
  ConfigConfigurationRecorder:
    Type: AWS::Config::ConfigurationRecorder
    Properties:
      Name: default
      RecordingGroup:
        AllSupported: true
        IncludeGlobalResourceTypes: true
      RoleARN: !Sub arn:aws:iam::DefaultSecuritySettings:role/aws-service-role/config.amazonaws.com/AWSServiceRoleForConfig
```

### Amazon S3 バケットを作成

**設定情報 （履歴ファイルやスナップショット）** を保存するために使用する、``Amazon S3`` **バケット**と、それに紐づく**バケットポリシー**を作成します。

```yaml
Resources:
  S3ForConfig:
    Type: 'AWS::S3::Bucket'
    UpdateReplacePolicy: Retain
    DeletionPolicy: Retain
    Properties:
      BucketEncryption:
        ServerSideEncryptionConfiguration: 
          - ServerSideEncryptionByDefault: 
              SSEAlgorithm: AES256
      BucketName: !Sub defaultsecuritysettings-config-${AWS::Region}-${AWS::AccountId}
      LifecycleConfiguration:
        Rules:
          - Id: ExpirationInDays
            ExpirationInDays: 60
            Status: Enabled
      PublicAccessBlockConfiguration: 
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
  S3BucketPolicyForConfig:
    Type: AWS::S3::BucketPolicy
    Properties: 
      Bucket: !Ref S3ForConfig
      PolicyDocument:
        Version: 2012-10-17
        Id: !Ref S3ForConfig
        Statement:
          - Effect: Allow
            Principal:
              Service: config.amazonaws.com
            Action:
              - 's3:GetBucketAcl'
              - 's3:ListBucket'
            Resource:
              - !GetAtt S3ForConfig.Arn
          - Effect: Allow
            Principal:
              Service: config.amazonaws.com
            Action:
              - 's3:PutObject'
            Resource:
              - !Join
                - ''
                - - !GetAtt S3ForConfig.Arn
                  - /AWSLogs/
                  - !Sub ${AWS::AccountId}
                  - /Config/*
            Condition:
              StringEquals:
                s3:x-amz-acl: bucket-owner-full-control
          - Effect: Deny
            Principal: '*'
            Action: 's3:*'
            Resource: 
              - !GetAtt S3ForConfig.Arn
              - !Join
                - ''
                - - !GetAtt S3ForConfig.Arn
                  - /*
            Condition:
              Bool: 
                aws:SecureTransport: false
```

### 2. AWS Configを用いた定期チェック

AWSによって管理されている `AWS Config マネージドルール` の中に、 `ACCESS_KEYS_ROTATED` という名称で、**アクティブなアクセスキーが指定された日数内にローテーションされるかどうかを確認できるルール** が用意されているので、これを追加します。

```yaml
Resources:
  ConfigIamAccessKeysRotated:
    DependsOn:
      - ConfigConfigurationRecorder
    Type: 'AWS::Config::ConfigRule'
    Properties:
      ConfigRuleName: access-keys-rotated
      Description: アクティブなアクセスキーが、maxAccessKeyAge で指定された日数内にローテーションされるかどうかを確認します。
      InputParameters:
        maxAccessKeyAge: 90
      Source:
        Owner: AWS
        SourceIdentifier: ACCESS_KEYS_ROTATED
```

上記で設定した AWS Config ルールは定期的に実行され、アカウントがこのルールに準拠しているかどうかの判定が行われます。しかし、これらの設定は、ルールに準拠しているかどうかを判定するだけであるため、もしルールに非準拠であることが判明した場合には、**該当するアクセスキーを手動で削除する必要があります**。

## 3. Lambdaを用いたアクセスキーの強制削除

そこで、以下の内容も設定に加えることで、**ガイドラインの基準に違反するアクセスキーを強制的に削除** することができます。ただし下記の設定は、**ユーザに確認を求めることなく問答無用にアクセスキーを削除してしまう** ため、下記設定を加えるかどうかについては **慎重な検討が必要** です。またもしこの設定を適用する場合でも、**ご自身のアカウントの運用ポリシーに合った内容に処理を書き換えることをオススメします**。

### Amazon EventBridgeの追加

`AWS Config` が発行する `Amazon EventBridge`のうち、AWS Configに非準拠のルールが検知された場合のみを抽出して、後述の **アクセスキーを強制削除するLambdaの実行トリガ** にします。

```yaml
  CloudWatchEventsForConfigIamAccessKeysRotated:
    Type: 'AWS::Events::Rule'
    Properties: 
      Description: EventBridge about Config when IAM Access Keys are rotated.
      EventPattern:
        source:
          - aws.config
        detail-type: 
          - Config Rules Compliance Change
        detail:
          configRuleName:
            - !Ref ConfigIamAccessKeysRotated
          messageType:
            - ComplianceChangeNotification
          newEvaluationResult:
            complianceType:
              - NON_COMPLIANT
      Name: Config
      State: ENABLED
      Targets:
        - Arn: !GetAtt LambdaDeleteExpiredAccessKeys.Arn
          Id: lambda
```

### Lambda 関数の追加

アクセスキーを強制削除するLambdaを作成します。このLambdaは、AWS Configから通知された内容が、 アクティブなアクセスキーが指定された日数（=**90日**）内にローテーションされるかどうかを確認できる、`access-keys-rotated` に関連するものであることを確認した上で、**作成から90日が経過したアクセスキーのみを削除** します。

```yaml
  LambdaDeleteExpiredAccessKeys:
    Type: 'AWS::Lambda::Function'
    Properties:
      Code:
        ZipFile: |
          import boto3
          import datetime
          import time
          import logging
          logger = logging.getLogger()
          logger.setLevel(logging.INFO)
          def lambda_handler(event, context):
              logger.info(str(event))
            
              if 'detail' in event:
                  detail = event['detail']
                  if 'configRuleName' in detail:
                      # access-keys-rotated
                      if detail['configRuleName'] == 'access-keys-rotated':
                          iam = boto3.client('iam')
                          users = iam.list_users()
                          for user in users['Users']:
                              if detail['resourceId'] == user['UserId']:
                                  access_keys = iam.list_access_keys(
                                      UserName=user['UserName']
                                      )
                                  for access_key in access_keys['AccessKeyMetadata']:
                                      create_date = access_key['CreateDate'].timestamp()
                                      now = time.time()
                                      if now - create_date > 60*60*24*90:
                                          response = iam.delete_access_key(
                                              UserName=user['UserName'],
                                              AccessKeyId=access_key['AccessKeyId']
                                          )
      Description: 有効期限が過ぎたアクセスキーを削除します
      FunctionName: deleteExpiredAccessKeys
      Handler: index.lambda_handler
      MemorySize: 128
      Role: !GetAtt IAMRoleForLambda.Arn
      Runtime: python3.7
      Tags:
        - Key: !Ref TagKey
          Value: !Ref TagValue
      Timeout: 3
      TracingConfig:
        Mode: Active
  LambdaDeleteExpiredAccessKeysPermission:
    Type: 'AWS::Lambda::Permission'
    Properties:
      Action: lambda:InvokeFunction
      FunctionName: !Ref LambdaDeleteExpiredAccessKeys
      Principal: events.amazonaws.com
      SourceArn: !GetAtt CloudWatchEventsForConfigIamAccessKeysRotated.Arn
  LambdaDeleteExpiredAccessKeysLogGroup:
    Type: AWS::Logs::LogGroup
    Properties: 
      LogGroupName: !Sub /aws/lambda/${LambdaDeleteExpiredAccessKeys}
      RetentionInDays: 60
```

下記のCloudFormationテンプレートを実行することで、上記設定を含む、 **AWS IAM Access Analyzer**, **AWS Security Hub**, **Amazon Detective**, **Amazon Inspector**, **Amazon GuardDuty**, **AWS CloudTrail**, **AWS Config** などのサービス全てを有効化することも、今回の設定を含むAWS Configのみを有効化することも可能です。

| 作成されるAWSサービス | CloudFormationテンプレート |
| --- | --- |
| 全てのセキュリティサービス | [![cloudformation-launch-stack](https://raw.githubusercontent.com/eijikominami/aws-cloudformation-templates/master/images/cloudformation-launch-stack.png)](https://console.aws.amazon.com/cloudformation/home?region=ap-northeast-1#/stacks/create/review?stackName=DefaultSecuritySettings&templateURL=https://eijikominami.s3-ap-northeast-1.amazonaws.com/aws-cloudformation-templates/security/template.yaml)  |
| AWS Configのみ | [![cloudformation-launch-stack](https://raw.githubusercontent.com/eijikominami/aws-cloudformation-templates/master/images/cloudformation-launch-stack.png)](https://console.aws.amazon.com/cloudformation/home?region=ap-northeast-1#/stacks/create/review?stackName=Config&templateURL=https://eijikominami.s3-ap-northeast-1.amazonaws.com/aws-cloudformation-templates/security/config.yaml&param_PrefixOfLogicalName=Config)|


# 関連リンク
1. [**サービスの有効化** - 「セキュアで堅牢なAWSアカウント」を実現する CloudFormationテンプレート](https://qiita.com/eijikominami/items/53f3a1d83df31981ad86)
2. [**パスワードポリシーの自動修復** - 「セキュアで堅牢なAWSアカウント」を実現する CloudFormationテンプレート](https://qiita.com/eijikominami/items/85acced082d0dc8d9901)
3. [**モニタリングと通知の設定** - 「セキュアで堅牢なAWSアカウント」を実現する CloudFormationテンプレート](https://qiita.com/eijikominami/items/1887d19ce3bb33f716ba)
4. **アクセスキーのローテーションと削除** - 「セキュアで堅牢なAWSアカウント」を実現する CloudFormationテンプレート 
5. [**全てのVPCでフローログを有効化する** - 「セキュアで堅牢なAWSアカウント」を実現する CloudFormationテンプレート](https://qiita.com/eijikominami/items/e5f7fcc7fd91fc4355be)
6. [**SSHとRDPのアクセスを制限する** - 「セキュアで堅牢なAWSアカウント」を実現する CloudFormationテンプレート](https://qiita.com/eijikominami/items/936912ee715079606e55)
7. [**デフォルトセキュリティグループを無効化する** - 「セキュアで堅牢なAWSアカウント」を実現する CloudFormationテンプレート](https://qiita.com/eijikominami/items/2fa109f00b5c63686c42)
8. [**S3バケットのサーバサイド暗号化** - セキュアで堅牢なAWSアカウント」を実現する CloudFormationテンプレート](https://qiita.com/eijikominami/items/581a8e0e424117322125)