この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
不要になったAWS Key Management Service (AWS KMS) のカスタマーマスターキー(CMK)は無効にしたり、削除することができます。
ここで気をつけないといけないのは、不要と判断した鍵が実は利用されていた場合です。
そこで
- CloudTrail
- CloudWatch Logs
- CloudWatch Alarm
を組み合わせて、削除予定・無効な CMK が利用されたときに、通知する仕組みを作ってみます。
CMK の無効化・削除処理の仕様について
仕様を簡単にまとめます。
無効処理について
CMK は即座に無効・無効解除できます。
無効状態の鍵で Encrypt/Decrypt すると DisabledException
エラーが発生します。
$ aws kms encrypt \
--key-id arn:aws:kms:us-east-1:123456789012:key/XXX \
--plaintext foo
An error occurred (DisabledException) when calling the Encrypt operation: arn:aws:kms:us-east-1:123456789012:key/XXX is disabled.
削除処理について
AWS KMS で作成した CMK を削除するには、7日〜30日の削除保留期間が必ず発生します。 保留期間中はいつでも削除を解除でき、一方で、保留期間がすぎて削除されると、鍵は復元できなくなります。当然、過去に暗号化されたデータは復号できなくなります。
鍵を利用していないことを100%確認が持てないときは、削除ではなく、無効化を検討してください。
削除保留状態の鍵で Encrypt/Decrypt すると KMSInvalidStateException
エラーが発生します。
$ aws kms encrypt \
--key-id arn:aws:kms:us-east-1:123456789012:key/XXX \
--plaintext foo
An error occurred (KMSInvalidStateException) when calling the Encrypt operation: arn:aws:kms:us-east-1:123456789012:key/XXX is pending deletion.
なお、インポートしたキーマテリアルを削除すると、CMK は即座に使用できなくなります。CMK を再び使用するには、同じキーマテリアルを再インポートする必要があります。
参考 https://docs.aws.amazon.com/kms/latest/developerguide/importing-keys-delete-key-material.html
CMK のステート
鍵の無効や削除に伴い CMK のステートは次のように推移します。
※鍵をインポートした場合は別途「PendingImport」というステートが存在します。
キーステータスがカスタマーマスターキーの使用に与える影響 も合わせてご確認ください。
有効でないCMKを検知するシステム
次のドキュメントに従い仕組みを作ります。
https://docs.aws.amazon.com/kms/latest/developerguide/deleting-keys-creating-cloudwatch-alarm.html
大まかには、以下の流れで作業します。
- CloudWatch Logs への CloudTrail ログファイル配信を設定
- エラーログを検出するメトリックフィルタを作成
- エラーが検出されたら通知する CloudWatch アラームを作成
検知で取りこぼしがないよう、KMS の API 呼び出しでエラーが発生しているもの全体を通知対象とします。
CloudTrail の有効化
CloudTrail はユーザーアクティビティと API 使用状況の追跡するサービスです。 まだ有効になっていない場合は、次のドキュメントに従い有効化してください。
http://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-create-and-update-a-trail.html
CloudWatch Logs への CloudTrail ログファイル配信を設定する
CloudTrail から CloudWatch Logs にイベントを送信するよう設定します。
管理コンソールを利用する場合、Trail の Configuration 画面から配信するロググループを指定します。
メトリックフィルターの作成
CloudWatch Logs 画面で配信先に指定したロググループを選択し、"Create Metric Filter" をクリックします。
Define Pattern
Filter Pattern に次のパターンを指定します。
{ $.eventSource = kms.amazonaws.com && ($.errorMessage = "*" || $.errorCode = "*") }
KMS サービスに対する API 呼び出しで、なにか例外が発生したログをフィルタ対象としています。
CloudTrail ログに errorMessage
または errorCode
の属性が存在する場合、エラーと判定しています。
Assign Metric
- Filter Name : eventSource-kms-errorCode-Exception
- Metric Namespace : CloudTrailLogMetrics
- Metric Name : KMSErrorCount
と入力して、フィルタを作成します。
フィルタ一覧画面にある "Create Alarm" リンクから CloudWatch Alarm の作成画面に移動します。
アラームの作成
先程作成したメトリック(KMSErrorCount
)は選択済みです。
このメトリックに対して
Whenever KMSErrorCound >= 1 for 1 out of 1 datapoints
のしきい値でアラームを設定します。
欠落データの扱い("Treat missing data as") は good (not breaching threshold) を選択します。 今回のように、異常時にしかメトリックデータが発生しないようなときに向いています。
詳細はドキュメントをご確認ください。
通知先は Actions で指定します。
個人検証では、メールアドレスに通知する SNS を利用しました。
動作確認
以上で準備が整いました。
CMK を無効・削除して KMS::Encrypt API を呼び出してエラーを発生させ、アラームが機能していることを確認します。
有効状態の CMK の動作確認
$ KEYID=arn:aws:kms:us-east-1:123456789012:key/XXX
$ aws kms describe-key --key-id $KEYID
{
"KeyMetadata": {
"KeyManager": "CUSTOMER",
"KeyId": "XXX",
"Description": "CMK For S3",
"CreationDate": 1522329556.173,
"KeyUsage": "ENCRYPT_DECRYPT",
"Arn": "arn:aws:kms:us-east-1:123456789012:key/XXX",
"KeyState": "Enabled",
"AWSAccountId": "123456789012",
"Origin": "AWS_KMS",
"Enabled": true
}
}
- Enabled : true
- KeyState : Enabled
となっています。
この状態で Encrypt
を呼び出します
$ aws kms encrypt \
--key-id $KEYID \
--plaintext foo
{
"KeyId": "arn:aws:kms:us-east-1:123456789012:key/XXX",
"CiphertextBlob": "AQICAHjm+..."
}
成功し、base64 エンコードされた ciphertext を返します。
CMK を無効化して API 呼び出し
次に、この CMK を無効化します。
$ aws kms disable-key --key-id $KEYID
$ aws kms describe-key --key-id $KEYID
{
"KeyMetadata": {
"KeyUsage": "ENCRYPT_DECRYPT",
"AWSAccountId": "123456789012",
"Arn": "arn:aws:kms:us-east-1:123456789012:key/XXX",
"Enabled": false,
"KeyId": "XXX",
"Description": "CMK For S3",
"KeyManager": "CUSTOMER",
"Origin": "AWS_KMS",
"KeyState": "Disabled",
"CreationDate": 1522329556.173
}
}
- Enabled : true → false
- KeyState : Enabled → Disabled
に変わっています。
この状態で Encrypt
を呼び出します。
$ aws kms encrypt \
--key-id $KEYID \
--plaintext foo
An error occurred (DisabledException) when calling the Encrypt operation: arn:aws:kms:us-east-1:123456789012:key/XXX is disabled.
DisabledException
エラーが発生しました。
CMK を削除してAPI呼び出し
次に、この CMK を削除します。
保留期間(--pending-window-in-days
) は10日とします。
$ aws kms schedule-key-deletion --key-id $KEYID \
--pending-window-in-days 10
{
"KeyId": "arn:aws:kms:us-east-1:123456789012:key/XXX",
"DeletionDate": 1534291200.0
}
$ aws kms describe-key --key-id $KEYID
{
"KeyMetadata": {
"Enabled": false,
"KeyId": "XXX",
"DeletionDate": 1534291200.0,
"Origin": "AWS_KMS",
"CreationDate": 1522329556.173,
"Arn": "arn:aws:kms:us-east-1:123456789012:key/XXX",
"KeyUsage": "ENCRYPT_DECRYPT",
"AWSAccountId": "123456789012",
"KeyManager": "CUSTOMER",
"Description": "CMK For S3",
"KeyState": "PendingDeletion"
}
}
- Enabled : false
- DeletionDate : 削除される UNIX TIME
- KeyState : PendingDeletion
に変わっています。
この状態で Encrypt
を呼び出します。
$ aws kms encrypt \
--key-id $KEYID \
--plaintext foo
An error occurred (KMSInvalidStateException) when calling the Encrypt operation: arn:aws:kms:us-east-1:123456789012:key/XXX is pending deletion.
KMSInvalidStateException
エラーが発生しました。
CloudWatch Alarm の確認
CloudTrail → CloudWatch Logs → CloudWatch Alarm が発火していることを確認します。
管理コンソール
エラー発生件数が記録されており、しきい値を超えているために ALARM ステートになっています。
期待通りです。
メール
SNS 経由で アラームメールが送信されています。
期待どおりです。
CloudTrail ログの確認
CloudTrail での KMS::Encrypt API 呼び出しのログを確認します
メトリックフィルターの条件になっている属性を確認します。
$ aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=EventName,AttributeValue=Encrypt \
--max-items 3 |
jq '.Events[].CloudTrailEvent |
fromjson |
{errorCode, errorMessage}'
{
"errorCode": "KMSInvalidStateException",
"errorMessage": "arn:aws:kms:us-east-1:123456789012:key/XXX is pending deletion."
}
{
"errorCode": "DisabledException",
"errorMessage": "arn:aws:kms:us-east-1:123456789012:key/XXX is disabled."
}
{
"errorCode": null,
"errorMessage": null
}
API 呼び出し時と同じです。
参考までに、CloudTrail の CMK 無効化時のログ全体は以下のようになっています。
{
"eventVersion": "1.05",
"userIdentity": {
...
},
"eventTime": "2018-08-04T11:00:07Z",
"eventSource": "kms.amazonaws.com",
"eventName": "Encrypt",
"awsRegion": "us-east-1",
...
"errorCode": "DisabledException",
"errorMessage": "arn:aws:kms:us-east-1:123456789012:key/XXX is disabled.",
"requestParameters": {
"keyId": "arn:aws:kms:us-east-1:123456789012:key/XXX"
},
"responseElements": null,
"requestID": "8c9cbb6e-97d5-11e8-a0a4-0d492c69e0cf",
"eventID": "63d702bd-52b4-45ad-b72d-88ada860d9f0",
"readOnly": true,
"resources": [
{
"ARN": "arn:aws:kms:us-east-1:123456789012:key/XXX",
"accountId": "123456789012",
"type": "AWS::KMS::Key"
}
],
"eventType": "AwsApiCall",
"recipientAccountId": "123456789012"
}
AWS Secrets Manager への応用
2018年4月にはシークレットを管理するマネージドサービス AWS Secrets Manager が発表されました。 このサービスで管理するシークレットは、CMK と同じく、即時に削除状態とはならず、保留期間が設けられています。
今回と同様の仕組みにより、削除予定のシークレットが利用された場合は、検知することができます。
具体的な手順は次のドキュメントを参照ください。
まとめ
無効・削除予定の AWS KMS CMK が利用されたときに、検知する仕組みを紹介しました。
一旦削除されると復元できなくなるため、100%不要であると確信が持てない限り CMK を削除しないようにしてください。
今回はエラー検知の取りこぼしがないように Filter Pattern を { $.eventSource = kms.amazonaws.com && ($.errorMessage = "*" || $.errorCode = "*") }
としました。
特定の errorMessage
だけを対象にするなど、要件に合わせてカスタマイズしてください。
今回紹介した仕組みは AWS Secrets Manager のシークレットにも活用できます。