From 70cdb321bbbb786f53a5896f928c04224e45687a Mon Sep 17 00:00:00 2001 From: Ryan Swanson Date: Mon, 3 Aug 2020 13:05:52 -0600 Subject: [PATCH 1/6] feat: adding route53 resolver resource Co-authored-by: Tom Neyland Co-authored-by: Max Krieg --- resources/route53-resolver-endpoints.go | 90 +++++++++++++++++++ resources/route53-resolver-rules.go | 114 ++++++++++++++++++++++++ 2 files changed, 204 insertions(+) create mode 100644 resources/route53-resolver-endpoints.go create mode 100644 resources/route53-resolver-rules.go diff --git a/resources/route53-resolver-endpoints.go b/resources/route53-resolver-endpoints.go new file mode 100644 index 000000000..aa02d6b75 --- /dev/null +++ b/resources/route53-resolver-endpoints.go @@ -0,0 +1,90 @@ +package resources + +import ( + "fmt" + + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/route53resolver" + "github.com/rebuy-de/aws-nuke/pkg/types" +) + +// Route53ResolverEndpoint is the resource type for nuking +type Route53ResolverEndpoint struct { + svc *route53resolver.Route53Resolver + id *string + name *string + ips []*route53resolver.IpAddressUpdate +} + +func init() { + register("Route53ResolverEndpoints", ListRoute53ResolverEndpoints) +} + +// ListRoute53ResolverEndpoints produces the resources to be nuked +func ListRoute53ResolverEndpoints(sess *session.Session) ([]Resource, error) { + svc := route53resolver.New(sess) + + params := &route53resolver.ListResolverEndpointsInput{} + + resources := make([]Resource, 0) + output, err := svc.ListResolverEndpoints(params) + + if err != nil { + return resources, err + } + + for _, endpoint := range output.ResolverEndpoints { + resolverEndpoint := &Route53ResolverEndpoint{ + svc: svc, + id: endpoint.Id, + name: endpoint.Name, + } + + ipsOutput, err := svc.ListResolverEndpointIpAddresses( + &route53resolver.ListResolverEndpointIpAddressesInput{ + ResolverEndpointId: endpoint.Id, + }) + + if err != nil { + return resources, err + } + + for _, ip := range ipsOutput.IpAddresses { + resolverEndpoint.ips = append(resolverEndpoint.ips, &route53resolver.IpAddressUpdate{ + Ip: ip.Ip, + IpId: ip.IpId, + SubnetId: ip.SubnetId, + }) + } + + resources = append(resources, resolverEndpoint) + } + + return resources, err +} + +// Remove implements Resource +func (r *Route53ResolverEndpoint) Remove() error { + _, err := r.svc.DeleteResolverEndpoint( + &route53resolver.DeleteResolverEndpointInput{ + ResolverEndpointId: r.id, + }) + + if err != nil { + return err + } + + return nil +} + +// Properties provides debugging output +func (r *Route53ResolverEndpoint) Properties() types.Properties { + return types.NewProperties(). + Set("EndpointID", r.id). + Set("Name", r.name) +} + +// String implements Stringer +func (r *Route53ResolverEndpoint) String() string { + return fmt.Sprintf("%s (%s)", *r.id, *r.name) +} diff --git a/resources/route53-resolver-rules.go b/resources/route53-resolver-rules.go new file mode 100644 index 000000000..d7e6c8102 --- /dev/null +++ b/resources/route53-resolver-rules.go @@ -0,0 +1,114 @@ +package resources + +import ( + "fmt" + + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/route53resolver" + "github.com/rebuy-de/aws-nuke/pkg/types" +) + +type ( + // Route53ResolverRule is the resource type + Route53ResolverRule struct { + svc *route53resolver.Route53Resolver + id *string + name *string + domainName *string + vpcIds []*string + } + + association struct { + id *string + vpcID *string + } +) + +func init() { + register("Route53ResolverRules", ListRout53ResolverRules) +} + +// ListRout53ResolverRules produces the resources to be nuked. +func ListRout53ResolverRules(sess *session.Session) ([]Resource, error) { + svc := route53resolver.New(sess) + + var resources []Resource + output, err := svc.ListResolverRules(&route53resolver.ListResolverRulesInput{}) + + if err != nil { + return resources, err + } + + associationsOutput, err := svc.ListResolverRuleAssociations(&route53resolver.ListResolverRuleAssociationsInput{}) + + if err != nil { + return resources, err + } + + vpcAssociations := map[string][]*string{} + for _, ruleAssociation := range associationsOutput.ResolverRuleAssociations { + vpcId := ruleAssociation.VPCId + if vpcId != nil { + resolverRuleID := *ruleAssociation.ResolverRuleId + + if _, ok := vpcAssociations[resolverRuleID]; !ok { + vpcAssociations[resolverRuleID] = []*string{vpcId} + } else { + vpcAssociations[resolverRuleID] = append(vpcAssociations[resolverRuleID], vpcId) + } + } + } + + for _, rule := range output.ResolverRules { + resources = append(resources, &Route53ResolverRule{ + svc: svc, + id: rule.Id, + name: rule.Name, + domainName: rule.DomainName, + vpcIds: vpcAssociations[*rule.Id], + }) + } + + return resources, nil +} + +// Filter removes resources automatically from being nuked +func (r *Route53ResolverRule) Filter() error { + if *r.domainName == "." { + return fmt.Errorf(`Filtering DomainName "."`) + } + + return nil +} + +// Remove implements Resource +func (r *Route53ResolverRule) Remove() error { + for _, vpcID := range r.vpcIds { + _, err := r.svc.DisassociateResolverRule(&route53resolver.DisassociateResolverRuleInput{ + ResolverRuleId: r.id, + VPCId: vpcID, + }) + + if err != nil { + return err + } + } + + _, err := r.svc.DeleteResolverRule(&route53resolver.DeleteResolverRuleInput{ + ResolverRuleId: r.id, + }) + + return err +} + +// Properties provides debugging output +func (r *Route53ResolverRule) Properties() types.Properties { + return types.NewProperties(). + Set("Id", r.id). + Set("Name", r.name) +} + +// String implements Stringer +func (r *Route53ResolverRule) String() string { + return fmt.Sprintf("%s (%s)", *r.id, *r.name) +} From 61a71da6fb9a9d48f2ba8fac6ab7a4e9030bed36 Mon Sep 17 00:00:00 2001 From: Tom Neyland Date: Thu, 10 Sep 2020 16:23:33 -0400 Subject: [PATCH 2/6] feat: adding route53 traffic policies Co-authored-by: Ryan Swanson Co-authored-by: Russell Centanni --- resources/route53-traffic-policies.go | 98 +++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 resources/route53-traffic-policies.go diff --git a/resources/route53-traffic-policies.go b/resources/route53-traffic-policies.go new file mode 100644 index 000000000..bac65c403 --- /dev/null +++ b/resources/route53-traffic-policies.go @@ -0,0 +1,98 @@ +package resources + +import ( + "fmt" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/route53" + "github.com/rebuy-de/aws-nuke/pkg/types" +) + +func init() { + register("Route53TrafficPolicy", ListRoute53TrafficPolicies) +} + +func ListRoute53TrafficPolicies(sess *session.Session) ([]Resource, error) { + svc := route53.New(sess) + params := &route53.ListTrafficPoliciesInput{} + resources := make([]Resource, 0) + + for { + resp, err := svc.ListTrafficPolicies(params) + if err != nil { + return nil, err + } + + for _, trafficPolicy := range resp.TrafficPolicySummaries { + instances, err := instancesForPolicy(svc, trafficPolicy.Id, trafficPolicy.LatestVersion) + + if err != nil { + return nil, fmt.Errorf("failed to get instance for policy %s %w", *trafficPolicy.Id, err) + } + + resources = append(resources, &Route53TrafficPolicy{ + svc: svc, + id: trafficPolicy.Id, + name: trafficPolicy.Name, + instances: instances, + }) + } + + if aws.BoolValue(resp.IsTruncated) == false { + break + } + params.TrafficPolicyIdMarker = resp.TrafficPolicyIdMarker + } + + return resources, nil +} + +func instancesForPolicy(svc *route53.Route53, policyID *string, version *int64) ([]*route53.TrafficPolicyInstance, error) { + var instances []*route53.TrafficPolicyInstance + params := &route53.ListTrafficPolicyInstancesByPolicyInput{ + TrafficPolicyId: policyID, + TrafficPolicyVersion: version, + } + + for { + resp, err := svc.ListTrafficPolicyInstancesByPolicy(params) + + if err != nil { + return nil, err + } + + for _, instance := range resp.TrafficPolicyInstances { + instances = append(instances, instance) + } + + if aws.BoolValue(resp.IsTruncated) { + break + } + + params.TrafficPolicyInstanceTypeMarker = resp.TrafficPolicyInstanceTypeMarker + } + return instances, nil +} + +type Route53TrafficPolicy struct { + svc *route53.Route53 + id *string + name *string + instances []*route53.TrafficPolicyInstance +} + +func (tp *Route53TrafficPolicy) Remove() error { + params := &route53.DeleteTrafficPolicyInput{ + Id: tp.id, + } + + _, err := tp.svc.DeleteTrafficPolicy(params) + return err +} + +func (tp *Route53TrafficPolicy) Properties() types.Properties { + return types.NewProperties(). + Set("ID", *tp.id). + Set("NAME", *tp.name) +} From 45b954968894e72d43c0a95b66495f6ed7c8c952 Mon Sep 17 00:00:00 2001 From: Ryan Swanson Date: Fri, 11 Sep 2020 07:44:43 -0600 Subject: [PATCH 3/6] fix: fixing traffic policy and instance deletion --- resources/route53-traffic-policies.go | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/resources/route53-traffic-policies.go b/resources/route53-traffic-policies.go index bac65c403..e5e698a68 100644 --- a/resources/route53-traffic-policies.go +++ b/resources/route53-traffic-policies.go @@ -35,6 +35,7 @@ func ListRoute53TrafficPolicies(sess *session.Session) ([]Resource, error) { svc: svc, id: trafficPolicy.Id, name: trafficPolicy.Name, + version: trafficPolicy.LatestVersion, instances: instances, }) } @@ -66,11 +67,12 @@ func instancesForPolicy(svc *route53.Route53, policyID *string, version *int64) instances = append(instances, instance) } - if aws.BoolValue(resp.IsTruncated) { + if aws.BoolValue(resp.IsTruncated) == false { break } params.TrafficPolicyInstanceTypeMarker = resp.TrafficPolicyInstanceTypeMarker + params.TrafficPolicyInstanceNameMarker = resp.TrafficPolicyInstanceNameMarker } return instances, nil } @@ -79,15 +81,28 @@ type Route53TrafficPolicy struct { svc *route53.Route53 id *string name *string + version *int64 instances []*route53.TrafficPolicyInstance } func (tp *Route53TrafficPolicy) Remove() error { + for _, instance := range tp.instances { + _, err := tp.svc.DeleteTrafficPolicyInstance(&route53.DeleteTrafficPolicyInstanceInput{ + Id: instance.Id, + }) + + if err != nil { + return fmt.Errorf("failed to delete instance %s %w", *instance.Id, err) + } + } + params := &route53.DeleteTrafficPolicyInput{ - Id: tp.id, + Id: tp.id, + Version: tp.version, } _, err := tp.svc.DeleteTrafficPolicy(params) + return err } From 1bef11cde3c9ed69a9e0a877e07c404f90409dd1 Mon Sep 17 00:00:00 2001 From: Ryan Swanson Date: Sat, 12 Sep 2020 09:13:29 -0600 Subject: [PATCH 4/6] Code review changes and NextToken checks (#508) * Removed unused types * Renamed resources to singular form * Updated return values * Updated loops to keep going until NextToken is nil --- resources/route53-resolver-endpoints.go | 43 +++++------- resources/route53-resolver-rules.go | 92 ++++++++++++++++--------- resources/route53-traffic-policies.go | 18 ++--- 3 files changed, 85 insertions(+), 68 deletions(-) diff --git a/resources/route53-resolver-endpoints.go b/resources/route53-resolver-endpoints.go index aa02d6b75..c26c37b86 100644 --- a/resources/route53-resolver-endpoints.go +++ b/resources/route53-resolver-endpoints.go @@ -13,11 +13,10 @@ type Route53ResolverEndpoint struct { svc *route53resolver.Route53Resolver id *string name *string - ips []*route53resolver.IpAddressUpdate } func init() { - register("Route53ResolverEndpoints", ListRoute53ResolverEndpoints) + register("Route53ResolverEndpoint", ListRoute53ResolverEndpoints) } // ListRoute53ResolverEndpoints produces the resources to be nuked @@ -26,41 +25,33 @@ func ListRoute53ResolverEndpoints(sess *session.Session) ([]Resource, error) { params := &route53resolver.ListResolverEndpointsInput{} - resources := make([]Resource, 0) - output, err := svc.ListResolverEndpoints(params) + var resources []Resource - if err != nil { - return resources, err - } + for { + resp, err := svc.ListResolverEndpoints(params) - for _, endpoint := range output.ResolverEndpoints { - resolverEndpoint := &Route53ResolverEndpoint{ - svc: svc, - id: endpoint.Id, - name: endpoint.Name, + if err != nil { + return nil, err } - ipsOutput, err := svc.ListResolverEndpointIpAddresses( - &route53resolver.ListResolverEndpointIpAddressesInput{ - ResolverEndpointId: endpoint.Id, - }) + for _, endpoint := range resp.ResolverEndpoints { + resolverEndpoint := &Route53ResolverEndpoint{ + svc: svc, + id: endpoint.Id, + name: endpoint.Name, + } - if err != nil { - return resources, err + resources = append(resources, resolverEndpoint) } - for _, ip := range ipsOutput.IpAddresses { - resolverEndpoint.ips = append(resolverEndpoint.ips, &route53resolver.IpAddressUpdate{ - Ip: ip.Ip, - IpId: ip.IpId, - SubnetId: ip.SubnetId, - }) + if resp.NextToken == nil { + break } - resources = append(resources, resolverEndpoint) + params.NextToken = resp.NextToken } - return resources, err + return resources, nil } // Remove implements Resource diff --git a/resources/route53-resolver-rules.go b/resources/route53-resolver-rules.go index d7e6c8102..8571de6ec 100644 --- a/resources/route53-resolver-rules.go +++ b/resources/route53-resolver-rules.go @@ -17,59 +17,85 @@ type ( domainName *string vpcIds []*string } - - association struct { - id *string - vpcID *string - } ) func init() { - register("Route53ResolverRules", ListRout53ResolverRules) + register("Route53ResolverRule", ListRout53ResolverRules) } // ListRout53ResolverRules produces the resources to be nuked. func ListRout53ResolverRules(sess *session.Session) ([]Resource, error) { svc := route53resolver.New(sess) - var resources []Resource - output, err := svc.ListResolverRules(&route53resolver.ListResolverRulesInput{}) - + vpcAssociations, err := resolverRulesToVpcIDs(svc) if err != nil { - return resources, err + return nil, err } - associationsOutput, err := svc.ListResolverRuleAssociations(&route53resolver.ListResolverRuleAssociationsInput{}) + var resources []Resource - if err != nil { - return resources, err + params := &route53resolver.ListResolverRulesInput{} + for { + resp, err := svc.ListResolverRules(params) + + if err != nil { + return nil, err + } + + for _, rule := range resp.ResolverRules { + resources = append(resources, &Route53ResolverRule{ + svc: svc, + id: rule.Id, + name: rule.Name, + domainName: rule.DomainName, + vpcIds: vpcAssociations[*rule.Id], + }) + } + + if resp.NextToken == nil { + break + } + + params.NextToken = resp.NextToken } + return resources, nil +} + +// Associate all the vpcIDs to their resolver rule ID to be disassociated before deleting the rule. +func resolverRulesToVpcIDs(svc *route53resolver.Route53Resolver) (map[string][]*string, error) { vpcAssociations := map[string][]*string{} - for _, ruleAssociation := range associationsOutput.ResolverRuleAssociations { - vpcId := ruleAssociation.VPCId - if vpcId != nil { - resolverRuleID := *ruleAssociation.ResolverRuleId - - if _, ok := vpcAssociations[resolverRuleID]; !ok { - vpcAssociations[resolverRuleID] = []*string{vpcId} - } else { - vpcAssociations[resolverRuleID] = append(vpcAssociations[resolverRuleID], vpcId) + + params := &route53resolver.ListResolverRuleAssociationsInput{} + + for { + resp, err := svc.ListResolverRuleAssociations(params) + + if err != nil { + return nil, err + } + + for _, ruleAssociation := range resp.ResolverRuleAssociations { + vpcID := ruleAssociation.VPCId + if vpcID != nil { + resolverRuleID := *ruleAssociation.ResolverRuleId + + if _, ok := vpcAssociations[resolverRuleID]; !ok { + vpcAssociations[resolverRuleID] = []*string{vpcID} + } else { + vpcAssociations[resolverRuleID] = append(vpcAssociations[resolverRuleID], vpcID) + } } } - } - for _, rule := range output.ResolverRules { - resources = append(resources, &Route53ResolverRule{ - svc: svc, - id: rule.Id, - name: rule.Name, - domainName: rule.DomainName, - vpcIds: vpcAssociations[*rule.Id], - }) + if resp.NextToken == nil { + break + } + + params.NextToken = resp.NextToken } - return resources, nil + return vpcAssociations, nil } // Filter removes resources automatically from being nuked @@ -104,7 +130,7 @@ func (r *Route53ResolverRule) Remove() error { // Properties provides debugging output func (r *Route53ResolverRule) Properties() types.Properties { return types.NewProperties(). - Set("Id", r.id). + Set("ID", r.id). Set("Name", r.name) } diff --git a/resources/route53-traffic-policies.go b/resources/route53-traffic-policies.go index e5e698a68..5caca0723 100644 --- a/resources/route53-traffic-policies.go +++ b/resources/route53-traffic-policies.go @@ -9,6 +9,14 @@ import ( "github.com/rebuy-de/aws-nuke/pkg/types" ) +type Route53TrafficPolicy struct { + svc *route53.Route53 + id *string + name *string + version *int64 + instances []*route53.TrafficPolicyInstance +} + func init() { register("Route53TrafficPolicy", ListRoute53TrafficPolicies) } @@ -77,14 +85,6 @@ func instancesForPolicy(svc *route53.Route53, policyID *string, version *int64) return instances, nil } -type Route53TrafficPolicy struct { - svc *route53.Route53 - id *string - name *string - version *int64 - instances []*route53.TrafficPolicyInstance -} - func (tp *Route53TrafficPolicy) Remove() error { for _, instance := range tp.instances { _, err := tp.svc.DeleteTrafficPolicyInstance(&route53.DeleteTrafficPolicyInstanceInput{ @@ -109,5 +109,5 @@ func (tp *Route53TrafficPolicy) Remove() error { func (tp *Route53TrafficPolicy) Properties() types.Properties { return types.NewProperties(). Set("ID", *tp.id). - Set("NAME", *tp.name) + Set("Name", *tp.name) } From 6c4146297e7973b2718012e68bd7f36f3b0a4548 Mon Sep 17 00:00:00 2001 From: Ryan Swanson Date: Sun, 13 Sep 2020 15:22:45 -0600 Subject: [PATCH 5/6] Code review change spelling fix (#508) --- resources/route53-resolver-rules.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/route53-resolver-rules.go b/resources/route53-resolver-rules.go index 8571de6ec..576308ccf 100644 --- a/resources/route53-resolver-rules.go +++ b/resources/route53-resolver-rules.go @@ -20,11 +20,11 @@ type ( ) func init() { - register("Route53ResolverRule", ListRout53ResolverRules) + register("Route53ResolverRule", ListRoute53ResolverRules) } -// ListRout53ResolverRules produces the resources to be nuked. -func ListRout53ResolverRules(sess *session.Session) ([]Resource, error) { +// ListRoute53ResolverRules produces the resources to be nuked. +func ListRoute53ResolverRules(sess *session.Session) ([]Resource, error) { svc := route53resolver.New(sess) vpcAssociations, err := resolverRulesToVpcIDs(svc) From caab8bc6875731b9a1fc7207f6877b68df26caef Mon Sep 17 00:00:00 2001 From: Ryan Swanson Date: Mon, 21 Sep 2020 15:21:04 -0600 Subject: [PATCH 6/6] =?UTF-8?q?Moving=20passed=20=20=E2=80=A2=20roles=20th?= =?UTF-8?q?at=20can=20be=20listed,=20but=20not=20retrieved.=20=20=E2=80=A2?= =?UTF-8?q?=20role=20policies=20that=20that=20fail=20to=20list.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- resources/iam-role-policy.go | 7 ++++++- resources/iam-roles.go | 8 +++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/resources/iam-role-policy.go b/resources/iam-role-policy.go index f7e4aa45d..ebcfa0741 100644 --- a/resources/iam-role-policy.go +++ b/resources/iam-role-policy.go @@ -2,6 +2,7 @@ package resources import ( "fmt" + "github.com/sirupsen/logrus" "strings" "github.com/aws/aws-sdk-go/aws" @@ -39,7 +40,11 @@ func ListIAMRolePolicies(sess *session.Session) ([]Resource, error) { for { policies, err := svc.ListRolePolicies(polParams) if err != nil { - return nil, err + logrus. + WithError(err). + WithField("roleName", *role.RoleName). + Error("Failed to list policies") + break } for _, policyName := range policies.PolicyNames { diff --git a/resources/iam-roles.go b/resources/iam-roles.go index 55c2d7dfc..d423df885 100644 --- a/resources/iam-roles.go +++ b/resources/iam-roles.go @@ -7,6 +7,7 @@ import ( "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/iam" "github.com/rebuy-de/aws-nuke/pkg/types" + "github.com/sirupsen/logrus" ) type IAMRole struct { @@ -37,8 +38,13 @@ func ListIAMRoles(sess *session.Session) ([]Resource, error) { } getroleOutput, err := svc.GetRole(getroleParams) if err != nil { - return nil, err + logrus. + WithError(err). + WithField("roleName", *out.RoleName). + Error("Failed to get listed role") + continue } + resources = append(resources, &IAMRole{ svc: svc, role: getroleOutput.Role,