Skip to content

Commit b378182

Browse files
authored
Merge branch 'main' into ajewell/buckets
2 parents 3b4940f + c8796d9 commit b378182

File tree

15 files changed

+903
-4
lines changed

15 files changed

+903
-4
lines changed

.github/workflows/ci_test_go.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,9 @@ jobs:
140140
run: |
141141
make test_go
142142
143-
- name: Test Examples
143+
- name: Run and Test Examples
144144
if: matrix.library == 'DynamoDbEncryption'
145145
working-directory: ./Examples/runtimes/go
146146
run: |
147147
go run main.go
148+
go test ./...
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Plaintext DynamoDB Table to AWS Database Encryption SDK Encrypted Table Migration
2+
3+
This projects demonstrates the steps necessary
4+
to migrate to the AWS Database Encryption SDK for DynamoDb
5+
from a plaintext database.
6+
7+
[Step 0](plaintext/step0.go) demonstrates the starting state for your system.
8+
9+
## Step 1
10+
11+
In Step 1, you update your system to do the following:
12+
13+
- continue to read plaintext items
14+
- continue to write plaintext items
15+
- prepare to read encrypted items
16+
17+
When you deploy changes in Step 1,
18+
you should not expect any behavior change in your system,
19+
and your dataset still consists of plaintext data.
20+
21+
You must ensure that the changes in Step 1 make it to all your readers before you proceed to Step 2.
22+
23+
## Step 2
24+
25+
In Step 2, you update your system to do the following:
26+
27+
- continue to read plaintext items
28+
- start writing encrypted items
29+
- continue to read encrypted items
30+
31+
When you deploy changes in Step 2,
32+
you are introducing encrypted items to your system,
33+
and must make sure that all your readers are updated with the changes from Step 1.
34+
35+
Before you move onto the next step, you will need to encrypt all plaintext items in your dataset.
36+
Once you have completed this step,
37+
while new items are being encrypted using the new format and will be authenticated on read,
38+
your system will still accept reading plaintext, unauthenticated items.
39+
In order to complete migration to a system where you always authenticate your items,
40+
you should prioritize moving on to Step 3.
41+
42+
## Step 3
43+
44+
Once all old items are encrypted,
45+
update your system to do the following:
46+
47+
- continue to write encrypted items
48+
- continue to read encrypted items
49+
- do not accept reading plaintext items
50+
51+
Once you have deployed these changes to your system, you have completed migration.
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package awsdbe
2+
3+
import (
4+
"context"
5+
6+
mpl "github.com/aws/aws-cryptographic-material-providers-library/releases/go/mpl/awscryptographymaterialproviderssmithygenerated"
7+
mpltypes "github.com/aws/aws-cryptographic-material-providers-library/releases/go/mpl/awscryptographymaterialproviderssmithygeneratedtypes"
8+
dbesdkdynamodbencryptiontypes "github.com/aws/aws-database-encryption-sdk-dynamodb/releases/go/dynamodb-esdk/awscryptographydbencryptionsdkdynamodbsmithygeneratedtypes"
9+
dbesdkstructuredencryptiontypes "github.com/aws/aws-database-encryption-sdk-dynamodb/releases/go/dynamodb-esdk/awscryptographydbencryptionsdkstructuredencryptionsmithygeneratedtypes"
10+
"github.com/aws/aws-database-encryption-sdk-dynamodb/releases/go/dynamodb-esdk/examples/utils"
11+
)
12+
13+
func configureTable(kmsKeyID, ddbTableName string, plaintextOverride dbesdkdynamodbencryptiontypes.PlaintextOverride) dbesdkdynamodbencryptiontypes.DynamoDbTablesEncryptionConfig {
14+
15+
// Create a Keyring. This Keyring will be responsible for protecting the data keys that protect your data.
16+
// We will use the `CreateMrkMultiKeyring` method to create this keyring,
17+
// as it will correctly handle both single region and Multi-Region KMS Keys.
18+
matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{})
19+
utils.HandleError(err)
20+
21+
keyringInput := mpltypes.CreateAwsKmsMrkMultiKeyringInput{
22+
Generator: &kmsKeyID,
23+
}
24+
kmsKeyring, err := matProv.CreateAwsKmsMrkMultiKeyring(context.Background(), keyringInput)
25+
utils.HandleError(err)
26+
27+
// Configure which attributes are encrypted and/or signed when writing new items.
28+
// For each attribute that may exist on the items we plan to write to our DynamoDbTable,
29+
// we must explicitly configure how they should be treated during item encryption:
30+
// - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature
31+
// - SIGN_ONLY: The attribute not encrypted, but is still included in the signature
32+
// - DO_NOTHING: The attribute is not encrypted and not included in the signature
33+
partitionKeyName := "partition_key"
34+
sortKeyName := "sort_key"
35+
36+
attributeActions := map[string]dbesdkstructuredencryptiontypes.CryptoAction{
37+
partitionKeyName: dbesdkstructuredencryptiontypes.CryptoActionSignOnly,
38+
sortKeyName: dbesdkstructuredencryptiontypes.CryptoActionSignOnly,
39+
"attribute1": dbesdkstructuredencryptiontypes.CryptoActionEncryptAndSign,
40+
"attribute2": dbesdkstructuredencryptiontypes.CryptoActionSignOnly,
41+
"attribute3": dbesdkstructuredencryptiontypes.CryptoActionDoNothing,
42+
}
43+
44+
// Configure which attributes we expect to be excluded in the signature
45+
// when reading items. This value represents all unsigned attributes
46+
// across our entire dataset. If you ever want to add new unsigned attributes
47+
// in the future, you must make an update to this field to all your readers
48+
// before deploying any change to start writing that new data. It is not safe
49+
// to remove attributes from this field.
50+
unsignedAttributes := []string{"attribute3"}
51+
52+
// Create encryption configuration for table.
53+
tableConfig := dbesdkdynamodbencryptiontypes.DynamoDbTableEncryptionConfig{
54+
LogicalTableName: ddbTableName,
55+
PartitionKeyName: partitionKeyName,
56+
SortKeyName: &sortKeyName,
57+
AttributeActionsOnEncrypt: attributeActions,
58+
Keyring: kmsKeyring,
59+
AllowedUnsignedAttributes: unsignedAttributes,
60+
PlaintextOverride: &plaintextOverride,
61+
}
62+
63+
tableConfigsMap := make(map[string]dbesdkdynamodbencryptiontypes.DynamoDbTableEncryptionConfig)
64+
tableConfigsMap[ddbTableName] = tableConfig
65+
66+
listOfTableConfigs := dbesdkdynamodbencryptiontypes.DynamoDbTablesEncryptionConfig{
67+
TableEncryptionConfigs: tableConfigsMap,
68+
}
69+
70+
return listOfTableConfigs
71+
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
package awsdbe
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/aws/aws-sdk-go-v2/aws"
8+
"github.com/aws/aws-sdk-go-v2/config"
9+
"github.com/aws/aws-sdk-go-v2/service/dynamodb"
10+
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
11+
12+
"github.com/aws/aws-database-encryption-sdk-dynamodb/releases/go/dynamodb-esdk/dbesdkmiddleware"
13+
plaintexttoawsdbe "github.com/aws/aws-database-encryption-sdk-dynamodb/releases/go/dynamodb-esdk/examples/migration/PlaintextToAWSDBE"
14+
"github.com/aws/aws-database-encryption-sdk-dynamodb/releases/go/dynamodb-esdk/examples/utils"
15+
16+
dbesdkdynamodbencryptiontypes "github.com/aws/aws-database-encryption-sdk-dynamodb/releases/go/dynamodb-esdk/awscryptographydbencryptionsdkdynamodbsmithygeneratedtypes"
17+
)
18+
19+
/*
20+
Migration Step 1: This is an example demonstrating how to start using the
21+
AWS Database Encryption SDK with a pre-existing table with plaintext items.
22+
In this example, we configure a DynamoDb Encryption Interceptor to do the following:
23+
- Write items only in plaintext
24+
- Read items in plaintext or, if the item is encrypted, decrypt with our encryption configuration
25+
26+
While this step configures your client to be ready to start reading encrypted items,
27+
we do not yet expect to be reading any encrypted items,
28+
as our client still writes plaintext items.
29+
Before you move on to step 2, ensure that these changes have successfully been deployed
30+
to all of your readers.
31+
32+
Running this example requires access to the DDB Table whose name
33+
is provided in the function parameter.
34+
This table must be configured with the following
35+
primary key configuration:
36+
- Partition key is named "partition_key" with type (S)
37+
- Sort key is named "sort_key" with type (S)
38+
*/
39+
func MigrationStep1(kmsKeyID, ddbTableName, partitionKeyValue, sortKeyWriteValue, sortKeyReadValue string) error {
40+
cfg, err := config.LoadDefaultConfig(context.TODO())
41+
utils.HandleError(err)
42+
43+
// 1. Configure your Keyring, attribute actions,
44+
// allowedUnsignedAttributes, and encryption configuration for table.
45+
// This is common across all the steps.
46+
47+
// Note that while we still are not writing encrypted items,
48+
// and our key will not be used to encrypt items in this example,
49+
// our configuration specifies that we may read encrypted items,
50+
// and we should expect to be able to decrypt and process any encrypted items.
51+
// To that end, we must fully define our encryption configuration in
52+
// this step.
53+
54+
// This `PlaintextOverrideForcePlaintextWriteAllowPlaintextRead` means:
55+
// - Write: Items are forced to be written as plaintext.
56+
// Items may not be written as encrypted items.
57+
// - Read: Items are allowed to be read as plaintext.
58+
// Items are allowed to be read as encrypted items.
59+
listOfTableConfigs := configureTable(kmsKeyID, ddbTableName, dbesdkdynamodbencryptiontypes.PlaintextOverrideForcePlaintextWriteAllowPlaintextRead)
60+
61+
// 2. Create DynamoDB client with dbEsdkMiddleware
62+
dbEsdkMiddleware, err := dbesdkmiddleware.NewDBEsdkMiddleware(listOfTableConfigs)
63+
utils.HandleError(err)
64+
65+
ddb := dynamodb.NewFromConfig(cfg, dbEsdkMiddleware.CreateMiddleware())
66+
67+
// 3. Put an item into your table.
68+
// This item will be stored in plaintext.
69+
encryptedAndSignedValue := "this will be encrypted and signed"
70+
signOnlyValue := "this will never be encrypted, but it will be signed"
71+
doNothingValue := "this will never be encrypted nor signed"
72+
item := map[string]types.AttributeValue{
73+
"partition_key": &types.AttributeValueMemberS{Value: partitionKeyValue},
74+
"sort_key": &types.AttributeValueMemberN{Value: sortKeyWriteValue},
75+
"attribute1": &types.AttributeValueMemberS{Value: encryptedAndSignedValue},
76+
"attribute2": &types.AttributeValueMemberS{Value: signOnlyValue},
77+
"attribute3": &types.AttributeValueMemberS{Value: doNothingValue},
78+
}
79+
80+
putInput := dynamodb.PutItemInput{
81+
TableName: &ddbTableName,
82+
Item: item,
83+
}
84+
85+
_, err = ddb.PutItem(context.TODO(), &putInput)
86+
87+
// We return this error because we run test against the error.
88+
// When used in production code, you can decide how you want to handle errors.
89+
if err != nil {
90+
return err
91+
}
92+
93+
// 4. Get an item back from the table using the DynamoDb Client.
94+
// If this is an item written in plaintext (i.e. any item written
95+
// during Step 0 or 1), then the item will still be in plaintext.
96+
// If this is an item that was encrypted client-side (i.e. any item written
97+
// during Step 2 or after), then the item will be decrypted client-side
98+
// and surfaced as a plaintext item.
99+
key := map[string]types.AttributeValue{
100+
"partition_key": &types.AttributeValueMemberS{Value: partitionKeyValue},
101+
"sort_key": &types.AttributeValueMemberN{Value: sortKeyReadValue},
102+
}
103+
104+
getInput := &dynamodb.GetItemInput{
105+
TableName: &ddbTableName,
106+
Key: key,
107+
ConsistentRead: aws.Bool(true),
108+
}
109+
110+
result, err := ddb.GetItem(context.TODO(), getInput)
111+
// We return this error because we run test against the error.
112+
// When used in production code, you can decide how you want to handle errors.
113+
if err != nil {
114+
return err
115+
}
116+
117+
// Verify we got the expected item back
118+
err = plaintexttoawsdbe.VerifyReturnedItem(result, partitionKeyValue, sortKeyReadValue, encryptedAndSignedValue, signOnlyValue, doNothingValue)
119+
if err != nil {
120+
return err
121+
}
122+
fmt.Println("MigrationStep1 completed successfully")
123+
return nil
124+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package awsdbe
2+
3+
import (
4+
"testing"
5+
6+
"github.com/aws/aws-database-encryption-sdk-dynamodb/releases/go/dynamodb-esdk/examples/migration/PlaintextToAWSDBE/plaintext"
7+
"github.com/aws/aws-database-encryption-sdk-dynamodb/releases/go/dynamodb-esdk/examples/utils"
8+
"github.com/google/uuid"
9+
)
10+
11+
func TestMigrationStep1(t *testing.T) {
12+
kmsKeyID := utils.KmsKeyID()
13+
tableName := utils.DdbTableName()
14+
partitionKey := uuid.New().String()
15+
sortKeys := []string{"0", "1", "2", "3"}
16+
17+
// Successfully executes Step 1
18+
err := MigrationStep1(kmsKeyID, tableName, partitionKey, sortKeys[1], sortKeys[1])
19+
utils.HandleError(err)
20+
21+
// Given: Step 0 has succeeded
22+
err = plaintext.MigrationStep0(tableName, partitionKey, sortKeys[0], sortKeys[0])
23+
utils.HandleError(err)
24+
25+
// When: Execute Step 1 with sortReadValue=0, Then: Success (i.e. can read plaintext values)
26+
err = MigrationStep1(kmsKeyID, tableName, partitionKey, sortKeys[1], sortKeys[0])
27+
utils.HandleError(err)
28+
29+
// Given: Step 2 has succeeded
30+
err = MigrationStep2(kmsKeyID, tableName, partitionKey, sortKeys[2], sortKeys[2])
31+
utils.HandleError(err)
32+
33+
// When: Execute Step 1 with sortReadValue=2, Then: Success (i.e. can read encrypted values)
34+
err = MigrationStep1(kmsKeyID, tableName, partitionKey, sortKeys[1], sortKeys[2])
35+
utils.HandleError(err)
36+
37+
// Given: Step 3 has succeeded
38+
err = MigrationStep3(kmsKeyID, tableName, partitionKey, sortKeys[3], sortKeys[3])
39+
utils.HandleError(err)
40+
41+
// When: Execute Step 1 with sortReadValue=3, Then: Success (i.e. can read encrypted values)
42+
err = MigrationStep1(kmsKeyID, tableName, partitionKey, sortKeys[1], sortKeys[3])
43+
utils.HandleError(err)
44+
45+
// Cleanup
46+
for _, sortKey := range sortKeys {
47+
utils.DeleteItem(tableName, "partition_key", partitionKey, "sort_key", sortKey)
48+
}
49+
50+
}

0 commit comments

Comments
 (0)