-
Notifications
You must be signed in to change notification settings - Fork 16
chore(python): Plaintext migration examples #1957
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
3 changes: 3 additions & 0 deletions
3
Examples/runtimes/python/Migration/plaintext_to_awsdbe/__init__.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. | ||
# SPDX-License-Identifier: Apache-2.0 | ||
"""Stub to allow relative imports of examples from tests.""" |
51 changes: 51 additions & 0 deletions
51
Examples/runtimes/python/Migration/plaintext_to_awsdbe/src/README.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
# Plaintext to AWS Database Encryption SDK for DynamoDB Migration | ||
|
||
This project demonstrates the steps necessary | ||
to migrate to the AWS Database Encryption SDK for DynamoDb | ||
from a plaintext database. | ||
|
||
[Step 0](./plaintext/README.md) demonstrates the starting state for your system. | ||
|
||
## Step 1 | ||
|
||
In Step 1, you update your system to do the following: | ||
|
||
- continue to read plaintext items | ||
- continue to write plaintext items | ||
- prepare to read encrypted items | ||
|
||
When you deploy changes in Step 1, | ||
you should not expect any behavior change in your system, | ||
and your dataset still consists of plaintext data. | ||
|
||
You must ensure that the changes in Step 1 make it to all your readers before you proceed to Step 2. | ||
|
||
## Step 2 | ||
|
||
In Step 2, you update your system to do the following: | ||
|
||
- continue to read plaintext items | ||
- start writing encrypted items | ||
- continue to read encrypted items | ||
|
||
Before you deploy changes in Step 2, | ||
you are introducing encrypted items to your system, | ||
and must make sure that all your readers are updated with the changes from Step 1. | ||
|
||
Before you move onto the next step, you will need to encrypt all plaintext items in your dataset. | ||
Once you have completed this step, | ||
while new items are encrypted and will be authenticated on read, | ||
your system will still accept reading plaintext, unauthenticated items. | ||
In order to complete migration to a system where you always authenticate your items, | ||
you should prioritize moving on to Step 3. | ||
|
||
## Step 3 | ||
|
||
Once all plaintext items are encrypted, | ||
update your system to do the following: | ||
|
||
- continue to write encrypted items | ||
- continue to read encrypted items | ||
- do not accept reading plaintext items | ||
|
||
Once you have deployed these changes to your system, you have completed migration. |
3 changes: 3 additions & 0 deletions
3
Examples/runtimes/python/Migration/plaintext_to_awsdbe/src/__init__.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. | ||
# SPDX-License-Identifier: Apache-2.0 | ||
"""Stub to allow relative imports of examples from tests.""" |
3 changes: 3 additions & 0 deletions
3
Examples/runtimes/python/Migration/plaintext_to_awsdbe/src/awsdbe/__init__.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. | ||
# SPDX-License-Identifier: Apache-2.0 | ||
"""Stub to allow relative imports of examples from tests.""" |
3 changes: 3 additions & 0 deletions
3
Examples/runtimes/python/Migration/plaintext_to_awsdbe/src/awsdbe/client/__init__.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. | ||
# SPDX-License-Identifier: Apache-2.0 | ||
"""Stub to allow relative imports of examples from tests.""" |
206 changes: 206 additions & 0 deletions
206
Examples/runtimes/python/Migration/plaintext_to_awsdbe/src/awsdbe/client/common.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,206 @@ | ||
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. | ||
# SPDX-License-Identifier: Apache-2.0 | ||
"""Common Utilities for Migration Examples.""" | ||
import boto3 | ||
from aws_cryptographic_material_providers.mpl import AwsCryptographicMaterialProviders | ||
from aws_cryptographic_material_providers.mpl.config import MaterialProvidersConfig | ||
from aws_cryptographic_material_providers.mpl.models import ( | ||
CreateAwsKmsMrkMultiKeyringInput, | ||
DBEAlgorithmSuiteId, | ||
) | ||
from aws_cryptographic_material_providers.mpl.references import IKeyring | ||
from aws_dbesdk_dynamodb.encrypted.client import EncryptedClient | ||
from aws_dbesdk_dynamodb.structures.dynamodb import ( | ||
DynamoDbTableEncryptionConfig, | ||
DynamoDbTablesEncryptionConfig, | ||
) | ||
from aws_dbesdk_dynamodb.structures.structured_encryption import ( | ||
CryptoAction, | ||
) | ||
|
||
|
||
def setup_awsdbe_client_without_plaintext_override(kms_key_id: str, ddb_table_name: str): | ||
""" | ||
Set up a pure AWS Database Encryption SDK EncryptedClient without plaintext override. | ||
|
||
:param kms_key_id: The ARN of the KMS key to use for encryption | ||
:param ddb_table_name: The name of the DynamoDB table | ||
:returns EncryptedClient for DynamoDB | ||
""" | ||
# 1. Create a Keyring. This Keyring will be responsible for protecting the data keys that protect your data. | ||
# For this example, we will create a AWS KMS Keyring with the AWS KMS Key we want to use. | ||
# We will use the `CreateMrkMultiKeyring` method to create this keyring, | ||
# as it will correctly handle both single region and Multi-Region KMS Keys. | ||
mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders(config=MaterialProvidersConfig()) | ||
kms_mrk_multi_keyring_input: CreateAwsKmsMrkMultiKeyringInput = CreateAwsKmsMrkMultiKeyringInput( | ||
generator=kms_key_id, | ||
) | ||
kms_mrk_multi_keyring: IKeyring = mat_prov.create_aws_kms_mrk_multi_keyring(input=kms_mrk_multi_keyring_input) | ||
|
||
# 2. Configure which attributes are encrypted and/or signed when writing new items. | ||
# For each attribute that may exist on the items we plan to write to our DynamoDbTable, | ||
# we must explicitly configure how they should be treated during item encryption: | ||
# - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature | ||
# - SIGN_ONLY: The attribute not encrypted, but is still included in the signature | ||
# - DO_NOTHING: The attribute is not encrypted and not included in the signature | ||
attribute_actions_on_encrypt = { | ||
"partition_key": CryptoAction.SIGN_ONLY, | ||
"sort_key": CryptoAction.SIGN_ONLY, | ||
"attribute1": CryptoAction.ENCRYPT_AND_SIGN, | ||
"attribute2": CryptoAction.SIGN_ONLY, | ||
":attribute3": CryptoAction.DO_NOTHING, | ||
} | ||
|
||
# 3. Configure which attributes we expect to be included in the signature | ||
# when reading items. There are two options for configuring this: | ||
# | ||
# - (Recommended) Configure `allowedUnsignedAttributesPrefix`: | ||
# When defining your DynamoDb schema and deciding on attribute names, | ||
# choose a distinguishing prefix (such as ":") for all attributes that | ||
# you do not want to include in the signature. | ||
# This has two main benefits: | ||
# - It is easier to reason about the security and authenticity of data within your item | ||
# when all unauthenticated data is easily distinguishable by their attribute name. | ||
# - If you need to add new unauthenticated attributes in the future, | ||
# you can easily make the corresponding update to your `attributeActionsOnEncrypt` | ||
# and immediately start writing to that new attribute, without | ||
# any other configuration update needed. | ||
# Once you configure this field, it is not safe to update it. | ||
# | ||
# - Configure `allowedUnsignedAttributes`: You may also explicitly list | ||
# a set of attributes that should be considered unauthenticated when encountered | ||
# on read. Be careful if you use this configuration. Do not remove an attribute | ||
# name from this configuration, even if you are no longer writing with that attribute, | ||
# as old items may still include this attribute, and our configuration needs to know | ||
# to continue to exclude this attribute from the signature scope. | ||
# If you add new attribute names to this field, you must first deploy the update to this | ||
# field to all readers in your host fleet before deploying the update to start writing | ||
# with that new attribute. | ||
# | ||
# For this example, we have designed our DynamoDb table such that any attribute name with | ||
# the ":" prefix should be considered unauthenticated. | ||
unsignAttrPrefix: str = ":" | ||
|
||
# 4. Create the DynamoDb Encryption configuration for the table we will be writing to. | ||
table_configs = {} | ||
table_config = DynamoDbTableEncryptionConfig( | ||
logical_table_name=ddb_table_name, | ||
partition_key_name="partition_key", | ||
sort_key_name="sort_key", | ||
attribute_actions_on_encrypt=attribute_actions_on_encrypt, | ||
keyring=kms_mrk_multi_keyring, | ||
allowed_unsigned_attribute_prefix=unsignAttrPrefix, | ||
# Specifying an algorithm suite is not required, | ||
# but is done here to demonstrate how to do so. | ||
# We suggest using the | ||
# `ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY_ECDSA_P384_SYMSIG_HMAC_SHA384` suite, | ||
# which includes AES-GCM with key derivation, signing, and key commitment. | ||
# This is also the default algorithm suite if one is not specified in this config. | ||
# For more information on supported algorithm suites, see: | ||
# https://docs.aws.amazon.com/database-encryption-sdk/latest/devguide/supported-algorithms.html | ||
algorithm_suite_id=DBEAlgorithmSuiteId.ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY_ECDSA_P384_SYMSIG_HMAC_SHA384, | ||
) | ||
table_configs[ddb_table_name] = table_config | ||
tables_config = DynamoDbTablesEncryptionConfig(table_encryption_configs=table_configs) | ||
|
||
# 5. Create the EncryptedClient | ||
return EncryptedClient( | ||
client=boto3.client("dynamodb"), | ||
encryption_config=tables_config, | ||
) | ||
|
||
|
||
def setup_awsdbe_client_with_plaintext_override(kms_key_id: str, ddb_table_name: str, policy: str): | ||
""" | ||
Set up an AWS Database Encryption SDK EncryptedClient with plaintext override. | ||
|
||
:param kms_key_id: The ARN of the KMS key to use for encryption | ||
:param ddb_table_name: The name of the DynamoDB table | ||
:param policy: The policy required for the Plaintext Override configuration | ||
:returns EncryptedClient for DynamoDB | ||
|
||
""" | ||
# 1. Create a Keyring. This Keyring will be responsible for protecting the data keys that protect your data. | ||
# For this example, we will create a AWS KMS Keyring with the AWS KMS Key we want to use. | ||
# We will use the `CreateMrkMultiKeyring` method to create this keyring, | ||
# as it will correctly handle both single region and Multi-Region KMS Keys. | ||
mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders(config=MaterialProvidersConfig()) | ||
kms_mrk_multi_keyring_input: CreateAwsKmsMrkMultiKeyringInput = CreateAwsKmsMrkMultiKeyringInput( | ||
generator=kms_key_id, | ||
) | ||
kms_mrk_multi_keyring: IKeyring = mat_prov.create_aws_kms_mrk_multi_keyring(input=kms_mrk_multi_keyring_input) | ||
|
||
# 2. Configure which attributes are encrypted and/or signed when writing new items. | ||
# For each attribute that may exist on the items we plan to write to our DynamoDbTable, | ||
# we must explicitly configure how they should be treated during item encryption: | ||
# - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature | ||
# - SIGN_ONLY: The attribute not encrypted, but is still included in the signature | ||
# - DO_NOTHING: The attribute is not encrypted and not included in the signature | ||
attribute_actions_on_encrypt = { | ||
"partition_key": CryptoAction.SIGN_ONLY, | ||
"sort_key": CryptoAction.SIGN_ONLY, | ||
"attribute1": CryptoAction.ENCRYPT_AND_SIGN, | ||
"attribute2": CryptoAction.SIGN_ONLY, | ||
":attribute3": CryptoAction.DO_NOTHING, | ||
} | ||
|
||
# 3. Configure which attributes we expect to be included in the signature | ||
# when reading items. There are two options for configuring this: | ||
# | ||
# - (Recommended) Configure `allowedUnsignedAttributesPrefix`: | ||
# When defining your DynamoDb schema and deciding on attribute names, | ||
# choose a distinguishing prefix (such as ":") for all attributes that | ||
# you do not want to include in the signature. | ||
# This has two main benefits: | ||
# - It is easier to reason about the security and authenticity of data within your item | ||
# when all unauthenticated data is easily distinguishable by their attribute name. | ||
# - If you need to add new unauthenticated attributes in the future, | ||
# you can easily make the corresponding update to your `attributeActionsOnEncrypt` | ||
# and immediately start writing to that new attribute, without | ||
# any other configuration update needed. | ||
# Once you configure this field, it is not safe to update it. | ||
# | ||
# - Configure `allowedUnsignedAttributes`: You may also explicitly list | ||
# a set of attributes that should be considered unauthenticated when encountered | ||
# on read. Be careful if you use this configuration. Do not remove an attribute | ||
# name from this configuration, even if you are no longer writing with that attribute, | ||
# as old items may still include this attribute, and our configuration needs to know | ||
# to continue to exclude this attribute from the signature scope. | ||
# If you add new attribute names to this field, you must first deploy the update to this | ||
# field to all readers in your host fleet before deploying the update to start writing | ||
# with that new attribute. | ||
# | ||
# For this example, we have designed our DynamoDb table such that any attribute name with | ||
# the ":" prefix should be considered unauthenticated. | ||
unsignAttrPrefix: str = ":" | ||
|
||
# 4. Create the DynamoDb Encryption configuration for the table we will be writing to. | ||
# Include the plaintext override with the provided policy | ||
table_configs = {} | ||
table_config = DynamoDbTableEncryptionConfig( | ||
logical_table_name=ddb_table_name, | ||
partition_key_name="partition_key", | ||
sort_key_name="sort_key", | ||
attribute_actions_on_encrypt=attribute_actions_on_encrypt, | ||
keyring=kms_mrk_multi_keyring, | ||
# Provide plaintext_override policy to the config here | ||
plaintext_override=policy, | ||
allowed_unsigned_attribute_prefix=unsignAttrPrefix, | ||
# Specifying an algorithm suite is not required, | ||
# but is done here to demonstrate how to do so. | ||
# We suggest using the | ||
# `ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY_ECDSA_P384_SYMSIG_HMAC_SHA384` suite, | ||
# which includes AES-GCM with key derivation, signing, and key commitment. | ||
# This is also the default algorithm suite if one is not specified in this config. | ||
# For more information on supported algorithm suites, see: | ||
# https://docs.aws.amazon.com/database-encryption-sdk/latest/devguide/supported-algorithms.html | ||
algorithm_suite_id=DBEAlgorithmSuiteId.ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY_ECDSA_P384_SYMSIG_HMAC_SHA384, | ||
) | ||
table_configs[ddb_table_name] = table_config | ||
tables_config = DynamoDbTablesEncryptionConfig(table_encryption_configs=table_configs) | ||
|
||
# 5. Create the EncryptedClient | ||
return EncryptedClient( | ||
client=boto3.client("dynamodb"), | ||
encryption_config=tables_config, | ||
) |
90 changes: 90 additions & 0 deletions
90
Examples/runtimes/python/Migration/plaintext_to_awsdbe/src/awsdbe/client/migration_step_1.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
""" | ||
Migration Step 1. | ||
|
||
This is an example demonstrating how to start using the | ||
AWS Database Encryption SDK with a pre-existing table with plaintext items. | ||
In this example, we configure an EncryptedClient to do the following: | ||
- Write items only in plaintext | ||
- Read items in plaintext or, if the item is encrypted, decrypt with our encryption configuration | ||
|
||
While this step configures your client to be ready to start reading encrypted items, | ||
we do not yet expect to be reading any encrypted items, | ||
as our client still writes plaintext items. | ||
Before you move on to step 2, ensure that these changes have successfully been deployed | ||
to all of your readers. | ||
|
||
Running this example requires access to the DDB Table whose name | ||
is provided in CLI arguments. | ||
This table must be configured with the following | ||
primary key configuration: | ||
- Partition key is named "partition_key" with type (S) | ||
- Sort key is named "sort_key" with type (S) | ||
""" | ||
from aws_dbesdk_dynamodb.structures.dynamodb import PlaintextOverride | ||
|
||
from .common import setup_awsdbe_client_with_plaintext_override | ||
|
||
|
||
def migration_step_1_with_client(kms_key_id: str, ddb_table_name: str, sort_read_value: int = 1): | ||
""" | ||
Migration Step 1: Using the AWS Database Encryption SDK with Plaintext Override. | ||
|
||
:param kms_key_id: The ARN of the KMS key to use for encryption | ||
:param ddb_table_name: The name of the DynamoDB table | ||
:param sort_read_value: The sort key value to read | ||
""" | ||
# 1. Create an EncryptedClient with plaintext override. | ||
# For Plaintext Override, use `FORCE_PLAINTEXT_WRITE_ALLOW_PLAINTEXT_READ`. | ||
# This plaintext override means: | ||
# - Write: Items are forced to be written as plaintext. | ||
# Items may not be written as encrypted items. | ||
# - Read: Items are allowed to be read as plaintext. | ||
# Items are allowed to be read as encrypted items. | ||
policy = PlaintextOverride.FORCE_PLAINTEXT_WRITE_ALLOW_PLAINTEXT_READ | ||
encrypted_client = setup_awsdbe_client_with_plaintext_override( | ||
kms_key_id=kms_key_id, ddb_table_name=ddb_table_name, policy=policy | ||
) | ||
|
||
# 2. Put an item into your table using the Encrypted Client. | ||
# This item will be stored in plaintext since we are using a plaintext override | ||
# with FORCE_PLAINTEXT_WRITE_ALLOW_PLAINTEXT_READ policy | ||
item_to_encrypt = { | ||
"partition_key": {"S": "PlaintextMigrationExample"}, | ||
"sort_key": {"N": str(1)}, | ||
"attribute1": {"S": "encrypt and sign me!"}, | ||
"attribute2": {"S": "sign me!"}, | ||
":attribute3": {"S": "ignore me!"}, | ||
} | ||
|
||
put_item_request = { | ||
"TableName": ddb_table_name, | ||
"Item": item_to_encrypt, | ||
} | ||
|
||
put_item_response = encrypted_client.put_item(**put_item_request) | ||
# Demonstrate that PutItem succeeded | ||
assert put_item_response["ResponseMetadata"]["HTTPStatusCode"] == 200 | ||
|
||
# 3. Get an item back from the table using the Encrypted Client. | ||
# If this is an item written in plaintext (e.g. any item written | ||
# during Step 0 or 1), then it will be returned as plaintext. | ||
# If this is an encrypted item (e.g. any item written during Step 2 or after), | ||
# then it will be decrypted before being returned. | ||
key_to_get = {"partition_key": {"S": "PlaintextMigrationExample"}, "sort_key": {"N": str(sort_read_value)}} | ||
|
||
get_item_request = {"TableName": ddb_table_name, "Key": key_to_get} | ||
get_item_response = encrypted_client.get_item(**get_item_request) | ||
|
||
# Demonstrate that GetItem succeeded and returned the decrypted item | ||
assert get_item_response["ResponseMetadata"]["HTTPStatusCode"] == 200 | ||
returned_item = get_item_response["Item"] | ||
|
||
# Demonstrate we get the expected item back | ||
assert returned_item["partition_key"]["S"] == "PlaintextMigrationExample" | ||
assert returned_item["sort_key"]["N"] == str(sort_read_value) | ||
assert returned_item["attribute1"]["S"] == "encrypt and sign me!" | ||
assert returned_item["attribute2"]["S"] == "sign me!" | ||
assert returned_item[":attribute3"]["S"] == "ignore me!" |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.