diff --git a/DESIGN.md b/DESIGN.md index 93b65bcf..a5e8ca54 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -77,8 +77,12 @@ type Resource struct { ID string `json:"id,omitempty"` // PublicIPv4 is the public ipv4 address of the instance. PublicIPv4 string `json:"public_ipv4,omitempty"` + // PublicIPv6 is the public ipv6 address of the instance. + PublicIPv6 string `json:"public_ipv6,omitempty"` // PrivateIpv4 is the private ipv4 address of the instance PrivateIpv4 string `json:"private_ipv4,omitempty"` + // PrivateIpv6 is the private ipv6 address of the instance + PrivateIpv6 string `json:"private_ipv6,omitempty"` // DNSName is the DNS name of the resource DNSName string `json:"dns_name,omitempty"` } diff --git a/internal/runner/runner.go b/internal/runner/runner.go index 79f6a74e..63bee2a0 100644 --- a/internal/runner/runner.go +++ b/internal/runner/runner.go @@ -108,9 +108,15 @@ func (r *Runner) Enumerate() { if instance.PrivateIpv4 != "" { ipCount++ } + if instance.PrivateIpv6 != "" { + ipCount++ + } if instance.PublicIPv4 != "" { ipCount++ } + if instance.PublicIPv6 != "" { + ipCount++ + } gologger.Silent().Msgf("%s", builder.String()) builder.Reset() } @@ -137,6 +143,14 @@ func (r *Runner) Enumerate() { builder.Reset() gologger.Silent().Msgf("%s", instance.PublicIPv4) } + if instance.PublicIPv6 != "" { + ipCount++ + builder.WriteString(instance.PublicIPv6) + builder.WriteRune('\n') + output.WriteString(builder.String()) //nolint + builder.Reset() + gologger.Silent().Msgf("%s", instance.PublicIPv6) + } if instance.PrivateIpv4 != "" && !r.options.ExcludePrivate { ipCount++ builder.WriteString(instance.PrivateIpv4) @@ -145,6 +159,14 @@ func (r *Runner) Enumerate() { builder.Reset() gologger.Silent().Msgf("%s", instance.PrivateIpv4) } + if instance.PrivateIpv6 != "" && !r.options.ExcludePrivate { + ipCount++ + builder.WriteString(instance.PrivateIpv6) + builder.WriteRune('\n') + output.WriteString(builder.String()) //nolint + builder.Reset() + gologger.Silent().Msgf("%s", instance.PrivateIpv6) + } continue } @@ -164,6 +186,14 @@ func (r *Runner) Enumerate() { builder.Reset() gologger.Silent().Msgf("%s", instance.PublicIPv4) } + if instance.PublicIPv6 != "" { + ipCount++ + builder.WriteString(instance.PublicIPv6) + builder.WriteRune('\n') + output.WriteString(builder.String()) //nolint + builder.Reset() + gologger.Silent().Msgf("%s", instance.PublicIPv6) + } if instance.PrivateIpv4 != "" && !r.options.ExcludePrivate { ipCount++ builder.WriteString(instance.PrivateIpv4) @@ -172,6 +202,14 @@ func (r *Runner) Enumerate() { builder.Reset() gologger.Silent().Msgf("%s", instance.PrivateIpv4) } + if instance.PrivateIpv6 != "" && !r.options.ExcludePrivate { + ipCount++ + builder.WriteString(instance.PrivateIpv6) + builder.WriteRune('\n') + output.WriteString(builder.String()) //nolint + builder.Reset() + gologger.Silent().Msgf("%s", instance.PrivateIpv6) + } } logBuilder := &strings.Builder{} if hostsCount != 0 { diff --git a/pkg/providers/arvancloud/dns.go b/pkg/providers/arvancloud/dns.go index f52ce951..9f3ae514 100644 --- a/pkg/providers/arvancloud/dns.go +++ b/pkg/providers/arvancloud/dns.go @@ -43,14 +43,21 @@ func (d *dnsProvider) GetResource(ctx context.Context) (*schema.Resources, error for _, record := range arrayRecord.GetValue() { if v, ok := record.(map[string]interface{}); ok { - list.Append(&schema.Resource{ - Public: true, - Provider: providerName, - DNSName: fmt.Sprintf("%s.%s", arrayRecord.GetName(), domain.GetName()), - PublicIPv4: v["ip"].(string), - ID: d.id, - Service: d.name(), - }) + resource := &schema.Resource{ + Public: true, + Provider: providerName, + DNSName: fmt.Sprintf("%s.%s", arrayRecord.GetName(), domain.GetName()), + ID: d.id, + Service: d.name(), + } + + if arrayRecord.GetType() == "a" { + resource.PublicIPv4 = v["ip"].(string) + } else { + resource.PublicIPv6 = v["ip"].(string) + } + + list.Append(resource) } else { return nil, errors.Wrap(err, fmt.Sprintf("could not get ip for `%s` record", arrayRecord.GetName())) } diff --git a/pkg/providers/aws/ecs.go b/pkg/providers/aws/ecs.go index b305c651..f4c569e6 100644 --- a/pkg/providers/aws/ecs.go +++ b/pkg/providers/aws/ecs.go @@ -125,25 +125,27 @@ func (ep *ecsProvider) listECSResources(ecsClient *ecs.ECS, ec2Client *ec2.EC2) for _, reservation := range describeInstancesOutput.Reservations { for _, instance := range reservation.Instances { - privateIP := aws.StringValue(instance.PrivateIpAddress) - publicIP := aws.StringValue(instance.PublicIpAddress) + ip4 := aws.StringValue(instance.PublicIpAddress) + ip6 := aws.StringValue(instance.Ipv6Address) + privateIp4 := aws.StringValue(instance.PrivateIpAddress) - if privateIP != "" { + if privateIp4 != "" { resource := &schema.Resource{ ID: aws.StringValue(instance.InstanceId), Provider: "aws", - PrivateIpv4: privateIP, + PrivateIpv4: privateIp4, Public: false, Service: ep.name(), } list.Append(resource) } - if publicIP != "" { + if ip4 != "" || ip6 != "" { resource := &schema.Resource{ ID: aws.StringValue(instance.InstanceId), Provider: "aws", - PublicIPv4: publicIP, + PublicIPv4: ip4, + PublicIPv6: ip6, Public: true, Service: ep.name(), } diff --git a/pkg/providers/aws/instances.go b/pkg/providers/aws/instances.go index 4d7eb824..40db14ff 100644 --- a/pkg/providers/aws/instances.go +++ b/pkg/providers/aws/instances.go @@ -64,6 +64,7 @@ func (i *instanceProvider) getEC2Resources(ec2Client *ec2.EC2) (*schema.Resource for _, reservation := range resp.Reservations { for _, instance := range reservation.Instances { ip4 := aws.StringValue(instance.PublicIpAddress) + ip6 := aws.StringValue(instance.Ipv6Address) privateIp4 := aws.StringValue(instance.PrivateIpAddress) if privateIp4 != "" { @@ -79,6 +80,7 @@ func (i *instanceProvider) getEC2Resources(ec2Client *ec2.EC2) (*schema.Resource ID: i.options.Id, Provider: providerName, PublicIPv4: ip4, + PublicIPv6: ip6, Public: true, Service: i.name(), }) diff --git a/pkg/providers/aws/lightsail.go b/pkg/providers/aws/lightsail.go index 5ec5ec0c..bf164931 100644 --- a/pkg/providers/aws/lightsail.go +++ b/pkg/providers/aws/lightsail.go @@ -69,6 +69,11 @@ func (l *lightsailProvider) listListsailResources(lsClient *lightsail.Lightsail) Public: publicIPv4 != "", Service: l.name(), } + + if len(instance.Ipv6Addresses) > 0 { + resource.PublicIPv6 = aws.StringValue(instance.Ipv6Addresses[0]) + } + list.Append(resource) } if aws.StringValue(resp.NextPageToken) == "" { diff --git a/pkg/providers/aws/route53.go b/pkg/providers/aws/route53.go index f159c0a7..145c8751 100644 --- a/pkg/providers/aws/route53.go +++ b/pkg/providers/aws/route53.go @@ -88,9 +88,9 @@ func (r *route53Provider) listResourcesByZone(zones []*route53.HostedZone, clien } name := strings.TrimSuffix(*item.Name, ".") - var ip4 string + var record string if len(item.ResourceRecords) >= 1 { - ip4 = aws.StringValue(item.ResourceRecords[0].Value) + record = aws.StringValue(item.ResourceRecords[0].Value) } list.Append(&schema.Resource{ ID: r.options.Id, @@ -99,13 +99,21 @@ func (r *route53Provider) listResourcesByZone(zones []*route53.HostedZone, clien Provider: providerName, Service: r.name(), }) - list.Append(&schema.Resource{ - ID: r.options.Id, - Public: public, - PublicIPv4: ip4, - Provider: providerName, - Service: r.name(), - }) + + resource := &schema.Resource{ + ID: r.options.Id, + Public: public, + Provider: providerName, + Service: r.name(), + } + + if *item.Type == "A" { + resource.PublicIPv4 = record + } else if *item.Type == "AAAA" { + resource.PublicIPv6 = record + } + + list.Append(resource) } if aws.BoolValue(sets.IsTruncated) && *sets.NextRecordName != "" { req.SetStartRecordName(*sets.NextRecordName) diff --git a/pkg/providers/azure/publicips.go b/pkg/providers/azure/publicips.go index be9b6b39..f21e2800 100644 --- a/pkg/providers/azure/publicips.go +++ b/pkg/providers/azure/publicips.go @@ -35,13 +35,21 @@ func (pip *publicIPProvider) GetResource(ctx context.Context) (*schema.Resources if ip.IPAddress == nil { continue } - list.Append(&schema.Resource{ - Provider: providerName, - PublicIPv4: *ip.IPAddress, - ID: pip.id, - Public: true, - Service: pip.name(), - }) + + resource := &schema.Resource{ + Provider: providerName, + ID: pip.id, + Public: true, + Service: pip.name(), + } + + if ip.PublicIPAddressVersion == network.IPv4 { + resource.PublicIPv4 = *ip.IPAddress + } else { + resource.PublicIPv6 = *ip.IPAddress + } + + list.Append(resource) } return list, nil } diff --git a/pkg/providers/azure/vm.go b/pkg/providers/azure/vm.go index 702be09a..e58682b5 100644 --- a/pkg/providers/azure/vm.go +++ b/pkg/providers/azure/vm.go @@ -76,13 +76,20 @@ func (d *vmProvider) GetResource(ctx context.Context) (*schema.Resources, error) continue } - list.Append(&schema.Resource{ + resource := &schema.Resource{ Provider: providerName, - PublicIPv4: *publicIP.IPAddress, ID: d.id, PrivateIpv4: *privateIP, - Service: d.name(), - }) + Service: d.name(), + } + + if publicIP.PublicIPAddressVersion == network.IPv4 { + resource.PublicIPv4 = *publicIP.IPAddress + } else { + resource.PublicIPv6 = *publicIP.IPAddress + } + + list.Append(resource) } } } diff --git a/pkg/providers/cloudflare/dns.go b/pkg/providers/cloudflare/dns.go index 090dacdc..b28ea71a 100644 --- a/pkg/providers/cloudflare/dns.go +++ b/pkg/providers/cloudflare/dns.go @@ -47,13 +47,21 @@ func (d *dnsProvider) GetResource(ctx context.Context) (*schema.Resources, error if record.Type == "CNAME" { continue } - list.Append(&schema.Resource{ - Public: true, - Provider: providerName, - PublicIPv4: record.Content, - ID: d.id, - Service: d.name(), - }) + + resource := &schema.Resource{ + Public: true, + Provider: providerName, + ID: d.id, + Service: d.name(), + } + + if record.Type == "A" { + resource.PublicIPv4 = record.Content + } else { + resource.PublicIPv6 = record.Content + } + + list.Append(resource) } } return list, nil diff --git a/pkg/providers/digitalocean/instances.go b/pkg/providers/digitalocean/instances.go index 13d65f91..193451ae 100644 --- a/pkg/providers/digitalocean/instances.go +++ b/pkg/providers/digitalocean/instances.go @@ -30,6 +30,7 @@ func (d *instanceProvider) GetResource(ctx context.Context) (*schema.Resources, for _, droplet := range droplets { ip4, _ := droplet.PublicIPv4() + ip6, _ := droplet.PublicIPv6() privateIP4, _ := droplet.PrivateIPv4() if privateIP4 != "" { @@ -44,6 +45,7 @@ func (d *instanceProvider) GetResource(ctx context.Context) (*schema.Resources, Provider: providerName, ID: d.id, PublicIPv4: ip4, + PublicIPv6: ip6, Public: true, Service: d.name(), }) diff --git a/pkg/providers/gcp/dns.go b/pkg/providers/gcp/dns.go index 22293c97..72dc3827 100644 --- a/pkg/providers/gcp/dns.go +++ b/pkg/providers/gcp/dns.go @@ -58,14 +58,21 @@ func (d *cloudDNSProvider) parseRecordsForResourceSet(r *dns.ResourceRecordSetsL } for _, data := range resource.Rrdatas { - list.Append(&schema.Resource{ - DNSName: resource.Name, - Public: true, - PublicIPv4: data, - ID: d.id, - Provider: providerName, - Service: d.name(), - }) + dst := &schema.Resource{ + DNSName: resource.Name, + Public: true, + ID: d.id, + Provider: providerName, + Service: d.name(), + } + + if resource.Type == "A" { + dst.PublicIPv4 = data + } else if resource.Type == "AAAA" { + dst.PublicIPv6 = data + } + + list.Append(dst) } } return list diff --git a/pkg/providers/gcp/vms.go b/pkg/providers/gcp/vms.go index 39337335..b614632f 100644 --- a/pkg/providers/gcp/vms.go +++ b/pkg/providers/gcp/vms.go @@ -43,7 +43,8 @@ func (d *cloudVMProvider) GetResource(ctx context.Context) (*schema.Resources, e Public: true, Provider: providerName, PublicIPv4: cfg.NatIP, - Service: d.name(), + PublicIPv6: cfg.ExternalIpv6, + Service: d.name(), }) } } diff --git a/pkg/providers/hetzner/instances.go b/pkg/providers/hetzner/instances.go index 2853cf93..8702ab42 100644 --- a/pkg/providers/hetzner/instances.go +++ b/pkg/providers/hetzner/instances.go @@ -31,8 +31,9 @@ func (p *instanceProvider) GetResource(ctx context.Context) (*schema.Resources, Provider: providerName, ID: p.id, PublicIPv4: server.PublicNet.IPv4.IP.String(), + PublicIPv6: server.PublicNet.IPv6.IP.String(), Public: true, - Service: p.name(), + Service: p.name(), }) } for _, privateNet := range server.PrivateNet { diff --git a/pkg/providers/linode/instances.go b/pkg/providers/linode/instances.go index ca31dd4a..62c91d7a 100644 --- a/pkg/providers/linode/instances.go +++ b/pkg/providers/linode/instances.go @@ -36,6 +36,7 @@ func (d *instanceProvider) GetResource(ctx context.Context) (*schema.Resources, list.Append(&schema.Resource{ Provider: providerName, PublicIPv4: ip4, + PublicIPv6: inst.IPv6, ID: d.id, Public: ip4 != "", Service: d.name(), diff --git a/pkg/providers/scaleway/instances.go b/pkg/providers/scaleway/instances.go index d7940049..3a5191d3 100644 --- a/pkg/providers/scaleway/instances.go +++ b/pkg/providers/scaleway/instances.go @@ -36,10 +36,13 @@ func (d *instanceProvider) GetResource(ctx context.Context) (*schema.Resources, for _, server := range resp.Servers { totalResults++ - var ip4, privateIP4 string + var ip4, ip6, privateIP4 string if server.PublicIP != nil && server.PublicIP.Address != nil { ip4 = server.PublicIP.Address.String() } + if server.IPv6 != nil && server.IPv6.Address != nil { + ip6 = server.IPv6.Address.String() + } if server.PrivateIP != nil { privateIP4 = *server.PrivateIP } @@ -55,6 +58,7 @@ func (d *instanceProvider) GetResource(ctx context.Context) (*schema.Resources, Provider: providerName, ID: d.id, PublicIPv4: ip4, + PublicIPv6: ip6, Public: true, Service: d.name(), }) diff --git a/pkg/providers/terraform/state_file.go b/pkg/providers/terraform/state_file.go index 6e025073..e53adb2a 100644 --- a/pkg/providers/terraform/state_file.go +++ b/pkg/providers/terraform/state_file.go @@ -37,19 +37,31 @@ func (d *instanceProvider) GetResource(ctx context.Context) (*schema.Resources, return resources, nil } -var ipRegex = regexp.MustCompile(`(?:[0-9]{1,3})\.(?:[0-9]{1,3})\.(?:[0-9]{1,3})\.(?:[0-9]{1,3})`) +var ip4Regex = regexp.MustCompile(`(?:[0-9]{1,3})\.(?:[0-9]{1,3})\.(?:[0-9]{1,3})\.(?:[0-9]{1,3})`) +var ip6Regex = regexp.MustCompile(`(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))`) func (d *instanceProvider) extractIPsFromText(text string) *schema.Resources { resources := schema.NewResources() - matches := ipRegex.FindAllStringSubmatch(text, -1) + matches := ip4Regex.FindAllStringSubmatch(text, -1) for _, match := range matches { resources.Append(&schema.Resource{ Provider: providerName, ID: d.id, PublicIPv4: match[0], - Service: d.name(), + Service: d.name(), }) } + + matches = ip6Regex.FindAllStringSubmatch(text, -1) + for _, match := range matches { + resources.Append(&schema.Resource{ + Provider: providerName, + ID: d.id, + PublicIPv6: match[0], + Service: d.name(), + }) + } + return resources } diff --git a/pkg/schema/schema.go b/pkg/schema/schema.go index ff339d70..135a0539 100644 --- a/pkg/schema/schema.go +++ b/pkg/schema/schema.go @@ -66,11 +66,16 @@ func (r *Resources) appendResourceWithTypeAndMeta(resourceType validate.Resource case validate.DNSName: resource.Public = true resource.DNSName = item - case validate.PublicIP: + case validate.PublicIPv4: resource.Public = true resource.PublicIPv4 = item - case validate.PrivateIP: + case validate.PublicIPv6: + resource.Public = true + resource.PublicIPv6 = item + case validate.PrivateIPv4: resource.PrivateIpv4 = item + case validate.PrivateIPv6: + resource.PrivateIpv6 = item default: return } @@ -89,11 +94,21 @@ func (r *Resources) appendResource(resource *Resource, uniqueMap *sync.Map) { r.appendResourceWithTypeAndMeta(resourceType, resource.PublicIPv4, resource.ID, resource.Provider, resource.Service) uniqueMap.Store(resource.PublicIPv4, struct{}{}) } + if _, ok := uniqueMap.Load(resource.PublicIPv6); !ok && resource.PublicIPv6 != "" { + resourceType := validator.Identify(resource.PublicIPv6) + r.appendResourceWithTypeAndMeta(resourceType, resource.PublicIPv6, resource.ID, resource.Provider, resource.Service) + uniqueMap.Store(resource.PublicIPv6, struct{}{}) + } if _, ok := uniqueMap.Load(resource.PrivateIpv4); !ok && resource.PrivateIpv4 != "" { resourceType := validator.Identify(resource.PrivateIpv4) r.appendResourceWithTypeAndMeta(resourceType, resource.PrivateIpv4, resource.ID, resource.Provider, resource.Service) uniqueMap.Store(resource.PrivateIpv4, struct{}{}) } + if _, ok := uniqueMap.Load(resource.PrivateIpv6); !ok && resource.PrivateIpv6 != "" { + resourceType := validator.Identify(resource.PrivateIpv6) + r.appendResourceWithTypeAndMeta(resourceType, resource.PrivateIpv6, resource.ID, resource.Provider, resource.Service) + uniqueMap.Store(resource.PrivateIpv6, struct{}{}) + } } // Append appends a single resource to the resource list @@ -124,8 +139,12 @@ type Resource struct { ID string `json:"id,omitempty"` // PublicIPv4 is the public ipv4 address of the instance. PublicIPv4 string `json:"public_ipv4,omitempty"` + // PublicIPv6 is the public ipv6 address of the instance. + PublicIPv6 string `json:"public_ipv6,omitempty"` // PrivateIpv4 is the private ipv4 address of the instance PrivateIpv4 string `json:"private_ipv4,omitempty"` + // PrivateIpv6 is the private ipv6 address of the instance + PrivateIpv6 string `json:"private_ipv6,omitempty"` // DNSName is the DNS name of the resource DNSName string `json:"dns_name,omitempty"` } diff --git a/pkg/schema/validate/validate.go b/pkg/schema/validate/validate.go index 780cbb98..9b710f5f 100644 --- a/pkg/schema/validate/validate.go +++ b/pkg/schema/validate/validate.go @@ -70,8 +70,10 @@ type ResourceType int // A list of supported resource types const ( DNSName ResourceType = iota + 1 - PublicIP - PrivateIP + PublicIPv4 + PublicIPv6 + PrivateIPv4 + PrivateIPv6 None ) @@ -91,15 +93,15 @@ func (v *Validator) Identify(item string) ResourceType { if strings.Contains(item, ":") { // Check ipv6 private address list if v.containsIPv6(parsed) { - return PrivateIP + return PrivateIPv6 } - return PublicIP + return PublicIPv6 } // Check ipv4 private address list if v.containsIPv4(parsed) { - return PrivateIP + return PrivateIPv4 } - return PublicIP + return PublicIPv4 } // isDNSName will validate the given string as a DNS name diff --git a/pkg/schema/validate/validate_test.go b/pkg/schema/validate/validate_test.go index 222216c0..674f8e5d 100644 --- a/pkg/schema/validate/validate_test.go +++ b/pkg/schema/validate/validate_test.go @@ -12,13 +12,17 @@ func TestValidateIdentify(t *testing.T) { resource ResourceType }{ {"www.example.com", DNSName}, - {"17.5.7.8", PublicIP}, - {"1.1.1.1", PublicIP}, - {"192.168.1.10", PrivateIP}, - {"185.199.110.153", PublicIP}, + {"17.5.7.8", PublicIPv4}, + {"1.1.1.1", PublicIPv4}, + {"192.168.1.10", PrivateIPv4}, + {"185.199.110.153", PublicIPv4}, {"www.example.com:22", DNSName}, - {"17.5.7.8:443", PublicIP}, - {"192.168.1.10:80", PrivateIP}, + {"17.5.7.8:443", PublicIPv4}, + {"192.168.1.10:80", PrivateIPv4}, + {"2a0c:5a80::1", PublicIPv6}, + {"[2a0c:5a80::1]:80", PublicIPv6}, + {"2001:db8::1", PrivateIPv6}, + {"[2001:db8::a]:443", PrivateIPv6}, } validator, err := NewValidator()