diff --git a/go.mod b/go.mod index b91b36bbc..6793fd0eb 100644 --- a/go.mod +++ b/go.mod @@ -9,8 +9,11 @@ require ( github.com/Azure/go-autorest/autorest v0.11.30 github.com/Azure/go-autorest/autorest/azure/auth v0.5.13 github.com/apparentlymart/go-cidr v1.1.0 - github.com/aws/aws-sdk-go v1.55.7 + github.com/aws/aws-sdk-go-v2 v1.36.5 github.com/aws/aws-sdk-go-v2/config v1.29.17 + github.com/aws/aws-sdk-go-v2/credentials v1.17.70 + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.32 + github.com/aws/aws-sdk-go-v2/service/route53 v1.52.2 github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.35.7 github.com/coredns/caddy v1.1.2-0.20241029205200-8de985351a98 github.com/dnstap/golang-dnstap v0.4.0 @@ -75,9 +78,6 @@ require ( github.com/DataDog/sketches-go v1.4.7 // indirect github.com/Masterminds/semver/v3 v3.3.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/aws/aws-sdk-go-v2 v1.36.5 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.17.70 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.32 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.36 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.36 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect @@ -120,7 +120,6 @@ require ( github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect github.com/googleapis/gax-go/v2 v2.14.2 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect - github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kylelemons/godebug v1.1.0 // indirect diff --git a/go.sum b/go.sum index b1d2b59a4..10542608b 100644 --- a/go.sum +++ b/go.sum @@ -72,8 +72,6 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/apparentlymart/go-cidr v1.1.0 h1:2mAhrMoF+nhXqxTzSZMUzDHkLjmIHC+Zzn4tdgBZjnU= github.com/apparentlymart/go-cidr v1.1.0/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc= -github.com/aws/aws-sdk-go v1.55.7 h1:UJrkFq7es5CShfBwlWAC8DA077vp8PyVbQd3lqLiztE= -github.com/aws/aws-sdk-go v1.55.7/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/aws/aws-sdk-go-v2 v1.36.5 h1:0OF9RiEMEdDdZEMqF9MRjevyxAQcf6gY+E7vwBILFj0= github.com/aws/aws-sdk-go-v2 v1.36.5/go.mod h1:EYrzvCCN9CMUTa5+6lf6MM4tq3Zjp8UhSGR/cBsjai0= github.com/aws/aws-sdk-go-v2/config v1.29.17 h1:jSuiQ5jEe4SAMH6lLRMY9OVC+TqJLP5655pBGjmnjr0= @@ -92,6 +90,8 @@ github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.4 h1:CXV68E2 github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.4/go.mod h1:/xFi9KtvBXP97ppCz1TAEvU1Uf66qvid89rbem3wCzQ= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.17 h1:t0E6FzREdtCsiLIoLCWsYliNsRBgyGD/MCK571qk4MI= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.17/go.mod h1:ygpklyoaypuyDvOM5ujWGrYWpAK3h7ugnmKCU/76Ys4= +github.com/aws/aws-sdk-go-v2/service/route53 v1.52.2 h1:dXHWVVPx2W2fq2PTugj8QXpJ0YTRAGx0KLPKhMBmcsY= +github.com/aws/aws-sdk-go-v2/service/route53 v1.52.2/go.mod h1:wi1naoiPnCQG3cyjsivwPON1ZmQt/EJGxFqXzubBTAw= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.35.7 h1:d+mnMa4JbJlooSbYQfrJpit/YINaB30JEVgrhtjZneA= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.35.7/go.mod h1:1X1NotbcGHH7PCQJ98PsExSxsJj/VWzz8MfFz43+02M= github.com/aws/aws-sdk-go-v2/service/sso v1.25.5 h1:AIRJ3lfb2w/1/8wOOSqYb9fUKGwQbtysJ2H1MofRUPg= @@ -213,10 +213,6 @@ github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 h1 github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw= github.com/infobloxopen/go-trees v0.0.0-20200715205103-96a057b8dfb9 h1:w66aaP3c6SIQ0pi3QH1Tb4AMO3aWoEPxd1CNvLphbkA= github.com/infobloxopen/go-trees v0.0.0-20200715205103-96a057b8dfb9/go.mod h1:BaIJzjD2ZnHmx2acPF6XfGLPzNCMiBbMRqJr+8/8uRI= -github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= -github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -533,10 +529,7 @@ gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/plugin/route53/route53.go b/plugin/route53/route53.go index 3d3c0f44a..fc1f06d16 100644 --- a/plugin/route53/route53.go +++ b/plugin/route53/route53.go @@ -17,9 +17,9 @@ import ( "github.com/coredns/coredns/plugin/pkg/upstream" "github.com/coredns/coredns/request" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/route53" - "github.com/aws/aws-sdk-go/service/route53/route53iface" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/route53" + "github.com/aws/aws-sdk-go-v2/service/route53/types" "github.com/miekg/dns" ) @@ -29,7 +29,7 @@ type Route53 struct { Fall fall.F zoneNames []string - client route53iface.Route53API + client route53Client upstream *upstream.Upstream refresh time.Duration @@ -50,12 +50,12 @@ type zones map[string][]*zone // does exist, and returns a new *Route53. In addition to this, upstream is use // for doing recursive queries against CNAMEs. Returns error if it cannot // verify any given domain name/zone id pair. -func New(ctx context.Context, c route53iface.Route53API, keys map[string][]string, refresh time.Duration) (*Route53, error) { +func New(ctx context.Context, c route53Client, keys map[string][]string, refresh time.Duration) (*Route53, error) { zones := make(map[string][]*zone, len(keys)) zoneNames := make([]string, 0, len(keys)) for dns, hostedZoneIDs := range keys { for _, hostedZoneID := range hostedZoneIDs { - _, err := c.ListHostedZonesByNameWithContext(ctx, &route53.ListHostedZonesByNameInput{ + _, err := c.ListHostedZonesByName(ctx, &route53.ListHostedZonesByNameInput{ DNSName: aws.String(dns), HostedZoneId: aws.String(hostedZoneID), }) @@ -211,19 +211,19 @@ func maybeUnescape(s string) (string, error) { } } -func updateZoneFromRRS(rrs *route53.ResourceRecordSet, z *file.Zone) error { +func updateZoneFromRRS(rrs *types.ResourceRecordSet, z *file.Zone) error { for _, rr := range rrs.ResourceRecords { - n, err := maybeUnescape(aws.StringValue(rrs.Name)) + n, err := maybeUnescape(aws.ToString(rrs.Name)) if err != nil { - return fmt.Errorf("failed to unescape `%s' name: %v", aws.StringValue(rrs.Name), err) + return fmt.Errorf("failed to unescape `%s' name: %v", aws.ToString(rrs.Name), err) } - v, err := maybeUnescape(aws.StringValue(rr.Value)) + v, err := maybeUnescape(aws.ToString(rr.Value)) if err != nil { - return fmt.Errorf("failed to unescape `%s' value: %v", aws.StringValue(rr.Value), err) + return fmt.Errorf("failed to unescape `%s' value: %v", aws.ToString(rr.Value), err) } // Assemble RFC 1035 conforming record to pass into dns scanner. - rfc1035 := fmt.Sprintf("%s %d IN %s %s", n, aws.Int64Value(rrs.TTL), aws.StringValue(rrs.Type), v) + rfc1035 := fmt.Sprintf("%s %d IN %s %s", n, aws.ToInt64(rrs.TTL), rrs.Type, v) r, err := dns.NewRR(rfc1035) if err != nil { return fmt.Errorf("failed to parse resource record: %v", err) @@ -253,21 +253,28 @@ func (h *Route53) updateZones(ctx context.Context) error { newZ.Upstream = h.upstream in := &route53.ListResourceRecordSetsInput{ HostedZoneId: aws.String(hostedZone.id), - MaxItems: aws.String("1000"), + MaxItems: aws.Int32(1000), } - err = h.client.ListResourceRecordSetsPagesWithContext(ctx, in, - func(out *route53.ListResourceRecordSetsOutput, last bool) bool { - for _, rrs := range out.ResourceRecordSets { - if err := updateZoneFromRRS(rrs, newZ); err != nil { - // Maybe unsupported record type. Log and carry on. - log.Warningf("Failed to process resource record set: %v", err) - } + complete := false + var out *route53.ListResourceRecordSetsOutput + for out, err = h.client.ListResourceRecordSets(ctx, in); !complete; out, err = h.client.ListResourceRecordSets(ctx, in) { + if err != nil { + err = fmt.Errorf("failed to list resource records for %v:%v from route53: %v", zName, hostedZone.id, err) + return + } + for _, rrs := range out.ResourceRecordSets { + if err := updateZoneFromRRS(&rrs, newZ); err != nil { + // Maybe unsupported record type. Log and carry on. + log.Warningf("Failed to process resource record set: %v", err) } - return true - }) - if err != nil { - err = fmt.Errorf("failed to list resource records for %v:%v from route53: %v", zName, hostedZone.id, err) - return + } + if out.IsTruncated { + in.StartRecordName = out.NextRecordName + in.StartRecordType = out.NextRecordType + in.StartRecordIdentifier = out.NextRecordIdentifier + } else { + complete = true + } } h.zMu.Lock() (*z[i]).z = newZ diff --git a/plugin/route53/route53_test.go b/plugin/route53/route53_test.go index d9b2fa102..538f05a3f 100644 --- a/plugin/route53/route53_test.go +++ b/plugin/route53/route53_test.go @@ -12,28 +12,28 @@ import ( "github.com/coredns/coredns/plugin/test" crequest "github.com/coredns/coredns/request" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/service/route53" - "github.com/aws/aws-sdk-go/service/route53/route53iface" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/route53" + "github.com/aws/aws-sdk-go-v2/service/route53/types" "github.com/miekg/dns" ) type fakeRoute53 struct { - route53iface.Route53API + route53Client } -func (fakeRoute53) ListHostedZonesByNameWithContext(_ aws.Context, input *route53.ListHostedZonesByNameInput, _ ...request.Option) (*route53.ListHostedZonesByNameOutput, error) { +func (fakeRoute53) ListHostedZonesByName(_ context.Context, input *route53.ListHostedZonesByNameInput, optFns ...func(*route53.Options)) (*route53.ListHostedZonesByNameOutput, error) { return nil, nil } -func (fakeRoute53) ListResourceRecordSetsPagesWithContext(_ aws.Context, in *route53.ListResourceRecordSetsInput, fn func(*route53.ListResourceRecordSetsOutput, bool) bool, _ ...request.Option) error { - if aws.StringValue(in.HostedZoneId) == "0987654321" { - return errors.New("bad. zone is bad") +func (fakeRoute53) ListResourceRecordSets(_ context.Context, in *route53.ListResourceRecordSetsInput, optFns ...func(*route53.Options)) (*route53.ListResourceRecordSetsOutput, error) { + if aws.ToString(in.HostedZoneId) == "0987654321" { + return nil, errors.New("bad. zone is bad") } - rrsResponse := map[string][]*route53.ResourceRecordSet{} + rrsResponse := map[string][]types.ResourceRecordSet{} for _, r := range []struct { - rType, name, value, hostedZoneID string + rType types.RRType + name, value, hostedZoneID string }{ {"A", "example.org.", "1.2.3.4", "1234567890"}, {"A", "www.example.org", "1.2.3.4", "1234567890"}, @@ -54,11 +54,11 @@ func (fakeRoute53) ListResourceRecordSetsPagesWithContext(_ aws.Context, in *rou } { rrs, ok := rrsResponse[r.hostedZoneID] if !ok { - rrs = make([]*route53.ResourceRecordSet, 0) + rrs = make([]types.ResourceRecordSet, 0) } - rrs = append(rrs, &route53.ResourceRecordSet{Type: aws.String(r.rType), + rrs = append(rrs, types.ResourceRecordSet{Type: r.rType, Name: aws.String(r.name), - ResourceRecords: []*route53.ResourceRecord{ + ResourceRecords: []types.ResourceRecord{ { Value: aws.String(r.value), }, @@ -68,12 +68,10 @@ func (fakeRoute53) ListResourceRecordSetsPagesWithContext(_ aws.Context, in *rou rrsResponse[r.hostedZoneID] = rrs } - if ok := fn(&route53.ListResourceRecordSetsOutput{ - ResourceRecordSets: rrsResponse[aws.StringValue(in.HostedZoneId)], - }, true); !ok { - return errors.New("paging function return false") - } - return nil + return &route53.ListResourceRecordSetsOutput{ + ResourceRecordSets: rrsResponse[aws.ToString(in.HostedZoneId)], + IsTruncated: false, + }, nil } func TestRoute53(t *testing.T) { diff --git a/plugin/route53/setup.go b/plugin/route53/setup.go index 2fbd374cd..857158053 100644 --- a/plugin/route53/setup.go +++ b/plugin/route53/setup.go @@ -14,12 +14,11 @@ import ( "github.com/coredns/coredns/plugin/pkg/fall" clog "github.com/coredns/coredns/plugin/pkg/log" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/credentials" - "github.com/aws/aws-sdk-go/aws/defaults" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/route53" - "github.com/aws/aws-sdk-go/service/route53/route53iface" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/credentials" + "github.com/aws/aws-sdk-go-v2/feature/ec2/imds" + "github.com/aws/aws-sdk-go-v2/service/route53" ) var log = clog.NewWithPlugin("route53") @@ -27,8 +26,27 @@ var log = clog.NewWithPlugin("route53") func init() { plugin.Register("route53", setup) } // exposed for testing -var f = func(opts session.Options) route53iface.Route53API { - return route53.New(session.Must(session.NewSessionWithOptions(opts))) +type route53Client interface { + ActivateKeySigningKey(ctx context.Context, params *route53.ActivateKeySigningKeyInput, optFns ...func(*route53.Options)) (*route53.ActivateKeySigningKeyOutput, error) + ListHostedZonesByName(ctx context.Context, params *route53.ListHostedZonesByNameInput, optFns ...func(*route53.Options)) (*route53.ListHostedZonesByNameOutput, error) + ListResourceRecordSets(ctx context.Context, params *route53.ListResourceRecordSetsInput, optFns ...func(*route53.Options)) (*route53.ListResourceRecordSetsOutput, error) +} + +var f = func(ctx context.Context, cfgOpts []func(*config.LoadOptions) error, clientOpts []func(*route53.Options)) (route53Client, error) { + cfg, err := config.LoadDefaultConfig(ctx, cfgOpts...) + if err != nil { + return nil, err + } + // If no region is specified, retrieve one from IMDS (SDK v1 used the AWS global partition as a fallback, v2 doesn't) + if cfg.Region == "" { + imdsClient := imds.NewFromConfig(cfg) + region, err := imdsClient.GetRegion(ctx, &imds.GetRegionInput{}) + if err != nil { + return nil, fmt.Errorf("failed to get region from IMDS: %w", err) + } + cfg.Region = region.Region + } + return route53.NewFromConfig(cfg, clientOpts...), nil } func setup(c *caddy.Controller) error { @@ -43,7 +61,8 @@ func setup(c *caddy.Controller) error { // * Shared Credentials file // * Shared Configuration file (if AWS_SDK_LOAD_CONFIG is set to truthy value) // * EC2 Instance Metadata (credentials only) - opts := session.Options{} + cfgOpts := []func(*config.LoadOptions) error{} + clientOpts := []func(*route53.Options){} var fall fall.F refresh := time.Duration(1) * time.Minute // default update frequency to 1 minute @@ -74,11 +93,13 @@ func setup(c *caddy.Controller) error { if len(v) < 2 { return plugin.Error("route53", c.Errf("invalid access key: '%v'", v)) } - opts.Config.Credentials = credentials.NewStaticCredentials(v[0], v[1], "") + cfgOpts = append(cfgOpts, config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(v[0], v[1], ""))) log.Warningf("Save aws_access_key in Corefile has been deprecated, please use other authentication methods instead") case "aws_endpoint": if c.NextArg() { - opts.Config.Endpoint = aws.String(c.Val()) + clientOpts = append(clientOpts, func(o *route53.Options) { + o.BaseEndpoint = aws.String(c.Val()) + }) } else { return plugin.Error("route53", c.ArgErr()) } @@ -86,17 +107,18 @@ func setup(c *caddy.Controller) error { c.RemainingArgs() // eats args case "credentials": if c.NextArg() { - opts.Profile = c.Val() + cfgOpts = append(cfgOpts, config.WithSharedConfigProfile(c.Val())) } else { return c.ArgErr() } if c.NextArg() { - opts.SharedConfigFiles = []string{c.Val()} + sharedConfigFiles := []string{c.Val()} // If AWS_SDK_LOAD_CONFIG is set also load ~/.aws/config to stay consistent // with default SDK behavior. if ok, _ := strconv.ParseBool(os.Getenv("AWS_SDK_LOAD_CONFIG")); ok { - opts.SharedConfigFiles = append(opts.SharedConfigFiles, defaults.SharedConfigFilename()) + sharedConfigFiles = append(sharedConfigFiles, config.DefaultSharedConfigFilename()) } + cfgOpts = append(cfgOpts, config.WithSharedConfigFiles(sharedConfigFiles)) } case "fallthrough": fall.SetZonesFromArgs(c.RemainingArgs()) @@ -122,8 +144,12 @@ func setup(c *caddy.Controller) error { } } - client := f(opts) ctx, cancel := context.WithCancel(context.Background()) + client, err := f(ctx, cfgOpts, clientOpts) + if err != nil { + cancel() + return plugin.Error("route53", c.Errf("failed to create route53 client: %v", err)) + } h, err := New(ctx, client, keys, refresh) if err != nil { cancel() diff --git a/plugin/route53/setup_test.go b/plugin/route53/setup_test.go index 5d2792f5f..c8bb3cc2f 100644 --- a/plugin/route53/setup_test.go +++ b/plugin/route53/setup_test.go @@ -1,17 +1,18 @@ package route53 import ( + "context" "testing" "github.com/coredns/caddy" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/route53/route53iface" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/route53" ) func TestSetupRoute53(t *testing.T) { - f = func(opts session.Options) route53iface.Route53API { - return fakeRoute53{} + f = func(_ context.Context, _ []func(*config.LoadOptions) error, _ []func(*route53.Options)) (route53Client, error) { + return fakeRoute53{}, nil } tests := []struct {