Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

common: use regional STS on non-default regions #479

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions builder/chroot/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"fmt"
"runtime"

"github.com/aws/aws-sdk-go/aws/endpoints"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/hcl/v2/hcldec"
awscommon "github.com/hashicorp/packer-plugin-amazon/builder/common"
Expand Down Expand Up @@ -420,6 +421,22 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
if err != nil {
return nil, err
}

// If the AMI copies to a region that is not part of the default regions,
// we will switch to using regional STS endpoints for authentication, as
// these non-default regions only support STSv2 tokens, and by default
// we reach global endpoints which provide STSv1 tokens, leading to
// errors when copying to those non-default regional endpoints.
nonDefaultRegions := b.config.AMIConfig.NonDefaultRegions(&b.config.AccessConfig)
if nonDefaultRegions != nil &&
session.Config.STSRegionalEndpoint == endpoints.LegacySTSEndpoint {
ui.Say(fmt.Sprintf("The configuration uses non-default regions: %v\n"+
"This will likely fail when contacting those endpoints.\n"+
"To make this message disappear, AWS_STS_REGIONAL_ENDPOINTS=regional "+
"should be set in your environment", nonDefaultRegions))
session.Config.STSRegionalEndpoint = endpoints.RegionalSTSEndpoint
}

ec2conn := ec2.New(session)

wrappedCommand := func(command string) (string, error) {
Expand Down
1 change: 1 addition & 0 deletions builder/common/access_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ func (c *AccessConfig) Session() (*session.Session, error) {
return nil, err
}
log.Printf("Found region %s", *sess.Config.Region)

c.session = sess

cp, err := c.session.Config.Credentials.Get()
Expand Down
77 changes: 77 additions & 0 deletions builder/common/ami_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,83 @@ func (c *AMIConfig) prepareRegions(accessConfig *AccessConfig) (errs []error) {
return errs
}

func (c AMIConfig) getRegions() []string {
regions := map[string]struct{}{}

for _, region := range c.AMIRegions {
regions[region] = struct{}{}
}

for region := range c.AMIRegionKMSKeyIDs {
regions[region] = struct{}{}
}

ret := make([]string, 0, len(regions))
for region := range regions {
ret = append(ret, region)
}

return ret
}

// NonDefaultRegions attempts to detect usage of non-default regions.
//
// If a non-default region is defined, the build should use regional STS
// endpoints instead of the global one, as these do not support the type of
// token required by those endpoints.
//
// So this is meant to be called by builders/post-processors that need to
// interact with AWS in those regions, so they can change the value of the
// Session.STSEndpoint
func (c *AMIConfig) NonDefaultRegions(accessConfig *AccessConfig) []string {
var retRegions []string

// If the default (build) region is already a non-default one, we will
// automatically use STSv2 tokens, even in 'legacy' (default) mode. Therefore
// in such a case, we can immediately return as we won't have a problem
// afterwards when it is time to copy the AMI to other regions.
if IsNonDefaultRegion(accessConfig.RawRegion) {
return retRegions
}

regions := c.getRegions()
for _, reg := range regions {
if IsNonDefaultRegion(reg) {
retRegions = append(retRegions, reg)
}
}

return retRegions
}

// Return true if the `region` is not a default one.
//
// Any region that is not one of those that are defined here will require opt-in
// and STSv2, hence why we try to figure it out here.
func IsNonDefaultRegion(region string) bool {
switch region {
case "ap-south-1",
"eu-north-1",
"eu-west-3",
"eu-west-2",
"eu-west-1",
"ap-northeast-3",
"ap-northeast-2",
"ap-northeast-1",
"ca-central-1",
"sa-east-1",
"ap-southeast-1",
"ap-southeast-2",
"eu-central-1",
"us-east-1",
"us-east-2",
"us-west-1",
"us-west-2":
return false
}
return true
}

// See https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_CopyImage.html
func ValidateKmsKey(kmsKey string) (valid bool) {
//Pattern for matching KMS Key ID for multi-region keys
Expand Down
16 changes: 16 additions & 0 deletions builder/ebs/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"fmt"
"time"

"github.com/aws/aws-sdk-go/aws/endpoints"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/iam"
"github.com/hashicorp/hcl/v2/hcldec"
Expand Down Expand Up @@ -208,6 +209,21 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
return nil, err
}

// If the AMI copies to a region that is not part of the default regions,
// we will switch to using regional STS endpoints for authentication, as
// these non-default regions only support STSv2 tokens, and by default
// we reach global endpoints which provide STSv1 tokens, leading to
// errors when copying to those non-default regional endpoints.
nonDefaultRegions := b.config.AMIConfig.NonDefaultRegions(&b.config.AccessConfig)
if nonDefaultRegions != nil &&
session.Config.STSRegionalEndpoint == endpoints.LegacySTSEndpoint {
ui.Say(fmt.Sprintf("The configuration uses non-default regions: %v\n"+
"This will likely fail when contacting those endpoints.\n"+
"To make this message disappear, AWS_STS_REGIONAL_ENDPOINTS=regional "+
"should be set in your environment", nonDefaultRegions))
session.Config.STSRegionalEndpoint = endpoints.RegionalSTSEndpoint
}

ec2conn := ec2.New(session)
iam := iam.New(session)
// Setup the state bag and initial state for the steps
Expand Down
6 changes: 3 additions & 3 deletions builder/ebs/builder_acc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func TestAccBuilder_EbsRegionCopy(t *testing.T) {
}
_ = ami.CleanUpAmi()
ami = amazon_acc.AMIHelper{
Region: "us-west-2",
Region: "ca-west-1",
Name: amiName,
}
_ = ami.CleanUpAmi()
Expand All @@ -76,7 +76,7 @@ func TestAccBuilder_EbsRegionCopy(t *testing.T) {
return fmt.Errorf("Bad exit code. Logfile: %s", logfile)
}
}
return checkRegionCopy(amiName, []string{"us-east-1", "us-west-2"})
return checkRegionCopy(amiName, []string{"us-east-1", "ca-west-1"})
},
}
acctest.TestPlugin(t, testCase)
Expand Down Expand Up @@ -1546,7 +1546,7 @@ const testBuilderAccRegionCopy = `
"source_ami": "ami-76b2a71e",
"ssh_username": "ubuntu",
"ami_name": "%s",
"ami_regions": ["us-east-1", "us-west-2"]
"ami_regions": ["us-east-1", "ca-west-1"]
}]
}
`
Expand Down
16 changes: 16 additions & 0 deletions builder/ebssurrogate/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"errors"
"fmt"

"github.com/aws/aws-sdk-go/aws/endpoints"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/iam"
"github.com/hashicorp/hcl/v2/hcldec"
Expand Down Expand Up @@ -232,6 +233,21 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
return nil, err
}

// If the AMI copies to a region that is not part of the default regions,
// we will switch to using regional STS endpoints for authentication, as
// these non-default regions only support STSv2 tokens, and by default
// we reach global endpoints which provide STSv1 tokens, leading to
// errors when copying to those non-default regional endpoints.
nonDefaultRegions := b.config.AMIConfig.NonDefaultRegions(&b.config.AccessConfig)
if nonDefaultRegions != nil &&
session.Config.STSRegionalEndpoint == endpoints.LegacySTSEndpoint {
ui.Say(fmt.Sprintf("The configuration uses non-default regions: %v\n"+
"This will likely fail when contacting those endpoints.\n"+
"To make this message disappear, AWS_STS_REGIONAL_ENDPOINTS=regional "+
"should be set in your environment", nonDefaultRegions))
session.Config.STSRegionalEndpoint = endpoints.RegionalSTSEndpoint
}

ec2conn := ec2.New(session)
iam := iam.New(session)

Expand Down
17 changes: 17 additions & 0 deletions builder/instance/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"os"
"strings"

"github.com/aws/aws-sdk-go/aws/endpoints"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/iam"
"github.com/hashicorp/hcl/v2/hcldec"
Expand Down Expand Up @@ -258,6 +259,22 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
if err != nil {
return nil, err
}

// If the AMI copies to a region that is not part of the default regions,
// we will switch to using regional STS endpoints for authentication, as
// these non-default regions only support STSv2 tokens, and by default
// we reach global endpoints which provide STSv1 tokens, leading to
// errors when copying to those non-default regional endpoints.
nonDefaultRegions := b.config.AMIConfig.NonDefaultRegions(&b.config.AccessConfig)
if nonDefaultRegions != nil &&
session.Config.STSRegionalEndpoint == endpoints.LegacySTSEndpoint {
ui.Say(fmt.Sprintf("The configuration uses non-default regions: %v\n"+
"This will likely fail when contacting those endpoints.\n"+
"To make this message disappear, AWS_STS_REGIONAL_ENDPOINTS=regional "+
"should be set in your environment", nonDefaultRegions))
session.Config.STSRegionalEndpoint = endpoints.RegionalSTSEndpoint
}

ec2conn := ec2.New(session)
iam := iam.New(session)

Expand Down
Loading