· 6 years ago · Sep 01, 2019, 06:18 PM
1package awsssm
2
3import (
4 "fmt"
5 "os"
6 "regexp"
7 "strconv"
8 "strings"
9
10 "github.com/aws/aws-sdk-go/aws"
11 "github.com/aws/aws-sdk-go/aws/awserr"
12 "github.com/aws/aws-sdk-go/service/ssm"
13 "github.com/aws/aws-sdk-go/service/ssm/ssmiface"
14)
15
16const (
17 // DefaultKeyID is the default alias for the KMS key used to encrypt/decrypt secrets
18 DefaultKeyID = "alias/parameter_store_key"
19)
20
21// validPathKeyFormat is the format that is expected for key names inside parameter store
22// when using paths
23var validPathKeyFormat = regexp.MustCompile(`^(\/[\w\-\.]+)+$`)
24
25// validKeyFormat is the format that is expected for key names inside parameter store when
26// not using paths
27var validKeyFormat = regexp.MustCompile(`^[\w\-\.]+$`)
28
29// ensure SSMStore confirms to Store interface
30var _ Store = &SSMStore{}
31
32// SSMStore implements the Store interface for storing secrets in SSM Parameter
33// Store
34type SSMStore struct {
35 svc ssmiface.SSMAPI
36 usePaths bool
37}
38
39// ListRaw lists all secrets keys and values for a given service. Does not include any
40// other meta-data. Uses faster AWS APIs with much higher rate-limits. Suitable for
41// use in production environments.
42func (s *SSMStore) ListRaw(service string) ([]RawSecret, error) {
43 if s.usePaths {
44 secrets := map[string]RawSecret{}
45 getParametersByPathInput := &ssm.GetParametersByPathInput{
46 Path: aws.String("/" + service + "/"),
47 WithDecryption: aws.Bool(true),
48 }
49
50 err := s.svc.GetParametersByPathPages(getParametersByPathInput, func(resp *ssm.GetParametersByPathOutput, lastPage bool) bool {
51 for _, param := range resp.Parameters {
52 if !s.validateName(*param.Name) {
53 continue
54 }
55
56 secrets[*param.Name] = RawSecret{
57 Value: *param.Value,
58 Key: *param.Name,
59 }
60 }
61 return true
62 })
63
64 if err != nil {
65 // If the error is an access-denied exception
66 awsErr, isAwserr := err.(awserr.Error)
67 if isAwserr {
68 if awsErr.Code() == "AccessDeniedException" && strings.Contains(awsErr.Message(), "is not authorized to perform: ssm:GetParametersByPath on resource") {
69 // Fall-back to using the old list method in case some users haven't updated their IAM permissions yet, but warn about it and
70 // tell them to fix their permissions
71 fmt.Fprintf(
72 os.Stderr,
73 "Warning: %s\nFalling-back to using ssm:DescribeParameters. This may cause delays or failures due to AWS rate-limiting.\n"+
74 "This is behavior deprecated and will be removed in a future version of chamber. Please update your IAM permissions to grant ssm:GetParametersByPath.\n\n",
75 awsErr)
76
77 // Delegate to List
78 return s.listRawViaList(service)
79 }
80 }
81
82 return nil, err
83 }
84
85 rawSecrets := make([]RawSecret, len(secrets))
86 i := 0
87 for _, rawSecret := range secrets {
88 rawSecrets[i] = rawSecret
89 i += 1
90 }
91 return rawSecrets, nil
92 }
93
94 // Delete to List (which uses the DescribeParameters API)
95 return s.listRawViaList(service)
96}