-
Notifications
You must be signed in to change notification settings - Fork 8
/
path_secrets.go
220 lines (193 loc) · 7.03 KB
/
path_secrets.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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package alicloud
import (
"context"
"errors"
"fmt"
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/vault-plugin-secrets-alicloud/clients"
"github.com/hashicorp/vault/sdk/framework"
"github.com/hashicorp/vault/sdk/helper/jsonutil"
"github.com/hashicorp/vault/sdk/logical"
)
const secretType = "alicloud"
func (b *backend) pathSecrets() *framework.Secret {
return &framework.Secret{
Type: secretType,
Fields: map[string]*framework.FieldSchema{
"access_key": {
Type: framework.TypeString,
Description: "Access Key",
},
"secret_key": {
Type: framework.TypeString,
Description: "Secret Key",
},
},
Renew: b.operationRenew,
Revoke: b.operationRevoke,
}
}
func (b *backend) operationRenew(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
roleTypeRaw, ok := req.Secret.InternalData["role_type"]
if !ok {
return nil, errors.New("role_type missing from secret")
}
nameOfRoleType, ok := roleTypeRaw.(string)
if !ok {
return nil, fmt.Errorf("unable to read role_type: %+v", roleTypeRaw)
}
rType, err := parseRoleType(nameOfRoleType)
if err != nil {
return nil, err
}
switch rType {
case roleTypeSTS:
// STS already has a lifetime, and we don'nameOfRoleType support renewing it.
return nil, nil
case roleTypeRAM:
roleName, err := getStringValue(req.Secret.InternalData, "role_name")
if err != nil {
return nil, err
}
role, err := readRole(ctx, req.Storage, roleName)
if err != nil {
return nil, err
}
if role == nil {
// The role has been deleted since the secret was issued or last renewed.
// The user's expectation is probably that the caller won'nameOfRoleType continue being
// able to perform renewals.
return nil, fmt.Errorf("role %s has been deleted so no further renewals are allowed", roleName)
}
resp := &logical.Response{Secret: req.Secret}
if role.TTL != 0 {
resp.Secret.TTL = role.TTL
}
if role.MaxTTL != 0 {
resp.Secret.MaxTTL = role.MaxTTL
}
return resp, nil
default:
return nil, fmt.Errorf("unrecognized role_type: %s", nameOfRoleType)
}
}
func (b *backend) operationRevoke(ctx context.Context, req *logical.Request, _ *framework.FieldData) (*logical.Response, error) {
roleTypeRaw, ok := req.Secret.InternalData["role_type"]
if !ok {
return nil, errors.New("role_type missing from secret")
}
nameOfRoleType, ok := roleTypeRaw.(string)
if !ok {
return nil, fmt.Errorf("unable to read role_type: %+v", roleTypeRaw)
}
rType, err := parseRoleType(nameOfRoleType)
if err != nil {
return nil, err
}
switch rType {
case roleTypeSTS:
// STS cleans up after itself so we can skip this if is_sts internal data
// element set to true.
return nil, nil
case roleTypeRAM:
creds, err := readCredentials(ctx, req.Storage)
if err != nil {
return nil, err
}
if creds == nil {
return nil, errors.New("unable to delete access key because no credentials are configured")
}
client, err := clients.NewRAMClient(b.sdkConfig, creds.AccessKey, creds.SecretKey)
if err != nil {
return nil, err
}
userName, err := getStringValue(req.Secret.InternalData, "username")
if err != nil {
return nil, err
}
accessKeyID, err := getStringValue(req.Secret.InternalData, "access_key_id")
if err != nil {
return nil, err
}
// On the first pass here, if we successfully delete an access key but fail later and return an
// error, we'll never again be able to progress past deleting the access key because it'll be
// gone, leaving dangling objects. So, even if we get an error, we still try to do the remaining
// things. Multierror flags whether to return an error or nil, and if it return any error we'll
// try again, but if it doesn't we know we're done.
apiErrs := &multierror.Error{}
// Delete the access key first so if all else fails, the access key is revoked.
if err := client.DeleteAccessKey(userName, accessKeyID); err != nil {
apiErrs = multierror.Append(apiErrs, err)
}
// Inline policies are currently stored as remote policies, because they have been
// instantiated remotely and we need their name and type to now detach and delete them.
inlinePolicies, err := getRemotePolicies(req.Secret.InternalData, "inline_policies")
if err != nil {
// This shouldn't be part of the multierror because if it returns empty inline policies,
// then we won't go through the inlinePolicies loop and we'll think we're successful
// when we actually didn't delete the inlinePolicies we need to.
return nil, err
}
for _, inlinePolicy := range inlinePolicies {
if err := client.DetachPolicy(userName, inlinePolicy.Name, inlinePolicy.Type); err != nil {
apiErrs = multierror.Append(apiErrs, err)
}
if err := client.DeletePolicy(inlinePolicy.Name); err != nil {
apiErrs = multierror.Append(apiErrs, err)
}
}
// These just need to be detached, but we're not going to delete them because they're
// supposed to be longstanding.
remotePolicies, err := getRemotePolicies(req.Secret.InternalData, "remote_policies")
if err != nil {
// This shouldn't be part of the multierror because if it returns empty remote policies,
// then we won't go through the remotePolicies loop and we'll think we're successful
// when we actually didn't delete the remotePolicies we need to.
return nil, err
}
for _, remotePolicy := range remotePolicies {
if err := client.DetachPolicy(userName, remotePolicy.Name, remotePolicy.Type); err != nil {
apiErrs = multierror.Append(apiErrs, err)
}
}
// Finally, delete the user. Note: this will fail if any other new associations have been
// created with the user out of band from Vault. For example, if a new API key had been
// manually created for them in their console that Vault didn't know about, or some other
// thing had been created. Luckily the err returned is pretty explanatory so that will
// help with debugging.
if err := client.DeleteUser(userName); err != nil {
apiErrs = multierror.Append(apiErrs, err)
}
return nil, apiErrs.ErrorOrNil()
default:
return nil, fmt.Errorf("unrecognized role_type: %s", nameOfRoleType)
}
}
func getStringValue(internalData map[string]interface{}, key string) (string, error) {
valueRaw, ok := internalData[key]
if !ok {
return "", fmt.Errorf("secret is missing %s internal data", key)
}
value, ok := valueRaw.(string)
if !ok {
return "", fmt.Errorf("secret is missing %s internal data", key)
}
return value, nil
}
func getRemotePolicies(internalData map[string]interface{}, key string) ([]*remotePolicy, error) {
valuesRaw, ok := internalData[key]
if !ok {
return nil, fmt.Errorf("secret is missing %s internal data", key)
}
valuesJSON, err := jsonutil.EncodeJSON(valuesRaw)
if err != nil {
return nil, fmt.Errorf("malformed %s internal data", key)
}
policies := []*remotePolicy{}
if err := jsonutil.DecodeJSON(valuesJSON, &policies); err != nil {
return nil, fmt.Errorf("failed to unmarshal %s internal data as remotePolicy", key)
}
return policies, nil
}