のぴぴのメモ

自分用のLinuxとかの技術メモ

IAMでサーバ証明書をインポートするCLI手順

ACM(AWS Certificate Manager)ではなく、IAMでサーバ証明書をインポートするときのCLI手順のメモです。

サーバ証明書のインポート

PROFILE=default

aws --profile ${PROFILE} iam upload-server-certificate \
    --server-certificate-name hoge.com \
    --certificate-body file://hoge.com.crt \
    --private-key file://hoge.com.key 

インポートしたサーバ証明書の確認

aws --profile ${PROFILE} iam  list-server-certificates

KMS暗号化されてるEC2インスタンスのAMI取得し別リージョンにコピーする検証(AWS CLI)

はじめに

KMS鍵(CMK)で暗号化したEBSを持つEC2インスタンスのAMIを取得し、別リージョンにコピーする場合、鍵がどうなるのかという確認をCLIで確認したエビデンスです。

結論としては、他リージョンにAMIをコピーする時にコピー先リージョンのCMKを指定することになります。
こちらのRDSの検証と同じ結果です。
nopipi.hatenablog.com

検証環境

  • この検証はAWS CLIをセットアップしたMacで実施
    • bash環境なので、Linuxなどbash環境下であれば実行はできると思います。
  • AWS CLIのプロファイルは設定済み前提
  • プロファイルのIAMロール(IAMユーザ)にはAdministratorAccessポリシーがある前提

AWS CLIによる検証手順

(1) 検証設定

CLIに渡すパラメータのいくつかを、シェルの変数として事前設定しています。

#作業端末のプロファイル指定(デフォルトでない場合適時変更して下さい)
PROFILE=ExSPoC

#東京リージョンの設定
# 実際に検証する環境に合わせて、RDSをデプロイする先のVPCのIPと、サブネットのIDを変更してください。
REGION_SRC=ap-northeast-1
REGION_SRC_VPCID=vpc-8e6c5ce9
REGION_SRC_SUBNET=subnet-48cfe313
REGION_SRC_AMZL2_AMI_ID=ami-0ff21806645c5e492
REGION_SRC_EC2_KEY_PAIR=KEY_PAIR_NAME

#シドニーリージョンの設定
# 実際に検証する環境に合わせて、RDSをデプロイする先のVPCのIPと、サブネットのIDを変更してください。
REGION_DST=ap-southeast-2
REGION_DST_VPCID=vpc-01cfe566
REGION_DST_SUBNET=subnet-1f292656
REGION_DST_EC2_KEY_PAIR=KEY_PAIR_NAME

AWS_ACCOUNT=$(
aws --profile ${PROFILE} --region ${REGION_SRC} --output text \
    sts get-caller-identity --query 'Account' )

(2) KMSキー作成

EC2インスタンス(EBS)暗号化に利用するKMSキーを、コピー元リージョン、コピー先リージョンそれぞれで作成します。

(2)-(a) コピー元リージョンでのKMSキー作成
SRC_KEY_ID=$( \
aws --profile ${PROFILE} --region ${REGION_SRC} --output text \
    kms create-key \
	    --description "CMK to test Ec2 instance encryption" \
	    --origin AWS_KMS \
	--query 'KeyMetadata.KeyId' )

aws --profile ${PROFILE} --region ${REGION_SRC} \
    kms create-alias \
	    --alias-name alias/Key_To_Test_Instance_Encription \
	    --target-key-id ${SRC_KEY_ID}
(2)-(b) コピー先リージョンでのKMSキー作成
DST_KEY_ID=$( \
aws --profile ${PROFILE} --region ${REGION_DST} --output text \
    kms create-key \
	    --description "CMK to test Ec2 instance encryption" \
	    --origin AWS_KMS \
	--query 'KeyMetadata.KeyId' )

aws --profile ${PROFILE} --region ${REGION_DST} \
    kms create-alias \
	    --alias-name alias/Key_To_Test_Instance_Encription \
	    --target-key-id ${DST_KEY_ID}

(3)コピー元リージョンのEC2作成

セキュリティーグループを作成し、EC2インスタンスを作成します。

(3)-(a) SecurityGroup作成
SRC_EC2_SG_ID=$( \
aws --profile ${PROFILE} --region ${REGION_SRC} --output text \
    ec2 create-security-group \
        --group-name SgForEc2 \
        --description "SG for EC2 Instances" \
        --vpc-id ${REGION_SRC_VPCID} \
	--query 'GroupId' )

aws --profile ${PROFILE} --region ${REGION_SRC} \
    ec2 authorize-security-group-ingress \
        --group-id ${SRC_EC2_SG_ID} \
        --protocol tcp \
        --port 22 \
        --cidr 0.0.0.0/0
(3)-(b) EC2インスタンス作成
# Block-device-mappings用のJSON作成
JsonBlockDeviceMappings='
[
    {
        "DeviceName":"/dev/xvda",
        "Ebs":{
            "VolumeType": "gp2",
            "VolumeSize":8,
            "DeleteOnTermination":true,
            "Encrypted": true,
            "KmsKeyId": "'"${SRC_KEY_ID}"'"
        }
    }
]
'

# EC2インスタンスの起動
aws --profile ${PROFILE} --region ${REGION_SRC} \
    ec2 run-instances \
        --count 1 \
        --instance-type t2.micro \
        --image-id ${REGION_SRC_AMZL2_AMI_ID} \
        --key-name ${REGION_SRC_EC2_KEY_PAIR} \
        --monitoring Enabled=true \
        --security-group-ids ${SRC_EC2_SG_ID} \
        --subnet-id ${REGION_SRC_SUBNET} \
        --associate-public-ip-address \
        --block-device-mappings "${JsonBlockDeviceMappings}" \
        --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=EC2-KMS-TEST-SRC}]'
    
# EC2インスタンスのID取得
SRC_INSTANCE_ID=$(
aws --profile ${PROFILE} --region ${REGION_SRC} --output text \
    ec2 describe-instances \
        --filters "Name=tag:Name,Values=EC2-KMS-TEST-SRC" \
    --query 'Reservations[*].Instances[*].InstanceId'
)

(4) (オプション)コピー元リージョンのEC2でファイルを作成する

作成したEC2にsshログインし、ファイルを作成します。

#sshで、RDSのあるVPC上のEC2にログインして下記を実行

echo 'Hello World!!' > test.txt 

(5)コピー元リージョンのEC2のAMI取得

作成したEC2インスタンスのAMIを取得します。
データの静止点を取るため最初にインスタンスを停止し、AMIを取得します。
EBSが暗号化されている場合、AMI(正確にはAMI用に取得されたEBSのスナップショット)もインスタンスのEBSと同じCMKで暗号化されます。

(5)-(a) EC2インスタンス停止
aws --profile ${PROFILE} --region ${REGION_SRC} \
    ec2 stop-instances \
        --instance-ids ${SRC_INSTANCE_ID}
(5)-(b) AMI取得
#AMI取得
aws --profile ${PROFILE} --region ${REGION_SRC} \
    ec2 create-image \
        --instance-id ${SRC_INSTANCE_ID} \
        --name "SRC_INSTANCE_AMI"

#取得したAMIのID取得
SRC_AMI_ID=$(
aws --profile ${PROFILE} --region ${REGION_SRC} --output text \
    ec2 describe-images \
        --filters "Name=name,Values=SRC_INSTANCE_AMI"  \
    --query 'Images[*].{ID:ImageId}'
)

(6)取得したAMIの他リージョンへのコピー

コピー元リージョンで取得したAMIを、コピー先リージョンにコピーします。
コピー先のシンガポールリージョンで実行しているところに注意ください。(--reogionオプション指定を見てください)

#AMIのコピー
aws --profile ${PROFILE} --region ${REGION_DST} \
    ec2 copy-image \
        --source-image-id ${SRC_AMI_ID} \
        --source-region ${REGION_SRC} \
        --name COPY_AMI_FROM_SRC_REGION \
        --encrypted \
        --kms-key-id ${DST_KEY_ID}

#コピーしたAMIのID取得
DST_AMI_ID=$(
aws --profile ${PROFILE} --region ${REGION_DST} --output text \
    ec2 describe-images \
        --filters "Name=name,Values=COPY_AMI_FROM_SRC_REGION"  \
    --query 'Images[*].{ID:ImageId}'
)

(7)コピー先リージョンでAMIからインスタンスを起動

セキュリティーグループを作成し、コピー元リージョンからコピーしたAMIからインスタンスを起動します。

(7)-(a) SecurityGroup作成
DST_EC2_SG_ID=$( \
aws --profile ${PROFILE} --region ${REGION_DST} --output text \
    ec2 create-security-group \
        --group-name SgForEc2 \
        --description "SG for EC2 Instances" \
        --vpc-id ${REGION_DST_VPCID} \
	--query 'GroupId' )

aws --profile ${PROFILE} --region ${REGION_DST} \
    ec2 authorize-security-group-ingress \
        --group-id ${DST_EC2_SG_ID} \
        --protocol tcp \
        --port 22 \
        --cidr 0.0.0.0/0
(7)-(b) EC2インスタンス作成
# EC2インスタンスの起動
aws --profile ${PROFILE} --region ${REGION_DST} \
    ec2 run-instances \
        --count 1 \
        --instance-type t2.micro \
        --image-id ${DST_AMI_ID} \
        --key-name ${REGION_DST_EC2_KEY_PAIR} \
        --monitoring Enabled=true \
        --security-group-ids ${DST_EC2_SG_ID} \
        --subnet-id ${REGION_DST_SUBNET} \
        --associate-public-ip-address \
        --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=EC2-KMS-TEST-DST}]'

(8) (オプション)コピー元リージョンのEC2でファイルを確認する

作成したEC2にsshログインし、コピー元リージョンで作成したファイルがあるか確認します。

#sshで、RDSのあるVPC上のEC2にログインして下記を実行

ll
cat test.txt 

(追加確認)EBSスナップショットの他リージョンコピーの場合

EBSスナップショットを他リージョンにコピーする場合も、スナップショットコピー時にコピー先リージョンのCMKを指定します。

#コピー元リージョンで、AMI作成時に作成されたEBSスナップショットを設定
#"Created by CreateImage(i-xxxxx) for ami-xxxx"という説明のスナップショット
SRC_SNAPSHOT_ID=snap-06e05677d40f66db5

#EBSスナップショットを他リージョンにコピー
aws --profile ${PROFILE} --region ${REGION_DST} \
    ec2 copy-snapshot \
        --source-snapshot-id ${SRC_SNAPSHOT_ID} \
        --source-region ${REGION_SRC} \
        --encrypted \
        --kms-key-id ${DST_KEY_ID}

RDS KMS暗号化されてるDBインスタンスをスナップショットで別リージョンにコピーする検証(AWS CLI)

はじめに

RDSをKMSの鍵で暗号化する場合、RDSインスタンスがあるそれぞれのリージョンKMS鍵(CMK:Customer Master Key)で暗号化しますが、DBスナップショットで、他リージョンにDBをコピーする場合、鍵はどうなるのかということを確認するための検証手順です。

結論としては、他リージョンにDBスナップショットをコピーする時にコピー先リージョンのCMKを指定することになります。

f:id:nopipi:20191008201217p:plain:w500

検証環境

  • この検証はAWS CLIをセットアップしたMacで実施
    • bash環境なので、Linuxなどbash環境下であれば実行はできると思います。
  • AWS CLIのプロファイルは設定済み前提
  • プロファイルのIAMロール(IAMユーザ)にはAdministratorAccessポリシーがある前提
  • 検証ではRDS(MySQL)を利用
  • オプションのMySQL書き込み用のEC2インスタンスが既存であることが前提

AWS CLIによる検証手順

(1) 検証設定

CLIに渡すパラメータのいくつかを、シェルの変数として事前設定しています。

#作業端末のプロファイル指定(デフォルトでない場合適時変更して下さい)
PROFILE=default

#東京リージョンの設定
# 実際に検証する環境に合わせて、RDSをデプロイする先のVPCのIPと、サブネットのIDを変更してください。
REGION_SRC=ap-northeast-1
REGION_SRC_VPCID="vpc-8e6c5ce9"
REGION_SRC_SUBNETS="subnet-48cfe313 subnet-a2448189 subnet-0214e64a"

#シンガポールリージョンの設定
# 実際に検証する環境に合わせて、RDSをデプロイする先のVPCのIPと、サブネットのIDを変更してください。
REGION_DST=ap-southeast-1
REGION_DST_VPCID="vpc-0bcdca6c"
REGION_DST_SUBNETS="subnet-8307a0da subnet-1eae4578 subnet-58cdd511"

#RDS設定
RDS_INSTANCE_CLASS=db.m5.large
RDS_ENGIN=MySQL
RDS_ENGIN_VERSION=8.0.16
RDS_DB_ADMIN=admin
RDS_DB_ADMIN_PASSWD=password

AWS_ACCOUNT=$(
aws --profile ${PROFILE} --region ${REGION_SRC} --output text \
    sts get-caller-identity --query 'Account' )

(2) KMSキー作成

RDS暗号化に利用するKMSキーを、東京リージョン、シンガポールリージョンそれぞれで作成します。

f:id:nopipi:20191008201222p:plain:w500

(2)-(a) 送付元リージョンでのKMSキー作成
SRC_KEY_ID=$( \
aws --profile ${PROFILE} --region ${REGION_SRC} --output text \
    kms create-key \
	    --description Key_For_Source_Snapshot \
	    --origin AWS_KMS \
	--query 'KeyMetadata.KeyId' )

aws --profile ${PROFILE} --region ${REGION_SRC} \
    kms create-alias \
	    --alias-name alias/Key_For_Source_Snapshot \
	    --target-key-id ${SRC_KEY_ID}
(2)-(b) 送付先リージョンでのKMSキー作成
DST_KEY_ID=$( \
aws --profile ${PROFILE} --region ${REGION_DST} --output text \
    kms create-key \
	    --description Key_For_Source_Snapshot \
	    --origin AWS_KMS \
	--query 'KeyMetadata.KeyId' )

aws --profile ${PROFILE} --region ${REGION_DST} \
    kms create-alias \
	    --alias-name alias/Key_For_Destination_Snapshot \
	    --target-key-id ${DST_KEY_ID}

(3)東京リージョンのRDS作成

セキュリティーグループを作成とRDSのサブネットグループを作成し、RDSを作成します。

f:id:nopipi:20191008201231p:plain:w500

(3)-(a) SecurityGroup作成
SRC_RDS_SG_ID=$( \
aws --profile ${PROFILE} --region ${REGION_SRC} --output text \
    ec2 create-security-group \
        --group-name SgForRdsMySQL \
        --description "SG for RDS MySQL" \
        --vpc-id ${REGION_SRC_VPCID} \
	--query 'GroupId' )

aws --profile ${PROFILE} --region ${REGION_SRC} \
    ec2 authorize-security-group-ingress \
        --group-id ${SRC_RDS_SG_ID} \
        --protocol tcp \
        --port 3306 \
        --cidr 0.0.0.0/0
(3)-(b) RDS作成(サブネットグループ作成)
aws --profile ${PROFILE} --region ${REGION_SRC} \
    rds create-db-subnet-group \
        --db-subnet-group-name rds_mysql \
        --db-subnet-group-description "KMS TEST for RDS MySQL" \
        --subnet-ids ${REGION_SRC_SUBNETS}
(3)-(c) RDS作成(MySQLインスタンス作成)
aws --profile ${PROFILE} --region ${REGION_SRC} \
    rds create-db-instance \
    --db-instance-identifier Test-RDS-MySQL \
    --db-instance-class ${RDS_INSTANCE_CLASS} \
    --engine ${RDS_ENGIN} \
    --engine-version ${RDS_ENGIN_VERSION} \
    --allocated-storage 20 \
    --master-username ${RDS_DB_ADMIN} \
    --master-user-password ${RDS_DB_ADMIN_PASSWD} \
    --backup-retention-period 3 \
    --vpc-security-group-ids ${SRC_RDS_SG_ID} \
    --no-publicly-accessible \
    --db-subnet-group-name rds_mysql \
    --storage-encrypted \
    --kms-key-id ${SRC_KEY_ID}

(4) (オプション)東京リージョンRDS(MySQL)にデータを書き込む

RDSと同じVPC上のEC2から、RDSにアクセスしテーブル作成とデータインサートを行います。

#sshで、RDSのあるVPC上のEC2にログインして下記を実行

sudo yum -y install mysql

mysql -h <RDSのエンドポイントURL> -u admin -p
#パスワード(password)入力

MySQL > CREATE DATABASE sampledb;
MySQL > USE sampledb;
MySQL > 
CREATE TABLE IF NOT EXISTS `sample` (
  `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(255) NOT NULL DEFAULT 'hoge',
  PRIMARY KEY(`id`)
);
MySQL > INSERT INTO sample( name ) VALUES ( '鈴木' );
MySQL > COMMIT;
MySQL > SELECT * FROM sample;
MySQL > quit

(5)東京リージョンのRDS スナップショットの取得

RDSを作成し、DBインスタンスが起動したらスナップショットを手動取得します。
暗号化されたRDSから取得されたスナップショットは暗号されており、KMSでDBを暗号化している場合同じCMKでスナップショットも暗号化されます。

f:id:nopipi:20191008201235p:plain:w500

aws --profile ${PROFILE} --region ${REGION_SRC} \
    rds create-db-snapshot \
        --db-instance-identifier Test-RDS-MySQL \
        --db-snapshot-identifier snapshot-for-cross-region-replication-src-db

(6)取得したスナップショットのシンガポールリージョンにコピー

東京リージョンで取得したスナップショットを、シンガポールリージョンにコピーします。
コピー先のシンガポールリージョンで実行しているところに注意ください。(--reogionオプション指定を見てください)

f:id:nopipi:20191008201241p:plain:w500

aws --profile ${PROFILE} --region ${REGION_DST} \
    rds copy-db-snapshot \
        --source-db-snapshot-identifier arn:aws:rds:${REGION_SRC}:${AWS_ACCOUNT}:snapshot:snapshot-for-cross-region-replication-src-db \
        --target-db-snapshot-identifier snapshot-for-cross-region-replication-dst-db \
        --source-region ${REGION_SRC} \
        --kms-key-id ${DST_KEY_ID}

(7)シンガポールリージョンでスナップショットからRDSを作成

セキュリティーグループを作成とRDSのサブネットグループを作成し、東京リージョンからコピーしたスナップショットからRDSインスタンスを複製します。スナップショットからのインスタンスの複製には" restore-db-instance-from-db-snapshot"を利用します。

f:id:nopipi:20191008201247p:plain:w500

(7)-(a) SecurityGroup作成
DST_RDS_SG_ID=$( \
aws --profile ${PROFILE} --region ${REGION_DST} --output text \
    ec2 create-security-group \
        --group-name SgForRdsMySQL \
        --description "SG for RDS MySQL" \
        --vpc-id ${REGION_DST_VPCID} \
	--query 'GroupId' )

aws --profile ${PROFILE} --region ${REGION_DST} \
    ec2 authorize-security-group-ingress \
        --group-id ${DST_RDS_SG_ID} \
        --protocol tcp \
        --port 3306 \
        --cidr 0.0.0.0/0
(7)-(b) RDS作成(サブネットグループ作成)
aws --profile ${PROFILE} --region ${REGION_DST} \
    rds create-db-subnet-group \
        --db-subnet-group-name rds_mysql \
        --db-subnet-group-description "KMS TEST for RDS MySQL" \
        --subnet-ids ${REGION_DST_SUBNETS}
(7)-(c) RDS作成(MySQLインスタンスのスナップショットからの復元)
aws --profile ${PROFILE} --region ${REGION_DST} \
    rds restore-db-instance-from-db-snapshot \
        --db-instance-identifier Test-RDS-MySQL-DST \
        --db-snapshot-identifier snapshot-for-cross-region-replication-dst-db \
        --vpc-security-group-ids ${DST_RDS_SG_ID} \
        --db-subnet-group-name rds_mysql

(8) (オプション)シンガポールリージョンRDS(MySQL)のデータを確認する

復元したRDSで、東京リージョンでインサートしたデータがあるか確認します。

#sshで、RDSのあるVPC上のEC2にログインして下記を実行

sudo yum -y install mysql

mysql -h <RDSのエンドポイントURL> -u admin -p
#パスワード(password)入力

MySQL > USE sampledb;
MySQL > SELECT * FROM sample;
MySQL > quit

AWS CLIでVPCエンドポイントを作成する時のポリシー(JSON)の指定方法

CLIVPCエンドポイントを作成するときに、エンドポイントポリシーのJSONをどうやって渡せばいいのかを悩んだので、そのメモ書きです。
JSONの文字列をシェルの変数に投入してあげるという解決策です。

実行例

AWS_PROFILE=default

POLICY='
{
    "Statement": [
        {
            "Action": "*",
            "Effect": "Allow",
            "Resource": "*",
            "Principal": "*"
        }
    ]
}'

aws --profile ${AWS_PROFILE} ec2 create-vpc-endpoint \
    --vpc-endpoint-type Interface \
    --vpc-id vpc-f1dcfc96 \
    --subnet-ids subnet-cde80585 subnet-0bf40020 subnet-742e712f \
    --security-group-ids sg-beca3fc2 \
    --service-name com.amazonaws.ap-northeast-1.sqs \
    --policy-document "${POLICY}";

解説

ミソは、エンドポイントのポリシーをシェル変数(ここでは、POLICY)に登録して、--policy-document の引数に "${POLICY}"と入れること。
引数に入れる"${POLICY}"は、必ずダブルクォーテーションで括ること。(bashのコマンド文字列解析時に、JSONの途中のスペースを引数の区切りと誤認するのを回避するため)

Linuxでファイル名がSJIS(CP932)のものをUTF-8のファイル名に一括変換する

convmvをインストール

sudo yum -y install convmv

使い方

cd  <変換したいファイルがあるディレクトリ>
convmv -r -f cp932 -t utf-8 --notest *
  • -r 再帰的に変換を実行
  • -f cp932 : 変換元のコード指定。cp932は、windowsSJISコード名称
  • -t utf-8: 変換先のコード指定。utf-8に変換する
  • --notest: 変換を実行する(デフォルトではテストだけのため)

JSONの文字列変換(JSON.dumps)をワンライナーのコマンド実行で実現する

コマンドでの作業で、pythonのコードを書くまででない場合の方法。
変数"POLICY"に設定したJSONを、pythonで文字列に変換して、変数"POLICY_ESCAPE"に格納する。

JSON→文字列にエンコード

やること

このJSONを、

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Stmt1565607714000",
            "Effect": "Allow",
            "Action": [
                "iam:List*",
                "iam:PassRole"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}

これに変換します。

"{ \"Version\": \"2012-10-17\", \"Statement\": [ { \"Sid\": \"Stmt1565607714000\", \"Effect\": \"Allow\", \"Action\": [ \"iam:List*\", \"iam:PassRole\" ], \"Resource\": [ \"*\" ] } ] }\n"

コマンド

POLICY='{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Stmt1565607714000",
            "Effect": "Allow",
            "Action": [
                "iam:List*",
                "iam:PassRole"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}'
POLICY_ESCAPE=$(echo ${POLICY} | python -c 'import json; import sys; print(json.dumps(sys.stdin.read()))')

文字列→JSONにデコード

上記の逆です。

POLICY_ESCAPE="{ \"Version\": \"2012-10-17\", \"Statement\": [ { \"Sid\": \"Stmt1565607714000\", \"Effect\": \"Allow\", \"Action\": [ \"iam:List*\", \"iam:PassRole\" ], \"Resource\": [ \"*\" ] } ] }\n"

POLICY=$(echo ${POLICY_ESCAPE} | python -c 'import json; import sys; str=sys.stdin.read(); dic=json.loads(str.replace("\\n","")); print("{}".format(json.dumps(dic,indent=4)))' )

実行結果

$ echo $POLICY
{ "Version": "2012-10-17", "Statement": [ { "Action": [ "iam:List*", "iam:PassRole" ], "Resource": [ "*" ], "Effect": "Allow", "Sid": "Stmt1565607714000" } ] }

蛇足

もっといい方法があるような気がしますが、わかったらアップデートします。

EC2の有償OSの課金コード("billingProducts")

"billingProducts"の確認方法

課金コード(billingProduct)をどうやって取得するのかという話ですが、そのものズバリの記載が下記にあり、下記コマンドで取得できます。

curl http://169.254.169.254/latest/meta-data/product-codes

RHEL8での確認

OSに課金が発生するRHEL8の場合、"billingProducts"に"bp-6fa54006" というのが見えます。

$ curl http://169.254.169.254/latest/dynamic/instance-identity/document/ 
{
  "accountId" : "270025184181",
  "availabilityZone" : "ap-northeast-1a",
  "kernelId" : null,
  "ramdiskId" : null,
  "pendingTime" : "2019-06-02T14:03:10Z",
  "architecture" : "x86_64",
  "privateIp" : "172.31.32.137",
  "version" : "2017-09-30",
  "region" : "ap-northeast-1",
  "devpayProductCodes" : null,
  "marketplaceProductCodes" : null,
  "imageId" : "ami-03c6a4362c5fb8c61",
  "billingProducts" : [ "bp-6fa54006" ],
  "instanceId" : "i-0abf28d14a86f3c6c",
  "instanceType" : "m5a.large"
}

Amazon Linux2

無料のAmazon Linux2の場合、"billingProducts"がnullですね。

$ curl http://169.254.169.254/latest/dynamic/instance-identity/document/ 
{
  "devpayProductCodes" : null,
  "marketplaceProductCodes" : null,
  "accountId" : "270025184181",
  "availabilityZone" : "ap-northeast-1a",
  "kernelId" : null,
  "ramdiskId" : null,
  "pendingTime" : "2019-06-02T14:03:47Z",
  "architecture" : "x86_64",
  "privateIp" : "172.31.37.11",
  "version" : "2017-09-30",
  "region" : "ap-northeast-1",
  "imageId" : "ami-0f9ae750e8274075b",
  "billingProducts" : null,
  "instanceId" : "i-0d1ca8d3b54832823",
  "instanceType" : "m5a.2xlarge"
}

調べるに至った経緯

AMI作成手順のドキュメントにこんな記載があるのですが、「 billingProduct コード」をどうやったら確認できるのか書いてなかったので調べたものです。

Red Hat Enterprise Linux (RHEL) や SUSE Linux Enterprise Server (SLES) などの一部の Linux ディストリビューションは、AMI に関連付けられた Amazon EC2 の billingProduct コードを使用して、パッケージの更新に関するサブスクリプションのステータスを確認します

docs.aws.amazon.com

AWS CodeCommitでgit認証にAWS CLIの認証情報ヘルパーを使用する

CodeCommitに作成したgitレポジトリに接続するための認証に、AWS CLIを利用する方法です。gitの認証ヘルパーに"aws codecommit credential-helper"を利用するのがポイントです。

1.手順: IAMユーザ作成とAWS CLIプロファイル設定

1.1 CodeCommit用のIAMユーザを作成

グループ"CodeCommitUserGrp"を作成し、AWSCodeCommitPowerUserポリシーを付与。
ユーザ"User01"を、"CodeCommitUserGrp"で作成する。

  • グループ設定
    • グループ名: CodeCommitUserGrp
    • アタッチするポリシ: AWSCodeCommitPowerUser
# 設定
aws iam create-group --group-name CodeCommitUserGrp
aws iam attach-group-policy --group-name CodeCommitUserGrp --policy-arn arn:aws:iam::aws:policy/AWSCodeCommitPowerUser
# 設定確認
aws iam get-group --group-name CodeCommitUserGrp
aws iam list-attached-group-policies --group-name CodeCommitUserGrp
  • ユーザ設定
    • ユーザ名: User01
    • 所属グループ: CodeCommitUserGrp
# 設定
aws iam create-user --user-name User01
aws iam add-user-to-group --user-name User01 --group-name CodeCommitUserGrp 
# 設定確認
aws iam get-user --user-name User01
aws iam list-groups-for-user --user-name User01

1.2 ユーザのアクセスキーとシークレットキーを生成

PCのAWS CLIに設定するアクセスキーとシークレットキーを生成します。AccessKeyIdとSecretAccessKeyを控えます。(漏洩しないよう注意!!)

aws iam create-access-key --user-name User01
{
    "AccessKey": {
        "UserName": "User01", 
        "Status": "Active", 
        "CreateDate": "2019-08-12T09:55:52Z", 
        "SecretAccessKey": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", 
        "AccessKeyId": "YYYYYYYYYYYYYYYYYYYY"
    }
}

1.3 AWS CLIにプロファイルを作成

AWS CLIがセットアップされていない場合は、こちらの記事を参照してCLIをインストールして下さい。
AWS CLIをセットアップ後に、aws CLIにcodecommitというプロファイルを作成し、先ほど生成したアクセスキーとシークレットキーを登録します。

aws --profile codecommit configure
AWS Access Key ID [None]: YYYYYYYYYYYYYYYYYYYY
AWS Secret Access Key [None]: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Default region name [None]: ap-northeast-1
Default output format [None]: json

2.手順: gitの認証ヘルパー設定

gitコマンドがインストールされていない場合は、先にgitをインストールします。(インストール方法は記載しないので、別途検索して下さい)

2.1 gitの初期設定(ユーザ設定&pushモード設定)

(1)メールアドレス、(2)名前、(3)push時のモードを設定します。

git config --global user.email "xxxxxxx@gmail.com"
git config --global user.name "xxxxxxxx"
git config --global push.default simple

git config --global -l

2.2 gitの認証ヘルパーにAWSコマンドを設定

公式ドキュメントの情報を参考に、下記コマンドで認証ヘルパーとしてAWSコマンドを指定します。

git config --global credential.helper '!aws --profile codecommit codecommit credential-helper $@'
git config --global credential.UseHttpPath true

ただし、私の環境では上記コマンドで正しく反映されなかったため、"~/.gitconfig"のcredential句の部分を、下記の通り正しい内容に修正しました。(こちらの記事を参考にしました)

[credential]
   helper = "!aws --profile codecommit codecommit credential-helper $@"
   UseHttpPath = true

3.手順: CodeCommitのレポジトリからcloneのテスト

git clone <レポジトリhttpsのURL>

VPCのsubnetのCIDRの割り当てのメモ

VPCのCIDRを"10.1.0.0/16"とした時のCIDR例。
私は検証用途での利用でIPが枯渇するほどインスタンスを起動するわけではなので、IPアドレス範囲をキツキツには設計していない。

  • ポイント
    • Privateサブネットは少し大きめの19bitで切る。
    • Publicサブネットは、10.1.0.0/19の範囲をさらに24bitで切って利用する
    • AZ間は間違えないように離れた番号を利用するようにアドレスを割り当てる

サブネットのCIDR割り当て例

#大区分小区分項目CIDRIPアドレス範囲
1PublicAZ-1PublicSubnet110.1.0.0/2410.1.0.1-10.1.0.254
2未使用10.1.1.0/24

10.1.15.0/24
10.1.1.1-10.1.15.254
3AZ-2PublicSubnet210.1.16.0/2410.1.16.1-10.1.16.254
4未使用10.1.17.0/24

10.1.31.0/24
10.1.0.1-10.1.7.254
5PrivateAZ-1PrivateSubnet110.1.32.0/1910.1.32.1-10.1.63.254
6未使用10.1.64.0/1910.1.64.1-10.1.95.254
7未使用10.1.96.0/1910.1.96.1-10.1.127.254
8AZ-2PrivateSubnet210.1.128.0/1910.1.128.1-10.1.159.254
9未使用10.1.160.0/1910.1.160.1-10.1.191.254
10未使用10.1.192.0/1910.1.192.1-10.1.223.254
11未使用未使用未使用10.1.224.0/1910.1.224.1-10.1.255.254

参考

"10.1.0.0/16"の範囲を19bitで分割した時のCIDR一覧

CIDRIP範囲IPアドレス(2進数)
Subnet11111111 11111111 11100000 00000000
10.1.0.0/1910.1.0.1 - 10.1.31.25400001010 00000001 00000000 00000000
10.1.32.0/1910.1.32.1 - 10.1.63.25400001010 00000001 00100000 00000000
10.1.64.0/1910.1.64.1 - 10.1.95.25400001010 00000001 01000000 00000000
10.1.96.0/1910.1.96.0 - 10.1.127.25500001010 00000001 01100000 00000000
10.1.128.0/1910.1.128.0 - 10.1.159.25500001010 00000001 10000000 00000000
10.1.160.0/1910.1.160.0 - 10.1.191.25500001010 00000001 10100000 00000000
10.1.192.0/1910.1.192.0 - 10.1.223.25500001010 00000001 11000000 00000000
10.1.224.0/1910.1.224.0 - 10.1.255.25500001010 00000001 11100000 00000000

AWS CloudFormation 起動テンプレート+CloudFormationヘルパースクリプトを使ってFowardProxyインスタンスを複数作成する

やりたいこと

CloudFormationでsquidのFowardProxyインスタンスをCloudFormationヘルパースクリプトを利用してインスタンス起動時にセットアップするようにしています。このインスタンスをAutoscalingは使用せず複数個作成したいと思い、EC2の起動テンプレートを組み合わせることで、効率良く起動するようにしています。

構成

f:id:nopipi:20190427234531p:plain
CloudFormation:起動テンプレートとCfnヘルパーの組み合わせで複数のProxyを作成する

ポイント

  • Cfnヘルパースクリプト用の定義を、EC2起動テンプレートのリソースのMetadataとして定義
  • Cfnヘルパースクリプトのオプション
    • 起動テンプレートのUserDataでcfn_initコマンドでcfnヘルパーを実行するようにする
    • その時Cloudformationの指定リソースを、”--resource ProxyLunchTemplate ”と起動テンプレートのリソースを指定する

CloudFormationテンプレート例

AWSTemplateFormatVersion: '2010-09-09'
Description: Outbound from the On-premises Environment
#----------------------------------------------
Parameters:
  Environment:
    Type: String
    Default: Dev
    AllowedPattern: "[a-zA-Z0-9=-@+?[?]?<?>._]*"
    ConstraintDescription: Can contain only ASCII characters.
  #------------------
  VpcId:
    Type: AWS::EC2::VPC::Id
  Subnet1Id:
    Type: AWS::EC2::Subnet::Id
    Description: Specify 1st Subnet to run Proxy instances.
  Subnet2Id:
    Type: AWS::EC2::Subnet::Id
    Description: Specify 2nd Subnet to run Proxy instances.
  #------------------
  KeyName:
    Type: AWS::EC2::KeyPair::KeyName
    Description: (required) Name of an existing EC2 key pair
  ProxyInstanceType: 
    Type: String 
    Default: m5.large 
    ConstraintDescription : Must be a valid EC2 instance type
    AllowedValues: 
      - t2.micro 
      - t2.small 
      - t2.medium 
      - t2.large 
      - t2.xlarge 
      - t2.2xlarge 
      - m5.large 
      - m5.xlarge 
      - m5.2xlarge 
      - m5.4xlarge 
      - m5.12xlarge 
      - m5.24xlarge 
  ProxyAmiId:
    Type: AWS::EC2::Image::Id
    Description: (required) AMI ID
    Default: ami-0f9ae750e8274075b  #for Tokyo-region
    #Default: ami-0b419c3a4b01d1859  #for singapore-region
  ProxyAutoRecoveryMinutes:
    Type: Number
    Description: Auto Recovery Time(Minutes)
    Default: 1
    MinValue: 1
    MaxValue: 120
  #------------------
Metadata: 
  AWS::CloudFormation::Interface: 
    ParameterGroups: 
      -
        Label:
          default: "General"
        Parameters:
          - Environment
      -
        Label:
          default: "Network"
        Parameters:
          - VpcId
          - Subnet1Id
          - Subnet2Id
      -
        Label:
          default: "Forward Proxy"
        Parameters:
          - KeyName
          - ProxyInstanceType
          - ProxyAmiId
          - ProxyAutoRecoveryMinutes
#----------------------------------------------
Resources:
  ProxySG:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: !Sub ${Environment}-ProxySecurityGroup
      GroupDescription: Allow Proxy inbound access
      VpcId: !Ref VpcId
      SecurityGroupIngress:
        -
          IpProtocol: tcp
          FromPort: 3128
          ToPort: 3128
          CidrIp: 0.0.0.0/0
        -
          IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: 0.0.0.0/0
      Tags: 
        - Key: Name
          Value: !Sub ${Environment}-ProxySecurityGroup
  #------------------
  ProxyLunchTemplate:
    Type: AWS::EC2::LaunchTemplate
    Properties:
      LaunchTemplateName: FowardProxyLunchTemplate
      LaunchTemplateData:
        ImageId: !Ref ProxyAmiId
        InstanceType: !Ref ProxyInstanceType
        KeyName: !Ref KeyName
        SecurityGroupIds:
          - !Ref ProxySG
        Monitoring:
          Enabled: true
        TagSpecifications:
          -
            ResourceType: instance
            Tags:
              - Key: Name
                Value: !Sub ${Environment}-ProxyInstance
        UserData:
          Fn::Base64: !Sub | 
            #!/bin/bash -xe
            yum update -y 
            yum install -y aws-cfn-bootstrap cloud-init aws-cli 
            /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource ProxyLunchTemplate --region ${AWS::Region} --configsets proxy_setup
            /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource ProxyLunchTemplate --region ${AWS::Region}
    Metadata:
      AWS::CloudFormation::Init: 
        configSets: 
          proxy_setup:
            - install_squid 
            - setup_squid 
        install_squid: 
          packages: 
            yum: 
              squid: [] 
        setup_squid: 
          files: 
            "/etc/squid/squid.conf": 
              content: !Sub | 
                # define ip address 
                acl localnet src 127.0.0.0/8 
                acl localnet src ::1/128 
 
                acl SSL_ports port 443 
                acl Safe_ports port 443
                acl CONNECT method CONNECT 
                http_access deny !Safe_ports 
 
                # Deny CONNECT to other than secure SSL ports 
                http_access deny CONNECT !SSL_ports 
 
                # Only allow cachemgr access from localhost 
                http_access allow localhost manager 
                http_access deny manager 
 
                # from where browsing should be allowed 
                http_access allow localnet 
 
                # include url white list 
                acl whitelist dstdomain "/etc/squid/whitelist" 
                http_access allow whitelist 
 
                # And finally deny all other access to this proxy 
                http_access deny all 
                #------------------------------------------ 
                http_port 3128

                # Leave coredumps in the first cache dir
                coredump_dir /var/spool/squid
 
                # anonymouse host name
                visible_hostname unknown
              mode: '000640'
              owner: root
              group: squid
            "/etc/squid/whitelist":
              content: !Sub |
                .google.co.jp
                github.com
                gist.github.com
              mode: '000640'
              owner: root
              group: squid
          services:
            sysvinit:
              squid:
                enabled: true
                ensureRunning: true
                files:
                  - "/etc/squid/squid.conf"
                  - "/etc/squid/whitelist.conf"
  #------------------
  ProxyInstance1: 
    Type: AWS::EC2::Instance 
    Properties:
      LaunchTemplate:
        LaunchTemplateName: FowardProxyLunchTemplate
        Version: !GetAtt ProxyLunchTemplate.LatestVersionNumber
      SubnetId: !Ref Subnet1Id
      SourceDestCheck: false
  ProxyInstance2: 
    Type: AWS::EC2::Instance 
    Properties:
      LaunchTemplate:
        LaunchTemplateName: FowardProxyLunchTemplate
        Version: !GetAtt ProxyLunchTemplate.LatestVersionNumber
      SubnetId: !Ref Subnet2Id
      SourceDestCheck: false
  #------------------
Outputs:
  ProxyInstance1PrivateIp:
    Description: 'Foward Proxy #1 Private IP'
    Value: !GetAtt ProxyInstance1.PrivateIp
    Export:
      Name: !Sub "${AWS::StackName}-ProxyInstance1PrivateIp"
  ProxyInstance2PrivateIp:
    Description: 'Foward Proxy #2 Private IP'
    Value: !GetAtt ProxyInstance2.PrivateIp
    Export:
      Name: !Sub "${AWS::StackName}-ProxyInstance2PrivateIp"

(AWS) UserDataとCloudFormationヘルパースクリプトの定義情報取得や実行契機の違い

定義箇所とインスタンス内からの定義情報取得方法の違い

UserDataはEC2インスタンスの機能で、CloudFormationヘルパースクリプトはCloudFormationの機能です。
UserDataはインスタンスメタデータ*1から情報を取得するため、実質VPC設定やIAMロールを考慮する必要がありません。一方CloudFormationヘルパースクリプトは、CloudFormationのパブリックにあるエンドポイントから取得するためInternet GatewayやCloudFormationのVPC Endpointを用意してパブリックのエンドポイントにアクセスできるようにする必要があります。

機能 機能を提供しているサービス 定義 インスタンスからの取得方法 通信要件
UserData EC2 EC2インスタンス起動オプション インスタンスメタデータから取得 不要
Cfnヘルパー CloudFormation CloudFormationのテンプレート(ResourceのMetadata定義) CloudFormationのエンドポイントから取得 IGW経由またはVPCEndpoint経由でCloudFormationのパブリックエンドポイントにアクセス可能なこと

絵に描くとこんな感じでしょうか。(逆に判りずらいかも)

f:id:nopipi:20190427235711p:plain

実行モジュールと実行契機の違い

AWSが提供するAMIを利用した場合UserDataは、cloud-initというツールの中で実行されておりOS起動時に自動実行されます。一方CloudFormationヘルパースクリプトは自動実行されないため明示的に実行する必要があり、通常はUserDataにコマンドを記載して、UserDataから実行させます。

機能 パッケージ 起動契機 インスタンス無いでの設定ファイル
UserData cloud-init*2 OS起動時に自動実行(systemdからキックされる) /etc/cloud/配下
Cfnヘルパー aws-cfn-bootstrap*3 明示的に実行(UserDataに実行コマンドを埋め込む) なし(CfnのスタックのMetadataに定義)

VSCodeでAWS CloudFormation(YAML)を作成する環境を整備する

VSCodeAWS CloudFormationをYAMLで作成する環境の設定手順です。

やること

  1. VSCodeのセットアップ
  2. YAMLの構文チェック用にエクステンション"YAML Language Support by Red Hat"をVSCodeに追加
  3. CloudFormationの構文チェック用のカスタム設定をVSCode設定に追加

VSCodeのセットアップ

MacへのVSCodeインストール(とgit連携)はこちらを参照
nopipi.hatenablog.com

YAMLエクステンションの追加

VSCoderedhatYAMLエクステンションを追加します。ここではCUIGUIの両方の手順を記載します。

CUIでインストールする場合

下記コマンドでYAMLエクステンションを追加します。

code --install-extension redhat.vscode-yaml

GUIでインストールする場合

  1. 左のバーから"Extentions"をクリック
  2. 検索ボックスでYAMLでエクステンションを検索し、"YAML Language Support by Red Hat"を選択
  3. "Install"でエクステンションを追加する

(1)左バーからExtentionボタンをクリックし、(2)キーワードYAMLで検索、(3)redhat YAMLをインストール
GUI操作でのYAMLエクステンションインストール

CloudFormationの構文チェック用のカスタム設定追加

下記設置を追加します

  1. CloudFormation仕様の追加
  2. CloudFormationのカスタムタグ定義の追加

設定(1) CloudFormation仕様の追加

VSCodeの設定ファイル(Macの場合: $HOME/Library/Application Support/Code/User/settings.json)*1に、CloudFormationのリソース仕様を追加します。この設定でをすると、拡張子がcf.yamlのファイル、cfn/、cloudformation配下のYAMLファイルがCloudFormation用のYAMLとして指定したソース仕様にしたがってチェックされます。

  1. コマンドパレット(⇧⌘P)を開いて >settings (JSON) と打ち込みsettings.jsonを開く。
  2. 下記定義を追加する
    "yaml.schemas": {
        "https://d33vqc0rt9ld30.cloudfront.net/latest/gzip/CloudFormationResourceSpecification.json": [
            "*.cf.yaml",
            "*.cf.yml",
            "cfn/*.yaml",
            "cfn/*.yml",
            "cloudformation/*.yaml",
            "cloudformation/*.yml"
        ]
    },

設定(2) CloudFormationのカスタムタグ定義の追加

デフォルトのままだと!Sub, !RefなどのCloudFormation固有のスキーマYAMLの構文エラーとなるため、これら固有スキーマを追加します。内容はこちらのIssueAndyJPhillipsさんのコメント内容を流用しています。

    "yaml.customTags": [
        "!And sequence",
        "!Equals sequence",
        "!If sequence",
        "!Not sequence",
        "!Or sequence",
        "!Base64",
        "!Cidr sequence",
        "!FindInMap sequence",
        "!GetAtt",
        "!GetAZs",
        "!ImportValue",
        "!Join sequence",
        "!Select sequence",
        "!Split sequence",
        "!Sub",
        "!Ref"
    ]

(参考)setting.json全体の内容

設定(1)&(2)をすると、既存の設定に、下記設定が追加された形になります。

{
    <既存の設定>
    "yaml.schemas": {
        "https://d33vqc0rt9ld30.cloudfront.net/latest/gzip/CloudFormationResourceSpecification.json": [
            "*.cf.yaml",
            "*.cf.yml",
            "cfn/*.yaml",
            "cfn/*.yml",
            "cloudformation/*.yaml",
            "cloudformation/*.yml"
        ]
    },
    "yaml.customTags": [
        "!And sequence",
        "!Equals sequence",
        "!If sequence",
        "!Not sequence",
        "!Or sequence",
        "!Base64",
        "!Cidr sequence",
        "!FindInMap sequence",
        "!GetAtt",
        "!GetAZs",
        "!ImportValue",
        "!Join sequence",
        "!Select sequence",
        "!Split sequence",
        "!Sub",
        "!Ref"
    ]
}

mac版PowerPoint消えなくなった自動回復ファイルを手動削除する手順

Mac版のPowerPointを利用していて、PowerPointを起動するたびに古い自動回復ファイルが開いて対処するのが面倒でした。なんとか古い自動回復ファイルを消せないかなと調べた内容を備忘録で残します。

手順

MacPowerPointの自動回復ファイルの保存先フォルダパス

~/Library/Containers/com.microsoft.Word/Data/Library/Preferences/AutoRecovery/

削除手順

  • (1)PowerPointを停止して
  • (2)コンソールを開いて下記コマンドで自動回復ファイルを削除する
cd ~/Library/Containers/com.microsoft.Word/Data/Library/Preferences/AutoRecovery/
rm -i *

Amazon RDSのIOPS&容量変更をCLIで実行するサンプル

RDS プロビジョンド IOPSで、IOPSを変更して、DBがAvailableになるまでWaitするシェル(+AWS CLI)のサンプルです。
AWS CLIで”aws rds modify-db-instance”で変更をします。
RDS&プロビジョンド IOPSで、IOPS設定によるBLOBファイルインサートのパフォーマンスの影響を確認するための検証ツール作成に利用したものです。

コマンド

DBIdentifier="dbname"
RDS_SIZE=1024  #1TB
RDS_IOPS=4096

#Modify RDS storage(size and iops)
echo "$(date '+%Y/%m/%d %H:%M:%S'): Modify RDS storage"
aws rds modify-db-instance \
        --db-instance-identifier ${DBIdentifier} \
        --allocated-storage ${RDS_SIZE} \
        --iops ${RDS_IOPS} \
        --apply-immediately;

# Wait until the DBInstanceStatus is "Available"
sleep 30
while true
do
     STAT=$(aws --output text rds describe-db-instances \
          --db-instance-identifier ${DBIdentifier} \
          --query 'DBInstances[].DBInstanceStatus')
     if [ "A${STAT}" == "Aavailable" ]; then
          break
     else
          echo "$(date '+%Y/%m/%d %H:%M:%S'): DBInstanceStatus= ${STAT}"
          sleep 15
     fi
     done
echo "$(date '+%Y/%m/%d %H:%M:%S'): Done to modifying RDS"

注意事項

  • ストレージの設定変更後、6時間はストレージの再変更ができません。実行すると以下のような感じのエラーが出ます。
An error occurred (InvalidParameterCombination) when calling the ModifyDBInstance operation: You can't currently modify the storage of this DB instance. Try again after approximately 3 hours.

CloudTrailの「組織の証跡」でOrganizationsメンバアカウントに一括CloudTrailを設定する手順 (ロギング専用アカウント+KMS暗号化)

はじめに

AWS Oraganizationsを利用したマルチアカウント構成で、Oraganizationsの配下のAWSアカウントに共通のCloudTrail設定を行うことができる「組織の証跡」が CloudTrailにあります。ドキュメントは以下になりますが、logging専用のAWSアカウントのS3バケットを利用してかつKMSによる暗号化と組み合わせたやり方の情報がなかったので試行錯誤の結果をまとめました。
docs.aws.amazon.com

構成の説明

f:id:nopipi:20190308003130p:plain
組織の証跡の設定概要

  • 設定のポイント
    • CloudTrailのログは、ロギングアカウントのS3バケットに保存します。
    • またS3保管時にKMSによる暗号化を行います。
    • KMSの鍵はロギングアカウントで管理します。
  • アカウントとOrganizations構成
    • マスターアカウント:111111111111
    • リリースアカウント:222222222222
    • ロギングアカウント:333333333333
    • OrganizationsのID: o-yyyyyyyyyy
  • S3バケットとKMSか
  • 設定時の権限
    • 今回は、AdministratorAccessポリシーが付与されている環境で作業しています

設定手順

ロギングアカウント(S3とKMSの作成)

S3バケットの準備

S3バケットを作成します。CloudTrail設定で最も重要なバケットポリシーには、以下の設定を行います。

  • 1つめ: CloudTrailサービスからのバケットACL変更を許可する
  • 2つめ:マスターアカウントの証跡書き込みを許可する
  • 3つめ: 「組織の証跡」を許可する
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AWSCloudTrailAclCheck20150319",
            "Effect": "Allow",
            "Principal": {
                "Service": "cloudtrail.amazonaws.com"
            },
            "Action": "s3:GetBucketAcl",
            "Resource": "arn:aws:s3:::trailbucket"
        },
        {
            "Sid": "AWSCloudTrailWrite20150319",
            "Effect": "Allow",
            "Principal": {
                "Service": "cloudtrail.amazonaws.com"
            },
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::trailbucket/AWSLogs/111111111111/*",
            "Condition": {
                "StringEquals": {
                    "s3:x-amz-acl": "bucket-owner-full-control"
                }
            }
        },
        {
            "Sid": "AWSCloudTrailWrite20150319",
            "Effect": "Allow",
            "Principal": {
                "Service": "cloudtrail.amazonaws.com"
            },
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::trailbucket/AWSLogs/o-yyyyyyyyyy/*",
            "Condition": {
                "StringEquals": {
                    "s3:x-amz-acl": "bucket-owner-full-control"
                }
            }
        }
    ]
}

ドキュメントでは設定は以下に説明があります。
docs.aws.amazon.com

KMSの準備

CloudTrailがS3にPUTするときの暗号化に利用するKMSのカスタマーマスターキー(CMK) を作成します。
カスタマーポリシーは、今回はCloudTrailの書き込みに必要な最低限のポリシーを付与しています。

{
    "Version": "2012-10-17",
    "Id": "Key policy created by CloudTrail",
    "Statement": [
        {
            "Sid": "Enable IAM User Permissions",
            "Effect": "Allow",
            "Principal": {
                "AWS": [
                    "arn:aws:sts::333333333333:assumed-role/OrganizationAccountAccessRole/admin",
                    "arn:aws:iam::333333333333:root"
                ]
            },
            "Action": "kms:*",
            "Resource": "*"
        },
        {
            "Sid": "Allow CloudTrail to encrypt logs",
            "Effect": "Allow",
            "Principal": {
                "Service": "cloudtrail.amazonaws.com"
            },
            "Action": "kms:GenerateDataKey*",
            "Resource": "*",
            "Condition": {
                "StringLike": {
                    "kms:EncryptionContext:aws:cloudtrail:arn": "arn:aws:cloudtrail:*:111111111111:trail/*"
                }
            }
        },
        {
            "Sid": "Allow CloudTrail to describe key",
            "Effect": "Allow",
            "Principal": {
                "Service": "cloudtrail.amazonaws.com"
            },
            "Action": "kms:DescribeKey",
            "Resource": "*"
        }
    ]
}

マスターアカウント(CloudTrailの設定)

マスターアカウントのCloudTrailに移動し、「監査情報」から「証跡の作成」を実行します。

  • ポイント
    • 「組織に証跡を適用」を「はい」とすると、「組織の証跡」が作成されます
    • 「証跡情報を全てのリージョンに適用」を「はい」にして全リージョンに設定を適用します

f:id:nopipi:20190308010258p:plain:w550

  • 出力先S3バケット設定
    • バケット名:「新しいバケットを作成する」を「いいえ」にし、バケット名を入力する
    • KMS暗号化
      • 「SSE-KMS を使用してログファイルを暗号化」を「はい」にし、
      • 「新規の KMS キーの作成」を「いいえ」にして、
      • ロギングアカウントで作成したCMKのARNを入力する

f:id:nopipi:20190308011020p:plain:w550

  • メモ
    • 作成した証跡はメンバーアカウントでは削除できません
    • マスターアカウントで証跡を削除するとメンバーアカウントの証跡も削除されます
    • 証跡作成時に、S3バケットポリシーの権限が不足していてもマネコン上では「KMSの権限不足」と表示されるようです(私はそれでハマりました)

完了

作成に成功してしばらく経つと、各メンバーアカウント状に同じ設定のCloudTrail設定が作成されます。
今回の例の場合、指定したS3バケットには以下のような構成でCloudTrailのログが出力されます。

trailbucket
└ AWSLogs
  ├ 111111111111
  │ ├ CloudTrail-Digest
  │ └ CloudTrail
  └ o-yyyyyyyyyy
    ├ 111111111111
    │ ├ CloudTrail-Digest
    │ └ CloudTrail
    ├ 222222222222
    │ ├ CloudTrail-Digest
    │ └ CloudTrail
    └ 333333333333
      ├ CloudTrail-Digest
      └ CloudTrail