forked from hashicorp/vault
-
Notifications
You must be signed in to change notification settings - Fork 2
/
secret_access_keys.go
149 lines (128 loc) · 4.11 KB
/
secret_access_keys.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
package aws
import (
"fmt"
"math/rand"
"regexp"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/iam"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/logical/framework"
)
const SecretAccessKeyType = "access_keys"
func secretAccessKeys(b *backend) *framework.Secret {
return &framework.Secret{
Type: SecretAccessKeyType,
Fields: map[string]*framework.FieldSchema{
"access_key": &framework.FieldSchema{
Type: framework.TypeString,
Description: "Access Key",
},
"secret_key": &framework.FieldSchema{
Type: framework.TypeString,
Description: "Secret Key",
},
},
DefaultDuration: 1 * time.Hour,
DefaultGracePeriod: 10 * time.Minute,
Renew: b.secretAccessKeysRenew,
Revoke: secretAccessKeysRevoke,
}
}
func (b *backend) secretAccessKeysCreate(
s logical.Storage,
displayName, policyName string, policy string) (*logical.Response, error) {
client, err := clientIAM(s)
if err != nil {
return logical.ErrorResponse(err.Error()), nil
}
// Generate a random username. We don't put the policy names in the
// username because the AWS console makes it pretty easy to see that.
username := fmt.Sprintf("vault-%s-%d-%d", normalizeDisplayName(displayName), time.Now().Unix(), rand.Int31n(10000))
// Write to the WAL that this user will be created. We do this before
// the user is created because if switch the order then the WAL put
// can fail, which would put us in an awkward position: we have a user
// we need to rollback but can't put the WAL entry to do the rollback.
walId, err := framework.PutWAL(s, "user", &walUser{
UserName: username,
})
if err != nil {
return nil, fmt.Errorf("Error writing WAL entry: %s", err)
}
// Create the user
_, err = client.CreateUser(&iam.CreateUserInput{
UserName: aws.String(username),
})
if err != nil {
return logical.ErrorResponse(fmt.Sprintf(
"Error creating IAM user: %s", err)), nil
}
// Add the user to all the groups
_, err = client.PutUserPolicy(&iam.PutUserPolicyInput{
UserName: aws.String(username),
PolicyName: aws.String(policyName),
PolicyDocument: aws.String(policy),
})
if err != nil {
return logical.ErrorResponse(fmt.Sprintf(
"Error adding user to group: %s", err)), nil
}
// Create the keys
keyResp, err := client.CreateAccessKey(&iam.CreateAccessKeyInput{
UserName: aws.String(username),
})
if err != nil {
return logical.ErrorResponse(fmt.Sprintf(
"Error creating access keys: %s", err)), nil
}
// Remove the WAL entry, we succeeded! If we fail, we don't return
// the secret because it'll get rolled back anyways, so we have to return
// an error here.
if err := framework.DeleteWAL(s, walId); err != nil {
return nil, fmt.Errorf("Failed to commit WAL entry: %s", err)
}
// Return the info!
return b.Secret(SecretAccessKeyType).Response(map[string]interface{}{
"access_key": *keyResp.AccessKey.AccessKeyId,
"secret_key": *keyResp.AccessKey.SecretAccessKey,
}, map[string]interface{}{
"username": username,
"policy": policy,
}), nil
}
func (b *backend) secretAccessKeysRenew(
req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
lease, err := b.Lease(req.Storage)
if err != nil {
return nil, err
}
if lease == nil {
lease = &configLease{Lease: 1 * time.Hour}
}
f := framework.LeaseExtend(lease.Lease, lease.LeaseMax, false)
return f(req, d)
}
func secretAccessKeysRevoke(
req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
// Get the username from the internal data
usernameRaw, ok := req.Secret.InternalData["username"]
if !ok {
return nil, fmt.Errorf("secret is missing username internal data")
}
username, ok := usernameRaw.(string)
if !ok {
return nil, fmt.Errorf("secret is missing username internal data")
}
// Use the user rollback mechanism to delete this user
err := pathUserRollback(req, "user", map[string]interface{}{
"username": username,
})
if err != nil {
return nil, err
}
return nil, nil
}
func normalizeDisplayName(displayName string) string {
re := regexp.MustCompile("[^a-zA-Z+=,.@_-]")
return re.ReplaceAllString(displayName, "_")
}