2015-03-20 16:59:48 +00:00
package aws
import (
2018-01-08 18:31:38 +00:00
"context"
2015-03-20 16:59:48 +00:00
"fmt"
2018-08-16 10:38:13 +00:00
"strings"
2015-03-20 16:59:48 +00:00
2015-08-06 16:26:41 +00:00
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/iam"
2018-04-05 15:49:21 +00:00
"github.com/hashicorp/errwrap"
2018-08-16 10:38:13 +00:00
"github.com/hashicorp/vault/helper/strutil"
2015-03-20 16:59:48 +00:00
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/logical/framework"
2015-03-21 10:18:46 +00:00
"github.com/mitchellh/mapstructure"
2015-03-20 16:59:48 +00:00
)
func pathUser ( b * backend ) * framework . Path {
return & framework . Path {
2018-08-16 10:38:13 +00:00
Pattern : "(creds|sts)/" + framework . GenericNameRegex ( "name" ) ,
2015-03-20 16:59:48 +00:00
Fields : map [ string ] * framework . FieldSchema {
"name" : & framework . FieldSchema {
Type : framework . TypeString ,
2015-04-27 21:20:28 +00:00
Description : "Name of the role" ,
2015-03-20 16:59:48 +00:00
} ,
2018-08-16 10:38:13 +00:00
"role_arn" : & framework . FieldSchema {
Type : framework . TypeString ,
Description : "ARN of role to assume when credential_type is " + assumedRoleCred ,
} ,
"ttl" : & framework . FieldSchema {
Type : framework . TypeDurationSecond ,
Description : "Lifetime of the returned credentials in seconds" ,
Default : 3600 ,
} ,
2015-03-20 16:59:48 +00:00
} ,
Callbacks : map [ logical . Operation ] framework . OperationFunc {
2018-08-16 10:38:13 +00:00
logical . ReadOperation : b . pathCredsRead ,
logical . UpdateOperation : b . pathCredsRead ,
2015-03-20 16:59:48 +00:00
} ,
2015-04-04 04:10:54 +00:00
HelpSynopsis : pathUserHelpSyn ,
HelpDescription : pathUserHelpDesc ,
2015-03-20 16:59:48 +00:00
}
}
2018-08-16 10:38:13 +00:00
func ( b * backend ) pathCredsRead ( ctx context . Context , req * logical . Request , d * framework . FieldData ) ( * logical . Response , error ) {
roleName := d . Get ( "name" ) . ( string )
2015-03-20 16:59:48 +00:00
// Read the policy
2018-08-16 10:38:13 +00:00
role , err := b . roleRead ( ctx , req . Storage , roleName , true )
2015-03-20 16:59:48 +00:00
if err != nil {
2018-04-05 15:49:21 +00:00
return nil , errwrap . Wrapf ( "error retrieving role: {{err}}" , err )
2015-03-20 16:59:48 +00:00
}
2018-08-16 10:38:13 +00:00
if role == nil {
2015-03-20 16:59:48 +00:00
return logical . ErrorResponse ( fmt . Sprintf (
2018-08-16 10:38:13 +00:00
"Role '%s' not found" , roleName ) ) , nil
}
ttl := int64 ( d . Get ( "ttl" ) . ( int ) )
roleArn := d . Get ( "role_arn" ) . ( string )
var credentialType string
switch {
case len ( role . CredentialTypes ) == 1 :
credentialType = role . CredentialTypes [ 0 ]
// There is only one way for the CredentialTypes to contain more than one entry, and that's an upgrade path
// where it contains iamUserCred and federationTokenCred
// This ambiguity can be resolved based on req.Path, so resolve it assuming CredentialTypes only has those values
case len ( role . CredentialTypes ) > 1 :
if strings . HasPrefix ( req . Path , "creds" ) {
credentialType = iamUserCred
} else {
credentialType = federationTokenCred
}
// sanity check on the assumption above
if ! strutil . StrListContains ( role . CredentialTypes , credentialType ) {
return logical . ErrorResponse ( fmt . Sprintf ( "requested credential type %q not in allowed credential types %#v" , credentialType , role . CredentialTypes ) ) , nil
}
2015-03-21 10:18:46 +00:00
}
2018-08-16 10:38:13 +00:00
// creds requested through the sts path shouldn't be allowed to get iamUserCred type creds
// when the role is created from legacy data because they might have more privileges in AWS.
// See https://github.com/hashicorp/vault/issues/4229#issuecomment-380316788 for details.
if role . ProhibitFlexibleCredPath {
if credentialType == iamUserCred && strings . HasPrefix ( req . Path , "sts" ) {
return logical . ErrorResponse ( fmt . Sprintf ( "attempted to retrieve %s credentials through the sts path; this is not allowed for legacy roles" , iamUserCred ) ) , nil
}
if credentialType != iamUserCred && strings . HasPrefix ( req . Path , "creds" ) {
return logical . ErrorResponse ( fmt . Sprintf ( "attempted to retrieve %s credentials through the creds path; this is not allowed for legacy roles" , credentialType ) ) , nil
}
}
switch credentialType {
case iamUserCred :
return b . secretAccessKeysCreate ( ctx , req . Storage , req . DisplayName , roleName , role )
case assumedRoleCred :
switch {
case roleArn == "" :
if len ( role . RoleArns ) != 1 {
return logical . ErrorResponse ( "did not supply a role_arn parameter and unable to determine one" ) , nil
}
roleArn = role . RoleArns [ 0 ]
case ! strutil . StrListContains ( role . RoleArns , roleArn ) :
return logical . ErrorResponse ( fmt . Sprintf ( "role_arn %q not in allowed role arns for Vault role %q" , roleArn , roleName ) ) , nil
}
return b . assumeRole ( ctx , req . Storage , req . DisplayName , roleName , roleArn , role . PolicyDocument , ttl )
case federationTokenCred :
return b . secretTokenCreate ( ctx , req . Storage , req . DisplayName , roleName , role . PolicyDocument , ttl )
default :
return logical . ErrorResponse ( fmt . Sprintf ( "unknown credential_type: %q" , credentialType ) ) , nil
}
2015-03-20 16:59:48 +00:00
}
2015-03-21 10:18:46 +00:00
2018-01-19 06:44:44 +00:00
func pathUserRollback ( ctx context . Context , req * logical . Request , _kind string , data interface { } ) error {
2015-03-21 10:18:46 +00:00
var entry walUser
if err := mapstructure . Decode ( data , & entry ) ; err != nil {
return err
}
username := entry . UserName
// Get the client
2018-01-19 06:44:44 +00:00
client , err := clientIAM ( ctx , req . Storage )
2015-03-21 10:18:46 +00:00
if err != nil {
return err
}
// Get information about this user
2015-08-06 16:37:08 +00:00
groupsResp , err := client . ListGroupsForUser ( & iam . ListGroupsForUserInput {
2015-03-21 10:18:46 +00:00
UserName : aws . String ( username ) ,
2015-08-06 16:37:08 +00:00
MaxItems : aws . Int64 ( 1000 ) ,
2015-03-21 10:18:46 +00:00
} )
if err != nil {
return err
}
groups := groupsResp . Groups
2015-12-30 18:05:54 +00:00
// Inline (user) policies
2015-08-06 16:37:08 +00:00
policiesResp , err := client . ListUserPolicies ( & iam . ListUserPoliciesInput {
2015-03-21 10:18:46 +00:00
UserName : aws . String ( username ) ,
2015-08-06 16:37:08 +00:00
MaxItems : aws . Int64 ( 1000 ) ,
2015-03-21 10:18:46 +00:00
} )
if err != nil {
return err
}
policies := policiesResp . PolicyNames
2015-12-30 18:05:54 +00:00
// Attached managed policies
manPoliciesResp , err := client . ListAttachedUserPolicies ( & iam . ListAttachedUserPoliciesInput {
UserName : aws . String ( username ) ,
MaxItems : aws . Int64 ( 1000 ) ,
} )
if err != nil {
return err
}
manPolicies := manPoliciesResp . AttachedPolicies
2015-08-06 16:37:08 +00:00
keysResp , err := client . ListAccessKeys ( & iam . ListAccessKeysInput {
2015-03-21 10:18:46 +00:00
UserName : aws . String ( username ) ,
2015-08-06 16:37:08 +00:00
MaxItems : aws . Int64 ( 1000 ) ,
2015-03-21 10:18:46 +00:00
} )
if err != nil {
return err
}
keys := keysResp . AccessKeyMetadata
// Revoke all keys
for _ , k := range keys {
2015-08-06 16:37:08 +00:00
_ , err = client . DeleteAccessKey ( & iam . DeleteAccessKeyInput {
2015-08-19 01:12:51 +00:00
AccessKeyId : k . AccessKeyId ,
2015-03-21 10:18:46 +00:00
UserName : aws . String ( username ) ,
} )
if err != nil {
return err
}
}
2015-12-30 18:05:54 +00:00
// Detach managed policies
for _ , p := range manPolicies {
_ , err = client . DetachUserPolicy ( & iam . DetachUserPolicyInput {
UserName : aws . String ( username ) ,
PolicyArn : p . PolicyArn ,
} )
if err != nil {
return err
}
}
// Delete any inline (user) policies
2015-03-21 10:18:46 +00:00
for _ , p := range policies {
2015-08-06 16:37:08 +00:00
_ , err = client . DeleteUserPolicy ( & iam . DeleteUserPolicyInput {
2015-03-21 10:18:46 +00:00
UserName : aws . String ( username ) ,
2015-08-06 16:37:08 +00:00
PolicyName : p ,
2015-03-21 10:18:46 +00:00
} )
if err != nil {
return err
}
}
// Remove the user from all their groups
for _ , g := range groups {
2015-08-06 16:37:08 +00:00
_ , err = client . RemoveUserFromGroup ( & iam . RemoveUserFromGroupInput {
2015-03-21 10:18:46 +00:00
GroupName : g . GroupName ,
UserName : aws . String ( username ) ,
} )
if err != nil {
return err
}
}
// Delete the user
2015-08-06 16:37:08 +00:00
_ , err = client . DeleteUser ( & iam . DeleteUserInput {
2015-03-21 10:18:46 +00:00
UserName : aws . String ( username ) ,
} )
if err != nil {
return err
}
return nil
}
type walUser struct {
UserName string
}
2015-04-04 04:10:54 +00:00
const pathUserHelpSyn = `
2018-08-16 10:38:13 +00:00
Generate AWS credentials from a specific Vault role .
2015-04-04 04:10:54 +00:00
`
const pathUserHelpDesc = `
2018-08-16 10:38:13 +00:00
This path will generate new , never before used AWS credentials for
2015-04-04 04:10:54 +00:00
accessing AWS . The IAM policy used to back this key pair will be
the "name" parameter . For example , if this backend is mounted at "aws" ,
2015-04-27 21:20:28 +00:00
then "aws/creds/deploy" would generate access keys for the "deploy" role .
2015-04-04 04:10:54 +00:00
The access keys will have a lease associated with them . The access keys
2018-08-16 10:38:13 +00:00
can be revoked by using the lease ID when using the iam_user credential type .
When using AWS STS credential types ( assumed_role or federation_token ) ,
revoking the lease does not revoke the access keys .
2015-04-04 04:10:54 +00:00
`