2016-04-06 00:42:26 +00:00
package aws
import (
"fmt"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
2016-04-07 18:13:19 +00:00
"github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds"
"github.com/aws/aws-sdk-go/aws/ec2metadata"
2016-04-06 00:42:26 +00:00
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/go-cleanhttp"
"github.com/hashicorp/vault/logical"
)
2016-04-07 18:13:19 +00:00
// getClientConfig creates a aws-sdk-go config, which is used to create client
// that can interact with AWS API. This builds credentials in the following
// order of preference:
//
// * Static credentials from 'config/client'
// * Environment variables
// * Instance metadata role
2016-04-14 04:11:17 +00:00
func ( b * backend ) getClientConfig ( s logical . Storage , region string ) ( * aws . Config , error ) {
2016-04-06 00:42:26 +00:00
// Read the configured secret key and access key
2016-04-28 04:35:49 +00:00
config , err := b . clientConfigEntry ( s )
2016-04-06 00:42:26 +00:00
if err != nil {
return nil , err
}
2016-04-07 18:13:19 +00:00
var providers [ ] credentials . Provider
if config != nil {
switch {
case config . AccessKey != "" && config . SecretKey != "" :
2016-04-28 00:01:39 +00:00
// Add the static credential provider
2016-04-07 18:13:19 +00:00
providers = append ( providers , & credentials . StaticProvider {
Value : credentials . Value {
AccessKeyID : config . AccessKey ,
SecretAccessKey : config . SecretKey ,
} } )
case config . AccessKey == "" && config . AccessKey == "" :
// Attempt to get credentials from the IAM instance role below
default : // Have one or the other but not both and not neither
return nil , fmt . Errorf (
"static AWS client credentials haven't been properly configured (the access key or secret key were provided but not both); configure or remove them at the 'config/client' endpoint" )
}
2016-04-06 00:42:26 +00:00
}
2016-04-28 00:01:39 +00:00
// Add the environment credential provider
2016-04-07 18:13:19 +00:00
providers = append ( providers , & credentials . EnvProvider { } )
2016-04-28 00:01:39 +00:00
// Add the instance metadata role provider
2016-04-06 00:42:26 +00:00
// Create the credentials required to access the API.
2016-04-07 18:13:19 +00:00
providers = append ( providers , & ec2rolecreds . EC2RoleProvider {
Client : ec2metadata . New ( session . New ( & aws . Config {
Region : aws . String ( region ) ,
HTTPClient : cleanhttp . DefaultClient ( ) ,
} ) ) ,
ExpiryWindow : 15 ,
} )
creds := credentials . NewChainCredentials ( providers )
if creds == nil {
return nil , fmt . Errorf ( "could not compile valid credential providers from static config, environemnt, or instance metadata" )
}
2016-04-06 00:42:26 +00:00
// Create a config that can be used to make the API calls.
2016-05-02 21:21:52 +00:00
cfg := & aws . Config {
2016-04-06 00:42:26 +00:00
Credentials : creds ,
2016-04-07 18:13:19 +00:00
Region : aws . String ( region ) ,
2016-04-06 00:42:26 +00:00
HTTPClient : cleanhttp . DefaultClient ( ) ,
2016-05-02 21:21:52 +00:00
}
// Override the default endpoint with the configured endpoint.
if config . Endpoint != "" {
cfg . Endpoint = aws . String ( config . Endpoint )
}
return cfg , nil
2016-04-06 00:42:26 +00:00
}
2016-04-14 04:11:17 +00:00
// flushCachedEC2Clients deletes all the cached ec2 client objects from the backend.
2016-04-28 00:01:39 +00:00
// If the client credentials configuration is deleted or updated in the backend, all
// the cached EC2 client objects will be flushed.
2016-04-28 00:13:56 +00:00
//
// Lock should be actuired using b.configMutex.Lock() before calling this method and
// unlocked using b.configMutex.Unlock() after returning.
2016-04-14 04:11:17 +00:00
func ( b * backend ) flushCachedEC2Clients ( ) {
2016-04-28 00:01:39 +00:00
// deleting items in map during iteration is safe.
2016-04-14 04:11:17 +00:00
for region , _ := range b . EC2ClientsMap {
delete ( b . EC2ClientsMap , region )
}
}
2016-04-06 00:42:26 +00:00
// clientEC2 creates a client to interact with AWS EC2 API.
2016-04-28 00:01:39 +00:00
func ( b * backend ) clientEC2 ( s logical . Storage , region string ) ( * ec2 . EC2 , error ) {
b . configMutex . RLock ( )
if b . EC2ClientsMap [ region ] != nil {
defer b . configMutex . RUnlock ( )
// If the client object was already created, return it.
return b . EC2ClientsMap [ region ] , nil
2016-04-07 18:13:19 +00:00
}
2016-04-28 00:01:39 +00:00
// Release the read lock and acquire the write lock.
b . configMutex . RUnlock ( )
2016-04-07 18:13:19 +00:00
b . configMutex . Lock ( )
defer b . configMutex . Unlock ( )
2016-04-28 00:01:39 +00:00
// If the client gets created while switching the locks, return it.
if b . EC2ClientsMap [ region ] != nil {
return b . EC2ClientsMap [ region ] , nil
}
// Fetch the configured credentials
2016-04-14 04:11:17 +00:00
awsConfig , err := b . getClientConfig ( s , region )
2016-04-06 00:42:26 +00:00
if err != nil {
return nil , err
}
2016-04-07 18:13:19 +00:00
2016-04-28 00:01:39 +00:00
// Create a new EC2 client object, cache it and return the same.
2016-04-14 04:11:17 +00:00
b . EC2ClientsMap [ region ] = ec2 . New ( session . New ( awsConfig ) )
return b . EC2ClientsMap [ region ] , nil
2016-04-06 00:42:26 +00:00
}