CloudTrailの有効化とログのKMS暗号化をCloudFormation一発で
こんにちは、虎塚です。
2015年12月のAWSアップデートによって、AWS CloudFormationを使ってAWS Key Management Service (KMS) のカスタマーマスターキーを作成できるようになりました。
また、2015年10月のAWSアップデートでは、AWS CloudTrailのログファイルをKMSで暗号化できるようになっています。
- CloudTrail announces support for log file encryption using KMS Key and log file integrity validation
これらの機能を組み合わせると、CloudFormation一発で、全リージョンのCloudTrailを有効化し、そのログファイルをKMSで暗号化することが簡単にできます。今回の記事では、その手順とポイントを説明します。
手順
1. CloudFormationテンプレートの作成
ローカルで次のようなCloudFormationテンプレートを作成します。パラメータもアウトプットもない、リソース定義だけのテンプレートです。
{ "AWSTemplateFormatVersion" : "2010-09-09", "Description" : "set CloudTrail with KMS encryption", "Resources" : { "S3Bucket": { "DeletionPolicy" : "Retain", "Type": "AWS::S3::Bucket", "Properties": { } }, "BucketPolicy" : { "Type" : "AWS::S3::BucketPolicy", "Properties" : { "Bucket" : {"Ref" : "S3Bucket"}, "PolicyDocument" : { "Version": "2012-10-17", "Statement": [ { "Sid": "AWSCloudTrailAclCheck", "Effect": "Allow", "Principal": { "Service": "cloudtrail.amazonaws.com" }, "Action": "s3:GetBucketAcl", "Resource": { "Fn::Join" : ["", ["arn:aws:s3:::", {"Ref":"S3Bucket"}]]} }, { "Sid": "AWSCloudTrailWrite", "Effect": "Allow", "Principal": { "Service": "cloudtrail.amazonaws.com" }, "Action": "s3:PutObject", "Resource": { "Fn::Join" : ["", ["arn:aws:s3:::", {"Ref":"S3Bucket"}, "/AWSLogs/", {"Ref":"AWS::AccountId"}, "/*"]]}, "Condition": { "StringEquals": { "s3:x-amz-acl": "bucket-owner-full-control" } } } ] } } }, "myKey" : { "DeletionPolicy" : "Retain", "Type" : "AWS::KMS::Key", "Properties" : { "Description" : "for CloudTrail log files", "Enabled" : true, "KeyPolicy" : { "Version": "2012-10-17", "Id": "key-default-1", "Statement": [ { "Sid": "Allow administration of the key", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::123456789012:user/key-admin" }, "Action": [ "kms:Create*", "kms:Describe*", "kms:Enable*", "kms:List*", "kms:Put*", "kms:Update*", "kms:Revoke*", "kms:Disable*", "kms:Get*", "kms:Delete*", "kms:ScheduleKeyDeletion", "kms:CancelKeyDeletion" ], "Resource": "*" }, { "Sid": "Allow use of the key", "Effect": "Allow", "Principal": { "Service": "cloudtrail.amazonaws.com", "AWS": "arn:aws:iam::123456789012:user/audit-user" }, "Action": [ "kms:Encrypt", "kms:Decrypt", "kms:ReEncrypt", "kms:GenerateDataKey*", "kms:DescribeKey" ], "Resource": "*" } ] } } }, "myTrail" : { "DependsOn" : ["BucketPolicy", "myKey"], "Type" : "AWS::CloudTrail::Trail", "Properties" : { "EnableLogFileValidation" : true, "IncludeGlobalServiceEvents" : true, "IsLogging" : true, "KMSKeyId" : {"Ref" : "myKey"}, "S3BucketName" : {"Ref" : "S3Bucket"} } } } }
リソースとして、S3バケット、S3バケットポリシー、KMSのカスタマーマスターキー、CloudTrail設定を定義しています。
KMSリソースを作成した後、Ref関数を使ってKeyIDを取ることができます。CloudTrail (myTrail) のプロパティにあるように、次のように参照します。
"KMSKeyId" : {"Ref" : "myKey"}
さて、上のテンプレートについて、ポイントを4つご紹介します。
ポイント1. S3バケットとKMS鍵のDeletion Policy
S3バケットとKMS鍵のリソース定義で、Deletion Policyに「Retain」を設定しています。これによって、たとえCloudFormationスタックを削除しても、ログファイルを出力したS3バケットと、ログファイルの暗号化に使用した鍵は、削除されずに残ります。
特に後者は重要です。ログファイルの暗号化に使った鍵を削除したら最後、ログファイルを復号できなくなってしまいます。誤ってスタックを削除した時のための保険として、設定しておきましょう。
ポイント2. KMSの鍵の権限分離
KMSの鍵を管理する権限は、key-adminユーザに与えています。一方、KMSの鍵を使った暗号化や復号の権限は、CloudTrailサービスと監査用ユーザ audit-user に与えています。なお、これら2個のIAMユーザの作成は、上のテンプレートに含まれませんので、あらかじめ作成しておいてください。
ログを閲覧するだけのユーザに、ログファイルを暗号化/復号する鍵を無効化したり削除したりできる権限を与える必要はないかと思います。鍵の管理者と利用者を分離できるというKMSの特長を活かした設定をしましょう。
ポイント3. KMSのキーにDescriptionをつける
CloudFormationで作成したKMSのカスタマーマスターキーには、エイリアスがつきません。エイリアスとは、「alias/」ではじまる文字列です。カスタマーマスターキーには、KeyIdやKeyのARN以外に、Keyを特定するための分かりやすいラベルをつけることができます。これをエイリアスといいます。
エイリアスがないと、あとから鍵の用途が分からなくなってしまいます。CloudFormationテンプレートのリソース定義で、KMSのDescriptionプロパティを設定して、用途を記述しておきましょう。
"Description" : "for CloudTrail log files"
ポイント4. S3バケット名を指定しない
リソース定義でS3バケット名を指定すると、リソースの置換をともなう更新ができなくなります。将来のテンプレート拡張の柔軟性を確保するために、S3バケット名を明示的に指定することを避けています。
2. CloudFormationスタックの作成
CloudFormationスタックを作成します。AWS Management Consoleで作業してもよいですが、全リージョンで実行する必要があるため、AWS CLIを使ったほうが楽だと思います。
テンプレートファイルを置いたディレクトリに移動して、次のAWS CLIコマンドを実行します。実行には、jqのインストールが必要です。Mac OS X 10.10.5 (Yosemite) の iTerm2 (2.1.4) 上のbashで動作確認しました。
$ for region in $(aws ec2 describe-regions | jq -r ".Regions[].RegionName | @text") do echo "[$region]" ; aws --region $region cloudformation create-stack --stack-name cloudtrail-$region --template-body file://kms.template ; done
まず、ec2 describe-regionsコマンドですべてのリージョン名を取得します。次に、各リージョンを指定して、cloudformation create-stackコマンドを実行しています。S3バケットもKMSの鍵も、CloudTrailを有効にするリージョンごとに1つ作る必要があるためです。
ちょっとした工夫として、CloudFormationのスタック名にリージョン名を入れています。CloudFormationで作成したS3バケットには、スタック名がプリフィックスとして使われます。バケット名からリージョン名が分かると、ログを閲覧する時に便利です。前述のようにS3バケット名の指定を回避しつつ、リージョン名をバケット名に挿入するために、スタック名を活用しましょう。
スタック作成の実行結果
$ for region in $(aws ec2 describe-regions | jq -r ".Regions[].RegionName | @text") > do echo "[$region]" ; > aws --region $region cloudformation create-stack --stack-name cloudtrail-$region --template-body file://kms.template ; > done [eu-west-1] { "StackId": "arn:aws:cloudformation:eu-west-1:123456789012:stack/cloudtrail-eu-west-1/********-****-****-****-************" } [ap-southeast-1] { [...]
3. CloudTrail設定の確認
ステップ2で作成したCloudTrailの設定を確認するには、次のコマンドを実行します。
for region in $(aws ec2 describe-regions | jq -r ".Regions[].RegionName | @text") do echo "[$region]" ; aws --region $region cloudtrail describe-trails ; done
CloudTrail設定の確認の実行結果
$ for region in $(aws ec2 describe-regions | jq -r ".Regions[].RegionName | @text") > do echo "[$region]" ; > aws --region $region cloudtrail describe-trails ; > done [eu-west-1] { "trailList": [ { "IncludeGlobalServiceEvents": true, "Name": "cloudtrail-eu-west-1-myTrail-************", "TrailARN": "arn:aws:cloudtrail:eu-west-1:123456789012:trail/cloudtrail-eu-west-1-myTrail-************", "LogFileValidationEnabled": true, "KmsKeyId": "arn:aws:kms:eu-west-1:123456789012:key/********-****-****-****-************", "S3BucketName": "cloudtrail-eu-west-1-s3bucket-************" } ] } [ap-southeast-1] { [...]
リージョンごとにCloudTrailが作成されていること、KMSの鍵が関連付けられていること、ログファイル出力先のS3バケット名などが確認できます。
留意事項
1. すでにCloudTrailが存在する場合のエラー
上のCloudFormationスタックを作成しようとした時、CloudTrailによるロギングがすでに有効だと、次のようなエラーが出ます。
User: 123456789012 already has 1 trails in ap-northeast-1.
スタックの作成を完了するには、CloudTrailを一旦削除する必要があります。単にロギングを停止するだけでなく、削除しなければなりません。削除は、次のコマンドでおこないます。
$ aws cloudtrail describe-trails { "trailList": [ { "TrailARN": "arn:aws:cloudtrail:ap-northeast-1:123456789012:trail/Default", "IncludeGlobalServiceEvents": true, "Name": "Default", "S3BucketName": "cloudtrail-ap-northeast-1-123456789012" } ] } $ aws cloudtrail delete-trail --name Default $ aws cloudtrail describe-trails { "trailList": [] }
2. CloudFormationで作成した鍵の削除
CloudFormationテンプレートのリソース定義で、KMSの鍵にDeletionPolicy「Retain」を設定しないと、どうなるでしょうか。
CloudFormationスタックを削除した場合、もしくは、スタックの作成中にロールバックした場合、スタックで作成されたKMSのカスタマーマスターキーは、ステータスがPending Deletionになります。
KMSのカスタマーマスターキーは即座に削除できませんが、削除までの猶予期間を設定できます。上記の場合、カスタマーマスターキーには猶予期間が設定された状態になります。猶予期間には、最短の7日間が設定されています。
おわりに
AWSアップデートのおかげで、CloudTrailの有効化とログファイルの暗号化をあわせて簡単に実施できるようになりました。スクリプトで自動化することによって、設定忘れや設定ミスを防いで、セキュアな環境を構築できるようになるのはうれしいですね。
それでは、また。