From 678efae94edeb8f49ca6808f635f304ab4bf4c84 Mon Sep 17 00:00:00 2001 From: Aaron Miller Date: Sun, 28 Jul 2024 18:18:10 -0700 Subject: [PATCH] add: `create kms-key` --- cmd/create/create.go | 2 + cmd/get/get.go | 4 +- pkg/aws/kms.go | 45 +++++++--- pkg/resource/amp_workspace/manager.go | 2 +- pkg/resource/error.go | 12 +++ pkg/resource/{kms_key => kms/key}/get.go | 12 ++- pkg/resource/{kms_key => kms/key}/kms_key.go | 15 ++-- pkg/resource/kms/key/manager.go | 86 ++++++++++++++++++++ pkg/resource/{kms_key => kms/key}/options.go | 8 +- pkg/resource/{kms_key => kms/key}/printer.go | 2 +- 10 files changed, 160 insertions(+), 28 deletions(-) rename pkg/resource/{kms_key => kms/key}/get.go (88%) rename pkg/resource/{kms_key => kms/key}/kms_key.go (66%) create mode 100644 pkg/resource/kms/key/manager.go rename pkg/resource/{kms_key => kms/key}/options.go (63%) rename pkg/resource/{kms_key => kms/key}/printer.go (98%) diff --git a/cmd/create/create.go b/cmd/create/create.go index 0c9f4ed..4c4bfdc 100644 --- a/cmd/create/create.go +++ b/cmd/create/create.go @@ -10,6 +10,7 @@ import ( "github.com/awslabs/eksdemo/pkg/resource/dns_record" "github.com/awslabs/eksdemo/pkg/resource/ec2/instance" "github.com/awslabs/eksdemo/pkg/resource/fargate_profile" + kmskey "github.com/awslabs/eksdemo/pkg/resource/kms/key" "github.com/awslabs/eksdemo/pkg/resource/log_group" "github.com/awslabs/eksdemo/pkg/resource/nodegroup" "github.com/awslabs/eksdemo/pkg/resource/organization" @@ -44,6 +45,7 @@ func NewCreateCmd() *cobra.Command { cmd.AddCommand(dns_record.NewResource().NewCreateCmd()) cmd.AddCommand(fargate_profile.NewResource().NewCreateCmd()) cmd.AddCommand(instance.NewResource().NewCreateCmd()) + cmd.AddCommand(kmskey.NewResource().NewCreateCmd()) cmd.AddCommand(NewKyvernoCmd()) cmd.AddCommand(NewCreateAliasCmds(kyvernoPolicies, "kyverno-")...) cmd.AddCommand(log_group.NewResource().NewCreateCmd()) diff --git a/cmd/get/get.go b/cmd/get/get.go index 4d7bd1e..2ef9614 100644 --- a/cmd/get/get.go +++ b/cmd/get/get.go @@ -28,7 +28,7 @@ import ( "github.com/awslabs/eksdemo/pkg/resource/iam_policy" "github.com/awslabs/eksdemo/pkg/resource/iam_role" "github.com/awslabs/eksdemo/pkg/resource/internet_gateway" - "github.com/awslabs/eksdemo/pkg/resource/kms_key" + kmskey "github.com/awslabs/eksdemo/pkg/resource/kms/key" "github.com/awslabs/eksdemo/pkg/resource/listener" "github.com/awslabs/eksdemo/pkg/resource/listener_rule" "github.com/awslabs/eksdemo/pkg/resource/load_balancer" @@ -100,7 +100,7 @@ func NewGetCmd() *cobra.Command { cmd.AddCommand(iam_policy.NewResource().NewGetCmd()) cmd.AddCommand(iam_role.NewResource().NewGetCmd()) cmd.AddCommand(internet_gateway.NewResource().NewGetCmd()) - cmd.AddCommand(kms_key.NewResource().NewGetCmd()) + cmd.AddCommand(kmskey.NewResource().NewGetCmd()) cmd.AddCommand(listener.NewResource().NewGetCmd()) cmd.AddCommand(listener_rule.NewResource().NewGetCmd()) cmd.AddCommand(load_balancer.NewResource().NewGetCmd()) diff --git a/pkg/aws/kms.go b/pkg/aws/kms.go index 471b667..2744f00 100644 --- a/pkg/aws/kms.go +++ b/pkg/aws/kms.go @@ -16,6 +16,39 @@ func NewKMSClient() *KMSClient { return &KMSClient{kms.NewFromConfig(GetConfig())} } +func (c *KMSClient) CreateAlias(aliasName, keyID string) error { + _, err := c.Client.CreateAlias(context.Background(), &kms.CreateAliasInput{ + AliasName: aws.String(aliasName), + TargetKeyId: aws.String(keyID), + }) + + return err +} + +func (c *KMSClient) CreateKey() (*types.KeyMetadata, error) { + result, err := c.Client.CreateKey(context.Background(), &kms.CreateKeyInput{ + KeySpec: types.KeySpecSymmetricDefault, + }) + + if err != nil { + return nil, err + } + + return result.KeyMetadata, nil +} + +func (c *KMSClient) DescribeKey(keyID string) (*types.KeyMetadata, error) { + result, err := c.Client.DescribeKey(context.Background(), &kms.DescribeKeyInput{ + KeyId: aws.String(keyID), + }) + + if err != nil { + return nil, err + } + + return result.KeyMetadata, nil +} + func (c *KMSClient) ListAliases() ([]types.AliasListEntry, error) { keys := []types.AliasListEntry{} pageNum := 0 @@ -51,15 +84,3 @@ func (c *KMSClient) ListKeys() ([]types.KeyListEntry, error) { return keys, nil } - -func (c *KMSClient) DescribeKey(keyId string) (*types.KeyMetadata, error) { - result, err := c.Client.DescribeKey(context.Background(), &kms.DescribeKeyInput{ - KeyId: aws.String(keyId), - }) - - if err != nil { - return nil, err - } - - return result.KeyMetadata, nil -} diff --git a/pkg/resource/amp_workspace/manager.go b/pkg/resource/amp_workspace/manager.go index 63763cf..b9683db 100644 --- a/pkg/resource/amp_workspace/manager.go +++ b/pkg/resource/amp_workspace/manager.go @@ -49,7 +49,7 @@ func (m *Manager) Create(options resource.Options) error { if err != nil { return err } - fmt.Printf("done\nCreated AMP Workspace Id: %s\n", *result.WorkspaceId) + fmt.Printf("done\nCreated AMP Workspace Id: %s\n", awssdk.ToString(result.WorkspaceId)) return nil } diff --git a/pkg/resource/error.go b/pkg/resource/error.go index 1953c1c..23306de 100644 --- a/pkg/resource/error.go +++ b/pkg/resource/error.go @@ -2,6 +2,7 @@ package resource import "fmt" +// TODO: phase this out. Doesn't work with errors.As type NotFoundError string func (e NotFoundError) Error() string { @@ -25,3 +26,14 @@ type NotFoundByNameError struct { func (e *NotFoundByNameError) Error() string { return fmt.Sprintf("%s with name %q not found", e.Type, e.Name) } + +// TODO: This error could potentially replace NotFoundByIDError and NotFoundByNameError +type NotFoundByError struct { + Type string + Name string + Value string +} + +func (e *NotFoundByError) Error() string { + return fmt.Sprintf("%s with %s %q not found", e.Type, e.Name, e.Value) +} diff --git a/pkg/resource/kms_key/get.go b/pkg/resource/kms/key/get.go similarity index 88% rename from pkg/resource/kms_key/get.go rename to pkg/resource/kms/key/get.go index 2b51c9a..673e31e 100644 --- a/pkg/resource/kms_key/get.go +++ b/pkg/resource/kms/key/get.go @@ -1,8 +1,9 @@ -package kms_key +package key import ( "fmt" "os" + "sort" awssdk "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/kms/types" @@ -31,7 +32,7 @@ func (g *Getter) Init() { } func (g *Getter) Get(alias string, output printer.Output, options resource.Options) error { - kmsOptions, ok := options.(*KmsKeyOptions) + kmsOptions, ok := options.(*Options) if !ok { return fmt.Errorf("internal error, unable to cast options to KmsKeyOptions") } @@ -85,6 +86,11 @@ func (g *Getter) GetAllKeys() ([]*KMSKey, error) { keys = append(keys, key) } + // Show recently created Keys at the end of the list + sort.Slice(keys, func(i, j int) bool { + return keys[i].Key.CreationDate.Before(awssdk.ToTime(keys[j].Key.CreationDate)) + }) + return keys, nil } @@ -109,7 +115,7 @@ func (g *Getter) GetByAlias(aliasName string) (*KMSKey, error) { return &KMSKey{filterAliasesByKeyId(aliases, keyId), key}, nil } - return nil, resource.NotFoundError(fmt.Sprintf("kms-key alias %q not found", aliasName)) + return nil, &resource.NotFoundByError{Type: "kms-key", Name: "alias", Value: aliasName} } func filterAliasesByKeyId(aliases []types.AliasListEntry, id string) []types.AliasListEntry { diff --git a/pkg/resource/kms_key/kms_key.go b/pkg/resource/kms/key/kms_key.go similarity index 66% rename from pkg/resource/kms_key/kms_key.go rename to pkg/resource/kms/key/kms_key.go index 2cedfb7..34eed14 100644 --- a/pkg/resource/kms_key/kms_key.go +++ b/pkg/resource/kms/key/kms_key.go @@ -1,4 +1,4 @@ -package kms_key +package key import ( "github.com/awslabs/eksdemo/pkg/cmd" @@ -6,18 +6,23 @@ import ( ) func NewResource() *resource.Resource { - res := &resource.Resource{ + options, getFlags := newOptions() + + return &resource.Resource{ Command: cmd.Command{ Name: "kms-key", Description: "KMS Key", Aliases: []string{"kms-keys", "kmskeys", "kmskey", "kms"}, Args: []string{"ALIAS"}, + CreateArgs: []string{"ALIAS"}, }, + GetFlags: getFlags, + Getter: &Getter{}, - } - res.Options, res.GetFlags = newOptions() + Manager: &Manager{}, - return res + Options: options, + } } diff --git a/pkg/resource/kms/key/manager.go b/pkg/resource/kms/key/manager.go new file mode 100644 index 0000000..47dc03c --- /dev/null +++ b/pkg/resource/kms/key/manager.go @@ -0,0 +1,86 @@ +package key + +import ( + "errors" + "fmt" + + awssdk "github.com/aws/aws-sdk-go-v2/aws" + "github.com/awslabs/eksdemo/pkg/aws" + "github.com/awslabs/eksdemo/pkg/resource" + "github.com/spf13/cobra" +) + +type Manager struct { + DryRun bool + kmsClient *aws.KMSClient + kmsGetter *Getter +} + +func (m *Manager) Init() { + if m.kmsClient == nil { + m.kmsClient = aws.NewKMSClient() + } + m.kmsGetter = NewGetter(m.kmsClient) +} + +func (m *Manager) Create(options resource.Options) error { + alias := options.Common().Name + + _, err := m.kmsGetter.GetByAlias(alias) + + // Return if the KMS alias already exists + if err == nil { + fmt.Printf("KMS Key with alias %q already exists\n", alias) + return nil + } + + // Return the error if it's anything other than resource not found + var notFoundErr *resource.NotFoundByError + if !errors.As(err, ¬FoundErr) { + return err + } + + fullAliasName := fmt.Sprintf("alias/%s", alias) + + if m.DryRun { + return m.dryRun(fullAliasName) + } + + fmt.Printf("Creating KMS Key with Alias %q...", alias) + + keyMeta, err := m.kmsClient.CreateKey() + if err != nil { + return err + } + + keyID := awssdk.ToString(keyMeta.KeyId) + + err = m.kmsClient.CreateAlias(fullAliasName, keyID) + if err != nil { + return fmt.Errorf("failed to create alias for key %q: %w", keyID, err) + } + fmt.Printf("done\nCreated KMS Key Id: %s\n", keyID) + + return nil +} + +func (m *Manager) Delete(_ resource.Options) error { + return fmt.Errorf("feature not supported") +} + +func (m *Manager) SetDryRun() { + m.DryRun = true +} + +func (m *Manager) Update(_ resource.Options, _ *cobra.Command) error { + return fmt.Errorf("feature not supported") +} + +func (m *Manager) dryRun(aliasName string) error { + fmt.Printf("\nKMS Key Manager Dry Run:\n") + fmt.Printf("KMS API Call %q with no request parameters\n", "CreateKey") + fmt.Printf("KMS API Call %q with parameters:\n", "CreateAlias") + fmt.Printf("\tAliasName: %q\n", aliasName) + fmt.Printf("\tTargetKeyId: \n") + return nil +} diff --git a/pkg/resource/kms_key/options.go b/pkg/resource/kms/key/options.go similarity index 63% rename from pkg/resource/kms_key/options.go rename to pkg/resource/kms/key/options.go index a702c78..71b24e2 100644 --- a/pkg/resource/kms_key/options.go +++ b/pkg/resource/kms/key/options.go @@ -1,16 +1,16 @@ -package kms_key +package key import ( "github.com/awslabs/eksdemo/pkg/cmd" "github.com/awslabs/eksdemo/pkg/resource" ) -type KmsKeyOptions struct { +type Options struct { resource.CommonOptions } -func newOptions() (options *KmsKeyOptions, getFlags cmd.Flags) { - options = &KmsKeyOptions{ +func newOptions() (options *Options, getFlags cmd.Flags) { + options = &Options{ CommonOptions: resource.CommonOptions{ ClusterFlagDisabled: true, }, diff --git a/pkg/resource/kms_key/printer.go b/pkg/resource/kms/key/printer.go similarity index 98% rename from pkg/resource/kms_key/printer.go rename to pkg/resource/kms/key/printer.go index dbf26d5..455579b 100644 --- a/pkg/resource/kms_key/printer.go +++ b/pkg/resource/kms/key/printer.go @@ -1,4 +1,4 @@ -package kms_key +package key import ( "fmt"