Commit b548e286 authored by louism517's avatar louism517 Committed by Jeff Mitchell
Browse files

Support for Cross-Account AWS Auth (#2148)

parent af105b91
Showing with 656 additions and 37 deletions
+656 -37
......@@ -45,17 +45,17 @@ type backend struct {
// of tidyCooldownPeriod.
nextTidyTime time.Time
// Map to hold the EC2 client objects indexed by region. 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.
EC2ClientsMap map[string]*ec2.EC2
// Map to hold the IAM client objects indexed by region. 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.
IAMClientsMap map[string]*iam.IAM
// 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) {
......@@ -71,8 +71,8 @@ func Backend(conf *logical.BackendConfig) (*backend, error) {
// If there is a real need, this can be made configurable.
tidyCooldownPeriod: time.Hour,
Salt: salt,
EC2ClientsMap: make(map[string]*ec2.EC2),
IAMClientsMap: make(map[string]*iam.IAM),
EC2ClientsMap: make(map[string]map[string]*ec2.EC2),
IAMClientsMap: make(map[string]map[string]*iam.IAM),
}
b.Backend = &framework.Backend{
......@@ -92,6 +92,8 @@ func Backend(conf *logical.BackendConfig) (*backend, error) {
pathRoleTag(b),
pathConfigClient(b),
pathConfigCertificate(b),
pathConfigSts(b),
pathListSts(b),
pathConfigTidyRoletagBlacklist(b),
pathConfigTidyIdentityWhitelist(b),
pathListCertificates(b),
......
......@@ -1275,3 +1275,117 @@ func TestBackendAcc_LoginAndWhitelistIdentity(t *testing.T) {
t.Fatalf("login attempt failed")
}
}
func TestBackend_pathStsConfig(t *testing.T) {
config := logical.TestBackendConfig()
storage := &logical.InmemStorage{}
config.StorageView = storage
b, err := Backend(config)
if err != nil {
t.Fatal(err)
}
_, err = b.Setup(config)
if err != nil {
t.Fatal(err)
}
stsReq := &logical.Request{
Operation: logical.CreateOperation,
Storage: storage,
Path: "config/sts/account1",
}
checkFound, exists, err := b.HandleExistenceCheck(stsReq)
if err != nil {
t.Fatal(err)
}
if !checkFound {
t.Fatal("existence check not found for path 'config/sts/account1'")
}
if exists {
t.Fatal("existence check should have returned 'false' for 'config/sts/account1'")
}
data := map[string]interface{}{
"sts_role": "arn:aws:iam:account1:role/myRole",
}
stsReq.Data = data
// test create operation
resp, err := b.HandleRequest(stsReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("resp: %#v, err: %v", resp, err)
}
stsReq.Data = nil
// test existence check
checkFound, exists, err = b.HandleExistenceCheck(stsReq)
if err != nil {
t.Fatal(err)
}
if !checkFound {
t.Fatal("existence check not found for path 'config/sts/account1'")
}
if !exists {
t.Fatal("existence check should have returned 'true' for 'config/sts/account1'")
}
stsReq.Operation = logical.ReadOperation
// test read operation
resp, err = b.HandleRequest(stsReq)
expectedStsRole := "arn:aws:iam:account1:role/myRole"
if resp.Data["sts_role"].(string) != expectedStsRole {
t.Fatalf("bad: expected:%s\n got:%s\n", expectedStsRole, resp.Data["sts_role"].(string))
}
stsReq.Operation = logical.CreateOperation
stsReq.Path = "config/sts/account2"
stsReq.Data = data
// create another entry to test the list operation
resp, err = b.HandleRequest(stsReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatal(err)
}
stsReq.Operation = logical.ListOperation
stsReq.Path = "config/sts"
// test list operation
resp, err = b.HandleRequest(stsReq)
if err != nil {
t.Fatal(err)
}
if resp == nil || resp.IsError() {
t.Fatalf("failed to list config/sts")
}
keys := resp.Data["keys"].([]string)
if len(keys) != 2 {
t.Fatalf("invalid keys listed: %#v\n", keys)
}
stsReq.Operation = logical.DeleteOperation
stsReq.Path = "config/sts/account1"
resp, err = b.HandleRequest(stsReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatal(err)
}
stsReq.Path = "config/sts/account2"
resp, err = b.HandleRequest(stsReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatal(err)
}
stsReq.Operation = logical.ListOperation
stsReq.Path = "config/sts"
// test list operation
resp, err = b.HandleRequest(stsReq)
if err != nil {
t.Fatal(err)
}
if resp == nil || resp.IsError() {
t.Fatalf("failed to list config/sts")
}
if resp.Data["keys"] != nil {
t.Fatalf("no entries should be present")
}
}
......@@ -4,6 +4,7 @@ import (
"fmt"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/iam"
......@@ -48,7 +49,7 @@ func (b *backend) getClientConfig(s logical.Storage, region string) (*aws.Config
return nil, err
}
if creds == nil {
return nil, fmt.Errorf("could not compile valid credential providers from static config, environemnt, shared, or instance metadata")
return nil, fmt.Errorf("could not compile valid credential providers from static config, environment, shared, or instance metadata")
}
// Create a config that can be used to make the API calls.
......@@ -60,6 +61,29 @@ func (b *backend) getClientConfig(s logical.Storage, region string) (*aws.Config
}, nil
}
// getStsClientConfig returns an aws-sdk-go config, with assumed credentials
// It uses getClientConfig to obtain config for the runtime environemnt, which is
// then used to obtain a set of assumed credentials. The credentials will expire
// after 15 minutes but will auto-refresh.
func (b *backend) getStsClientConfig(s logical.Storage, region string, stsRole string) (*aws.Config, error) {
config, err := b.getClientConfig(s, region)
if err != nil {
return nil, err
}
if config == nil {
return nil, fmt.Errorf("could not compile valid credentials through the default provider chain")
}
assumedCredentials := stscreds.NewCredentials(session.New(config), stsRole)
// Test that we actually have permissions to assume the role
if _, err = assumedCredentials.Get(); err != nil {
return nil, err
}
config.Credentials = assumedCredentials
return config, nil
}
// flushCachedEC2Clients deletes all the cached ec2 client objects from the backend.
// If the client credentials configuration is deleted or updated in the backend, all
// the cached EC2 client objects will be flushed. Config mutex lock should be
......@@ -83,12 +107,12 @@ func (b *backend) flushCachedIAMClients() {
}
// clientEC2 creates a client to interact with AWS EC2 API
func (b *backend) clientEC2(s logical.Storage, region string) (*ec2.EC2, error) {
func (b *backend) clientEC2(s logical.Storage, region string, stsRole string) (*ec2.EC2, error) {
b.configMutex.RLock()
if b.EC2ClientsMap[region] != nil {
if b.EC2ClientsMap[region] != nil && b.EC2ClientsMap[region][stsRole] != nil {
defer b.configMutex.RUnlock()
// If the client object was already created, return it
return b.EC2ClientsMap[region], nil
return b.EC2ClientsMap[region][stsRole], nil
}
// Release the read lock and acquire the write lock
......@@ -97,28 +121,49 @@ func (b *backend) clientEC2(s logical.Storage, region string) (*ec2.EC2, error)
defer b.configMutex.Unlock()
// If the client gets created while switching the locks, return it
if b.EC2ClientsMap[region] != nil {
return b.EC2ClientsMap[region], nil
if b.EC2ClientsMap[region] != nil && b.EC2ClientsMap[region][stsRole] != nil {
return b.EC2ClientsMap[region][stsRole], nil
}
// Create an AWS config object using a chain of providers
awsConfig, err := b.getClientConfig(s, region)
var awsConfig *aws.Config
var err error
// The empty stsRole signifies the master account
if stsRole == "" {
awsConfig, err = b.getClientConfig(s, region)
} else {
awsConfig, err = b.getStsClientConfig(s, region, stsRole)
}
if err != nil {
return nil, err
}
if awsConfig == nil {
return nil, fmt.Errorf("could not retrieve valid assumed credentials")
}
// Create a new EC2 client object, cache it and return the same
b.EC2ClientsMap[region] = ec2.New(session.New(awsConfig))
return b.EC2ClientsMap[region], nil
client := ec2.New(session.New(awsConfig))
if client == nil {
return nil, fmt.Errorf("could not obtain ec2 client")
}
if _, ok := b.EC2ClientsMap[region]; !ok {
b.EC2ClientsMap[region] = map[string]*ec2.EC2{stsRole: client}
} else {
b.EC2ClientsMap[region][stsRole] = client
}
return b.EC2ClientsMap[region][stsRole], nil
}
// clientIAM creates a client to interact with AWS IAM API
func (b *backend) clientIAM(s logical.Storage, region string) (*iam.IAM, error) {
func (b *backend) clientIAM(s logical.Storage, region string, stsRole string) (*iam.IAM, error) {
b.configMutex.RLock()
if b.IAMClientsMap[region] != nil {
if b.IAMClientsMap[region] != nil && b.IAMClientsMap[region][stsRole] != nil {
defer b.configMutex.RUnlock()
// If the client object was already created, return it
return b.IAMClientsMap[region], nil
return b.IAMClientsMap[region][stsRole], nil
}
// Release the read lock and acquire the write lock
......@@ -127,17 +172,37 @@ func (b *backend) clientIAM(s logical.Storage, region string) (*iam.IAM, error)
defer b.configMutex.Unlock()
// If the client gets created while switching the locks, return it
if b.IAMClientsMap[region] != nil {
return b.IAMClientsMap[region], nil
if b.IAMClientsMap[region] != nil && b.IAMClientsMap[region][stsRole] != nil {
return b.IAMClientsMap[region][stsRole], nil
}
// Create an AWS config object using a chain of providers
awsConfig, err := b.getClientConfig(s, region)
var awsConfig *aws.Config
var err error
// The empty stsRole signifies the master account
if stsRole == "" {
awsConfig, err = b.getClientConfig(s, region)
} else {
awsConfig, err = b.getStsClientConfig(s, region, stsRole)
}
if err != nil {
return nil, err
}
if awsConfig == nil {
return nil, fmt.Errorf("could not retrieve valid assumed credentials")
}
// Create a new IAM client object, cache it and return the same
b.IAMClientsMap[region] = iam.New(session.New(awsConfig))
return b.IAMClientsMap[region], nil
client := iam.New(session.New(awsConfig))
if client == nil {
return nil, fmt.Errorf("could not obtain iam client")
}
if _, ok := b.IAMClientsMap[region]; !ok {
b.IAMClientsMap[region] = map[string]*iam.IAM{stsRole: client}
} else {
b.IAMClientsMap[region][stsRole] = client
}
return b.IAMClientsMap[region][stsRole], nil
}
package awsec2
import (
"fmt"
"github.com/fatih/structs"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/logical/framework"
)
// awsStsEntry is used to store details of an STS role for assumption
type awsStsEntry struct {
StsRole string `json:"sts_role" structs:"sts_role" mapstructure:"sts_role"`
}
func pathListSts(b *backend) *framework.Path {
return &framework.Path{
Pattern: "config/sts/?",
Callbacks: map[logical.Operation]framework.OperationFunc{
logical.ListOperation: b.pathStsList,
},
HelpSynopsis: pathListStsHelpSyn,
HelpDescription: pathListStsHelpDesc,
}
}
func pathConfigSts(b *backend) *framework.Path {
return &framework.Path{
Pattern: "config/sts/" + framework.GenericNameRegex("account_id"),
Fields: map[string]*framework.FieldSchema{
"account_id": {
Type: framework.TypeString,
Description: `AWS account ID to be associated with STS role. If set,
Vault will use assumed credentials to verify any login attempts from EC2
instances in this account.`,
},
"sts_role": {
Type: framework.TypeString,
Description: `AWS ARN for STS role to be assumed when interacting with the account specified.
The Vault server must have permissions to assume this role.`,
},
},
ExistenceCheck: b.pathConfigStsExistenceCheck,
Callbacks: map[logical.Operation]framework.OperationFunc{
logical.CreateOperation: b.pathConfigStsCreateUpdate,
logical.UpdateOperation: b.pathConfigStsCreateUpdate,
logical.ReadOperation: b.pathConfigStsRead,
logical.DeleteOperation: b.pathConfigStsDelete,
},
HelpSynopsis: pathConfigStsSyn,
HelpDescription: pathConfigStsDesc,
}
}
// Establishes dichotomy of request operation between CreateOperation and UpdateOperation.
// Returning 'true' forces an UpdateOperation, CreateOperation otherwise.
func (b *backend) pathConfigStsExistenceCheck(req *logical.Request, data *framework.FieldData) (bool, error) {
accountID := data.Get("account_id").(string)
if accountID == "" {
return false, fmt.Errorf("missing account_id")
}
entry, err := b.lockedAwsStsEntry(req.Storage, accountID)
if err != nil {
return false, err
}
return entry != nil, nil
}
// pathStsList is used to list all the AWS STS role configurations
func (b *backend) pathStsList(
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
b.configMutex.RLock()
defer b.configMutex.RUnlock()
sts, err := req.Storage.List("config/sts/")
if err != nil {
return nil, err
}
return logical.ListResponse(sts), nil
}
// nonLockedSetAwsStsEntry creates or updates an STS role association with the given accountID
// This method does not acquire the write lock before creating or updating. If locking is
// desired, use lockedSetAwsStsEntry instead
func (b *backend) nonLockedSetAwsStsEntry(s logical.Storage, accountID string, stsEntry *awsStsEntry) error {
if accountID == "" {
return fmt.Errorf("missing AWS account ID")
}
if stsEntry == nil {
return fmt.Errorf("missing AWS STS Role ARN")
}
entry, err := logical.StorageEntryJSON("config/sts/"+accountID, stsEntry)
if err != nil {
return err
}
if entry == nil {
return fmt.Errorf("failed to create storage entry for AWS STS configuration")
}
return s.Put(entry)
}
// lockedSetAwsStsEntry creates or updates an STS role association with the given accountID
// This method acquires the write lock before creating or updating the STS entry.
func (b *backend) lockedSetAwsStsEntry(s logical.Storage, accountID string, stsEntry *awsStsEntry) error {
if accountID == "" {
return fmt.Errorf("missing AWS account ID")
}
if stsEntry == nil {
return fmt.Errorf("missing sts entry")
}
b.configMutex.Lock()
defer b.configMutex.Unlock()
return b.nonLockedSetAwsStsEntry(s, accountID, stsEntry)
}
// nonLockedAwsStsEntry returns the STS role associated with the given accountID.
// This method does not acquire the read lock before returning information. If locking is
// desired, use lockedAwsStsEntry instead
func (b *backend) nonLockedAwsStsEntry(s logical.Storage, accountID string) (*awsStsEntry, error) {
entry, err := s.Get("config/sts/" + accountID)
if err != nil {
return nil, err
}
if entry == nil {
return nil, nil
}
var stsEntry awsStsEntry
if err := entry.DecodeJSON(&stsEntry); err != nil {
return nil, err
}
return &stsEntry, nil
}
// lockedAwsStsEntry returns the STS role associated with the given accountID.
// This method acquires the read lock before returning the association.
func (b *backend) lockedAwsStsEntry(s logical.Storage, accountID string) (*awsStsEntry, error) {
b.configMutex.RLock()
defer b.configMutex.RUnlock()
return b.nonLockedAwsStsEntry(s, accountID)
}
// pathConfigStsRead is used to return information about an STS role/AWS accountID association
func (b *backend) pathConfigStsRead(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
accountID := data.Get("account_id").(string)
if accountID == "" {
return logical.ErrorResponse("missing account id"), nil
}
stsEntry, err := b.lockedAwsStsEntry(req.Storage, accountID)
if err != nil {
return nil, err
}
if stsEntry == nil {
return nil, nil
}
return &logical.Response{
Data: structs.New(stsEntry).Map(),
}, nil
}
// pathConfigStsCreateUpdate is used to associate an STS role with a given AWS accountID
func (b *backend) pathConfigStsCreateUpdate(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
accountID := data.Get("account_id").(string)
if accountID == "" {
return logical.ErrorResponse("missing AWS account ID"), nil
}
b.configMutex.Lock()
defer b.configMutex.Unlock()
// Check if an STS role is already registered
stsEntry, err := b.nonLockedAwsStsEntry(req.Storage, accountID)
if err != nil {
return nil, err
}
if stsEntry == nil {
stsEntry = &awsStsEntry{}
}
// Check that an STS role has actually been provided
stsRole, ok := data.GetOk("sts_role")
if ok {
stsEntry.StsRole = stsRole.(string)
} else if req.Operation == logical.CreateOperation {
return logical.ErrorResponse("missing sts role"), nil
}
if stsEntry.StsRole == "" {
return logical.ErrorResponse("sts role cannot be empty"), nil
}
// save the provided STS role
if err := b.nonLockedSetAwsStsEntry(req.Storage, accountID, stsEntry); err != nil {
return nil, err
}
return nil, nil
}
// pathConfigStsDelete is used to delete a previously configured STS configuration
func (b *backend) pathConfigStsDelete(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
b.configMutex.Lock()
defer b.configMutex.Unlock()
accountID := data.Get("account_id").(string)
if accountID == "" {
return logical.ErrorResponse("missing account id"), nil
}
return nil, req.Storage.Delete("config/sts/" + accountID)
}
const pathConfigStsSyn = `
Specify STS roles to be assumed for certain AWS accounts.
`
const pathConfigStsDesc = `
Allows the explicit association of STS roles to satellite AWS accounts (i.e. those
which are not the account in which the Vault server is running.) Login attempts from
EC2 instances running in these accounts will be verified using credentials obtained
by assumption of these STS roles.
The environment in which the Vault server resides must have access to assume the
given STS roles.
`
const pathListStsHelpSyn = `
List all the AWS account/STS role relationships registered with Vault.
`
const pathListStsHelpDesc = `
AWS accounts will be listed by account ID, along with their respective role names.
`
......@@ -79,12 +79,23 @@ needs to be supplied along with 'identity' parameter.`,
// instanceIamRoleARN fetches the IAM role ARN associated with the given
// instance profile name
func (b *backend) instanceIamRoleARN(s logical.Storage, instanceProfileName, region string) (string, error) {
func (b *backend) instanceIamRoleARN(s logical.Storage, instanceProfileName, region, accountID string) (string, error) {
if instanceProfileName == "" {
return "", fmt.Errorf("missing instance profile name")
}
iamClient, err := b.clientIAM(s, region)
// Check if an STS configuration exists for the AWS account
sts, err := b.lockedAwsStsEntry(s, accountID)
if err != nil {
return "", fmt.Errorf("error fetching STS config for account ID %q: %q\n", accountID, err)
}
// An empty STS role signifies the master account
stsRole := ""
if sts != nil {
stsRole = sts.StsRole
}
iamClient, err := b.clientIAM(s, region, stsRole)
if err != nil {
return "", err
}
......@@ -116,9 +127,21 @@ func (b *backend) instanceIamRoleARN(s logical.Storage, instanceProfileName, reg
// validateInstance queries the status of the EC2 instance using AWS EC2 API
// and checks if the instance is running and is healthy
func (b *backend) validateInstance(s logical.Storage, instanceID, region string) (*ec2.DescribeInstancesOutput, error) {
func (b *backend) validateInstance(s logical.Storage, instanceID, region, accountID string) (*ec2.DescribeInstancesOutput, error) {
// Check if an STS configuration exists for the AWS account
sts, err := b.lockedAwsStsEntry(s, accountID)
if err != nil {
return nil, fmt.Errorf("error fetching STS config for account ID %q: %q\n", accountID, err)
}
// An empty STS role signifies the master account
stsRole := ""
if sts != nil {
stsRole = sts.StsRole
}
// Create an EC2 client to pull the instance information
ec2Client, err := b.clientEC2(s, region)
ec2Client, err := b.clientEC2(s, region, stsRole)
if err != nil {
return nil, err
}
......@@ -380,7 +403,7 @@ func (b *backend) pathLoginUpdate(
// Validate the instance ID by making a call to AWS EC2 DescribeInstances API
// and fetching the instance description. Validation succeeds only if the
// instance is in 'running' state.
instanceDesc, err := b.validateInstance(req.Storage, identityDocParsed.InstanceID, identityDocParsed.Region)
instanceDesc, err := b.validateInstance(req.Storage, identityDocParsed.InstanceID, identityDocParsed.Region, identityDocParsed.AccountID)
if err != nil {
return logical.ErrorResponse(fmt.Sprintf("failed to verify instance ID: %v", err)), nil
}
......@@ -449,7 +472,7 @@ func (b *backend) pathLoginUpdate(
}
// Use instance profile ARN to fetch the associated role ARN
iamRoleARN, err := b.instanceIamRoleARN(req.Storage, iamInstanceProfileName, identityDocParsed.Region)
iamRoleARN, err := b.instanceIamRoleARN(req.Storage, iamInstanceProfileName, identityDocParsed.Region, identityDocParsed.AccountID)
if err != nil {
return nil, fmt.Errorf("IAM role ARN could not be fetched: %v", err)
}
......@@ -622,6 +645,7 @@ func (b *backend) pathLoginUpdate(
Metadata: map[string]string{
"instance_id": identityDocParsed.InstanceID,
"region": identityDocParsed.Region,
"account_id": identityDocParsed.AccountID,
"role_tag_max_ttl": rTagMaxTTL.String(),
"role": roleName,
"ami_id": identityDocParsed.AmiID,
......@@ -745,8 +769,16 @@ func (b *backend) pathLoginRenew(
return nil, fmt.Errorf("unable to fetch region from metadata during renewal")
}
// Ensure backwards compatibility for older clients without account_id saved in metadata
accountID, ok := req.Auth.Metadata["account_id"]
if ok {
if accountID == "" {
return nil, fmt.Errorf("unable to fetch account_id from metadata during renewal")
}
}
// Cross check that the instance is still in 'running' state
_, err := b.validateInstance(req.Storage, instanceID, region)
_, err := b.validateInstance(req.Storage, instanceID, region, accountID)
if err != nil {
return nil, fmt.Errorf("failed to verify instance ID %q: %q", instanceID, err)
}
......
......@@ -262,6 +262,20 @@ will not be aware of such events. The token issued will still be valid, until
it expires. The token will likely be expired sooner than its lifetime when the
instance fails to renew the token on time.
### Cross Account Access
To allow Vault to authenticate EC2 instances running in other accounts, AWS STS (Security
Token Service) can be used to retrieve temporary credentials by assuming an IAM Role
in those accounts.
The account in which Vault is running (i.e. the master account) must be listed as
a trusted entity in the IAM Role being assumed on the remote account. The Role itself
must allow the `ec2:DescribeInstances` action, and `iam:GetInstanceProfile` if IAM Role
binding is used (see below).
Furthermore, in the master account, Vault must be granted the action `sts:AssumeRole`
for the IAM Role to be assumed.
## Authentication
### Via the CLI
......@@ -609,6 +623,150 @@ The response will be in JSON. For example:
</dd>
</dl>
### /auth/aws-ec2/config/sts/<account_id>
#### POST
<dl class="api">
<dt>Description</dt>
<dd>
Allows the explicit association of STS roles to satellite AWS accounts (i.e. those
which are not the account in which the Vault server is running.) Login attempts from
EC2 instances running in these accounts will be verified using credentials obtained
by assumption of these STS roles.
</dd>
<dt>Method</dt>
<dd>POST</dd>
<dt>URL</dt>
<dd>`/auth/aws-ec2/config/certificate/<account_id>`</dd>
<dt>Parameters</dt>
<dd>
<ul>
<li>
<span class="param">account_id</span>
<span class="param-flags">required</span>
AWS account ID to be associated with STS role. If set,
Vault will use assumed credentials to verify any login attempts from EC2
instances in this account.
</li>
</ul>
<ul>
<li>
<span class="param">sts_role</span>
<span class="param-flags">required</span>
AWS ARN for STS role to be assumed when interacting with the account specified.
The Vault server must have permissions to assume this role.
</li>
</ul>
</dd>
<dt>Returns</dt>
<dd>`204` response code.
</dd>
</dl>
#### GET
<dl class="api">
<dt>Description</dt>
<dd>
Returns the previously configured STS role.
</dd>
<dt>Method</dt>
<dd>GET</dd>
<dt>URL</dt>
<dd>`/auth/aws-ec2/config/sts/<account_id>`</dd>
<dt>Parameters</dt>
<dd>
None.
</dd>
<dt>Returns</dt>
<dd>
```javascript
{
"auth": null,
"warnings": null,
"data": {
"sts_role ": "arn:aws:iam:<account_id>:role/myRole"
},
"lease_duration": 0,
"renewable": false,
"lease_id": ""
}
```
</dd>
</dl>
#### LIST
<dl class="api">
<dt>Description</dt>
<dd>
Lists all the AWS Account IDs for which an STS role is registered
</dd>
<dt>Method</dt>
<dd>LIST/GET</dd>
<dt>URL</dt>
<dd>`/auth/aws-ec2/config/sts` (LIST) or `/auth/aws-ec2/config/sts?list=true` (GET)</dd>
<dt>Parameters</dt>
<dd>
None.
</dd>
<dt>Returns</dt>
<dd>
```javascript
{
"auth": null,
"warnings": null,
"data": {
"keys": [
"<account_id_1>",
"<account_id_2>"
]
},
"lease_duration": 0,
"renewable": false,
"lease_id": ""
}
```
</dd>
</dl>
#### DELETE
<dl class="api">
<dt>Description</dt>
<dd>
Deletes a previously configured AWS account/STS role association
</dd>
<dt>Method</dt>
<dd>DELETE</dd>
<dt>URL</dt>
<dd>`/auth/aws-ec2/config/sts/<account_id>`</dd>
<dt>Parameters</dt>
<dd>
None.
</dd>
<dt>Returns</dt>
<dd>`204` response code.
</dd>
</dl>
### /auth/aws-ec2/config/tidy/identity-whitelist
##### POST
<dl class="api">
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment