-
Notifications
You must be signed in to change notification settings - Fork 8
/
gcpsecretmanager.go
164 lines (144 loc) · 5.55 KB
/
gcpsecretmanager.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
package gcpsecretsmanager
import (
"context"
"encoding/json"
"fmt"
secretmanager "cloud.google.com/go/secretmanager/apiv1"
"github.com/chrismellard/secretfacade/pkg/iam/gcpiam"
"github.com/chrismellard/secretfacade/pkg/secretstore"
"github.com/pkg/errors"
"golang.org/x/oauth2/google"
"google.golang.org/api/option"
secretmanagerpb "google.golang.org/genproto/googleapis/cloud/secretmanager/v1"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/oauth"
)
func NewGcpSecretsManager(creds google.Credentials) secretstore.Interface {
return &gcpSecretsManager{creds}
}
type gcpSecretsManager struct {
creds google.Credentials
}
func (g *gcpSecretsManager) SetSecret(projectId string, secretName string, secretValue *secretstore.SecretValue) error {
client, closer, err := getSecretOpsClient()
if err != nil {
return errors.Wrapf(err, "error setting GCP Secrets Manager secret %s in project %s", secretName, projectId)
}
defer closer()
var existingSecretProps map[string]string
secret, err := getSecret(client, projectId, secretName)
if err != nil {
secret, err = createSecret(client, projectId, secretName)
if err != nil {
return errors.Wrapf(err, "error creating new secret %s in GCP secret manager project %s", secretName, projectId)
}
} else if secretValue.Value == "" && secretValue.PropertyValues != nil {
sv, err := getSecretValue(client, projectId, secretName)
if err != nil {
return errors.Wrapf(err, "error getting GCP secrets manager secret value for secret name %s in project %s", secretName, projectId)
}
existingSecretProps, err = getSecretPropertyMap(sv)
}
req := &secretmanagerpb.AddSecretVersionRequest{
Parent: secret.Name,
Payload: &secretmanagerpb.SecretPayload{
Data: []byte(secretValue.MergeExistingSecret(existingSecretProps)),
},
}
_, err = client.AddSecretVersion(context.TODO(), req)
if err != nil {
return errors.Wrapf(err, "unable to set secret %s in GCP secret manager project %s", secretName, projectId)
}
return nil
}
func (_ *gcpSecretsManager) GetSecret(projectId string, secretName string, secretKey string) (string, error) {
client, closer, err := getSecretOpsClient()
if err != nil {
return "", errors.Wrap(err, "error creating GCP secret manager client")
}
defer closer()
secret, err := getSecretValue(client, projectId, secretName)
if err != nil {
return "", errors.Wrapf(err, "error getting secret %s for GCP secret manager in project %s", secretName, projectId)
}
var secretString string
if secretKey != "" {
secretString, err = getSecretProperty(secret, secretKey)
if err != nil {
return "", errors.Wrapf(err, "error retrieving secret property from secret %s returned from GCP secrets manager in project %s", secretName, projectId)
}
} else {
secretString = string(secret.Data)
}
return secretString, nil
}
func getSecretPropertyMap(v *secretmanagerpb.SecretPayload) (map[string]string, error) {
m := make(map[string]string)
err := json.Unmarshal(v.Data, &m)
if err != nil {
return nil, errors.Wrap(err, "error unmarshalling GCP secrets manager secret payload in to map[string]string")
}
return m, nil
}
func getSecretProperty(v *secretmanagerpb.SecretPayload, propertyName string) (string, error) {
m, err := getSecretPropertyMap(v)
if err != nil {
return "", errors.Wrapf(err, "error reading property %s from secret JSON object", propertyName)
}
return m[propertyName], nil
}
func getSecretOpsClient() (*secretmanager.Client, func(), error) {
creds, err := gcpiam.DefaultCredentials()
if err != nil {
return nil, nil, errors.Wrap(err, "error getting GCP default credentials")
}
client, err := secretmanager.NewClient(context.TODO(),
option.WithGRPCDialOption(
grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, "")),
),
option.WithTokenSource(oauth.TokenSource{TokenSource: creds.TokenSource}),
)
if err != nil {
return nil, nil, err
}
return client, func() { _ = client.Close() }, nil
}
func createSecret(client *secretmanager.Client, projectId string, secretName string) (*secretmanagerpb.Secret, error) {
req := &secretmanagerpb.CreateSecretRequest{
Parent: fmt.Sprintf("projects/%s", projectId),
SecretId: secretName,
Secret: &secretmanagerpb.Secret{
Replication: &secretmanagerpb.Replication{
Replication: &secretmanagerpb.Replication_Automatic_{
Automatic: &secretmanagerpb.Replication_Automatic{},
},
},
},
}
secret, err := client.CreateSecret(context.TODO(), req)
if err != nil {
return nil, errors.Wrapf(err, "error creating secret %s in GCP secrets manager for project %s", secretName, projectId)
}
return secret, nil
}
func getSecret(client *secretmanager.Client, projectId string, secretName string) (*secretmanagerpb.Secret, error) {
req := &secretmanagerpb.GetSecretRequest{
Name: fmt.Sprintf("projects/%s/secrets/%s", projectId, secretName),
}
secret, err := client.GetSecret(context.TODO(), req)
if err != nil {
return nil, errors.Wrapf(err, "error getting secret %s for GCP secrets manager project %s", secretName, projectId)
}
return secret, nil
}
func getSecretValue(client *secretmanager.Client, projectId string, secretName string) (*secretmanagerpb.SecretPayload, error) {
req := &secretmanagerpb.AccessSecretVersionRequest{
Name: fmt.Sprintf("projects/%s/secrets/%s/versions/latest", projectId, secretName),
}
secret, err := client.AccessSecretVersion(context.TODO(), req)
if err != nil {
return nil, errors.Wrapf(err, "error getting secret value for secret %s for GCP secrets manager project %s", secretName, projectId)
}
return secret.Payload, nil
}