Kinesisストリームのサーバーサイド暗号を使ってみた

コンプライアンスや情報セキュリティの観点から、データの暗号化が求められるケースは多々あります。 S3やRDSでは以前から保存時のデータ暗号化(encryption at rest)を提供してきました。

この度、KinesisストリームもKMSと連携したサーバーサイド暗号に対応したため、早速触ってみました。

Kinesisストリームの暗号化の方針

Kinesisストリームの暗号化は大きく以下の2方針があります。

  • サーバーサイド暗号 : Kinesisストリームがストレージに保存・取り出すタイミングで暗号・復号
  • クライアント暗号 : Producerが暗号化したデータをKinesisストリームにPut。ConsumerがGetしたデータを復号

今回紹介する機能は前者のサーバーサイド暗号です。 後者のクライアント暗号については、次のブログを参考にして下さい。

Encrypt and Decrypt Amazon Kinesis Records Using AWS KMS | AWS Big Data Blog

サーバーサイド暗号の処理の流れ

サーバーサイド暗号では、Kinesisストリームのストレージ I/O のタイミングで暗号・復号処理を行います。

AWS Big Data Blogに解説記事があるため、こちらをベースに紹介します。

Under the Hood of Server-Side Encryption for Amazon Kinesis Streams | AWS Big Data Blog

AWs CLIからの利用は次のブログを参照下さい。

https://dev.classmethod.jp/cloud/aws/kinesis-streams-server-side-encryption/

共通処理

Amazon KMSでエンベロープ暗号します。 そのため、マスターキーとは別にデータ毎に異なるデータキーが生成されます。

参照 : エンベロープ暗号の流れ

暗号処理

https://aws.amazon.com/blogs/big-data/under-the-hood-of-server-side-encryption-for-amazon-kinesis-streams/

画像はブログから

登場人物

  • Data Producer
  • AWS KMS
  • Kinesis Streams Front End
  • Kinesis Streams Storage

処理の流れ

Kinesis Streams Front End は Data Producer からプレインデータを受け取り、AWS KMSで暗号化したあと、Kinesis Streams Storage に保存します。

復号処理

https://aws.amazon.com/blogs/big-data/under-the-hood-of-server-side-encryption-for-amazon-kinesis-streams/

画像はブログから

登場人物

  • Data Producer
  • AWS KMS
  • Kinesis Streams Front End
  • Kinesis Streams Storage

処理の流れ

Kinesis Streams Front End は Kinesis Streams Storage から暗号化されたデータを取得し、AWS KMSで復号したあと、Data Consumerにプレインデータを渡します。

実際に試してみる

サーバーサイド暗号の流れを確認した上で、実際に操作してみましょう。

作業の流れ

  1. Kinesis ストリームの作成
  2. Kinesis ストリームの暗号を有効化
  3. Kinesis ストリームにデータを投入
  4. Kinesis ストリームからデータを取得

1. Kinesis ストリームの作成

Kinesisストリームを作成します。 サーバーサイド暗号が対応している以下のリージョンで作成して下さい。

  • US East (N. Virginia)
  • US West (N. California and Oregon)
  • EU (Ireland)
  • Asia Pacific (Tokyo)
  • Asia Pacific (Singapore)

今回は EU (Ireland) を利用しました。

2. Kinesis ストリームの暗号を有効化

Kinesis ストリームの暗号化を有効します。

管理画面でストリームの"Details"画面に移動します。 "Server-side encryption"のセクションが追加されているため、"Edit"ボタンで編集モードに入ります。

初期状態ではServer-side encryptionは無効化(Disabled)されているため、Enabled を選択肢、KMS master keyを選択します。

KMS master key には、AWSが用意するKinesisストリーム専用のデフォルトのマスターキーの他、ユーザーがストリームと同じリージョンに作成したマスターキー(CMK)も選択可能です。

kinesis-sse-setting

20秒程度すると、暗号化は有効化されます。

kinesis-stream-encrypted-amc

CLI で暗号化されたストリームを確認

Kinesisストリームの describe-stream APIで暗号前後のKinesisストリームの表示の違いを確認してみましょう。

暗号有効前
  • KeyId
  • EncryptionType

が今回追加された項目です。 暗号有効化前はどちらも null/None と未設定です。

$ aws kinesis describe-stream --stream-name foo
{
    "StreamDescription": {
        "KeyId": null,
        "EncryptionType": "NONE",
        "StreamStatus": "ACTIVE",
        "StreamName": "foo",
        "Shards": [
            {
                "ShardId": "shardId-000000000000",
                "HashKeyRange": {
                    "EndingHashKey": "340282366920938463463374607431768211455",
                    "StartingHashKey": "0"
                },
                "SequenceNumberRange": {
                    "StartingSequenceNumber": "49575028884351352808466151260471461389056657087524765698"
                }
            }
        ],
        "StreamARN": "arn:aws:kinesis:eu-west-1:123456789012:stream/foo",
        "EnhancedMonitoring": [
            {
                "ShardLevelMetrics": []
            }
        ],
        "RetentionPeriodHours": 24
    }
}
暗号有効後
  • KeyID に KMS のマスターキーの ARN
  • EncryptionType に KMS

と出力されているのがわかります。

$ aws kinesis describe-stream --stream-name foo
{
    "StreamDescription": {
        "KeyId": "arn:aws:kms:eu-west-1:123456789012:key/DUMMY-DUMMY",
        "EncryptionType": "KMS",
        "StreamStatus": "ACTIVE",
        "StreamName": "foo",
        "Shards": [
            {
                "ShardId": "shardId-000000000000",
                "HashKeyRange": {
                    "EndingHashKey": "340282366920938463463374607431768211455",
                    "StartingHashKey": "0"
                },
                "SequenceNumberRange": {
                    "StartingSequenceNumber": "49575028884351352808466151260471461389056657087524765698"
                }
            }
        ],
        "StreamARN": "arn:aws:kinesis:eu-west-1:123456789012:stream/foo",
        "EnhancedMonitoring": [
            {
                "ShardLevelMetrics": []
            }
        ],
        "RetentionPeriodHours": 24
    }
}

3. Kinesis ストリームにデータを投入

ストリームにデータを投入します。

暗号処理はサーバーサイドで行われるため、クライアント側の特別な対応は不要です。

注意

暗号鍵にKMSにCMKを設定した場合は、後述の手順に従い、IAMパーミッションを見直して下さい。 デフォルトのマスターキー(aws/kinesis)を設定した場合は、特別なIAM対応は不要です。

暗号化前

$ aws kinesis put-record --stream-name foo --data a --partition-key a
{
    "ShardId": "shardId-000000000000",
    "SequenceNumber": "49574905023871648630326283351695319731000495113848750082"
}

暗号化後

暗号化後は、レスポンスに "EncryptionType": "KMS" という情報が追加されます

$ aws kinesis put-record --stream-name foo --data b --partition-key b
{
    "ShardId": "shardId-000000000000",
    "EncryptionType": "KMS",
    "SequenceNumber": "49574905023871648630326283351698946508459383806471700482"
}

4. Kinesis ストリームからデータを取得

ストリームからデータを取得します。

暗号処理はサーバーサイドで行われるため、クライアント側の特別な対応は不要です。

暗号化を有効にしたストリームに取り込まれたデータは、get-records 時に "EncryptionType" 属性が追加されます。

注意

暗号鍵にKMSにCMKを設定した場合は、後述の手順に従い、IAMパーミッションを見直して下さい。 デフォルトのマスターキー(aws/kinesis)を設定した場合は、特別なIAM対応は不要です。

$ aws kinesis get-shard-iterator --stream-name foo --shard-id shardId-000000000000 --shard-iterator-type TRIM_HORIZON
{
    "ShardIterator": "DUMMY"
}
$ aws kinesis get-records --shard-iterator "DUMMY"
{
    "Records": [
        {
            "Data": "YQ==",
            "PartitionKey": "a",
            "ApproximateArrivalTimestamp": 1499878905.118,
            "SequenceNumber": "49575030693030991390093810531762068269413316054617161730"
        },
        {
            "Data": "Yg==",
            "PartitionKey": "b",
            "ApproximateArrivalTimestamp": 1499878938.69,
            "EncryptionType": "KMS",
            "SequenceNumber": "49575030693030991390093810531763277195232933088973553666"
        }
    ],
    "NextShardIterator": "DUMMY-NEXT",
    "MillisBehindLatest": 0
}

## base64デコードして実データを確認
$ echo "YQ==" | base64 -d
a
$ echo "Yg==" | base64 -d
b

KMSにCMKを利用する場合の対応

暗号鍵にはAWSが管理するデフォルトのマスターキーとは別に、ユーザーが作成したKMSマスターキー(CMK)を設定する事もできます。

この場合、追加で以下の手順が発生します。

  • KMS マスターキーの作成
  • IAM パーミッションの設定

なお、CMK は API 利用費とは別に月あたり1ドルの利用費が発生します。ご注意下さい。

KMS マスターキーの作成

暗号化したいKinesis ストリームと同じリージョンに マスターキーを作成します。

具体的な手順は次のページを参照下さい。

AWS Documentation » AWS KMS » Developer Guide » Getting Started » Creating Keys

IAM パーミッションの設定

暗号鍵にデフォルト鍵を設定する場合、IAMパーミッションの特別な対応は不要ですが、CMK を設定する場合、クライアントがその鍵を使えるようにパーミッションを修正する必要があります。

Producer向けパーミッション

データを生成するProducer向けパーミッションです。 データを暗号化するためのデータキーを生成出来るようにします。

  • 8行目には作成した KMS CMK の ARN を設定
  • 15行目には Kinesis Stream の ARN を設定
{
    "Version": "2012-10-17",
        "Statement": [{
            "Effect": "Allow",
            "Action": [
                "kms:GenerateDataKey"
            ],
            "Resource": "arn:aws:kms:eu-west-1:123456789012:key/Dummy"
            }, {
            "Effect": "Allow",
            "Action": [
                "kinesis:PutRecord",
                "kinesis:PutRecords"
            ],
            "Resource": "arn:aws:kinesis:*:123456789012:foo"
       }
    ]
}

パーミッションが足りない場合は、以下のようなエラーメッセージが表示されます

$ aws kinesis put-record --stream-name foo --data f --partition-key f  --region eu-west-1

An error occurred (KMSAccessDeniedException) when calling the PutRecord operation: User ASDF is not authorized to encrypt records in stream 123456789012:foo:1499523089

Consumer向けパーミッション

データを受け取るConsumer向けパーミッションです。 暗号化されたデータキーをマスターキーで復号出来るようにします。

  • 8行目には作成した KMS CMK の ARN を設定
  • 15行目には Kinesis Stream の ARN を設定
{
    "Version": "2012-10-17",
        "Statement": [{
            "Effect": "Allow",
            "Action": [
                "kms:Decrypt"
            ],
            "Resource": "arn:aws:kms:eu-west-1:123456789012:key/Dummy"
            }, {
            "Effect": "Allow",
            "Action": [
                "kinesis:GetRecords",
                "kinesis:DescribeStream"
            ],
            "arn:aws:kinesis:*:123456789012:foo"
        }
    ]
}

パーミッションが足りない場合は、以下のようなエラーメッセージが表示されます

$ aws kinesis get-records --shard-iterator "DUMMY"

An error occurred (KMSAccessDeniedException) when calling the GetRecords operation: User DUMMY is not authorized to decrypt records in stream 1234556789012:foo:1499523089

利用可能なリージョン

2017/07/12 時点では以下のリージョンでのみ利用できます

  • US East (N. Virginia)
  • US West (N. California and Oregon)
  • EU (Ireland)
  • Asia Pacific (Tokyo)
  • Asia Pacific (Singapore)

2017/07/19更新

対応リージョンが以下に増えました

  • US East (Ohio) us-east-2
  • US East (N. Virginia) us-east-1
  • US West (Oregon) us-west-2
  • US West (N. California) us-west-1
  • AWS GovCloud (US) us-gov-west-1
  • Canada (Central) ca-central-1
  • EU (Ireland) eu-west-1
  • EU (London) eu-west-2
  • EU (Frankfurt) eu-central-1
  • Asia Pacific (Tokyo) Region ap-northeast-1
  • Asia Pacific (Seoul) Region ap-northeast-2
  • Asia Pacific (Singapore) ap-southeast-1
  • Asia Pacific (Mumbai) ap-south-1
  • Asia Pacific (Sydney) ap-southeast-2
  • South America (São Paulo) sa-east-1

コスト

Kinesis ストリーム

追加コストは発生しません。

KMS

  • データキーの生成
  • マスターキーによるデータキーの復号

などで KMS の API を呼び出します。 月当り数ドル以下と予想されます。

また、CMK を作成すると月あたり $1 のコストが発生します。 AWSが管理するマスターキーは無料です。

パフォーマンス

マニュアルによると、暗号処理のオーバーヘッドにより、PUT/GETともに 0.1ミリ秒(100μs)以下のレイテンシーが発生すると記載があります。

Due to the service overhead of applying encryption, applying server-side encryption will increase the typical latency of PutRecord, PutRecords, and GetRecords by less than 100μs.

AWS Big Data Blogによると、暗号処理のオーバーヘッドにより、PUT/GET ともにレコードあたり 0.2 ミリ秒以下のレイテンシーが観測されました。

During testing, AWS discovered that there was a slight increase (typically 0.2 millisecond or less per record) with put and get record latencies due to the additional overhead of encryption.

まとめ

  • ちょっとした設定変更
  • 軽微なコスト増加
  • 微小なレイテンシー

を受け入れれば、クライアント側の変更無しに、サクッとKinesisストリームのサーバーサイド暗号を実現できます。

参考