Skip to content
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

ROX-18428: implement KMS encryption and decryption #1178

Merged
merged 10 commits into from
Aug 3, 2023
Merged
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# This runs tests that check the AWS RDS provisioning / deprovisioning logic
# This runs tests that check the AWS integartion (RDS,KMS) of fleetshard-sync
name: AWS integration tests

on:
Expand Down Expand Up @@ -36,7 +36,7 @@ on:

jobs:
verify-test:
name: "Test RDS Provisioning"
name: "Test AWS Integration"
runs-on: ubuntu-latest
if: ${{ !github.event.pull_request.head.repo.fork }} # do not run for PRs from forks
permissions:
Expand Down Expand Up @@ -67,5 +67,5 @@ jobs:
AWS_REGION: ${{ secrets.AWS_REGION }}
AWS_AUTH_HELPER: "none"
run: |
./dev/env/scripts/exec_fleetshard_sync.sh make test/rds
./dev/env/scripts/exec_fleetshard_sync.sh make test/aws
timeout-minutes: 50
13 changes: 7 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -327,12 +327,13 @@ test: $(GOTESTSUM_BIN)
$(shell go list ./... | grep -v /test)
.PHONY: test

# Runs the AWS RDS integration tests.
test/rds: $(GOTESTSUM_BIN)
RUN_RDS_TESTS=true \
$(GOTESTSUM_BIN) --junitfile data/results/rds-integration-tests.xml --format $(GOTESTSUM_FORMAT) -- -p 1 -v -timeout 45m -count=1 \
./fleetshard/pkg/central/cloudprovider/awsclient/...
.PHONY: test/rds
# Runs the AWS integration tests.
test/aws: $(GOTESTSUM_BIN)
RUN_AWS_INTEGRATION=true \
$(GOTESTSUM_BIN) --junitfile data/results/aws-integration-tests.xml --format $(GOTESTSUM_FORMAT) -- -p 1 -v -timeout 45m -count=1 \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need timeout here if it's specified in the actions job?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In order to also timeout for local executions of this tests.

./fleetshard/pkg/central/cloudprovider/awsclient/... \
./fleetshard/pkg/cipher/...
.PHONY: test/aws

# Runs the integration tests.
#
Expand Down
1 change: 1 addition & 0 deletions dev/env/scripts/exec_fleetshard_sync.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ CLUSTER_NAME="cluster-acs-dev-dp-01"
ARGS="CLUSTER_ID=${CLUSTER_ID:-$(chamber read ${CLUSTER_NAME} ID -q -b ssm)} \
MANAGED_DB_SECURITY_GROUP=${MANAGED_DB_SECURITY_GROUP:-$(chamber read ${CLUSTER_NAME} MANAGED_DB_SECURITY_GROUP -q -b ssm)} \
MANAGED_DB_SUBNET_GROUP=${MANAGED_DB_SUBNET_GROUP:-$(chamber read ${CLUSTER_NAME} MANAGED_DB_SUBNET_GROUP -q -b ssm)} \
SECRET_ENCRYPTION_KEY_ID=${SECRET_ENCRYPTION_KEY_ID:-$(chamber read ${CLUSTER_NAME} SECRET_ENCRYPTION_KEY_ID -q -b ssm)} \
AWS_ROLE_ARN=${FLEETSHARD_SYNC_AWS_ROLE_ARN:-$(chamber read fleetshard-sync AWS_ROLE_ARN -q -b ssm)} \
$ARGS"

Expand Down
12 changes: 6 additions & 6 deletions fleetshard/pkg/central/cloudprovider/awsclient/rds_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,8 @@ func waitForFinalSnapshotToExist(ctx context.Context, rdsClient *RDS, clusterID
}

func TestRDSProvisioning(t *testing.T) {
if os.Getenv("RUN_RDS_TESTS") != "true" {
t.Skip("Skip RDS tests. Set RUN_RDS_TESTS=true env variable to enable RDS tests.")
if os.Getenv("RUN_AWS_INTEGRATION") != "true" {
t.Skip("Skip RDS tests. Set RUN_AWS_INTEGRATION=true env variable to enable RDS tests.")
}

rdsClient, err := newTestRDS()
Expand Down Expand Up @@ -179,8 +179,8 @@ func TestRDSProvisioning(t *testing.T) {
}

func TestGetDBConnection(t *testing.T) {
if os.Getenv("RUN_RDS_TESTS") != "true" {
t.Skip("Skip RDS tests. Set RUN_RDS_TESTS=true env variable to enable RDS tests.")
if os.Getenv("RUN_AWS_INTEGRATION") != "true" {
t.Skip("Skip RDS tests. Set RUN_AWS_INTEGRATION=true env variable to enable RDS tests.")
}

rdsClient, err := newTestRDS()
Expand All @@ -193,8 +193,8 @@ func TestGetDBConnection(t *testing.T) {
}

func TestGetAccountQuotas(t *testing.T) {
if os.Getenv("RUN_RDS_TESTS") != "true" {
t.Skip("Skip RDS tests. Set RUN_RDS_TESTS=true env variable to enable RDS tests.")
if os.Getenv("RUN_AWS_INTEGRATION") != "true" {
t.Skip("Skip RDS tests. Set RUN_AWS_INTEGRATION=true env variable to enable RDS tests.")
}

rdsClient, err := newTestRDS()
Expand Down
53 changes: 53 additions & 0 deletions fleetshard/pkg/cipher/kms_cipher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package cipher

import (
"fmt"

"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/kms"
)

type kmsCipher struct {
keyID string
kms *kms.KMS
}

// NewKMSCipher return a new Cipher using AWS KMS with the given keyId
func NewKMSCipher(keyID string) (Cipher, error) {
sess, err := session.NewSession()
if err != nil {
return nil, fmt.Errorf("unable to create session for KMS client %w", err)
}

return kmsCipher{
keyID: keyID,
kms: kms.New(sess),
}, nil
}

func (k kmsCipher) Encrypt(plaintext []byte) ([]byte, error) {
encryptInput := &kms.EncryptInput{
KeyId: &k.keyID,
Plaintext: plaintext,
}

encryptOut, err := k.kms.Encrypt(encryptInput)
if err != nil {
return nil, fmt.Errorf("error encrypting data: %w", err)
}

return encryptOut.CiphertextBlob, nil
}

func (k kmsCipher) Decrypt(ciphertext []byte) ([]byte, error) {
decryptInput := &kms.DecryptInput{
KeyId: &k.keyID,
CiphertextBlob: ciphertext,
}

decryptOut, err := k.kms.Decrypt(decryptInput)
if err != nil {
return nil, fmt.Errorf("error decrypting data: %w", err)
}
return decryptOut.Plaintext, nil
}
31 changes: 31 additions & 0 deletions fleetshard/pkg/cipher/kms_cipher_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package cipher

import (
"os"
"testing"

"github.com/stretchr/testify/require"
)

func TestKMSEncryptDecrypt(t *testing.T) {
if os.Getenv("RUN_AWS_INTEGRATION") != "true" {
t.Skip("Skip KMS tests. Set RUN_AWS_INTEGRATION=true env variable to enable KMS tests.")
}

keyID := os.Getenv("SECRET_ENCRYPTION_KEY_ID")
require.NotEmpty(t, keyID, "SECRET_ENCRYPTION_KEY_ID not set")

cipher, err := NewKMSCipher(keyID)
require.NoError(t, err, "creating KMS cipher")

plaintext := "This is example plain text"
plaintextB := []byte(plaintext)
ciphertextB, err := cipher.Encrypt(plaintextB)
require.NoError(t, err, "encrypting plaintext")

decrypted, err := cipher.Decrypt(ciphertextB)
require.NoError(t, err, "decrypting ciphertext")

require.NotEqual(t, plaintext, string(ciphertextB))
require.Equal(t, plaintext, string(decrypted))
}