ekristen/aws-nuke

View on GitHub
resources/kms-key.go

Summary

Maintainability
A
2 hrs
Test Coverage
package resources

import (
    "context"
    "errors"
    "fmt"

    "github.com/gotidy/ptr"
    "github.com/sirupsen/logrus"

    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/awserr"
    "github.com/aws/aws-sdk-go/service/kms"
    "github.com/aws/aws-sdk-go/service/kms/kmsiface"

    "github.com/ekristen/libnuke/pkg/registry"
    "github.com/ekristen/libnuke/pkg/resource"
    "github.com/ekristen/libnuke/pkg/types"

    "github.com/ekristen/aws-nuke/v3/pkg/nuke"
)

const KMSKeyResource = "KMSKey"

func init() {
    registry.Register(&registry.Registration{
        Name:     KMSKeyResource,
        Scope:    nuke.Account,
        Resource: &KMSKey{},
        Lister:   &KMSKeyLister{},
        DependsOn: []string{
            KMSAliasResource,
        },
    })
}

type KMSKeyLister struct {
    mockSvc kmsiface.KMSAPI
}

func (l *KMSKeyLister) List(_ context.Context, o interface{}) ([]resource.Resource, error) {
    opts := o.(*nuke.ListerOpts)
    resources := make([]resource.Resource, 0)

    var svc kmsiface.KMSAPI
    if l.mockSvc != nil {
        svc = l.mockSvc
    } else {
        svc = kms.New(opts.Session)
    }

    inaccessibleKeys := false

    if err := svc.ListKeysPages(nil, func(keysOut *kms.ListKeysOutput, lastPage bool) bool {
        for _, key := range keysOut.Keys {
            resp, err := svc.DescribeKey(&kms.DescribeKeyInput{
                KeyId: key.KeyId,
            })
            if err != nil {
                var awsError awserr.Error
                if errors.As(err, &awsError) {
                    if awsError.Code() == "AccessDeniedException" {
                        inaccessibleKeys = true
                        logrus.WithField("arn", key.KeyArn).WithError(err).Debug("unable to describe key")
                        continue
                    }
                }

                logrus.WithError(err).Error("unable to describe key")
                continue
            }

            kmsKey := &KMSKey{
                svc:     svc,
                ID:      resp.KeyMetadata.KeyId,
                State:   resp.KeyMetadata.KeyState,
                Manager: resp.KeyMetadata.KeyManager,
            }

            // Note: we check for customer managed keys here because we can't list tags for AWS managed keys
            // This way AWS managed keys still show up but get filtered out by the Filter method
            if ptr.ToString(resp.KeyMetadata.KeyManager) == kms.KeyManagerTypeCustomer {
                tags, err := svc.ListResourceTags(&kms.ListResourceTagsInput{
                    KeyId: key.KeyId,
                })
                if err != nil {
                    var awsError awserr.Error
                    if errors.As(err, &awsError) {
                        if awsError.Code() == "AccessDeniedException" {
                            inaccessibleKeys = true
                            logrus.WithError(err).Debug("unable to list tags")
                            continue
                        } else {
                            logrus.WithError(err).Error("unable to list tags")
                        }
                    }
                } else {
                    kmsKey.Tags = tags.Tags
                }
            }

            keyAliases, err := svc.ListAliases(&kms.ListAliasesInput{
                KeyId: key.KeyId,
            })
            if err != nil {
                logrus.WithError(err).Error("unable to list aliases")
            }

            if len(keyAliases.Aliases) > 0 {
                kmsKey.Alias = keyAliases.Aliases[0].AliasName
            }

            resources = append(resources, kmsKey)
        }

        return !lastPage
    }); err != nil {
        return nil, err
    }

    if inaccessibleKeys {
        logrus.Warn("one or more KMS keys were inaccessible, debug logging will contain more information")
    }

    return resources, nil
}

type KMSKey struct {
    svc     kmsiface.KMSAPI
    ID      *string
    State   *string
    Manager *string
    Alias   *string
    Tags    []*kms.Tag
}

func (r *KMSKey) Filter() error {
    if state := ptr.ToString(r.State); state == kms.KeyStatePendingDeletion || state == kms.KeyStatePendingReplicaDeletion {
        return fmt.Errorf("is already in %v state", state)
    }

    if ptr.ToString(r.Manager) == kms.KeyManagerTypeAws {
        return fmt.Errorf("cannot delete AWS managed key")
    }

    return nil
}

func (r *KMSKey) Remove(_ context.Context) error {
    _, err := r.svc.ScheduleKeyDeletion(&kms.ScheduleKeyDeletionInput{
        KeyId:               r.ID,
        PendingWindowInDays: aws.Int64(7),
    })
    return err
}

func (r *KMSKey) String() string {
    return *r.ID
}

func (r *KMSKey) Properties() types.Properties {
    return types.NewPropertiesFromStruct(r)
}