diff --git a/main.go b/main.go index b551cf32a5..dd1ab4b52a 100644 --- a/main.go +++ b/main.go @@ -94,7 +94,7 @@ func main() { var p provider.Provider switch cfg.Provider { case "aws": - p, err = provider.NewAWSProvider(domainFilter, zoneIDFilter, zoneTypeFilter, cfg.DryRun) + p, err = provider.NewAWSProvider(domainFilter, zoneIDFilter, zoneTypeFilter, cfg.AWSAssumeRole, cfg.DryRun) case "azure": p, err = provider.NewAzureProvider(cfg.AzureConfigFile, domainFilter, zoneIDFilter, cfg.AzureResourceGroup, cfg.DryRun) case "cloudflare": diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go index 78f639e308..3bc23ad15b 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -50,6 +50,7 @@ type Config struct { DomainFilter []string ZoneIDFilter []string AWSZoneType string + AWSAssumeRole string AzureConfigFile string AzureResourceGroup string CloudflareProxied bool @@ -90,6 +91,7 @@ var defaultConfig = &Config{ GoogleProject: "", DomainFilter: []string{}, AWSZoneType: "", + AWSAssumeRole: "", AzureConfigFile: "/etc/kubernetes/azure.json", AzureResourceGroup: "", CloudflareProxied: false, @@ -164,6 +166,7 @@ func (cfg *Config) ParseFlags(args []string) error { app.Flag("zone-id-filter", "Filter target zones by hosted zone id; specify multiple times for multiple zones (optional)").Default("").StringsVar(&cfg.ZoneIDFilter) app.Flag("google-project", "When using the Google provider, current project is auto-detected, when running on GCP. Specify other project with this. Must be specified when running outside GCP.").Default(defaultConfig.GoogleProject).StringVar(&cfg.GoogleProject) app.Flag("aws-zone-type", "When using the AWS provider, filter for zones of this type (optional, options: public, private)").Default(defaultConfig.AWSZoneType).EnumVar(&cfg.AWSZoneType, "", "public", "private") + app.Flag("aws-assume-role", "When using the AWS provider, assume this IAM role. Useful for hosted zones in another AWS account. Specify the full ARN, e.g. `arn:aws:iam::123455567:role/external-dns` (optional)").Default(defaultConfig.AWSAssumeRole).StringVar(&cfg.AWSAssumeRole) app.Flag("azure-config-file", "When using the Azure provider, specify the Azure configuration file (required when --provider=azure").Default(defaultConfig.AzureConfigFile).StringVar(&cfg.AzureConfigFile) app.Flag("azure-resource-group", "When using the Azure provider, override the Azure resource group to use (optional)").Default(defaultConfig.AzureResourceGroup).StringVar(&cfg.AzureResourceGroup) app.Flag("cloudflare-proxied", "When using the Cloudflare provider, specify if the proxy mode must be enabled (default: disabled)").BoolVar(&cfg.CloudflareProxied) diff --git a/pkg/apis/externaldns/types_test.go b/pkg/apis/externaldns/types_test.go index 1cfb0d97ca..a256a4ab3d 100644 --- a/pkg/apis/externaldns/types_test.go +++ b/pkg/apis/externaldns/types_test.go @@ -40,6 +40,7 @@ var ( DomainFilter: []string{""}, ZoneIDFilter: []string{""}, AWSZoneType: "", + AWSAssumeRole: "", AzureConfigFile: "/etc/kubernetes/azure.json", AzureResourceGroup: "", CloudflareProxied: false, @@ -74,6 +75,7 @@ var ( DomainFilter: []string{"example.org", "company.com"}, ZoneIDFilter: []string{"/hostedzone/ZTST1", "/hostedzone/ZTST2"}, AWSZoneType: "private", + AWSAssumeRole: "some-other-role", AzureConfigFile: "azure.json", AzureResourceGroup: "arg", CloudflareProxied: true, @@ -141,6 +143,7 @@ func TestParseFlags(t *testing.T) { "--zone-id-filter=/hostedzone/ZTST1", "--zone-id-filter=/hostedzone/ZTST2", "--aws-zone-type=private", + "--aws-assume-role=some-other-role", "--policy=upsert-only", "--registry=noop", "--txt-owner-id=owner-1", @@ -180,6 +183,7 @@ func TestParseFlags(t *testing.T) { "EXTERNAL_DNS_DOMAIN_FILTER": "example.org\ncompany.com", "EXTERNAL_DNS_ZONE_ID_FILTER": "/hostedzone/ZTST1\n/hostedzone/ZTST2", "EXTERNAL_DNS_AWS_ZONE_TYPE": "private", + "EXTERNAL_DNS_AWS_ASSUME_ROLE": "some-other-role", "EXTERNAL_DNS_POLICY": "upsert-only", "EXTERNAL_DNS_REGISTRY": "noop", "EXTERNAL_DNS_TXT_OWNER_ID": "owner-1", diff --git a/provider/aws.go b/provider/aws.go index 6204cf8d7b..eb284de46e 100644 --- a/provider/aws.go +++ b/provider/aws.go @@ -21,6 +21,7 @@ import ( "strings" "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/route53" "github.com/kubernetes-incubator/external-dns/endpoint" @@ -79,10 +80,10 @@ type AWSProvider struct { } // NewAWSProvider initializes a new AWS Route53 based Provider. -func NewAWSProvider(domainFilter DomainFilter, zoneIDFilter ZoneIDFilter, zoneTypeFilter ZoneTypeFilter, dryRun bool) (*AWSProvider, error) { +func NewAWSProvider(domainFilter DomainFilter, zoneIDFilter ZoneIDFilter, zoneTypeFilter ZoneTypeFilter, assumeRole string, dryRun bool) (*AWSProvider, error) { config := aws.NewConfig() - config = config.WithHTTPClient( + config.WithHTTPClient( instrumented_http.NewClient(config.HTTPClient, &instrumented_http.Callbacks{ PathProcessor: func(path string) string { parts := strings.Split(path, "/") @@ -99,6 +100,11 @@ func NewAWSProvider(domainFilter DomainFilter, zoneIDFilter ZoneIDFilter, zoneTy return nil, err } + if assumeRole != "" { + log.Infof("Assuming role: %s", assumeRole) + session.Config.WithCredentials(stscreds.NewCredentials(session, assumeRole)) + } + provider := &AWSProvider{ client: route53.New(session), domainFilter: domainFilter,