ablog

不器用で落着きのない技術者のメモ

EMRFS で KMS のカスタマー管理 CMK で暗号化する

EMR で KMS のカスタマー管理CMKを利用するには、S3 バケットのデフォルト暗号化で CMK を指定し、S3 と KMS に対する必要な権限を持つロールを EMR にアタッチすればよい。
また、S3 のバケットポリシーで特定の CMK 以外での PUT を禁止すると、「CMK 指定なし」と「許可した CMK 指定する」とアップロードできるが、「許可した CMK 以外を指定する」と失敗する。

検証結果

  • ファイルを作成する。
$ perl -le 'print for 1..10000' > number10000.txt
  • AWS CLIでCMK指定なしでのアップロードは成功する
$ aws s3 cp number10000.txt s3://az-emr-kms-test/
upload: ./number10000.txt to s3://az-emr-kms-test/number10000.txt
  • AWS CLIバケットポリシーで許可されたCMKを指定してのアップロードは成功する。
$ aws s3 cp number10000.txt s3://az-emr-kms-test/number10000.txt --sse aws:kms --sse-kms-key-id 'arn:aws:kms:ap-northeast-1:123456789012:key/c3******-168d-4***-9***-9***********'
upload: ./number10000.txt to s3://az-emr-kms-test/number10000.txt
  • AWS CLIで違うCMKを指定してのアップロードは失敗する。
$ aws s3 cp number10000.txt s3://az-emr-kms-test/number10000.txt --sse aws:kms --sse-kms-key-id 'arn:aws:kms:ap-northeast-1:123456789012:key/2a******-8b8f-4***-9***-0***********'
upload failed: ./number10000.txt to s3://az-emr-kms-test/number10000.txt An error occurred (AccessDenied) when calling the PutObject operation: Access Denied
  • PySpark on EMR で、EMRFS で S3 に書ける(CMK指定なし)。
$ pyspark
Python 2.7.15 (default, Nov 28 2018, 22:38:08)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-28)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
Setting default log level to "WARN".
To adjust logging level use sc.setLogLevel(newLevel). For SparkR, use setLogLevel(newLevel).
19/04/01 07:54:31 WARN Utils: Service 'SparkUI' could not bind on port 4040. Attempting port 4041.
19/04/01 07:54:31 WARN Utils: Service 'SparkUI' could not bind on port 4041. Attempting port 4042.
19/04/01 07:54:31 WARN Utils: Service 'SparkUI' could not bind on port 4042. Attempting port 4043.
19/04/01 07:54:34 WARN Client: Neither spark.yarn.jars nor spark.yarn.archive is set, falling back to uploading libraries under SPARK_HOME.
Welcome to
      ____              __
     / __/__  ___ _____/ /__
    _\ \/ _ \/ _ `/ __/  '_/
   /__ / .__/\_,_/_/ /_/\_\   version 2.4.0
      /_/

Using Python version 2.7.15 (default, Nov 28 2018 22:38:08)
SparkSession available as 'spark'.
>>> df = spark.read.csv("s3://az-emr-kms-test/number10000.txt")
>>> df.head(10)
[Row(_c0=u'1'), Row(_c0=u'2'), Row(_c0=u'3'), Row(_c0=u'4'), Row(_c0=u'5'), Row(_c0=u'6'), Row(_c0=u'7'), Row(_c0=u'8'), Row(_c0=u'9'), Row(_c0=u'10')]
>>> df.write.mode('overwrite').parquet("s3://az-emr-kms-test/number10000.parquet")
>>> exit
  • Sparkから書いたファイルを確認する。
$ aws s3 ls --recursive s3://az-emr-kms-test/
2019-04-01 07:34:49      48894 num1
2019-04-01 05:39:51  888888898 number.txt
2019-04-01 07:55:47          0 number10000.parquet/_SUCCESS
2019-04-01 07:55:47      43888 number10000.parquet/part-00000-4c91ddce-b347-4239-85ec-0eba160c6c15-c000.snappy.parquet
2019-04-01 07:53:21      48894 number10000.txt

準備

  • EMRクラスタを作成する。
  • IAMポリシー「KMSUserPolicy1」を作成して、ロール「EMR_EC2_DefaultRole」にアタッチする
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "kms:Encrypt",
                "kms:Decrypt",
                "kms:ReEncrypt*",
                "kms:GenerateDataKey*",
                "kms:DescribeKey",
                "kms:ListAliases"
            ],
            "Resource": "*"
        }
    ]
}
  • S3バケットポリシーで指定したCMK以外での暗号化を禁止する。
{
    "Version": "2012-10-17",
    "Id": "S3KeyPolicy",
    "Statement": [
        {
            "Sid": "Force KMS Key",
            "Effect": "Deny",
            "Principal": "*",
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::az-emr-kms-test/*",
            "Condition": {
                "StringNotEquals": {
                    "s3:x-amz-server-side-encryption-aws-kms-key-id": "arn:aws:kms:ap-northeast-1:123456789012:key/c3******-168d-4***-9***-9***********"
                }
            }
        }
    ]
}

「AWS 認定ビッグデータ – 専門知識」試験の準備方法

AWS 認定ビッグデータ – 専門知識(AWS Certified Big Data - Specialty)」の試験を受けるにあたっての勉強の仕方をメモ。AWS 認定 – 認定の準備に書かれている通りだが、Linux Academy の模擬試験が効果的だった。

準備方法

VPCエンドポイントポリシーのサイズ上限

VPC エンドポイント ポリシーの使用
VPC エンドポイントポリシーは、エンドポイントの作成時または変更時にエンドポイントにアタッチする IAM リソースポリシーです。エンドポイントの作成時にポリシーをアタッチしない場合、サービスへのフルアクセスを許可するデフォルトのポリシーがアタッチされます。エンドポイントポリシーは、IAM ユーザーポリシーやサービス固有のポリシー (S3 バケットポリシーなど) を上書き、または置き換えません。これは、エンドポイントから指定されたサービスへのアクセスを制御するための別のポリシーです。
(中略)
エンドポイントポリシーのサイズが 20,480 文字を超えることはできません (空白を含む)。

VPC エンドポイント によるサービスのアクセスコントロール - Amazon Virtual Private Cloud

Organization ID を利用してS3のクロスアカウントアクセスを許可する。

課題

S3 のバケットポリシーでクロスアカウントアクセスを許可する場合、Principal に指定するAWSアカウントIDの数が多くなると、バケットポリシーのサイズの上限に達してしまう。

解決策

{
    "Version": "2012-10-17",
    "Id": "Policy1543071477610",
    "Statement": [
        {
            "Sid": "AllowGetObject",
            "Effect": "Allow",
            "Principal": "*",
            "Action": [
                "s3:GetObject",
                "s3:ListBucket"
            ],
            "Resource": [
                "arn:aws:s3:::orgid-test-bucket",
                "arn:aws:s3:::orgid-test-bucket/*"
            ],
            "Condition": {
                "StringEquals": {
                    "aws:PrincipalOrgID": [
                        "o-xxxxxxxxxx",
                        "o-yyyyyyyyyy",
                        "o-zzzzzzzzzz"
                    ]
                }
            }
        }
    ]
}
  • Organization ID はマスターアカウントで マネジメントコンソールにログインして、子アカウントを選択して[設定]をクリックして確認する。

CloudTrail で CloudWatch Logs に配信しようとすると「ロールポリシーを検証できません」と怒られる

事象

  • CloudTrail で CloudWatch Logs に配信しようとすると「ロールポリシーを検証できません」と怒られる

再現手順

  • AWSマネジメントコンソールで[CloudTrail]-[証跡情報]から任意の Trail を選択する。
  • CloudWatch Logs で「次へ」をクリックする。

  • 新しいロールの作成を選択し、「許可」をクリックする。

解決策

  • 再度同じ手順で作成し、先程の手順で作成されたiAMロールとポリシー名を選択する。

  • 無事成功。

cfnで作成したバケットポリシーを変更後にcfnで元に戻す

S3バケット作成とバケットポリシー設定を CloudFormation で行った後に、手動でバケットポリシーを変更し、CloudFormation で元に戻してみた。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": [
                    "arn:aws:iam::<AWSアカウントID>:root",
                    "arn:aws:iam::<AWSアカウントID>:root",
                    "arn:aws:iam::<AWSアカウントID>:root",
                    "arn:aws:iam::<AWSアカウントID>:root"
                ]
            },
            "Action": [
                "s3:GetObject",
                "s3:PutObject",
                "s3:PutObjectTagging"
            ],
            "Resource": [
                "arn:aws:s3:::az-test-20190318-6",
                "arn:aws:s3:::az-test-20190318-6/*"
            ]
        }
    ]
}
  • 手動で以下の CloudFormation テンプレートを実行し、バケットポリシーを元に戻す。
AWSTemplateFormatVersion: '2010-09-09'
Description: This CloudFormation template to create S3 Bucket
 
Parameters:
  S3BucketName:
    Description: Type of this BacketName.
    Type: String
 
Resources:
  S3BucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties: 
      Bucket: !Sub ${S3BucketName}
      PolicyDocument: 
        Version: "2012-10-17"
        Statement: 
          - 
            Action: 
              - "s3:GetObject"
            Effect: "Allow"
            Resource: 
              Fn::Join: 
                - ""
                - 
                  - "arn:aws:s3:::"
                  - 
                    !Sub ${S3BucketName}
                  - "/*"
            Principal: 
              AWS:
                Fn::Join:
                  - ""
                  - 
                    - "arn:aws:iam::"
                    - Ref: "AWS::AccountId"
                    - ":root"

Outputs:
  S3BucketName:
    Value: !Sub ${S3BucketName}
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::<AWSアカウントID>:root"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::az-test-20190318-6/*"
        }
    ]
}

CloudFormationでS3バケットを作成してバケットポリシーを設定する

CloudFormationでS3バケットを作成してバケットポリシーを設定してみたメモ。

手順

  • CreateS3Bucket.yml をローカルに作成する。
AWSTemplateFormatVersion: '2010-09-09'
Description: This CloudFormation template to create S3 Bucket
 
Parameters:
  S3BucketName:
    Description: Type of this BacketName.
    Type: String
 
Resources:
  S3Bucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub ${S3BucketName}
      AccessControl: Private
      PublicAccessBlockConfiguration:
        BlockPublicAcls: True
        BlockPublicPolicy: True
        IgnorePublicAcls: True
        RestrictPublicBuckets: True
  S3BucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties: 
      Bucket: 
        Ref: "S3Bucket"
      PolicyDocument: 
        Version: "2012-10-17"
        Statement: 
          - 
            Action: 
              - "s3:GetObject"
            Effect: "Allow"
            Resource: 
              Fn::Join: 
                - ""
                - 
                  - "arn:aws:s3:::"
                  - 
                    Ref: "S3Bucket"
                  - "/*"
            Principal: 
              AWS:
                Fn::Join:
                  - ""
                  - 
                    - "arn:aws:iam::"
                    - Ref: "AWS::AccountId"
                    - ":root"

Outputs:
  S3BucketName:
    Value: !Ref S3Bucket
  • AWSマネジメントコンソール-[CloudFormation]-[スタックの作成]をクリック

  • テンプレートの選択: [テンプレートを Amazon S3 にアップロード] を選択
  • ファイルの選択でローカルの"CreateS3Bucket.yml"を選択
  • スタックの名前: 任意の名前を入力
  • S3BucketName: S3バケット名を入力

結果

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::<AWSアカウントID>:root"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::az-test-20190318-6/*"
        }
    ]
}