IAM Role Switching

Adds initial IAM Role Switching support and support for AWS CLI Credential and Config files.

See: https://github.com/mitchellh/packer/issues/3109
pull/3143/head
Christopher Gerber 10 years ago
parent 114bddfe36
commit 883acb18fa

@ -10,41 +10,73 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds"
"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/mitchellh/packer/template/interpolate"
)
// AccessConfig is for common configuration related to AWS access
type AccessConfig struct {
AccessKey string `mapstructure:"access_key"`
SecretKey string `mapstructure:"secret_key"`
RawRegion string `mapstructure:"region"`
Token string `mapstructure:"token"`
AccessKey string `mapstructure:"access_key"`
SecretKey string `mapstructure:"secret_key"`
RawRegion string `mapstructure:"region"`
Token string `mapstructure:"token"`
ProfileName string `mapstructure:"profile"`
}
// Config returns a valid aws.Config object for access to AWS services, or
// an error if the authentication and region couldn't be resolved
func (c *AccessConfig) Config() (*aws.Config, error) {
creds := credentials.NewChainCredentials([]credentials.Provider{
&credentials.StaticProvider{Value: credentials.Value{
AccessKeyID: c.AccessKey,
SecretAccessKey: c.SecretKey,
SessionToken: c.Token,
}},
&credentials.EnvProvider{},
&credentials.SharedCredentialsProvider{Filename: "", Profile: ""},
&ec2rolecreds.EC2RoleProvider{},
})
var creds *credentials.Credentials
profile := &CLIConfig{}
err := profile.Prepare(c.ProfileName)
if err != nil {
return nil, err
}
region, err := c.Region()
if err != nil {
return nil, err
}
return &aws.Config{
Region: aws.String(region),
Credentials: creds,
MaxRetries: aws.Int(11),
}, nil
config := &aws.Config{
Region: aws.String(region),
MaxRetries: aws.Int(11),
}
if c.ProfileName != "" {
creds = c.assumeRoleCreds(config, profile)
} else {
creds = credentials.NewChainCredentials([]credentials.Provider{
&credentials.StaticProvider{Value: credentials.Value{
AccessKeyID: c.AccessKey,
SecretAccessKey: c.SecretKey,
SessionToken: c.Token,
}},
&credentials.EnvProvider{},
&credentials.SharedCredentialsProvider{Filename: "", Profile: ""},
&ec2rolecreds.EC2RoleProvider{},
})
}
return config.WithCredentials(creds), nil
}
func (c *AccessConfig) assumeRoleCreds(conf *aws.Config, profile *CLIConfig) (*credentials.Credentials) {
src_creds := credentials.NewStaticCredentials(
profile.Source.AccessKeyID,
profile.Source.SecretAccessKey,
profile.Source.SessionToken,
)
role_cfg := aws.NewConfig().WithCredentials(src_creds)
role_cfg.MergeIn(conf)
sess := session.New(role_cfg)
return stscreds.NewCredentials(sess, *profile.AssumeRoleInput.RoleArn, func(p *stscreds.AssumeRoleProvider) {
p.RoleSessionName = *profile.AssumeRoleInput.RoleSessionName
if extId := *profile.AssumeRoleInput.ExternalId; extId != "" {
p.ExternalID = &extId
}
})
}
// Region returns the aws.Region object for access to AWS services, requesting

@ -0,0 +1,95 @@
package common
import (
"fmt"
"os"
"path"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/service/sts"
"github.com/go-ini/ini"
)
type CLIConfig struct {
SourceProfile string
Source credentials.Value
AssumeRoleInput sts.AssumeRoleInput
}
// Sets params in the struct based on the file section
func (c *CLIConfig) Prepare(name string) (error) {
cfg, err := c.config()
cfg_profile_name := fmt.Sprintf("profile %s", name)
profile_cfg, err := cfg.GetSection(cfg_profile_name)
if err != nil {
return err
}
c.SourceProfile = profile_cfg.Key("source_profile").Value();
if c.SourceProfile == "" {
c.SourceProfile = name
}
c.AssumeRoleInput.RoleArn = aws.String(profile_cfg.Key("role_arn").Value())
host, err := os.Hostname()
if err != nil {
return err
}
sessName := fmt.Sprintf("packer-%s", host)
c.AssumeRoleInput.RoleSessionName = &sessName
c.AssumeRoleInput.SerialNumber = aws.String(profile_cfg.Key("mfa_serial").Value())
if extId := aws.String(profile_cfg.Key("external_id").Value()); extId != nil {
c.AssumeRoleInput.ExternalId = extId
}
creds, err := c.credentials()
cred_cfg, err := creds.GetSection(c.SourceProfile)
if err != nil {
return err
}
if len(c.SourceProfile) != 0 {
c.Source.AccessKeyID = cred_cfg.Key("aws_access_key_id").Value()
c.Source.SecretAccessKey = cred_cfg.Key("aws_secret_access_key").Value()
c.Source.SessionToken = cred_cfg.Key("aws_session_token").Value()
}
return nil
}
func (c *CLIConfig) config() (*ini.File, error) {
config_path := os.Getenv("AWS_CONFIG_FILE")
if config_path == "" {
config_path = path.Join(os.Getenv("HOME"), ".aws", "config")
}
ini, err := c.readFile(config_path)
if err != nil {
return nil, err
}
return ini, nil
}
func (c *CLIConfig) credentials() (*ini.File, error) {
cred_path := os.Getenv("AWS_SHARED_CREDENTIALS_FILE")
if cred_path == "" {
cred_path = path.Join(os.Getenv("HOME"), ".aws", "credentials")
}
ini, err := c.readFile(cred_path)
if err != nil {
return nil, err
}
return ini, nil
}
func (c *CLIConfig) readFile(path string) (*ini.File, error) {
cfg, err := ini.Load(path)
if err != nil {
return nil, err
}
return cfg, nil
}
Loading…
Cancel
Save