Joel Thompson 5a934e6b2f Create unified aws auth backend (#2441)
* Rename builtin/credential/aws-ec2 to aws

The aws-ec2 authentication backend is being expanded and will become the
generic aws backend. This is a small rename commit to keep the commit
history clean.

* Expand aws-ec2 backend to more generic aws

This adds the ability to authenticate arbitrary AWS IAM principals using
AWS's sts:GetCallerIdentity method. The AWS-EC2 auth backend is being to
just AWS with the expansion.

* Add missing aws auth handler to CLI

This was omitted from the previous commit

* aws auth backend general variable name cleanup

Also fixed a bug where allowed auth types weren't being checked upon
login, and added tests for it.

* Update docs for the aws auth backend

* Refactor aws bind validation

* Fix env var override in aws backend test

Intent is to override the AWS environment variables with the TEST_*
versions if they are set, but the reverse was happening.

* Update docs on use of IAM authentication profile

AWS now allows you to change the instance profile of a running instance,
so the use case of "a long-lived instance that's not in an instance
profile" no longer means you have to use the the EC2 auth method. You
can now just change the instance profile on the fly.

* Fix typo in aws auth cli help

* Respond to PR feedback

* More PR feedback

* Respond to additional PR feedback

* Address more feedback on aws auth PR

* Make aws auth_type immutable per role

* Address more aws auth PR feedback

* Address more iam auth PR feedback

* Rename aws-ec2.html.md to aws.html.md

Per PR feedback, to go along with new backend name.

* Add MountType to logical.Request

* Make default aws auth_type dependent upon MountType

When MountType is aws-ec2, default to ec2 auth_type for backwards
compatibility with legacy roles. Otherwise, default to iam.

* Pass MountPoint and MountType back up to the core

Previously the request router reset the MountPoint and MountType back to
the empty string before returning to the core. This ensures they get set
back to the correct values.
2017-04-24 15:15:50 -04:00

211 lines
6.6 KiB
Go

package awsauth
import (
"sync"
"time"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/iam"
"github.com/hashicorp/vault/helper/salt"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/logical/framework"
)
func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
b, err := Backend(conf)
if err != nil {
return nil, err
}
return b.Setup(conf)
}
type backend struct {
*framework.Backend
Salt *salt.Salt
// Used during initialization to set the salt
view logical.Storage
// Lock to make changes to any of the backend's configuration endpoints.
configMutex sync.RWMutex
// Lock to make changes to role entries
roleMutex sync.RWMutex
// Lock to make changes to the blacklist entries
blacklistMutex sync.RWMutex
// Guards the blacklist/whitelist tidy functions
tidyBlacklistCASGuard uint32
tidyWhitelistCASGuard uint32
// Duration after which the periodic function of the backend needs to
// tidy the blacklist and whitelist entries.
tidyCooldownPeriod time.Duration
// nextTidyTime holds the time at which the periodic func should initiatite
// the tidy operations. This is set by the periodicFunc based on the value
// of tidyCooldownPeriod.
nextTidyTime time.Time
// Map to hold the EC2 client objects indexed by region and STS role.
// This avoids the overhead of creating a client object for every login request.
// When the credentials are modified or deleted, all the cached client objects
// will be flushed. The empty STS role signifies the master account
EC2ClientsMap map[string]map[string]*ec2.EC2
// Map to hold the IAM client objects indexed by region and STS role.
// This avoids the overhead of creating a client object for every login request.
// When the credentials are modified or deleted, all the cached client objects
// will be flushed. The empty STS role signifies the master account
IAMClientsMap map[string]map[string]*iam.IAM
}
func Backend(conf *logical.BackendConfig) (*backend, error) {
b := &backend{
// Setting the periodic func to be run once in an hour.
// If there is a real need, this can be made configurable.
tidyCooldownPeriod: time.Hour,
view: conf.StorageView,
EC2ClientsMap: make(map[string]map[string]*ec2.EC2),
IAMClientsMap: make(map[string]map[string]*iam.IAM),
}
b.Backend = &framework.Backend{
PeriodicFunc: b.periodicFunc,
AuthRenew: b.pathLoginRenew,
Help: backendHelp,
PathsSpecial: &logical.Paths{
Unauthenticated: []string{
"login",
},
LocalStorage: []string{
"whitelist/identity/",
},
},
Paths: []*framework.Path{
pathLogin(b),
pathListRole(b),
pathListRoles(b),
pathRole(b),
pathRoleTag(b),
pathConfigClient(b),
pathConfigCertificate(b),
pathConfigSts(b),
pathListSts(b),
pathConfigTidyRoletagBlacklist(b),
pathConfigTidyIdentityWhitelist(b),
pathListCertificates(b),
pathListRoletagBlacklist(b),
pathRoletagBlacklist(b),
pathTidyRoletagBlacklist(b),
pathListIdentityWhitelist(b),
pathIdentityWhitelist(b),
pathTidyIdentityWhitelist(b),
},
Invalidate: b.invalidate,
Init: b.initialize,
}
return b, nil
}
func (b *backend) initialize() error {
salt, err := salt.NewSalt(b.view, &salt.Config{
HashFunc: salt.SHA256Hash,
})
if err != nil {
return err
}
b.Salt = salt
return nil
}
// periodicFunc performs the tasks that the backend wishes to do periodically.
// Currently this will be triggered once in a minute by the RollbackManager.
//
// The tasks being done currently by this function are to cleanup the expired
// entries of both blacklist role tags and whitelist identities. Tidying is done
// not once in a minute, but once in an hour, controlled by 'tidyCooldownPeriod'.
// Tidying of blacklist and whitelist are by default enabled. This can be
// changed using `config/tidy/roletags` and `config/tidy/identities` endpoints.
func (b *backend) periodicFunc(req *logical.Request) error {
// Run the tidy operations for the first time. Then run it when current
// time matches the nextTidyTime.
if b.nextTidyTime.IsZero() || !time.Now().Before(b.nextTidyTime) {
// safety_buffer defaults to 180 days for roletag blacklist
safety_buffer := 15552000
tidyBlacklistConfigEntry, err := b.lockedConfigTidyRoleTags(req.Storage)
if err != nil {
return err
}
skipBlacklistTidy := false
// check if tidying of role tags was configured
if tidyBlacklistConfigEntry != nil {
// check if periodic tidying of role tags was disabled
if tidyBlacklistConfigEntry.DisablePeriodicTidy {
skipBlacklistTidy = true
}
// overwrite the default safety_buffer with the configured value
safety_buffer = tidyBlacklistConfigEntry.SafetyBuffer
}
// tidy role tags if explicitly not disabled
if !skipBlacklistTidy {
b.tidyBlacklistRoleTag(req.Storage, safety_buffer)
}
// reset the safety_buffer to 72h
safety_buffer = 259200
tidyWhitelistConfigEntry, err := b.lockedConfigTidyIdentities(req.Storage)
if err != nil {
return err
}
skipWhitelistTidy := false
// check if tidying of identities was configured
if tidyWhitelistConfigEntry != nil {
// check if periodic tidying of identities was disabled
if tidyWhitelistConfigEntry.DisablePeriodicTidy {
skipWhitelistTidy = true
}
// overwrite the default safety_buffer with the configured value
safety_buffer = tidyWhitelistConfigEntry.SafetyBuffer
}
// tidy identities if explicitly not disabled
if !skipWhitelistTidy {
b.tidyWhitelistIdentity(req.Storage, safety_buffer)
}
// Update the time at which to run the tidy functions again.
b.nextTidyTime = time.Now().Add(b.tidyCooldownPeriod)
}
return nil
}
func (b *backend) invalidate(key string) {
switch key {
case "config/client":
b.configMutex.Lock()
defer b.configMutex.Unlock()
b.flushCachedEC2Clients()
b.flushCachedIAMClients()
}
}
const backendHelp = `
aws-ec2 auth backend takes in PKCS#7 signature of an AWS EC2 instance and a client
created nonce to authenticates the EC2 instance with Vault.
Authentication is backed by a preconfigured role in the backend. The role
represents the authorization of resources by containing Vault's policies.
Role can be created using 'role/<role>' endpoint.
If there is need to further restrict the capabilities of the role on the instance
that is using the role, 'role_tag' option can be enabled on the role, and a tag
can be generated using 'role/<role>/tag' endpoint. This tag represents the
subset of capabilities set on the role. When the 'role_tag' option is enabled on
the role, the login operation requires that a respective role tag is attached to
the EC2 instance which performs the login.
`