mirror of
https://github.com/hashicorp/vault.git
synced 2025-11-27 05:31:40 +01:00
logical/aws: WAL entry for users, rollback
This commit is contained in:
parent
a04df95177
commit
f99f6c910e
@ -1,6 +1,8 @@
|
||||
package aws
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/vault/logical"
|
||||
"github.com/hashicorp/vault/logical/framework"
|
||||
)
|
||||
@ -26,6 +28,9 @@ func Backend() *framework.Backend {
|
||||
Secrets: []*framework.Secret{
|
||||
secretAccessKeys(),
|
||||
},
|
||||
|
||||
Rollback: rollback,
|
||||
RollbackMinAge: 5 * time.Minute,
|
||||
}
|
||||
|
||||
return b.Backend
|
||||
|
||||
@ -9,6 +9,7 @@ import (
|
||||
"github.com/hashicorp/aws-sdk-go/gen/iam"
|
||||
"github.com/hashicorp/vault/logical"
|
||||
"github.com/hashicorp/vault/logical/framework"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
func pathUser(b *backend) *framework.Path {
|
||||
@ -47,6 +48,19 @@ func (b *backend) pathUserRead(
|
||||
// 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-%d-%d", 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(req.Storage, "user", &walUser{
|
||||
UserName: username,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error writing WAL entry: %s", err)
|
||||
}
|
||||
|
||||
// Create the user
|
||||
_, err = client.CreateUser(&iam.CreateUserRequest{
|
||||
UserName: aws.String(username),
|
||||
})
|
||||
@ -70,11 +84,19 @@ func (b *backend) pathUserRead(
|
||||
keyResp, err := client.CreateAccessKey(&iam.CreateAccessKeyRequest{
|
||||
UserName: aws.String(username),
|
||||
})
|
||||
err = fmt.Errorf("SUCK!")
|
||||
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(req.Storage, 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,
|
||||
@ -83,3 +105,92 @@ func (b *backend) pathUserRead(
|
||||
"username": username,
|
||||
}), nil
|
||||
}
|
||||
|
||||
func pathUserRollback(req *logical.Request, _kind string, data interface{}) error {
|
||||
var entry walUser
|
||||
if err := mapstructure.Decode(data, &entry); err != nil {
|
||||
return err
|
||||
}
|
||||
username := entry.UserName
|
||||
|
||||
// Get the client
|
||||
client, err := clientIAM(req.Storage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get information about this user
|
||||
groupsResp, err := client.ListGroupsForUser(&iam.ListGroupsForUserRequest{
|
||||
UserName: aws.String(username),
|
||||
MaxItems: aws.Integer(1000),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
groups := groupsResp.Groups
|
||||
|
||||
policiesResp, err := client.ListUserPolicies(&iam.ListUserPoliciesRequest{
|
||||
UserName: aws.String(username),
|
||||
MaxItems: aws.Integer(1000),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
policies := policiesResp.PolicyNames
|
||||
|
||||
keysResp, err := client.ListAccessKeys(&iam.ListAccessKeysRequest{
|
||||
UserName: aws.String(username),
|
||||
MaxItems: aws.Integer(1000),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
keys := keysResp.AccessKeyMetadata
|
||||
|
||||
// Revoke all keys
|
||||
for _, k := range keys {
|
||||
err = client.DeleteAccessKey(&iam.DeleteAccessKeyRequest{
|
||||
AccessKeyID: k.AccessKeyID,
|
||||
UserName: aws.String(username),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Delete any policies
|
||||
for _, p := range policies {
|
||||
err = client.DeleteUserPolicy(&iam.DeleteUserPolicyRequest{
|
||||
UserName: aws.String(username),
|
||||
PolicyName: aws.String(p),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the user from all their groups
|
||||
for _, g := range groups {
|
||||
err = client.RemoveUserFromGroup(&iam.RemoveUserFromGroupRequest{
|
||||
GroupName: g.GroupName,
|
||||
UserName: aws.String(username),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the user
|
||||
err = client.DeleteUser(&iam.DeleteUserRequest{
|
||||
UserName: aws.String(username),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type walUser struct {
|
||||
UserName string
|
||||
}
|
||||
|
||||
21
builtin/logical/aws/rollback.go
Normal file
21
builtin/logical/aws/rollback.go
Normal file
@ -0,0 +1,21 @@
|
||||
package aws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/vault/logical"
|
||||
"github.com/hashicorp/vault/logical/framework"
|
||||
)
|
||||
|
||||
var rollbackMap = map[string]framework.RollbackFunc{
|
||||
"user": pathUserRollback,
|
||||
}
|
||||
|
||||
func rollback(req *logical.Request, kind string, data interface{}) error {
|
||||
f, ok := rollbackMap[kind]
|
||||
if !ok {
|
||||
return fmt.Errorf("unknown type to rollback")
|
||||
}
|
||||
|
||||
return f(req, kind, data)
|
||||
}
|
||||
@ -3,8 +3,6 @@ package aws
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/aws-sdk-go/aws"
|
||||
"github.com/hashicorp/aws-sdk-go/gen/iam"
|
||||
"github.com/hashicorp/vault/logical"
|
||||
"github.com/hashicorp/vault/logical/framework"
|
||||
)
|
||||
@ -42,68 +40,12 @@ func secretAccessKeysRevoke(
|
||||
return nil, fmt.Errorf("secret is missing username internal data")
|
||||
}
|
||||
|
||||
// Get the client
|
||||
client, err := clientIAM(req.Storage)
|
||||
if err != nil {
|
||||
return logical.ErrorResponse(err.Error()), nil
|
||||
}
|
||||
|
||||
// Get information about this user
|
||||
groupsResp, err := client.ListGroupsForUser(&iam.ListGroupsForUserRequest{
|
||||
UserName: aws.String(username),
|
||||
MaxItems: aws.Integer(1000),
|
||||
// Use the user rollback mechanism to delete this user
|
||||
err := pathUserRollback(req, "user", map[string]interface{}{
|
||||
"username": username,
|
||||
})
|
||||
if err != nil {
|
||||
return logical.ErrorResponse(err.Error()), nil
|
||||
}
|
||||
groups := groupsResp.Groups
|
||||
|
||||
policiesResp, err := client.ListUserPolicies(&iam.ListUserPoliciesRequest{
|
||||
UserName: aws.String(username),
|
||||
MaxItems: aws.Integer(1000),
|
||||
})
|
||||
if err != nil {
|
||||
return logical.ErrorResponse(err.Error()), nil
|
||||
}
|
||||
policies := policiesResp.PolicyNames
|
||||
|
||||
// Revoke it!
|
||||
err = client.DeleteAccessKey(&iam.DeleteAccessKeyRequest{
|
||||
AccessKeyID: aws.String(d.Get("access_key").(string)),
|
||||
UserName: aws.String(username),
|
||||
})
|
||||
if err != nil {
|
||||
return logical.ErrorResponse(err.Error()), nil
|
||||
}
|
||||
|
||||
// Delete any policies
|
||||
for _, p := range policies {
|
||||
err = client.DeleteUserPolicy(&iam.DeleteUserPolicyRequest{
|
||||
UserName: aws.String(username),
|
||||
PolicyName: aws.String(p),
|
||||
})
|
||||
if err != nil {
|
||||
return logical.ErrorResponse(err.Error()), nil
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the user from all their groups
|
||||
for _, g := range groups {
|
||||
err = client.RemoveUserFromGroup(&iam.RemoveUserFromGroupRequest{
|
||||
GroupName: g.GroupName,
|
||||
UserName: aws.String(username),
|
||||
})
|
||||
if err != nil {
|
||||
return logical.ErrorResponse(err.Error()), nil
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the user
|
||||
err = client.DeleteUser(&iam.DeleteUserRequest{
|
||||
UserName: aws.String(username),
|
||||
})
|
||||
if err != nil {
|
||||
return logical.ErrorResponse(err.Error()), nil
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user