Skip to content

Commit 040e5f2

Browse files
authored
feat(aws): Implement optional Amazon KMS (Key Management Service) based encryption (#1052)
Signed-off-by: Akhil Repala <arepala@blinklabs.io>
1 parent 94f866e commit 040e5f2

File tree

3 files changed

+88
-18
lines changed

3 files changed

+88
-18
lines changed

database/plugin/blob/aws/commit_timestamp.go

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,48 @@ package aws
1616

1717
import (
1818
"context"
19+
"encoding/json"
1920
"math/big"
21+
22+
dingosops "github.com/blinklabs-io/dingo/database/sops"
2023
)
2124

2225
const commitTimestampBlobKey = "metadata_commit_timestamp"
2326

2427
func (b *BlobStoreS3) GetCommitTimestamp(ctx context.Context) (int64, error) {
25-
data, err := b.Get(ctx, commitTimestampBlobKey)
28+
ciphertext, err := b.Get(ctx, commitTimestampBlobKey)
29+
if err != nil {
30+
return 0, err
31+
}
32+
plaintext, err := dingosops.Decrypt(ciphertext)
2633
if err != nil {
34+
if !json.Valid(ciphertext) {
35+
ts := new(big.Int).SetBytes(ciphertext).Int64()
36+
b.logger.Warningf(
37+
"commit timestamp stored plaintext in S3, migrating to SOPS encryption: %v",
38+
err,
39+
)
40+
if migrateErr := b.SetCommitTimestamp(ctx, ts); migrateErr != nil {
41+
b.logger.Errorf("failed to migrate plaintext commit timestamp: %v", migrateErr)
42+
}
43+
return ts, nil
44+
}
45+
b.logger.Errorf("failed to decrypt commit timestamp: %v", err)
2746
return 0, err
2847
}
29-
return new(big.Int).SetBytes(data).Int64(), nil
48+
return new(big.Int).SetBytes(plaintext).Int64(), nil
3049
}
3150

3251
func (b *BlobStoreS3) SetCommitTimestamp(ctx context.Context, ts int64) error {
3352
raw := new(big.Int).SetInt64(ts).Bytes()
34-
return b.Put(ctx, commitTimestampBlobKey, raw)
53+
ciphertext, err := dingosops.Encrypt(raw)
54+
if err != nil {
55+
b.logger.Errorf("failed to encrypt commit timestamp: %v", err)
56+
return err
57+
}
58+
if err := b.Put(ctx, commitTimestampBlobKey, ciphertext); err != nil {
59+
return err
60+
}
61+
b.logger.Infof("commit timestamp %d written to S3", ts)
62+
return nil
3563
}

database/plugin/blob/gcs/commit_timestamp.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package gcs
1616

1717
import (
1818
"context"
19+
"encoding/json"
1920
"io"
2021
"math/big"
2122

@@ -40,6 +41,17 @@ func (b *BlobStoreGCS) GetCommitTimestamp(ctx context.Context) (int64, error) {
4041

4142
plaintext, err := dingosops.Decrypt(ciphertext)
4243
if err != nil {
44+
if !json.Valid(ciphertext) && len(ciphertext) <= 8 {
45+
ts := new(big.Int).SetBytes(ciphertext).Int64()
46+
b.logger.Warningf(
47+
"commit timestamp stored plaintext in GCS, migrating to SOPS encryption: %v",
48+
err,
49+
)
50+
if migrateErr := b.SetCommitTimestamp(ctx, ts); migrateErr != nil {
51+
b.logger.Errorf("failed to migrate plaintext commit timestamp: %v", migrateErr)
52+
}
53+
return ts, nil
54+
}
4355
b.logger.Errorf("failed to decrypt commit timestamp: %v", err)
4456
return 0, err
4557
}

database/sops/sops.go

Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,24 +22,27 @@ import (
2222
sopsapi "github.com/getsops/sops/v3"
2323
"github.com/getsops/sops/v3/aes"
2424
scommon "github.com/getsops/sops/v3/cmd/sops/common"
25+
"github.com/getsops/sops/v3/config"
2526
"github.com/getsops/sops/v3/decrypt"
2627
"github.com/getsops/sops/v3/gcpkms"
2728
skeys "github.com/getsops/sops/v3/keys"
29+
awskms "github.com/getsops/sops/v3/kms"
2830
jsonstore "github.com/getsops/sops/v3/stores/json"
2931
"github.com/getsops/sops/v3/version"
3032
)
3133

3234
func Decrypt(data []byte) ([]byte, error) {
33-
ret, err := decrypt.Data(data, "json")
35+
ret, err := decrypt.Data(data, "binary")
3436
if err != nil {
3537
return nil, err
3638
}
3739
return ret, nil
3840
}
3941

4042
func Encrypt(data []byte) ([]byte, error) {
41-
input := &jsonstore.Store{}
42-
output := &jsonstore.Store{}
43+
storeConfig := &config.JSONBinaryStoreConfig{}
44+
input := jsonstore.NewBinaryStore(storeConfig)
45+
output := jsonstore.NewBinaryStore(storeConfig)
4346

4447
// prevent double encryption
4548
branches, err := input.LoadPlainFile(data)
@@ -56,20 +59,12 @@ func Encrypt(data []byte) ([]byte, error) {
5659

5760
// create tree and encrypt
5861
tree := sopsapi.Tree{Branches: branches}
59-
60-
// Configure Google KMS from env to encrypt
61-
rid := os.Getenv("DINGO_GCP_KMS_RESOURCE_ID")
62-
if rid == "" {
63-
return nil, errors.New(
64-
"DINGO_GCP_KMS_RESOURCE_ID not set: SOPS requires at least one master key to encrypt",
65-
)
66-
}
67-
keys := []skeys.MasterKey{}
68-
for _, k := range gcpkms.MasterKeysFromResourceIDString(rid) {
69-
keys = append(keys, k)
62+
keyGroups, err := getMasterKeyGroupsFromEnv()
63+
if err != nil {
64+
return nil, err
7065
}
7166
tree.Metadata = sopsapi.Metadata{
72-
KeyGroups: []sopsapi.KeyGroup{keys},
67+
KeyGroups: keyGroups,
7368
Version: version.Version,
7469
}
7570

@@ -91,3 +86,38 @@ func Encrypt(data []byte) ([]byte, error) {
9186
}
9287
return encrypted, nil
9388
}
89+
90+
func getMasterKeyGroupsFromEnv() ([]sopsapi.KeyGroup, error) {
91+
keyGroups := []sopsapi.KeyGroup{}
92+
93+
// Configure Google KMS from env to encrypt
94+
if rid := os.Getenv("DINGO_GCP_KMS_RESOURCE_ID"); rid != "" {
95+
keys := []skeys.MasterKey{}
96+
for _, k := range gcpkms.MasterKeysFromResourceIDString(rid) {
97+
keys = append(keys, k)
98+
}
99+
if len(keys) > 0 {
100+
keyGroups = append(keyGroups, keys)
101+
}
102+
}
103+
104+
// Configure AWS KMS from env to encrypt
105+
if arns := os.Getenv("DINGO_AWS_KMS_KEY_ARNS"); arns != "" {
106+
keys := []skeys.MasterKey{}
107+
profile := os.Getenv("DINGO_AWS_KMS_PROFILE")
108+
for _, k := range awskms.MasterKeysFromArnString(arns, nil, profile) {
109+
keys = append(keys, k)
110+
}
111+
if len(keys) > 0 {
112+
keyGroups = append(keyGroups, keys)
113+
}
114+
}
115+
116+
if len(keyGroups) == 0 {
117+
return nil, errors.New(
118+
"SOPS requires at least one master key to encrypt: set DINGO_GCP_KMS_RESOURCE_ID and/or DINGO_AWS_KMS_KEY_ARNS",
119+
)
120+
}
121+
122+
return keyGroups, nil
123+
}

0 commit comments

Comments
 (0)