open-vault/builtin/credential/aws/path_config_identity.go
Becca Petrin 3b420b0735
Add helper for aliasmetadata and add to AWS auth (#8783)
* add aliasmetadata sdk helper and add to aws auth

* split into ec2_metadata and iam_metadata fields

* fix tests

* strip pointer

* add test of default metadata

* more test <3

* switch from interface to custom marshallers

* add tests for marshalling

* store nil when selected fields are default

* separate loop into pieces

* separate acc test into multiple

* Update builtin/credential/aws/path_login.go

Co-Authored-By: Jim Kalafut <jkalafut@hashicorp.com>

* changes from feedback

* update aws test

* refactor to also populate auth metadata

* update how jsonification is tested

* only add populated metadata values

* add auth_type to ec2 logins

Co-authored-by: Jim Kalafut <jkalafut@hashicorp.com>
2020-04-27 10:06:07 -07:00

205 lines
6.8 KiB
Go

package awsauth
import (
"context"
"fmt"
"github.com/hashicorp/vault/sdk/framework"
"github.com/hashicorp/vault/sdk/helper/authmetadata"
"github.com/hashicorp/vault/sdk/helper/strutil"
"github.com/hashicorp/vault/sdk/logical"
)
var (
// iamAuthMetadataFields is a list of the default auth metadata
// added to tokens during login. The default alias type used
// by this back-end is the role ID. Subsequently, the default
// fields included are expected to have a low rate of change
// when the role ID is in use.
iamAuthMetadataFields = &authmetadata.Fields{
FieldName: "iam_metadata",
Default: []string{
"account_id",
"auth_type",
},
AvailableToAdd: []string{
"canonical_arn",
"client_arn",
"client_user_id",
"inferred_aws_region",
"inferred_entity_id",
"inferred_entity_type",
},
}
// ec2AuthMetadataFields is a list of the default auth metadata
// added to tokens during login. The default alias type used
// by this back-end is the role ID. Subsequently, the default
// fields included are expected to have a low rate of change
// when the role ID is in use.
ec2AuthMetadataFields = &authmetadata.Fields{
FieldName: "ec2_metadata",
Default: []string{
"account_id",
"auth_type",
},
AvailableToAdd: []string{
"ami_id",
"instance_id",
"region",
},
}
)
func (b *backend) pathConfigIdentity() *framework.Path {
return &framework.Path{
Pattern: "config/identity$",
Fields: map[string]*framework.FieldSchema{
"iam_alias": {
Type: framework.TypeString,
Default: identityAliasIAMUniqueID,
Description: fmt.Sprintf("Configure how the AWS auth method generates entity aliases when using IAM auth. Valid values are %q, %q, and %q. Defaults to %q.", identityAliasRoleID, identityAliasIAMUniqueID, identityAliasIAMFullArn, identityAliasRoleID),
},
iamAuthMetadataFields.FieldName: authmetadata.FieldSchema(iamAuthMetadataFields),
"ec2_alias": {
Type: framework.TypeString,
Default: identityAliasEC2InstanceID,
Description: fmt.Sprintf("Configure how the AWS auth method generates entity alias when using EC2 auth. Valid values are %q, %q, and %q. Defaults to %q.", identityAliasRoleID, identityAliasEC2InstanceID, identityAliasEC2ImageID, identityAliasRoleID),
},
ec2AuthMetadataFields.FieldName: authmetadata.FieldSchema(ec2AuthMetadataFields),
},
Operations: map[logical.Operation]framework.OperationHandler{
logical.ReadOperation: &framework.PathOperation{
Callback: pathConfigIdentityRead,
},
logical.UpdateOperation: &framework.PathOperation{
Callback: pathConfigIdentityUpdate,
},
},
HelpSynopsis: pathConfigIdentityHelpSyn,
HelpDescription: pathConfigIdentityHelpDesc,
}
}
func identityConfigEntry(ctx context.Context, s logical.Storage) (*identityConfig, error) {
entryRaw, err := s.Get(ctx, "config/identity")
if err != nil {
return nil, err
}
entry := &identityConfig{
IAMAuthMetadataHandler: authmetadata.NewHandler(iamAuthMetadataFields),
EC2AuthMetadataHandler: authmetadata.NewHandler(ec2AuthMetadataFields),
}
if entryRaw != nil {
if err := entryRaw.DecodeJSON(entry); err != nil {
return nil, err
}
}
if entry.IAMAlias == "" {
entry.IAMAlias = identityAliasRoleID
}
if entry.EC2Alias == "" {
entry.EC2Alias = identityAliasRoleID
}
return entry, nil
}
func pathConfigIdentityRead(ctx context.Context, req *logical.Request, _ *framework.FieldData) (*logical.Response, error) {
config, err := identityConfigEntry(ctx, req.Storage)
if err != nil {
return nil, err
}
return &logical.Response{
Data: map[string]interface{}{
"iam_alias": config.IAMAlias,
iamAuthMetadataFields.FieldName: config.IAMAuthMetadataHandler.AuthMetadata(),
"ec2_alias": config.EC2Alias,
ec2AuthMetadataFields.FieldName: config.EC2AuthMetadataHandler.AuthMetadata(),
},
}, nil
}
func pathConfigIdentityUpdate(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
config, err := identityConfigEntry(ctx, req.Storage)
if err != nil {
return nil, err
}
iamAliasRaw, ok := data.GetOk("iam_alias")
if ok {
iamAlias := iamAliasRaw.(string)
allowedIAMAliasValues := []string{identityAliasRoleID, identityAliasIAMUniqueID, identityAliasIAMFullArn}
if !strutil.StrListContains(allowedIAMAliasValues, iamAlias) {
return logical.ErrorResponse(fmt.Sprintf("iam_alias of %q not in set of allowed values: %v", iamAlias, allowedIAMAliasValues)), nil
}
config.IAMAlias = iamAlias
}
ec2AliasRaw, ok := data.GetOk("ec2_alias")
if ok {
ec2Alias := ec2AliasRaw.(string)
allowedEC2AliasValues := []string{identityAliasRoleID, identityAliasEC2InstanceID, identityAliasEC2ImageID}
if !strutil.StrListContains(allowedEC2AliasValues, ec2Alias) {
return logical.ErrorResponse(fmt.Sprintf("ec2_alias of %q not in set of allowed values: %v", ec2Alias, allowedEC2AliasValues)), nil
}
config.EC2Alias = ec2Alias
}
if err := config.IAMAuthMetadataHandler.ParseAuthMetadata(data); err != nil {
return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest
}
if err := config.EC2AuthMetadataHandler.ParseAuthMetadata(data); err != nil {
return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest
}
entry, err := logical.StorageEntryJSON("config/identity", config)
if err != nil {
return nil, err
}
err = req.Storage.Put(ctx, entry)
if err != nil {
return nil, err
}
return nil, nil
}
type identityConfig struct {
IAMAlias string `json:"iam_alias"`
IAMAuthMetadataHandler *authmetadata.Handler `json:"iam_auth_metadata_handler"`
EC2Alias string `json:"ec2_alias"`
EC2AuthMetadataHandler *authmetadata.Handler `json:"ec2_auth_metadata_handler"`
}
const identityAliasIAMUniqueID = "unique_id"
const identityAliasIAMFullArn = "full_arn"
const identityAliasEC2InstanceID = "instance_id"
const identityAliasEC2ImageID = "image_id"
const identityAliasRoleID = "role_id"
const pathConfigIdentityHelpSyn = `
Configure the way the AWS auth method interacts with the identity store
`
const pathConfigIdentityHelpDesc = `
The AWS auth backend defaults to aliasing an IAM principal's unique ID to the
identity store. This path allows users to change how Vault configures the
mapping to Identity aliases for more flexibility.
You can set the iam_alias parameter to one of the following values:
* 'unique_id': This retains Vault's default behavior
* 'full_arn': This maps the full authenticated ARN to the identity alias, e.g.,
"arn:aws:sts::<account_id>:assumed-role/<role_name>/<role_session_name>
This is useful where you have an identity provder that sets role_session_name
to a known value of a person, such as a username or email address, and allows
you to map those roles back to entries in your identity store.
`