internal: use go-kms-wrapping for KMS backends (#1012)

* Replace external KMS backend logic for AWS, Azure, and GCP with go-kms-wrapping

* Move kms client setup config into its own package for easier parsing

* Update kms integration flag naming

* Error if nil storage is passed to external KMS

---------

Signed-off-by: Daniel Weiße <dw@edgeless.systems>
This commit is contained in:
Daniel Weiße 2023-02-08 12:03:54 +01:00 committed by GitHub
parent 68ce23b909
commit 3a7b829107
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
36 changed files with 1319 additions and 3121 deletions

View file

@ -20,22 +20,20 @@ import (
"encoding/base64"
"fmt"
"net/url"
"strconv"
"cloud.google.com/go/kms/apiv1/kmspb"
"github.com/edgelesssys/constellation/v2/internal/kms/kms"
"github.com/edgelesssys/constellation/v2/internal/kms/kms/aws"
"github.com/edgelesssys/constellation/v2/internal/kms/kms/azure"
"github.com/edgelesssys/constellation/v2/internal/kms/kms/cluster"
"github.com/edgelesssys/constellation/v2/internal/kms/kms/gcp"
"github.com/edgelesssys/constellation/v2/internal/kms/storage"
"github.com/edgelesssys/constellation/v2/internal/kms/uri"
)
// Well known endpoints for KMS services.
const (
AWSKMSURI = "kms://aws?keyPolicy=%s&kekID=%s"
AzureKMSURI = "kms://azure-kms?name=%s&type=%s&kekID=%s"
AzureHSMURI = "kms://azure-hsm?name=%s&kekID=%s"
AzureKMSURI = "kms://azure?tenantID=%s&clientID=%s&clientSecret=%s&name=%s&type=%s&kekID=%s"
GCPKMSURI = "kms://gcp?project=%s&location=%s&keyRing=%s&protectionLvl=%s&kekID=%s"
ClusterKMSURI = "kms://cluster-kms?key=%s&salt=%s"
AWSS3URI = "storage://aws?bucket=%s"
@ -118,52 +116,45 @@ func getStore(ctx context.Context, storageURI string) (kms.Storage, error) {
// getKMS creates a KMS client with the given key store and depending on the given parameters.
func getKMS(ctx context.Context, kmsURI string, store kms.Storage) (kms.CloudKMS, error) {
uri, err := url.Parse(kmsURI)
url, err := url.Parse(kmsURI)
if err != nil {
return nil, err
}
if uri.Scheme != "kms" {
return nil, fmt.Errorf("invalid KMS URI: invalid scheme: %s", uri.Scheme)
if url.Scheme != "kms" {
return nil, fmt.Errorf("invalid KMS URI: invalid scheme: %s", url.Scheme)
}
switch uri.Host {
switch url.Host {
case "aws":
poliyProducer, kekID, err := getAWSKMSConfig(uri)
cfg, err := uri.DecodeAWSConfigFromURI(kmsURI)
if err != nil {
return nil, err
return nil, fmt.Errorf("invalid AWS KMS URI: %w", err)
}
return aws.New(ctx, poliyProducer, store, kekID)
return aws.New(ctx, store, cfg)
case "azure-kms":
vaultName, vaultType, kekID, err := getAzureKMSConfig(uri)
case "azure":
cfg, err := uri.DecodeAzureConfigFromURI(kmsURI)
if err != nil {
return nil, err
return nil, fmt.Errorf("invalid Azure Key Vault URI: %w", err)
}
return azure.New(ctx, vaultName, azure.VaultSuffix(vaultType), store, kekID, nil)
case "azure-hsm":
vaultName, kekID, err := getAzureHSMConfig(uri)
if err != nil {
return nil, err
}
return azure.NewHSM(ctx, vaultName, store, kekID, nil)
return azure.New(ctx, store, cfg)
case "gcp":
project, location, keyRing, protectionLvl, kekID, err := getGCPKMSConfig(uri)
cfg, err := uri.DecodeGCPConfigFromURI(kmsURI)
if err != nil {
return nil, err
return nil, fmt.Errorf("invalid GCP KMS URI: %w", err)
}
return gcp.New(ctx, project, location, keyRing, store, kmspb.ProtectionLevel(protectionLvl), kekID)
return gcp.New(ctx, store, cfg)
case "cluster-kms":
masterSecret, err := getClusterKMSConfig(uri)
cfg, err := uri.DecodeMasterSecretFromURI(kmsURI)
if err != nil {
return nil, err
}
return cluster.New(masterSecret.Key, masterSecret.Salt)
return cluster.New(cfg.Key, cfg.Salt)
default:
return nil, fmt.Errorf("unknown KMS type: %s", uri.Host)
return nil, fmt.Errorf("unknown KMS type: %s", url.Host)
}
}
@ -198,40 +189,6 @@ func getAWSKMSConfig(uri *url.URL) (*defaultPolicyProducer, string, error) {
return &defaultPolicyProducer{policy: r[0]}, string(kekID), err
}
func getAzureKMSConfig(uri *url.URL) (string, string, string, error) {
r, err := getConfig(uri.Query(), []string{"name", "type", "kekID"})
if err != nil {
return "", "", "", fmt.Errorf("getting config: %w", err)
}
if len(r) != 3 {
return "", "", "", fmt.Errorf("expected 3 KmsURI args, got %d", len(r))
}
kekID, err := base64.URLEncoding.DecodeString(r[2])
if err != nil {
return "", "", "", fmt.Errorf("parsing kekID from kmsUri: %w", err)
}
return r[0], r[1], string(kekID), err
}
func getAzureHSMConfig(uri *url.URL) (string, string, error) {
r, err := getConfig(uri.Query(), []string{"name", "kekID"})
if err != nil {
return "", "", fmt.Errorf("getting config: %w", err)
}
if len(r) != 2 {
return "", "", fmt.Errorf("expected 2 KmsURI args, got %d", len(r))
}
kekID, err := base64.URLEncoding.DecodeString(r[1])
if err != nil {
return "", "", fmt.Errorf("parsing kekID from kmsUri: %w", err)
}
return r[0], string(kekID), err
}
func getAzureBlobConfig(uri *url.URL) (string, string, error) {
r, err := getConfig(uri.Query(), []string{"container", "connectionString"})
if err != nil {
@ -240,55 +197,11 @@ func getAzureBlobConfig(uri *url.URL) (string, string, error) {
return r[0], r[1], nil
}
func getGCPKMSConfig(uri *url.URL) (project string, location string, keyRing string, protectionLvl int32, kekID string, err error) {
r, err := getConfig(uri.Query(), []string{"project", "location", "keyRing", "protectionLvl", "kekID"})
if err != nil {
return "", "", "", 0, "", err
}
if len(r) != 5 {
return "", "", "", 0, "", fmt.Errorf("expected 5 KmsURI args, got %d", len(r))
}
kekIDByte, err := base64.URLEncoding.DecodeString(r[4])
if err != nil {
return "", "", "", 0, "", fmt.Errorf("parsing kekID from kmsUri: %w", err)
}
protectionLvl32, err := strconv.ParseInt(r[3], 10, 32)
if err != nil {
return "", "", "", 0, "", err
}
return r[0], r[1], r[2], int32(protectionLvl32), string(kekIDByte), nil
}
func getGCPStorageConfig(uri *url.URL) (string, string, error) {
r, err := getConfig(uri.Query(), []string{"project", "bucket"})
return r[0], r[1], err
}
func getClusterKMSConfig(uri *url.URL) (MasterSecret, error) {
r, err := getConfig(uri.Query(), []string{"key", "salt"})
if err != nil {
return MasterSecret{}, err
}
if len(r) != 2 {
return MasterSecret{}, fmt.Errorf("expected 2 KmsURI args, got %d", len(r))
}
key, err := base64.URLEncoding.DecodeString(r[0])
if err != nil {
return MasterSecret{}, fmt.Errorf("parsing key from kmsUri: %w", err)
}
salt, err := base64.URLEncoding.DecodeString(r[1])
if err != nil {
return MasterSecret{}, fmt.Errorf("parsing salt from kmsUri: %w", err)
}
return MasterSecret{Key: key, Salt: salt}, nil
}
// getConfig parses url query values, returning a map of the requested values.
// Returns an error if a key has no value.
// This function MUST always return a slice of the same length as len(keys).