2016-06-01 14:10:12 +00:00
|
|
|
package awsec2
|
2016-04-06 00:42:26 +00:00
|
|
|
|
|
|
|
import (
|
2016-04-07 18:13:19 +00:00
|
|
|
"github.com/fatih/structs"
|
2016-04-06 00:42:26 +00:00
|
|
|
"github.com/hashicorp/vault/logical"
|
|
|
|
"github.com/hashicorp/vault/logical/framework"
|
|
|
|
)
|
|
|
|
|
|
|
|
func pathConfigClient(b *backend) *framework.Path {
|
|
|
|
return &framework.Path{
|
2016-04-19 01:06:26 +00:00
|
|
|
Pattern: "config/client$",
|
2016-04-06 00:42:26 +00:00
|
|
|
Fields: map[string]*framework.FieldSchema{
|
|
|
|
"access_key": &framework.FieldSchema{
|
|
|
|
Type: framework.TypeString,
|
2016-05-03 16:14:07 +00:00
|
|
|
Default: "",
|
2016-09-28 22:30:32 +00:00
|
|
|
Description: "AWS Access Key ID for the account used to make AWS API requests.",
|
2016-04-06 00:42:26 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
"secret_key": &framework.FieldSchema{
|
|
|
|
Type: framework.TypeString,
|
2016-05-03 16:14:07 +00:00
|
|
|
Default: "",
|
2016-09-28 22:30:32 +00:00
|
|
|
Description: "AWS Secret Access Key for the account used to make AWS API requests.",
|
2016-04-06 00:42:26 +00:00
|
|
|
},
|
2016-05-02 21:21:52 +00:00
|
|
|
|
|
|
|
"endpoint": &framework.FieldSchema{
|
|
|
|
Type: framework.TypeString,
|
2016-05-03 16:14:07 +00:00
|
|
|
Default: "",
|
2016-05-12 11:19:29 +00:00
|
|
|
Description: "URL to override the default generated endpoint for making AWS EC2 API calls.",
|
2016-05-02 21:21:52 +00:00
|
|
|
},
|
2016-04-06 00:42:26 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
ExistenceCheck: b.pathConfigClientExistenceCheck,
|
|
|
|
|
|
|
|
Callbacks: map[logical.Operation]framework.OperationFunc{
|
|
|
|
logical.CreateOperation: b.pathConfigClientCreateUpdate,
|
2016-04-28 02:25:15 +00:00
|
|
|
logical.UpdateOperation: b.pathConfigClientCreateUpdate,
|
2016-04-07 18:13:19 +00:00
|
|
|
logical.DeleteOperation: b.pathConfigClientDelete,
|
|
|
|
logical.ReadOperation: b.pathConfigClientRead,
|
2016-04-06 00:42:26 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
HelpSynopsis: pathConfigClientHelpSyn,
|
|
|
|
HelpDescription: pathConfigClientHelpDesc,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Establishes dichotomy of request operation between CreateOperation and UpdateOperation.
|
|
|
|
// Returning 'true' forces an UpdateOperation, CreateOperation otherwise.
|
|
|
|
func (b *backend) pathConfigClientExistenceCheck(
|
|
|
|
req *logical.Request, data *framework.FieldData) (bool, error) {
|
2016-05-03 16:14:07 +00:00
|
|
|
|
2016-05-18 00:39:24 +00:00
|
|
|
entry, err := b.lockedClientConfigEntry(req.Storage)
|
2016-04-06 00:42:26 +00:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
return entry != nil, nil
|
|
|
|
}
|
|
|
|
|
2016-05-18 00:39:24 +00:00
|
|
|
// Fetch the client configuration required to access the AWS API, after acquiring an exclusive lock.
|
|
|
|
func (b *backend) lockedClientConfigEntry(s logical.Storage) (*clientConfig, error) {
|
2016-05-05 18:12:22 +00:00
|
|
|
b.configMutex.RLock()
|
|
|
|
defer b.configMutex.RUnlock()
|
|
|
|
|
2016-05-18 00:39:24 +00:00
|
|
|
return b.nonLockedClientConfigEntry(s)
|
2016-05-05 18:12:22 +00:00
|
|
|
}
|
|
|
|
|
2016-05-18 00:39:24 +00:00
|
|
|
// Fetch the client configuration required to access the AWS API.
|
|
|
|
func (b *backend) nonLockedClientConfigEntry(s logical.Storage) (*clientConfig, error) {
|
2016-04-06 00:42:26 +00:00
|
|
|
entry, err := s.Get("config/client")
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if entry == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var result clientConfig
|
|
|
|
if err := entry.DecodeJSON(&result); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &result, nil
|
|
|
|
}
|
|
|
|
|
2016-04-07 18:13:19 +00:00
|
|
|
func (b *backend) pathConfigClientRead(
|
|
|
|
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
2016-05-18 00:39:24 +00:00
|
|
|
clientConfig, err := b.lockedClientConfigEntry(req.Storage)
|
2016-04-07 18:13:19 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if clientConfig == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return &logical.Response{
|
|
|
|
Data: structs.New(clientConfig).Map(),
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *backend) pathConfigClientDelete(
|
|
|
|
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
|
|
|
b.configMutex.Lock()
|
2016-04-28 02:25:15 +00:00
|
|
|
defer b.configMutex.Unlock()
|
2016-04-07 18:13:19 +00:00
|
|
|
|
2016-04-28 04:35:49 +00:00
|
|
|
if err := req.Storage.Delete("config/client"); err != nil {
|
2016-04-07 18:13:19 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2016-04-14 04:11:17 +00:00
|
|
|
// Remove all the cached EC2 client objects in the backend.
|
|
|
|
b.flushCachedEC2Clients()
|
2016-04-07 18:13:19 +00:00
|
|
|
|
2016-09-23 16:47:35 +00:00
|
|
|
// Remove all the cached EC2 client objects in the backend.
|
|
|
|
b.flushCachedIAMClients()
|
|
|
|
|
2016-04-07 18:13:19 +00:00
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2016-04-06 00:42:26 +00:00
|
|
|
// pathConfigClientCreateUpdate is used to register the 'aws_secret_key' and 'aws_access_key'
|
|
|
|
// that can be used to interact with AWS EC2 API.
|
|
|
|
func (b *backend) pathConfigClientCreateUpdate(
|
|
|
|
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
2016-05-05 16:44:40 +00:00
|
|
|
b.configMutex.Lock()
|
|
|
|
defer b.configMutex.Unlock()
|
|
|
|
|
2016-05-18 00:39:24 +00:00
|
|
|
configEntry, err := b.nonLockedClientConfigEntry(req.Storage)
|
2016-04-06 00:42:26 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if configEntry == nil {
|
|
|
|
configEntry = &clientConfig{}
|
|
|
|
}
|
|
|
|
|
2016-04-07 18:13:19 +00:00
|
|
|
changedCreds := false
|
2016-04-06 00:42:26 +00:00
|
|
|
|
|
|
|
accessKeyStr, ok := data.GetOk("access_key")
|
|
|
|
if ok {
|
2016-04-07 18:13:19 +00:00
|
|
|
if configEntry.AccessKey != accessKeyStr.(string) {
|
|
|
|
changedCreds = true
|
2016-04-14 04:11:17 +00:00
|
|
|
configEntry.AccessKey = accessKeyStr.(string)
|
2016-04-07 18:13:19 +00:00
|
|
|
}
|
2016-04-06 00:42:26 +00:00
|
|
|
} else if req.Operation == logical.CreateOperation {
|
2016-04-07 18:13:19 +00:00
|
|
|
// Use the default
|
|
|
|
configEntry.AccessKey = data.Get("access_key").(string)
|
2016-04-06 16:27:47 +00:00
|
|
|
}
|
2016-04-06 00:42:26 +00:00
|
|
|
|
|
|
|
secretKeyStr, ok := data.GetOk("secret_key")
|
|
|
|
if ok {
|
2016-04-07 18:13:19 +00:00
|
|
|
if configEntry.SecretKey != secretKeyStr.(string) {
|
|
|
|
changedCreds = true
|
2016-04-14 04:11:17 +00:00
|
|
|
configEntry.SecretKey = secretKeyStr.(string)
|
2016-04-07 18:13:19 +00:00
|
|
|
}
|
2016-04-06 00:42:26 +00:00
|
|
|
} else if req.Operation == logical.CreateOperation {
|
2016-04-07 18:13:19 +00:00
|
|
|
configEntry.SecretKey = data.Get("secret_key").(string)
|
2016-04-06 16:27:47 +00:00
|
|
|
}
|
2016-04-06 00:42:26 +00:00
|
|
|
|
2016-05-02 21:21:52 +00:00
|
|
|
endpointStr, ok := data.GetOk("endpoint")
|
|
|
|
if ok {
|
|
|
|
if configEntry.Endpoint != endpointStr.(string) {
|
|
|
|
changedCreds = true
|
|
|
|
configEntry.Endpoint = endpointStr.(string)
|
|
|
|
}
|
|
|
|
} else if req.Operation == logical.CreateOperation {
|
|
|
|
configEntry.Endpoint = data.Get("endpoint").(string)
|
|
|
|
}
|
|
|
|
|
2016-05-03 16:14:07 +00:00
|
|
|
// Since this endpoint supports both create operation and update operation,
|
|
|
|
// the error checks for access_key and secret_key not being set are not present.
|
|
|
|
// This allows calling this endpoint multiple times to provide the values.
|
|
|
|
// Hence, the readers of this endpoint should do the validation on
|
|
|
|
// the validation of keys before using them.
|
2016-04-06 00:42:26 +00:00
|
|
|
entry, err := logical.StorageEntryJSON("config/client", configEntry)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := req.Storage.Put(entry); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2016-04-07 18:13:19 +00:00
|
|
|
if changedCreds {
|
2016-04-14 04:11:17 +00:00
|
|
|
b.flushCachedEC2Clients()
|
2016-09-23 16:47:35 +00:00
|
|
|
b.flushCachedIAMClients()
|
2016-04-07 18:13:19 +00:00
|
|
|
}
|
|
|
|
|
2016-04-06 00:42:26 +00:00
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Struct to hold 'aws_access_key' and 'aws_secret_key' that are required to
|
|
|
|
// interact with the AWS EC2 API.
|
|
|
|
type clientConfig struct {
|
|
|
|
AccessKey string `json:"access_key" structs:"access_key" mapstructure:"access_key"`
|
|
|
|
SecretKey string `json:"secret_key" structs:"secret_key" mapstructure:"secret_key"`
|
2016-05-02 21:21:52 +00:00
|
|
|
Endpoint string `json:"endpoint" structs:"endpoint" mapstructure:"endpoint"`
|
2016-04-06 00:42:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const pathConfigClientHelpSyn = `
|
2016-10-05 19:39:21 +00:00
|
|
|
Configure AWS IAM credentials that are used to query instance and role details from the AWS API.
|
2016-04-06 00:42:26 +00:00
|
|
|
`
|
|
|
|
|
|
|
|
const pathConfigClientHelpDesc = `
|
2016-10-05 19:39:21 +00:00
|
|
|
The aws-ec2 auth backend makes AWS API queries to retrieve information
|
|
|
|
regarding EC2 instances that perform login operations. The 'aws_secret_key' and
|
|
|
|
'aws_access_key' parameters configured here should map to an AWS IAM user that
|
|
|
|
has permission to make the following API queries:
|
|
|
|
|
|
|
|
* ec2:DescribeInstances
|
|
|
|
* iam:GetInstanceProfile (if IAM Role binding is used)
|
2016-04-06 00:42:26 +00:00
|
|
|
`
|