AWSで安全なデータ破棄の仕組みを構築してみた

クラウドでは、利用者による物理メディアの破棄(消磁や破砕)はできません。今回は、クラウド環境で利用者が行えるデータ破棄の方法(Cryptographic Erase:暗号化消去)をためしてみました。

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは、nkhrです。

オンプレ環境では、データ漏洩(第三者による意図しないデータ取得)を防ぐために、メディア破棄の統制を行っていると思います。クラウドでは、利用者による物理的なメディア破棄が行えないため、統制を行う場合は別の方法を考える必要があります。

クラウド環境で利用できるデータ破棄の方法として、「Cryptographic Erase(CE:暗号化消去)」があります。

今回は、下記のブログで紹介されている暗号化消去(CE)を試してみました。暗号化消去は、NIST 800-88にも記載されている方法です。

クラウドにおける安全なデータの廃棄(Amazon Web Service ブログ)      

NIST 800-88や暗号化消去(CE)って何?

NIST 800-88

NIST 800-88(媒体のサニタイズに関するガイドライン)は、メディア破棄の統制を定めるために広く利用されているガイドラインです。(NIST 800-88 Rev.1、IPAによる日本語版はリンク先を参照)

3つのサニタイズ方法が定義されており、データ機密性により適する手法を選択します。

  • 削除(Clean)- デバイス再利用が可能:新しい値での上書きや、工場出荷時の状態へのリセット
  • 除去(Purge)- デバイス再利用が可能:最先端のデータ復元技術を使っても、対象データが復元できない状態にする
  • 破棄(Destroy)- デバイス再利用不可:最先端のデータ復元技術を使っても、どの破壊部分からも対象データが復元できない状態にする

暗号化消去

データを保存時に暗号化し、破棄時に暗号鍵を削除します。これにより、暗号化データを回復不能にします。NIST 800-88では、除去手法に分類されています。適用する場合は、以下の点に注意が必要です。

  • 保存を開始する前に、暗号化が有効になっていること
  • 適切な暗号鍵の強度や、暗号アルゴリズム/利用モードを使用していること
  • 暗号鍵の管理が適切に行われていること
  • 暗号鍵のバックアップが存在する場合、適切に保護されていること(また適切に削除されること)

ブログの流れ

ためしてみたコードを参照したい方は、「暗号化消去のリソース作成」を参照してください。

前提条件

対象としたサービス

今回は、以下のサービスの暗号化消去を試してみました。

  • S3
  • Redshift

データのライフサイクルについて

暗号化消去を考える場合、データのライフサイクルごとに鍵を作成する必要があります。(ライフサイクルが終了していないデータがある場合、鍵削除ができない)

今回の検証では、以下のライフサイクルを想定しています。

S3

  • Bucket単位のデータライフサイクルを想定(Bucketごとに暗号鍵を作成し、Bucket削除時に鍵削除)

Redshift

  • クラスタ単位でデータライフサイクルを想定(Clusterごとに暗号鍵を作成し、Cluster削除時に鍵削除、Snapshotは残さない)
    • Snapshotも暗号化されるので、鍵を削除すると復号不可
  • データを残したい場合は、S3にUnloadして長期保存用の鍵を作るなど別の考慮が必要

暗号化のための鍵生成

AWS Key Management Service (AWS KMS)を利用して鍵の生成および管理を行います。AWSではいくつかの暗号化方式が利用できます。今回は3番のKMSカスタマー鍵を利用します。

1. S3 Server Side Encryption

S3サービスが管理するデフォルト鍵を利用した暗号化、KMSでの管理外。

2. Customer Masker Key(CMK)※KMS登録なし

利用者側で独自に作成した暗号化鍵、KMS登録しない場合は、KMS管理外。

3. Customer Master Key (CMK) in KMS

KMSサービスで利用する暗号鍵。3パターンの作成方法があります。

  • AWS_KMS:KMSで鍵を作成する方法 ★今回の鍵作成はこれを利用★
  • EXTERNAL:KMSの外で鍵を生成し、KMSにImportする方法
    • Externalの場合は、Importした鍵をAWSが生成した鍵でラップして管理します
  • AWS_CLOUDHSM:専用ハードウェアセキュリティモジュール (HSM) インスタンスを使用して鍵作成、暗号化を行う方法
    • 指定したリージョンのVPCにある専用CloudHSMクラスター内のHSMインスタンスで、シングルテナント管理
    • AWS CloudHSMを利用した暗号化は、KMS利用料金に加えてCloudHSM インスタンス料金が発生

4. AWS managed Key in KMS

codecommitやS3、redshift、EBS、RDSなど、いくつかのAWSサービスは、AWS管理のKMS暗号鍵が利用できます。

Region間で鍵共有する場合

Region間で同じ鍵を利用したい場合は、Multi Region Keyが利用できます。今回はSingle Region Keyを作成しました。Multi Region Keyについては、以下のブログを参照してください。

暗号化消去のために利用するAWSサービス

データ暗号化について

データ暗号化の考慮点

暗号化消去を行う場合、前提として適切なデータ暗号化が必要です。注意点は以下の通りです。

  • 安全な暗号化方式の利用する
  • 鍵の管理を適切に行う(鍵のバックアップを含む)
    • AWS KMSで一元的に鍵管理を行うことで、バックアップも含めてKMS内で管理できます。
    • CMKをKMS外で作成し、Importする場合、Import元のCMKの鍵管理や削除が必要です。
  • データ格納前から暗号化が有効になっている
    • 暗号化消去を考えている場合は、AWS上にデータを格納する前から暗号化を有効にしておく必要があります。
  • どのレイヤーで保護したいかを意識する
    • 今回は、物理的なメディア破棄ができない場合の暗号化消去を意識しているためハードウェアレベルの暗号化を想定しています
    • アプリケーションレベルの保護が必要な場合は、KMS鍵へのアクセス権限をPolicyで制御するなど別の考慮が必要です
  • データの機密レベルを意識する
    • データ機密レベルに応じて、どのようなデータ破棄の統制が必要かを検討します

暗号化の強制方法

AWSサービスごとに、暗号化の強制(デフォルト暗号化の設定)方法が異なります。

S3 暗号化設定

S3では「Default Encryption (デフォルト暗号化)」を設定することで、ファイルアップロード時に自動でサーバサイドの暗号化が行われます。

バケット作成時にデフォルト暗号化を有効にせず、暗号化の指定なしにファイルをアップロードした場合は注意が必要です。後でデフォルト暗号化を有効にしても、有効化前のファイルは暗号化されません。暗号化が必要な場合は、バケット作成時に設定するようにしましょう。

 

S3 Bucket Keyとは

Bucket Keyは、KMSへのアクセス頻度を減らすために利用される鍵でKMSにアクセスする頻度を減らすことができます。(KMSサービスの料金

「Bucket Key」は、デフォルト「Enable」です。Bucket Keyの詳細については、以下のブログを参考にしてください。

Redshift 暗号化設定

Redshiftでは、暗号化を有効にすると設定前のデータも含めて、クラスター内のデータおよび、スナップショットのデータを全て暗号化します。途中から暗号化を有効にした場合のRedshift動作は、以下の通りです。

  • 暗号化を有効にした新規クラスターを起動
  • 暗号化設定前のクラスター(既存クラスター)から、新クラスターへデータを移動
  • 新しいクラスターのEndpointを変更

途中から暗号化した場合、データ破棄統制の観点からは、既存クラスターの破棄統制が行えません。そのため、暗号化消去を考える場合は、利用前から暗号化を有効にしておきましょう。

KMS暗号化後のデータアクセス制御

KMS鍵の利用制御は、Key PolicyとIAM Policyで行います。Policyが正しく設定されていない場合、データのアップロードやダウンロードができなくなります。KMSのPolicy制御については、下記のブログを参考にしてください。

暗号化消去のリソース作成

ここからが実際のやってみたです。以下のイメージを構築します。

1. auditlogBucket&CloudTrail作成(手動作成)

事前に、監査ログ保存用のS3バケット作成と、CloudTrailの有効化を行います。

Auditlog Bucket 作成

今回は以下の設定で作成しました。Object Lock(削除防止)は一度有効化すると無効化できません。ログの保存要件を踏まえて、Object Lockの適切なモード/期間を設定します。Object Lockを設定したBucketは、空であれば削除可能です。

  • Block all public access
  • Bucket Versioning enabled
  • Default encryption enabled (SSE-S3)
  • Object Lock enabled
    • Default retention enabled
    • Default retention mode Governance
    • Default retention period 10

CloudTrail設定

ログ出力先は、手動で作成したAuditlog Bucketにします。今回はCloudTrailから作成されるLog fileに対してSSE-KMSを有効化しません。要件に応じて、有効化してください。

ネットワークリソースの作成

以下のネットワークリソースは事前に作成しています。(Cloudformationテンプレートに含みません)

  • VPC
  • Public Subnet ※EC2/Redshiftの配置(通常、RedshiftはPrivate Subnetに配置すべきです)
  • Route Table
  • KeyPair
  • S3 Gateway Endpoint

2. 削除の対象外リソース作成

暗号化消去の対象外(削除しない)リソースを作成します。サンプルコードは、以下のリソースを作成します。

  • Lambda
  • Lambda用Role
  • EC2
  • EC2用Role & InstanceProfile
  • SecurityGroup
    • Inboundを許可せず、Session Manager経由の接続のみ可
  • SNS Topic
    • CloudFormationで作成後に、指定したメールアドレスにConfirmationメールが届くのでConfirmが必要
  • SNS Topic Policy
    • EventBridgeからSNS TopicにPushするためのPolicy
  • Event Rule
    • KMS Keyの「CancelKeyDeletion」、 「ScheduleKeyDeletion」、 「DeleteKey」イベントをメール通知するルールを設定
    • 鍵の完全削除の通知のために、EventBridgeのdetail-type「KMS CMK Deletion」のイベントを設定
    • EventBrigeは同じRegionのCloudTrailのイベントのみ取得可能

サンプルコード

AWSTemplateFormatVersion: '2010-09-09'

Parameters:
  VpcId:
    Type: String
  SubnetId:
    Type: String
  EC2AMI:
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2  
  KeyName:
    Type: AWS::EC2::KeyPair::KeyName
  Email:
    Type: String

Resources:
  LambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
        - Action: sts:AssumeRole
          Effect: Allow
          Principal:
            Service: lambda.amazonaws.com
      MaxSessionDuration: 36000
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      Policies:
        - PolicyName: lambda-s3empty-role
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                    - s3:ListBucket
                    - s3:ListBucketVersions
                    - s3:DeleteObject
                    - s3:DeleteObjectVersion
                Resource: '*'
      Path: "/"
      RoleName: test-lambda-s3empty-role

  LambdaForS3Empty:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: empty-to-s3
      Runtime: python3.8
      Handler: index.lambda_handler
      Role: !GetAtt LambdaExecutionRole.Arn
      Timeout: 60
      Code:
        ZipFile: |
          import json
          import boto3
          import cfnresponse

          s3 = boto3.resource('s3')

          def lambda_handler(event, context):
            bucket = event['ResourceProperties']['BucketName']
            try:
              bucket = s3.Bucket(bucket)
              if event['RequestType'] == 'Delete':
                bucket.object_versions.delete()
                bucket.objects.all().delete()
              sendResponseCfn(event, context, cfnresponse.SUCCESS)
            except Exception as e:
              print(e)
              sendResponseCfn(event, context, cfnresponse.FAILED)

          def sendResponseCfn(event, context, responseStatus):
            response_body = {'Status': responseStatus,
                      'Reason': 'Log stream name: ' + context.log_stream_name,
                      'PhysicalResourceId': context.log_stream_name,
                      'StackId': event['StackId'],
                      'RequestId': event['RequestId'],
                      'LogicalResourceId': event['LogicalResourceId'],
                      'Data': json.loads("{}")}
            cfnresponse.send(event, context, responseStatus, response_body)

  EC2Role:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
        - Action: sts:AssumeRole
          Effect: Allow
          Principal:
            Service: ec2.amazonaws.com
      MaxSessionDuration: 36000
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonS3FullAccess
        - arn:aws:iam::aws:policy/AmazonRedshiftFullAccess
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
      Policies:
        - PolicyName: kms-key-usage-policy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                    - kms:Decrypt
                    - kms:Encrypt
                    - kms:GenerateDataKey
                Resource: '*'
      Path: "/"
      RoleName: test-ec2-role
  IAMInstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Roles: 
        - !Ref EC2Role
      Path: "/"

  EC2TestInstance:
    Type: AWS::EC2::Instance
    DeletionPolicy: Delete
    CreationPolicy: 
      ResourceSignal:
        Timeout: PT30M
    Properties: 
      IamInstanceProfile: !Ref IAMInstanceProfile
      ImageId: !Ref EC2AMI
      InstanceType: t3a.micro
      KeyName: !Ref KeyName
      InstanceInitiatedShutdownBehavior: stop
      DisableApiTermination: false
      Monitoring: false
      BlockDeviceMappings:    
        - DeviceName: /dev/xvda
          Ebs:
            VolumeType: gp3
            DeleteOnTermination: true
            Encrypted: true
            VolumeSize: 20
      NetworkInterfaces:
        - AssociatePublicIpAddress: true
          DeviceIndex: 0
          SubnetId: !Ref SubnetId
          GroupSet: 
            - !Ref EC2SecurityGroup
      UserData: 
        Fn::Base64: !Sub 
          - |
            #!/bin/bash
            yum update -y
            echo '---------------- update aws cli ----------------'
            yum -y install jq gcc
            curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
            unzip awscliv2.zip
            ./aws/install
            echo '---------------- install amazon redshift rsql ----------------' 
            yum -y install unixODBC 
            curl "https://s3.amazonaws.com/redshift-downloads/drivers/odbc/1.4.40.1000/${ODBCFILE}" -o ./${ODBCFILE}
            yum -y --nogpgcheck localinstall ./${ODBCFILE} 
            echo "export ODBCINI=\$HOME/.odbc.ini" >> /etc/profile
            echo "export ODBCSYSINI=/opt/amazon/redshiftodbc/Setup" >> /etc/profile
            echo "export AMAZONREDSHIFTODBCINI=/opt/amazon/redshiftodbc/lib/64/amazon.redshiftodbc.ini" >> /etc/profile
            curl "https://s3.amazonaws.com/redshift-downloads/amazon-redshift-rsql/1.0.1/${RSQLFILE}" -o ./${RSQLFILE} 
            rpm -i ./${RSQLFILE}
            echo '---------------- send cfn-signal ----------------' 
            /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource EC2TestInstance --region ${AWS::Region}
          - {
              RSQLFILE:         "AmazonRedshiftRsql-1.0.1-1.x86_64.rpm",
              ODBCFILE:         "AmazonRedshiftODBC-64-bit-1.4.40.1000-1.x86_64.rpm"
            }

  EC2SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties: 
      VpcId: !Ref VpcId
      GroupName: ec2-test-securitygroup
      GroupDescription: ec2-test-securitygroup

  SNSTopic:
    Type: AWS::SNS::Topic
    Properties:
      TopicName: notify-mail-cloudtrail-kms-event
      Subscription:
        - Endpoint: !Ref Email
          Protocol: email
  SNSTopicPolicy:
    Type: AWS::SNS::TopicPolicy
    Properties: 
      Topics: 
          - !Ref SNSTopic
      PolicyDocument: 
        Version: 2012-10-17
        Statement:
          - Sid: TopicPolicy
            Effect: Allow
            Principal:
              Service: events.amazonaws.com
            Action:
              - sns:Publish
            Resource: !Ref SNSTopic
      
  KMSKeyDeletionRule:
    Type: AWS::Events::Rule
    Properties:
      Name: cloudtrail-test-cf-event-rule
      State: ENABLED
      EventPattern: |
        {
          "source": ["aws.kms"],
          "detail-type": ["AWS API Call via CloudTrail"],
          "detail": {
            "eventSource": ["kms.amazonaws.com"],
            "eventName": ["CancelKeyDeletion", "ScheduleKeyDeletion", "DisableKey", "DeleteKey"]
          }
        }
      Targets:
        - Arn: !Ref SNSTopic
          Id: KMSKeyAlert
  CMKDeletionRule:
    Type: AWS::Events::Rule
    Properties:
      Name: cloudtrail-test-aws-event-rule
      State: ENABLED
      EventPattern: |
        {
          "source": ["aws.kms"],
          "detail-type": ["KMS CMK Deletion"]
        }
      Targets:
        - Arn: !Ref SNSTopic
          Id: KMSKeyAlert

Outputs:
  EC2SecurityGroupId:
    Value: !GetAtt EC2SecurityGroup.GroupId
  EC2TestInstancePrivateIP:
    Value: !GetAtt EC2TestInstance.PrivateIp
  LambdaArn:
    Value: !GetAtt LambdaForS3Empty.Arn
  SendEmail:
    Value: !Ref Email

3. S3Bucket & KMS Key作成

サンプルでは以下のリソースを作成しています。

  • S3 Bucket
  • Custom Resource (LambdaによるS3 Bucket削除用)
  • KMS Key

サンプルコード

AWSTemplateFormatVersion: '2010-09-09'
Parameters:
  S3BucketName:
    Type: String
    Default: ttn-test-encrypt-data
  AuditBucketName:
    Type: String
    Default: ttn-test-audit-logs
  LambdaFunctionName:
    Type: String
    Default: empty-to-s3

Resources:
  LambdaForS3Empty:
      Type: Custom::cleanupbucket
      Properties:
        ServiceToken:
          !Sub 'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${LambdaFunctionName}'
        BucketName: !Ref S3BucketName
      DependsOn: Bucket

  Bucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Ref S3BucketName
      AccessControl: Private
      PublicAccessBlockConfiguration:
        BlockPublicAcls: TRUE
        BlockPublicPolicy: TRUE 
        IgnorePublicAcls: TRUE
        RestrictPublicBuckets: TRUE
      LoggingConfiguration:
        DestinationBucketName: !Ref AuditBucketName
        LogFilePrefix: !Sub s3accesslogs/${S3BucketName}/
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: aws:kms
              KMSMasterKeyID: !GetAtt S3BucketSSEKMS.KeyId
            BucketKeyEnabled: true

  S3BucketSSEKMS:
    Type: AWS::KMS::Key
    Properties:
      Description: s3 kms key sample
      Enabled: true
      # SYMMETRIC_DEFAULT is AES-256-GCM
      KeySpec: SYMMETRIC_DEFAULT
      MultiRegion: false
      EnableKeyRotation: true
      PendingWindowInDays: 7
      KeyPolicy:
        Version: 2012-10-17
        Id: s3-sse-key-default
        Statement:
          - Sid: Enable IAM User Permissions
            Effect: Allow
            Principal:
              AWS: !Sub arn:aws:iam::${AWS::AccountId}:root
            Action: 'kms:*'
            Resource: '*'

  S3KmsKeyAlias:
    Type: AWS::KMS::Alias
    Properties:
      AliasName: alias/test-s3-kms-key
      TargetKeyId: !Ref S3BucketSSEKMS

Outputs:
  KmsKeyAlias:
    Value: !Ref S3KmsKeyAlias
  BucketArn:
    Value: !GetAtt Bucket.Arn

サンプルを実行すると、S3 BucketとS3 BucketのKMS Keyが自動生成されます。

4. Redshift & KMS Key作成

サンプルでは以下のリソースを作成しています。

  • Redshift Cluster (SubnetGroup & Cluster Group)
  • SecurityGroup
  • KMS Key

サンプルコード

AWSTemplateFormatVersion: "2010-09-09"

Parameters:
  VpcId:
    Type: AWS::EC2::VPC::Id
  SubnetId:
    Type: AWS::EC2::Subnet::Id
  EC2SecurityGroupId:
    Type: AWS::EC2::SecurityGroup::Id
    # 作成したEC2にアタッチしたSecurity Groupを選択
  ClusterName:
    Type: String
    Default: test-redshit
  MasterUsername:
    Type: String
    Default: test_admin
  MasterUserPassword:
    # SecretManagerやParameter Storeを利用したほうが良いです
    Type: String
  Port:
    Type: String
    Default: 5439

Resources:
  S3AccessRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
        - Action: sts:AssumeRole
          Effect: Allow
          Principal:
            Service: redshift.amazonaws.com
      MaxSessionDuration: 3600
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonS3FullAccess
      Policies:
        - PolicyName: kms-key-usage-policy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                    - kms:Decrypt
                    - kms:Encrypt
                    - kms:GenerateDataKey
                Resource: '*'
      Path: "/"
      RoleName: RedshiftS3AccessRole

  SubnetGroup:
    Type: AWS::Redshift::ClusterSubnetGroup
    Properties:
      Description: Redshift cluster subnet group
      SubnetIds:
        - !Ref SubnetId

  SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties: 
      VpcId: !Ref VpcId
      GroupName: test-redshift-sg
      GroupDescription: test-redshift-sg
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: !Ref Port
          ToPort: !Ref Port
          SourceSecurityGroupId: !Ref EC2SecurityGroupId

  ClusterParameterGroup: 
    Type: AWS::Redshift::ClusterParameterGroup
    Properties: 
      Description: 'dwh test redshift'
      ParameterGroupFamily: redshift-1.0
      Parameters: 
        - ParameterName: require_ssl
          ParameterValue: true

  Cluster: 
    Type: AWS::Redshift::Cluster
    Properties:
      AllowVersionUpgrade: true
      AquaConfigurationStatus: disabled
      AvailabilityZone: ap-northeast-1a
      Classic: false
      ClusterIdentifier: !Ref ClusterName
      ClusterParameterGroupName: !Ref ClusterParameterGroup
      ClusterSubnetGroupName: !Ref SubnetGroup
      ClusterType: single-node
      NodeType: dc2.large
      Encrypted: true
      EnhancedVpcRouting: true
      IamRoles: 
        - !Sub arn:aws:iam::${AWS::AccountId}:role/aws-service-role/redshift.amazonaws.com/AWSServiceRoleForRedshift
        - !GetAtt S3AccessRole.Arn
      KmsKeyId: !GetAtt RedshiftKMS.KeyId
      DBName: test
      Port: !Ref Port
      MasterUsername: !Ref MasterUsername
      MasterUserPassword: !Ref MasterUserPassword
      AutomatedSnapshotRetentionPeriod: 1
      ManualSnapshotRetentionPeriod: 1
      PreferredMaintenanceWindow: 'Sun:07:00-Sun:07:30'
      PubliclyAccessible: false
      VpcSecurityGroupIds: 
        - !Ref SecurityGroup

  RedshiftKMS:
    Type: AWS::KMS::Key
    Properties:
      Description: redshift kms key sample
      Enabled: true
      # SYMMETRIC_DEFAULT is AES-256-GCM
      KeySpec: SYMMETRIC_DEFAULT
      MultiRegion: false
      EnableKeyRotation: true
      PendingWindowInDays: 7
      KeyPolicy:
        Version: 2012-10-17
        Id: redshift-key-default
        Statement:
          - Sid: Enable IAM User Permissions
            Effect: Allow
            Principal:
              AWS: !Sub arn:aws:iam::${AWS::AccountId}:root
            Action: 'kms:*'
            Resource: '*'

  RedshiftKmsKeyAlias:
    Type: AWS::KMS::Alias
    Properties:
      AliasName: alias/test-redshift-kms-key
      TargetKeyId: !Ref RedshiftKMS

Outputs:
  Cluster:
    Value: !Ref Cluster
  Endpoint:
    Value: !GetAtt Cluster.Endpoint.Address
  Port:
    Value: !GetAtt Cluster.Endpoint.Port
  DatabaseName:
    Value: Cluster
  Username:
    Value: !Ref MasterUsername
  AccessCmd:
    Value: !Sub "rsql -h ${Cluster.Endpoint.Address} -U ${MasterUsername} -d test"

サンプルを実行すると、Redshift ClusterとKMS Keyが自動生成されます。

データ投入とKMS暗号化確認

S3ファイルアップロード

[ec2-user@ip-10-0-0-33 ~]$ echo "test file" > test.txt
[ec2-user@ip-10-0-0-33 ~]$ aws s3 cp test.txt s3://ttn-test-encrypt-data/
upload: ./test.txt to s3://ttn-test-encrypt-data/test.txt

アップロードしたファイルは、自動でKMS鍵により、サーバサイド暗号化されます。(ファイルの「プロパティ>サーバ側の暗号化設定」で確認)

Redshiftへの接続

S3バケットのKMS暗号化ファイルをRedshiftにコピーできるか確認します。

[ec2-user@ip-10-0-0-33 ~]$ rsql -h test-redshit.cxrqsub7rc82.ap-northeast-1.redshift.amazonaws.com -U test_admin -d test
(test-redshit) test_admin@test=# CREATE TABLE test(col1 INT, col2 VARCHAR(100), col3 INT);
(test-redshit) test_admin@test=# COPY test from 's3://ttn-test-encrypt-data/redshift/copy_data.csv' iam_role 'arn:aws:iam:::role/RedshiftS3AccessRole' csv IGNOREHEADER 1;
INFO:  Load into table 'test' completed, 2 record(s) loaded successfully.
(test-redshit) test_admin@test=# select * from test;
 col1 | col2 | col3
------+------+------
    1 | aaa  |    3
    2 | bbb  |    4
(2 rows)

暗号化消去の動作確認

S3Bucket削除 & KMS鍵削除

CloudFormationスタックを削除し、S3 BucektとKMS鍵を削除します。

KMS Keyは削除保留中の状態になります。

Event Bridgeにより、ScheduleKeyDeletion(スケジュール削除)イベントの結果がメール通知されます。

Redshiftクラスター削除 & KMS鍵削除

CloudFormationスタックを削除し、RedshiftとKMS Keyを削除します。

Event Bridgeにより、ScheduleKeyDeletionイベントの結果がメール通知されます。メール内容は、S3 Bucket用のKMS鍵の削除で送信されるメッセージと同様です。

削除予約したKMS鍵の復元

KMS鍵は、削除保留期間の設定が必須であるため(最低7日)、削除保留期間中は鍵の削除をキャンセルできます。

完全削除の通知

削除保留期間が経過した鍵は、AWSにより自動で削除されます。EventBridge経由のSNS通知で以下のようなメールを取得できます。

まとめ

今回は、データ消去の統制を行うための仕組みを構築し、鍵削除の通知をメールで受け取れることを確認しました。

通知メール内容は、少し読みにくいので、SNSのメール通知前に、EventBridgeと他のサービス(LambdaやStep Function)を組み合わせて、レポートを作成したり、作成レポートをS3保存してもよいと思います。

以上、nkhrでした。

参考資料