2017-04-24 19:15:50 +00:00
package awsauth
2016-05-03 16:14:07 +00:00
import (
"fmt"
"strings"
"time"
"github.com/fatih/structs"
"github.com/hashicorp/go-uuid"
"github.com/hashicorp/vault/helper/policyutil"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/logical/framework"
)
func pathRole ( b * backend ) * framework . Path {
return & framework . Path {
2016-05-13 18:31:13 +00:00
Pattern : "role/" + framework . GenericNameRegex ( "role" ) ,
2016-05-03 16:14:07 +00:00
Fields : map [ string ] * framework . FieldSchema {
2016-08-09 21:29:45 +00:00
"role" : {
2016-05-03 16:14:07 +00:00
Type : framework . TypeString ,
Description : "Name of the role." ,
} ,
2017-04-24 19:15:50 +00:00
"auth_type" : {
Type : framework . TypeString ,
Description : ` The auth_type permitted to authenticate to this role . Must be one of
iam or ec2 and cannot be changed after role creation . ` ,
} ,
2016-08-09 21:29:45 +00:00
"bound_ami_id" : {
2016-05-12 11:19:29 +00:00
Type : framework . TypeString ,
Description : ` If set , defines a constraint on the EC2 instances that they should be
2017-10-23 14:53:09 +00:00
using the AMI ID specified by this parameter . This is only applicable when auth_type is ec2
or inferred_entity_type is ec2_instance . ` ,
2016-05-03 16:14:07 +00:00
} ,
2016-08-09 21:29:45 +00:00
"bound_account_id" : {
2016-06-14 15:58:19 +00:00
Type : framework . TypeString ,
Description : ` If set , defines a constraint on the EC2 instances that the account ID
2017-10-23 14:53:09 +00:00
in its identity document to match the one specified by this parameter . This is only
applicable when auth_type is ec2 or inferred_entity_type is ec2_instance . ` ,
2017-04-24 19:15:50 +00:00
} ,
"bound_iam_principal_arn" : {
Type : framework . TypeString ,
Description : ` ARN of the IAM principal to bind to this role . Only applicable when
auth_type is iam . ` ,
2016-09-23 16:47:35 +00:00
} ,
2017-02-24 19:19:10 +00:00
"bound_region" : {
2017-04-24 19:15:50 +00:00
Type : framework . TypeString ,
Description : ` If set , defines a constraint on the EC2 instances that the region in
2017-10-23 14:53:09 +00:00
its identity document to match the one specified by this parameter . This is only
applicable when auth_type is ec2 . ` ,
2017-02-24 19:19:10 +00:00
} ,
2016-09-23 16:47:35 +00:00
"bound_iam_role_arn" : {
Type : framework . TypeString ,
2016-09-28 22:30:32 +00:00
Description : ` If set , defines a constraint on the authenticating EC2 instance
that it must match the IAM role ARN specified by this parameter .
The value is prefix - matched ( as though it were a glob ending in
'*' ) . The configured IAM user or EC2 instance role must be allowed
2017-10-23 14:53:09 +00:00
to execute the ' iam : GetInstanceProfile ' action if this is specified . This is
only applicable when auth_type is ec2 or inferred_entity_type is
ec2_instance . ` ,
2016-06-14 15:58:19 +00:00
} ,
2016-09-23 15:22:23 +00:00
"bound_iam_instance_profile_arn" : {
Type : framework . TypeString ,
2016-09-28 22:30:32 +00:00
Description : ` If set , defines a constraint on the EC2 instances to be associated
with an IAM instance profile ARN which has a prefix that matches
the value specified by this parameter . The value is prefix - matched
2017-10-23 14:53:09 +00:00
( as though it were a glob ending in '*' ) . This is only applicable when
auth_type is ec2 or inferred_entity_type is ec2_instance . ` ,
2017-06-07 14:27:11 +00:00
} ,
"resolve_aws_unique_ids" : {
Type : framework . TypeBool ,
Default : true ,
Description : ` If set , resolve all AWS IAM ARNs into AWS ' s internal unique IDs .
When an IAM entity ( e . g . , user , role , or instance profile ) is deleted , then all references
to it within the role will be invalidated , which prevents a new IAM entity from being created
with the same name and matching the role ' s IAM binds . Once set , this cannot be unset . ` ,
2017-04-24 19:15:50 +00:00
} ,
"inferred_entity_type" : {
Type : framework . TypeString ,
Description : ` When auth_type is iam , the
AWS entity type to infer from the authenticated principal . The only supported
value is ec2_instance , which will extract the EC2 instance ID from the
authenticated role and apply the following restrictions specific to EC2
instances : bound_ami_id , bound_account_id , bound_iam_role_arn ,
bound_iam_instance_profile_arn , bound_vpc_id , bound_subnet_id . The configured
EC2 client must be able to find the inferred instance ID in the results , and the
instance must be running . If unable to determine the EC2 instance ID or unable
to find the EC2 instance ID among running instances , then authentication will
fail . ` ,
} ,
"inferred_aws_region" : {
Type : framework . TypeString ,
Description : ` When auth_type is iam and
inferred_entity_type is set , the region to assume the inferred entity exists in . ` ,
2017-02-24 19:19:10 +00:00
} ,
"bound_vpc_id" : {
Type : framework . TypeString ,
Description : `
If set , defines a constraint on the EC2 instance to be associated with the VPC
2017-10-23 14:53:09 +00:00
ID that matches the value specified by this parameter . This is only applicable
when auth_type is ec2 or inferred_entity_type is ec2_instance . ` ,
2017-02-24 19:19:10 +00:00
} ,
"bound_subnet_id" : {
Type : framework . TypeString ,
Description : `
If set , defines a constraint on the EC2 instance to be associated with the
2017-10-23 14:53:09 +00:00
subnet ID that matches the value specified by this parameter . This is only
applicable when auth_type is ec2 or inferred_entity_type is ec2_instance . ` ,
2016-06-14 14:49:36 +00:00
} ,
2016-08-09 21:29:45 +00:00
"role_tag" : {
2017-04-24 19:15:50 +00:00
Type : framework . TypeString ,
Default : "" ,
Description : ` If set , enables the role tags for this role . The value set for this
field should be the ' key ' of the tag on the EC2 instance . The ' value '
of the tag should be generated using ' role / < role > / tag ' endpoint .
Defaults to an empty string , meaning that role tags are disabled . This
is only allowed if auth_type is ec2 . ` ,
2016-05-03 16:14:07 +00:00
} ,
2017-02-02 18:28:01 +00:00
"period" : & framework . FieldSchema {
Type : framework . TypeDurationSecond ,
Default : 0 ,
Description : `
2017-10-23 14:53:09 +00:00
If set , indicates that the token generated using this role should never expire .
The token should be renewed within the duration specified by this value . At
each renewal , the token ' s TTL will be set to the value of this parameter . ` ,
2017-02-02 18:28:01 +00:00
} ,
2016-08-09 21:29:45 +00:00
"ttl" : {
Type : framework . TypeDurationSecond ,
Default : 0 ,
Description : ` Duration in seconds after which the issued token should expire . Defaults
to 0 , in which case the value will fallback to the system / mount defaults . ` ,
} ,
"max_ttl" : {
2016-05-03 16:14:07 +00:00
Type : framework . TypeDurationSecond ,
Default : 0 ,
2016-05-12 11:19:29 +00:00
Description : "The maximum allowed lifetime of tokens issued using this role." ,
2016-05-03 16:14:07 +00:00
} ,
2016-08-09 21:29:45 +00:00
"policies" : {
2017-09-13 15:36:52 +00:00
Type : framework . TypeCommaStringSlice ,
2016-05-03 16:14:07 +00:00
Default : "default" ,
2016-05-12 11:19:29 +00:00
Description : "Policies to be set on tokens issued using this role." ,
2016-05-03 16:14:07 +00:00
} ,
2016-08-09 21:29:45 +00:00
"allow_instance_migration" : {
2017-04-24 19:15:50 +00:00
Type : framework . TypeBool ,
Default : false ,
Description : ` If set , allows migration of the underlying instance where the client
resides . This keys off of pendingTime in the metadata document , so
essentially , this disables the client nonce check whenever the
instance is migrated to a new host and pendingTime is newer than the
previously - remembered time . Use with caution . This is only checked when
auth_type is ec2 . ` ,
2016-05-03 16:14:07 +00:00
} ,
2016-08-09 21:29:45 +00:00
"disallow_reauthentication" : {
2017-10-23 14:53:09 +00:00
Type : framework . TypeBool ,
Default : false ,
Description : ` If set , only allows a single token to be granted per
instance ID . In order to perform a fresh login , the entry in whitelist
for the instance ID needs to be cleared using
' auth / aws - ec2 / identity - whitelist / < instance_id > ' endpoint . This is only
applicable when auth_type is ec2 . ` ,
2016-05-03 16:14:07 +00:00
} ,
} ,
ExistenceCheck : b . pathRoleExistenceCheck ,
Callbacks : map [ logical . Operation ] framework . OperationFunc {
logical . CreateOperation : b . pathRoleCreateUpdate ,
logical . UpdateOperation : b . pathRoleCreateUpdate ,
logical . ReadOperation : b . pathRoleRead ,
logical . DeleteOperation : b . pathRoleDelete ,
} ,
HelpSynopsis : pathRoleSyn ,
HelpDescription : pathRoleDesc ,
}
}
2016-05-05 18:51:09 +00:00
func pathListRole ( b * backend ) * framework . Path {
return & framework . Path {
Pattern : "role/?" ,
Callbacks : map [ logical . Operation ] framework . OperationFunc {
logical . ListOperation : b . pathRoleList ,
} ,
HelpSynopsis : pathListRolesHelpSyn ,
HelpDescription : pathListRolesHelpDesc ,
}
}
2016-05-03 16:14:07 +00:00
func pathListRoles ( b * backend ) * framework . Path {
return & framework . Path {
Pattern : "roles/?" ,
Callbacks : map [ logical . Operation ] framework . OperationFunc {
logical . ListOperation : b . pathRoleList ,
} ,
HelpSynopsis : pathListRolesHelpSyn ,
HelpDescription : pathListRolesHelpDesc ,
}
}
// Establishes dichotomy of request operation between CreateOperation and UpdateOperation.
// Returning 'true' forces an UpdateOperation, CreateOperation otherwise.
func ( b * backend ) pathRoleExistenceCheck ( req * logical . Request , data * framework . FieldData ) ( bool , error ) {
2016-05-18 00:39:24 +00:00
entry , err := b . lockedAWSRole ( req . Storage , strings . ToLower ( data . Get ( "role" ) . ( string ) ) )
2016-05-03 16:14:07 +00:00
if err != nil {
return false , err
}
return entry != nil , nil
}
2016-09-23 15:22:23 +00:00
// lockedAWSRole returns the properties set on the given role. This method
// acquires the read lock before reading the role from the storage.
func ( b * backend ) lockedAWSRole ( s logical . Storage , roleName string ) ( * awsRoleEntry , error ) {
if roleName == "" {
return nil , fmt . Errorf ( "missing role name" )
}
2016-05-05 18:51:09 +00:00
b . roleMutex . RLock ( )
2017-04-24 19:15:50 +00:00
roleEntry , err := b . nonLockedAWSRole ( s , roleName )
// we manually unlock rather than defer the unlock because we might need to grab
// a read/write lock in the upgrade path
b . roleMutex . RUnlock ( )
if err != nil {
return nil , err
}
if roleEntry == nil {
return nil , nil
}
2017-06-07 14:27:11 +00:00
needUpgrade , err := b . upgradeRoleEntry ( s , roleEntry )
2017-04-24 19:15:50 +00:00
if err != nil {
return nil , fmt . Errorf ( "error upgrading roleEntry: %v" , err )
}
if needUpgrade {
b . roleMutex . Lock ( )
defer b . roleMutex . Unlock ( )
// Now that we have a R/W lock, we need to re-read the role entry in case it was
// written to between releasing the read lock and acquiring the write lock
roleEntry , err = b . nonLockedAWSRole ( s , roleName )
if err != nil {
return nil , err
}
// somebody deleted the role, so no use in putting it back
if roleEntry == nil {
return nil , nil
}
// now re-check to see if we need to upgrade
2017-06-07 14:27:11 +00:00
if needUpgrade , err = b . upgradeRoleEntry ( s , roleEntry ) ; err != nil {
2017-04-24 19:15:50 +00:00
return nil , fmt . Errorf ( "error upgrading roleEntry: %v" , err )
}
if needUpgrade {
if err = b . nonLockedSetAWSRole ( s , roleName , roleEntry ) ; err != nil {
return nil , fmt . Errorf ( "error saving upgraded roleEntry: %v" , err )
}
}
}
return roleEntry , nil
2016-09-23 15:22:23 +00:00
}
// lockedSetAWSRole creates or updates a role in the storage. This method
// acquires the write lock before creating or updating the role at the storage.
func ( b * backend ) lockedSetAWSRole ( s logical . Storage , roleName string , roleEntry * awsRoleEntry ) error {
if roleName == "" {
return fmt . Errorf ( "missing role name" )
}
if roleEntry == nil {
return fmt . Errorf ( "nil role entry" )
}
b . roleMutex . Lock ( )
defer b . roleMutex . Unlock ( )
return b . nonLockedSetAWSRole ( s , roleName , roleEntry )
}
// nonLockedSetAWSRole creates or updates a role in the storage. This method
// does not acquire the write lock before reading the role from the storage. If
// locking is desired, use lockedSetAWSRole instead.
func ( b * backend ) nonLockedSetAWSRole ( s logical . Storage , roleName string ,
roleEntry * awsRoleEntry ) error {
if roleName == "" {
return fmt . Errorf ( "missing role name" )
}
if roleEntry == nil {
return fmt . Errorf ( "nil role entry" )
}
entry , err := logical . StorageEntryJSON ( "role/" + strings . ToLower ( roleName ) , roleEntry )
if err != nil {
return err
}
if err := s . Put ( entry ) ; err != nil {
return err
}
return nil
2016-05-05 18:51:09 +00:00
}
2017-04-24 19:15:50 +00:00
// If needed, updates the role entry and returns a bool indicating if it was updated
// (and thus needs to be persisted)
2017-06-07 14:27:11 +00:00
func ( b * backend ) upgradeRoleEntry ( s logical . Storage , roleEntry * awsRoleEntry ) ( bool , error ) {
2017-04-24 19:15:50 +00:00
if roleEntry == nil {
return false , fmt . Errorf ( "received nil roleEntry" )
}
var upgraded bool
// Check if the value held by role ARN field is actually an instance profile ARN
if roleEntry . BoundIamRoleARN != "" && strings . Contains ( roleEntry . BoundIamRoleARN , ":instance-profile/" ) {
// If yes, move it to the correct field
roleEntry . BoundIamInstanceProfileARN = roleEntry . BoundIamRoleARN
// Reset the old field
roleEntry . BoundIamRoleARN = ""
upgraded = true
}
// Check if there was no pre-existing AuthType set (from older versions)
if roleEntry . AuthType == "" {
// then default to the original behavior of ec2
roleEntry . AuthType = ec2AuthType
upgraded = true
}
2017-06-07 14:27:11 +00:00
if roleEntry . AuthType == iamAuthType &&
roleEntry . ResolveAWSUniqueIDs &&
roleEntry . BoundIamPrincipalARN != "" &&
2017-08-30 21:51:48 +00:00
roleEntry . BoundIamPrincipalID == "" &&
! strings . HasSuffix ( roleEntry . BoundIamPrincipalARN , "*" ) {
2017-06-07 14:27:11 +00:00
principalId , err := b . resolveArnToUniqueIDFunc ( s , roleEntry . BoundIamPrincipalARN )
if err != nil {
return false , err
}
roleEntry . BoundIamPrincipalID = principalId
upgraded = true
}
2017-04-24 19:15:50 +00:00
return upgraded , nil
}
2016-09-23 15:22:23 +00:00
// nonLockedAWSRole returns the properties set on the given role. This method
// does not acquire the read lock before reading the role from the storage. If
// locking is desired, use lockedAWSRole instead.
2017-04-24 19:15:50 +00:00
// This method also does NOT check to see if a role upgrade is required. It is
// the responsibility of the caller to check if a role upgrade is required and,
// if so, to upgrade the role
2016-09-23 15:22:23 +00:00
func ( b * backend ) nonLockedAWSRole ( s logical . Storage , roleName string ) ( * awsRoleEntry , error ) {
if roleName == "" {
return nil , fmt . Errorf ( "missing role name" )
}
entry , err := s . Get ( "role/" + strings . ToLower ( roleName ) )
2016-05-03 16:14:07 +00:00
if err != nil {
return nil , err
}
if entry == nil {
return nil , nil
}
var result awsRoleEntry
if err := entry . DecodeJSON ( & result ) ; err != nil {
return nil , err
}
2016-09-23 15:22:23 +00:00
2016-05-03 16:14:07 +00:00
return & result , nil
}
// pathRoleDelete is used to delete the information registered for a given AMI ID.
func ( b * backend ) pathRoleDelete (
req * logical . Request , data * framework . FieldData ) ( * logical . Response , error ) {
2016-05-13 18:31:13 +00:00
roleName := data . Get ( "role" ) . ( string )
2016-05-03 16:14:07 +00:00
if roleName == "" {
2016-05-13 18:31:13 +00:00
return logical . ErrorResponse ( "missing role" ) , nil
2016-05-03 16:14:07 +00:00
}
2016-05-05 18:51:09 +00:00
b . roleMutex . Lock ( )
defer b . roleMutex . Unlock ( )
2016-05-03 16:14:07 +00:00
return nil , req . Storage . Delete ( "role/" + strings . ToLower ( roleName ) )
}
// pathRoleList is used to list all the AMI IDs registered with Vault.
func ( b * backend ) pathRoleList (
req * logical . Request , data * framework . FieldData ) ( * logical . Response , error ) {
2016-05-05 18:51:09 +00:00
b . roleMutex . RLock ( )
defer b . roleMutex . RUnlock ( )
2016-05-03 16:14:07 +00:00
roles , err := req . Storage . List ( "role/" )
if err != nil {
return nil , err
}
return logical . ListResponse ( roles ) , nil
}
// pathRoleRead is used to view the information registered for a given AMI ID.
func ( b * backend ) pathRoleRead (
req * logical . Request , data * framework . FieldData ) ( * logical . Response , error ) {
2016-05-18 00:39:24 +00:00
roleEntry , err := b . lockedAWSRole ( req . Storage , strings . ToLower ( data . Get ( "role" ) . ( string ) ) )
2016-05-03 16:14:07 +00:00
if err != nil {
return nil , err
}
if roleEntry == nil {
return nil , nil
}
// Prepare the map of all the entries in the roleEntry.
respData := structs . New ( roleEntry ) . Map ( )
// HMAC key belonging to the role should NOT be exported.
delete ( respData , "hmac_key" )
2017-02-15 15:57:57 +00:00
// Display all the durations in seconds
2016-08-09 21:29:45 +00:00
respData [ "ttl" ] = roleEntry . TTL / time . Second
2016-05-03 16:14:07 +00:00
respData [ "max_ttl" ] = roleEntry . MaxTTL / time . Second
2017-02-15 15:57:57 +00:00
respData [ "period" ] = roleEntry . Period / time . Second
2016-05-03 16:14:07 +00:00
2016-09-23 16:47:35 +00:00
return & logical . Response {
Data : respData ,
} , nil
2016-05-03 16:14:07 +00:00
}
// pathRoleCreateUpdate is used to associate Vault policies to a given AMI ID.
func ( b * backend ) pathRoleCreateUpdate (
req * logical . Request , data * framework . FieldData ) ( * logical . Response , error ) {
2016-05-13 18:31:13 +00:00
roleName := strings . ToLower ( data . Get ( "role" ) . ( string ) )
2016-05-03 16:14:07 +00:00
if roleName == "" {
2016-05-13 18:31:13 +00:00
return logical . ErrorResponse ( "missing role" ) , nil
2016-05-03 16:14:07 +00:00
}
2016-05-05 18:51:09 +00:00
b . roleMutex . Lock ( )
defer b . roleMutex . Unlock ( )
2016-05-18 00:39:24 +00:00
roleEntry , err := b . nonLockedAWSRole ( req . Storage , roleName )
2016-05-03 16:14:07 +00:00
if err != nil {
return nil , err
}
if roleEntry == nil {
roleEntry = & awsRoleEntry { }
2017-04-24 19:15:50 +00:00
} else {
2017-06-07 14:27:11 +00:00
needUpdate , err := b . upgradeRoleEntry ( req . Storage , roleEntry )
2017-04-24 19:15:50 +00:00
if err != nil {
return logical . ErrorResponse ( fmt . Sprintf ( "failed to update roleEntry: %v" , err ) ) , nil
}
if needUpdate {
err = b . nonLockedSetAWSRole ( req . Storage , roleName , roleEntry )
if err != nil {
return logical . ErrorResponse ( fmt . Sprintf ( "failed to save upgraded roleEntry: %v" , err ) ) , nil
}
}
2016-05-03 16:14:07 +00:00
}
2016-09-23 16:47:35 +00:00
// Fetch and set the bound parameters. There can't be default values
// for these.
2016-06-14 15:58:19 +00:00
if boundAmiIDRaw , ok := data . GetOk ( "bound_ami_id" ) ; ok {
roleEntry . BoundAmiID = boundAmiIDRaw . ( string )
}
if boundAccountIDRaw , ok := data . GetOk ( "bound_account_id" ) ; ok {
roleEntry . BoundAccountID = boundAccountIDRaw . ( string )
2016-05-03 16:14:07 +00:00
}
2017-02-24 19:19:10 +00:00
if boundRegionRaw , ok := data . GetOk ( "bound_region" ) ; ok {
roleEntry . BoundRegion = boundRegionRaw . ( string )
}
if boundVpcIDRaw , ok := data . GetOk ( "bound_vpc_id" ) ; ok {
roleEntry . BoundVpcID = boundVpcIDRaw . ( string )
}
if boundSubnetIDRaw , ok := data . GetOk ( "bound_subnet_id" ) ; ok {
roleEntry . BoundSubnetID = boundSubnetIDRaw . ( string )
}
2017-06-07 14:27:11 +00:00
if resolveAWSUniqueIDsRaw , ok := data . GetOk ( "resolve_aws_unique_ids" ) ; ok {
switch {
case req . Operation == logical . CreateOperation :
roleEntry . ResolveAWSUniqueIDs = resolveAWSUniqueIDsRaw . ( bool )
case roleEntry . ResolveAWSUniqueIDs && ! resolveAWSUniqueIDsRaw . ( bool ) :
return logical . ErrorResponse ( "changing resolve_aws_unique_ids from true to false is not allowed" ) , nil
default :
roleEntry . ResolveAWSUniqueIDs = resolveAWSUniqueIDsRaw . ( bool )
}
} else if req . Operation == logical . CreateOperation {
roleEntry . ResolveAWSUniqueIDs = data . Get ( "resolve_aws_unique_ids" ) . ( bool )
}
2016-09-23 16:47:35 +00:00
if boundIamRoleARNRaw , ok := data . GetOk ( "bound_iam_role_arn" ) ; ok {
roleEntry . BoundIamRoleARN = boundIamRoleARNRaw . ( string )
}
2016-09-23 15:22:23 +00:00
if boundIamInstanceProfileARNRaw , ok := data . GetOk ( "bound_iam_instance_profile_arn" ) ; ok {
roleEntry . BoundIamInstanceProfileARN = boundIamInstanceProfileARNRaw . ( string )
2016-06-14 18:46:08 +00:00
}
2017-04-24 19:15:50 +00:00
if boundIamPrincipalARNRaw , ok := data . GetOk ( "bound_iam_principal_arn" ) ; ok {
2017-06-07 14:27:11 +00:00
principalARN := boundIamPrincipalARNRaw . ( string )
roleEntry . BoundIamPrincipalARN = principalARN
// Explicitly not checking to see if the user has changed the ARN under us
// This allows the user to sumbit an update with the same ARN to force Vault
// to re-resolve the ARN to the unique ID, in case an entity was deleted and
// recreated
2017-08-30 21:51:48 +00:00
if roleEntry . ResolveAWSUniqueIDs && ! strings . HasSuffix ( roleEntry . BoundIamPrincipalARN , "*" ) {
2017-06-07 14:27:11 +00:00
principalID , err := b . resolveArnToUniqueIDFunc ( req . Storage , principalARN )
if err != nil {
return logical . ErrorResponse ( fmt . Sprintf ( "failed updating the unique ID of ARN %#v: %#v" , principalARN , err ) ) , nil
}
roleEntry . BoundIamPrincipalID = principalID
2017-08-30 21:51:48 +00:00
} else {
// Need to handle the case where we're switching from a non-wildcard principal to a wildcard principal
roleEntry . BoundIamPrincipalID = ""
2017-06-07 14:27:11 +00:00
}
2017-08-30 21:51:48 +00:00
} else if roleEntry . ResolveAWSUniqueIDs && roleEntry . BoundIamPrincipalARN != "" && ! strings . HasSuffix ( roleEntry . BoundIamPrincipalARN , "*" ) {
2017-06-07 14:27:11 +00:00
// we're turning on resolution on this role, so ensure we update it
principalID , err := b . resolveArnToUniqueIDFunc ( req . Storage , roleEntry . BoundIamPrincipalARN )
if err != nil {
return logical . ErrorResponse ( fmt . Sprintf ( "unable to resolve ARN %#v to internal ID: %#v" , roleEntry . BoundIamPrincipalARN , err ) ) , nil
}
roleEntry . BoundIamPrincipalID = principalID
2017-04-24 19:15:50 +00:00
}
if inferRoleTypeRaw , ok := data . GetOk ( "inferred_entity_type" ) ; ok {
roleEntry . InferredEntityType = inferRoleTypeRaw . ( string )
}
if inferredAWSRegionRaw , ok := data . GetOk ( "inferred_aws_region" ) ; ok {
roleEntry . InferredAWSRegion = inferredAWSRegionRaw . ( string )
}
// auth_type is a special case as it's immutable and can't be changed once a role is created
if authTypeRaw , ok := data . GetOk ( "auth_type" ) ; ok {
// roleEntry.AuthType should only be "" when it's a new role; existing roles without an
// auth_type should have already been upgraded to have one before we get here
if roleEntry . AuthType == "" {
switch authTypeRaw . ( string ) {
case ec2AuthType , iamAuthType :
roleEntry . AuthType = authTypeRaw . ( string )
default :
return logical . ErrorResponse ( fmt . Sprintf ( "unrecognized auth_type: %v" , authTypeRaw . ( string ) ) ) , nil
}
} else if authTypeRaw . ( string ) != roleEntry . AuthType {
return logical . ErrorResponse ( "changing auth_type on a role is not allowed" ) , nil
}
} else if req . Operation == logical . CreateOperation {
switch req . MountType {
// maintain backwards compatibility for old aws-ec2 auth types
case "aws-ec2" :
roleEntry . AuthType = ec2AuthType
// but default to iamAuth for new mounts going forward
case "aws" :
roleEntry . AuthType = iamAuthType
default :
roleEntry . AuthType = iamAuthType
}
}
allowEc2Binds := roleEntry . AuthType == ec2AuthType
if roleEntry . InferredEntityType != "" {
switch {
case roleEntry . AuthType != iamAuthType :
return logical . ErrorResponse ( "specified inferred_entity_type but didn't allow iam auth_type" ) , nil
case roleEntry . InferredEntityType != ec2EntityType :
return logical . ErrorResponse ( fmt . Sprintf ( "specified invalid inferred_entity_type: %s" , roleEntry . InferredEntityType ) ) , nil
case roleEntry . InferredAWSRegion == "" :
return logical . ErrorResponse ( "specified inferred_entity_type but not inferred_aws_region" ) , nil
}
allowEc2Binds = true
} else if roleEntry . InferredAWSRegion != "" {
return logical . ErrorResponse ( "specified inferred_aws_region but not inferred_entity_type" ) , nil
}
numBinds := 0
if roleEntry . BoundAccountID != "" {
if ! allowEc2Binds {
return logical . ErrorResponse ( fmt . Sprintf ( "specified bound_account_id but not allowing ec2 auth_type or inferring %s" , ec2EntityType ) ) , nil
}
numBinds ++
}
if roleEntry . BoundRegion != "" {
if roleEntry . AuthType != ec2AuthType {
return logical . ErrorResponse ( "specified bound_region but not allowing ec2 auth_type" ) , nil
}
numBinds ++
}
if roleEntry . BoundAmiID != "" {
if ! allowEc2Binds {
return logical . ErrorResponse ( fmt . Sprintf ( "specified bound_ami_id but not allowing ec2 auth_type or inferring %s" , ec2EntityType ) ) , nil
}
numBinds ++
}
if roleEntry . BoundIamInstanceProfileARN != "" {
if ! allowEc2Binds {
return logical . ErrorResponse ( fmt . Sprintf ( "specified bound_iam_instance_profile_arn but not allowing ec2 auth_type or inferring %s" , ec2EntityType ) ) , nil
}
numBinds ++
}
if roleEntry . BoundIamRoleARN != "" {
if ! allowEc2Binds {
return logical . ErrorResponse ( fmt . Sprintf ( "specified bound_iam_role_arn but not allowing ec2 auth_type or inferring %s" , ec2EntityType ) ) , nil
}
numBinds ++
}
if roleEntry . BoundIamPrincipalARN != "" {
if roleEntry . AuthType != iamAuthType {
return logical . ErrorResponse ( "specified bound_iam_principal_arn but not allowing iam auth_type" ) , nil
}
numBinds ++
}
if roleEntry . BoundVpcID != "" {
if ! allowEc2Binds {
return logical . ErrorResponse ( fmt . Sprintf ( "specified bound_vpc_id but not allowing ec2 auth_type or inferring %s" , ec2EntityType ) ) , nil
}
numBinds ++
}
if roleEntry . BoundSubnetID != "" {
if ! allowEc2Binds {
return logical . ErrorResponse ( fmt . Sprintf ( "specified bound_subnet_id but not allowing ec2 auth_type or inferring %s" , ec2EntityType ) ) , nil
}
numBinds ++
}
if numBinds == 0 {
2016-06-14 15:58:19 +00:00
return logical . ErrorResponse ( "at least be one bound parameter should be specified on the role" ) , nil
2016-05-03 16:14:07 +00:00
}
2017-09-13 15:36:52 +00:00
policiesRaw , ok := data . GetOk ( "policies" )
2016-05-03 16:14:07 +00:00
if ok {
2017-09-13 15:36:52 +00:00
roleEntry . Policies = policyutil . ParsePolicies ( policiesRaw )
2016-05-03 16:14:07 +00:00
} else if req . Operation == logical . CreateOperation {
2017-09-13 15:36:52 +00:00
roleEntry . Policies = [ ] string { }
2016-05-03 16:14:07 +00:00
}
disallowReauthenticationBool , ok := data . GetOk ( "disallow_reauthentication" )
if ok {
2017-04-24 19:15:50 +00:00
if roleEntry . AuthType != ec2AuthType {
return logical . ErrorResponse ( "specified disallow_reauthentication when not using ec2 auth type" ) , nil
}
2016-05-03 16:14:07 +00:00
roleEntry . DisallowReauthentication = disallowReauthenticationBool . ( bool )
2017-04-24 19:15:50 +00:00
} else if req . Operation == logical . CreateOperation && roleEntry . AuthType == ec2AuthType {
2016-05-03 16:14:07 +00:00
roleEntry . DisallowReauthentication = data . Get ( "disallow_reauthentication" ) . ( bool )
}
allowInstanceMigrationBool , ok := data . GetOk ( "allow_instance_migration" )
if ok {
2017-04-24 19:15:50 +00:00
if roleEntry . AuthType != ec2AuthType {
return logical . ErrorResponse ( "specified allow_instance_migration when not using ec2 auth type" ) , nil
}
2016-05-03 16:14:07 +00:00
roleEntry . AllowInstanceMigration = allowInstanceMigrationBool . ( bool )
2017-04-24 19:15:50 +00:00
} else if req . Operation == logical . CreateOperation && roleEntry . AuthType == ec2AuthType {
2016-05-03 16:14:07 +00:00
roleEntry . AllowInstanceMigration = data . Get ( "allow_instance_migration" ) . ( bool )
}
2016-05-05 19:32:14 +00:00
var resp logical . Response
2016-08-09 21:29:45 +00:00
ttlRaw , ok := data . GetOk ( "ttl" )
if ok {
ttl := time . Duration ( ttlRaw . ( int ) ) * time . Second
defaultLeaseTTL := b . System ( ) . DefaultLeaseTTL ( )
if ttl > defaultLeaseTTL {
resp . AddWarning ( fmt . Sprintf ( "Given ttl of %d seconds greater than current mount/system default of %d seconds; ttl will be capped at login time" , ttl / time . Second , defaultLeaseTTL / time . Second ) )
}
roleEntry . TTL = ttl
} else if req . Operation == logical . CreateOperation {
roleEntry . TTL = time . Duration ( data . Get ( "ttl" ) . ( int ) ) * time . Second
}
2016-05-03 16:14:07 +00:00
maxTTLInt , ok := data . GetOk ( "max_ttl" )
if ok {
maxTTL := time . Duration ( maxTTLInt . ( int ) ) * time . Second
systemMaxTTL := b . System ( ) . MaxLeaseTTL ( )
if maxTTL > systemMaxTTL {
2016-08-09 21:29:45 +00:00
resp . AddWarning ( fmt . Sprintf ( "Given max_ttl of %d seconds greater than current mount/system default of %d seconds; max_ttl will be capped at login time" , maxTTL / time . Second , systemMaxTTL / time . Second ) )
2016-05-03 16:14:07 +00:00
}
if maxTTL < time . Duration ( 0 ) {
return logical . ErrorResponse ( "max_ttl cannot be negative" ) , nil
}
roleEntry . MaxTTL = maxTTL
} else if req . Operation == logical . CreateOperation {
roleEntry . MaxTTL = time . Duration ( data . Get ( "max_ttl" ) . ( int ) ) * time . Second
}
2016-08-09 21:45:42 +00:00
if roleEntry . MaxTTL != 0 && roleEntry . MaxTTL < roleEntry . TTL {
2016-08-09 21:29:45 +00:00
return logical . ErrorResponse ( "ttl should be shorter than max_ttl" ) , nil
}
2017-02-02 18:28:01 +00:00
periodRaw , ok := data . GetOk ( "period" )
if ok {
roleEntry . Period = time . Second * time . Duration ( periodRaw . ( int ) )
} else if req . Operation == logical . CreateOperation {
roleEntry . Period = time . Second * time . Duration ( data . Get ( "period" ) . ( int ) )
}
if roleEntry . Period > b . System ( ) . MaxLeaseTTL ( ) {
return logical . ErrorResponse ( fmt . Sprintf ( "'period' of '%s' is greater than the backend's maximum lease TTL of '%s'" , roleEntry . Period . String ( ) , b . System ( ) . MaxLeaseTTL ( ) . String ( ) ) ) , nil
}
2016-05-03 16:14:07 +00:00
roleTagStr , ok := data . GetOk ( "role_tag" )
if ok {
2017-04-24 19:15:50 +00:00
if roleEntry . AuthType != ec2AuthType {
return logical . ErrorResponse ( "tried to enable role_tag when not using ec2 auth method" ) , nil
}
2016-05-03 16:14:07 +00:00
roleEntry . RoleTag = roleTagStr . ( string )
// There is a limit of 127 characters on the tag key for AWS EC2 instances.
// Complying to that requirement, do not allow the value of 'key' to be more than that.
if len ( roleEntry . RoleTag ) > 127 {
2016-05-05 18:51:09 +00:00
return logical . ErrorResponse ( "length of role tag exceeds the EC2 key limit of 127 characters" ) , nil
2016-05-03 16:14:07 +00:00
}
2017-04-24 19:15:50 +00:00
} else if req . Operation == logical . CreateOperation && roleEntry . AuthType == ec2AuthType {
2016-05-03 16:14:07 +00:00
roleEntry . RoleTag = data . Get ( "role_tag" ) . ( string )
}
2016-05-05 18:51:09 +00:00
if roleEntry . HMACKey == "" {
roleEntry . HMACKey , err = uuid . GenerateUUID ( )
if err != nil {
return nil , fmt . Errorf ( "failed to generate role HMAC key: %v" , err )
}
2016-05-03 16:14:07 +00:00
}
2016-09-23 15:22:23 +00:00
if err := b . nonLockedSetAWSRole ( req . Storage , roleName , roleEntry ) ; err != nil {
2016-05-03 16:14:07 +00:00
return nil , err
}
2017-06-05 14:52:43 +00:00
if len ( resp . Warnings ) == 0 {
2016-05-05 19:32:14 +00:00
return nil , nil
}
return & resp , nil
2016-05-03 16:14:07 +00:00
}
// Struct to hold the information associated with an AMI ID in Vault.
type awsRoleEntry struct {
2017-04-24 19:15:50 +00:00
AuthType string ` json:"auth_type" structs:"auth_type" mapstructure:"auth_type" `
2016-09-23 15:22:23 +00:00
BoundAmiID string ` json:"bound_ami_id" structs:"bound_ami_id" mapstructure:"bound_ami_id" `
BoundAccountID string ` json:"bound_account_id" structs:"bound_account_id" mapstructure:"bound_account_id" `
2017-04-24 19:15:50 +00:00
BoundIamPrincipalARN string ` json:"bound_iam_principal_arn" structs:"bound_iam_principal_arn" mapstructure:"bound_iam_principal_arn" `
2017-06-07 14:27:11 +00:00
BoundIamPrincipalID string ` json:"bound_iam_principal_id" structs:"bound_iam_principal_id" mapstructure:"bound_iam_principal_id" `
2016-09-23 16:47:35 +00:00
BoundIamRoleARN string ` json:"bound_iam_role_arn" structs:"bound_iam_role_arn" mapstructure:"bound_iam_role_arn" `
2016-09-23 15:22:23 +00:00
BoundIamInstanceProfileARN string ` json:"bound_iam_instance_profile_arn" structs:"bound_iam_instance_profile_arn" mapstructure:"bound_iam_instance_profile_arn" `
2017-04-24 19:15:50 +00:00
BoundRegion string ` json:"bound_region" structs:"bound_region" mapstructure:"bound_region" `
2017-02-24 19:19:10 +00:00
BoundSubnetID string ` json:"bound_subnet_id" structs:"bound_subnet_id" mapstructure:"bound_subnet_id" `
BoundVpcID string ` json:"bound_vpc_id" structs:"bound_vpc_id" mapstructure:"bound_vpc_id" `
2017-04-24 19:15:50 +00:00
InferredEntityType string ` json:"inferred_entity_type" structs:"inferred_entity_type" mapstructure:"inferred_entity_type" `
InferredAWSRegion string ` json:"inferred_aws_region" structs:"inferred_aws_region" mapstructure:"inferred_aws_region" `
2017-06-07 14:27:11 +00:00
ResolveAWSUniqueIDs bool ` json:"resolve_aws_unique_ids" structs:"resolve_aws_unique_ids" mapstructure:"resolve_aws_unique_ids" `
2016-09-23 15:22:23 +00:00
RoleTag string ` json:"role_tag" structs:"role_tag" mapstructure:"role_tag" `
AllowInstanceMigration bool ` json:"allow_instance_migration" structs:"allow_instance_migration" mapstructure:"allow_instance_migration" `
TTL time . Duration ` json:"ttl" structs:"ttl" mapstructure:"ttl" `
MaxTTL time . Duration ` json:"max_ttl" structs:"max_ttl" mapstructure:"max_ttl" `
Policies [ ] string ` json:"policies" structs:"policies" mapstructure:"policies" `
DisallowReauthentication bool ` json:"disallow_reauthentication" structs:"disallow_reauthentication" mapstructure:"disallow_reauthentication" `
HMACKey string ` json:"hmac_key" structs:"hmac_key" mapstructure:"hmac_key" `
2017-02-02 18:28:01 +00:00
Period time . Duration ` json:"period" mapstructure:"period" structs:"period" `
2016-05-03 16:14:07 +00:00
}
const pathRoleSyn = `
Create a role and associate policies to it .
`
const pathRoleDesc = `
A precondition for login is that a role should be created in the backend .
The login endpoint takes in the role name against which the instance
should be validated . After authenticating the instance , the authorization
for the instance to access Vault ' s resources is determined by the policies
that are associated to the role though this endpoint .
When the instances require only a subset of policies on the role , then
' role_tag ' option on the role can be enabled to create a role tag via the
2016-05-13 18:31:13 +00:00
endpoint ' role / < role > / tag ' . This tag then needs to be applied on the
2016-05-03 16:14:07 +00:00
instance before it attempts a login . The policies on the tag should be a
subset of policies that are associated to the role . In order to enable
login using tags , ' role_tag ' option should be set while creating a role .
2017-04-24 19:15:50 +00:00
This only applies when authenticating EC2 instances .
2016-05-03 16:14:07 +00:00
Also , a ' max_ttl ' can be configured in this endpoint that determines the maximum
2016-05-05 18:51:09 +00:00
duration for which a login can be renewed . Note that the ' max_ttl ' has an upper
2016-05-03 16:14:07 +00:00
limit of the ' max_ttl ' value on the backend ' s mount .
`
const pathListRolesHelpSyn = `
Lists all the roles that are registered with Vault .
`
const pathListRolesHelpDesc = `
Roles will be listed by their respective role names .
`