Skip to content

Commit

Permalink
add dns and rds support
Browse files Browse the repository at this point in the history
  • Loading branch information
luizm committed Apr 26, 2020
1 parent 18766a2 commit 1f965f4
Show file tree
Hide file tree
Showing 3 changed files with 209 additions and 45 deletions.
58 changes: 48 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,23 @@

**Still work in progress**

If you have a lot of accounts, this is a simple way to check which resource on AWS are using a specific public or private IP.
If you have a lot of accounts and VPCs, this is a simple way to check where or which resource on AWS are using a specific public or private IP.

## Install
## Install

```sh
```sh
$ brew install luizm/tap/aws-whois
```

Or download the binary from [github releases](https://github.com/luizm/aws-whois/releases)

## Usage

`$ aws-whois --profile example --ip 172.20.X.X`
Basically use the `flag` --ip or `--dns`.

Examples:

`$ aws-whois --profile example --ip 54.X.X.X`

```log
{
Expand All @@ -29,30 +33,64 @@ Or download the binary from [github releases](https://github.com/luizm/aws-whois
}
```

`$ aws-whois --profile example --profile example2 --ip 54.X.X.X`
When use the flag `--dns` the address will be resolved before find out the resource.

`$ aws-whois --profile example --profile example2 --dns example.mydomain.local`

```log
{
"Profile": "example",
"Message": "ip 54.X.X.X not found"
"Message": "ip 172.20.X.X not found"
}
{
"Profile": "example2",
"ENI": {
"OwnerId": "xxxxxx",
"Status": "in-use",
"VPCId": "vpc-xxxxx",
"VPCName": "example",
"Description": "Primary network interface",
"InterfaceType": "interface",
"AvailabilityZone": "us-east-1a",
"EC2Associated": {
"VpcId": "vpc-xxxxxx",
"InstanceId": "i-xxxxxx",
"InstanceId": "i-xxxxx",
"InstanceState": "running",
"LaunchTime": "2020-04-20T19:59:40Z",
"InstanceType": "t3.micro",
"Tags": [
{
"Key": "Name",
"Value": "example.local"
"Value": "example.mydomain.local"
}
]
},
"RDSAssociated": null
}
}
```

`$ aws-whois --profile example --profile example3 --ip 10.100.X.X`

```log
{
"Profile": "example",
"Message": "ip 10.100.X.X not found"
}
{
"Profile": "example3",
"ENI": {
"Status": "in-use",
"VPCId": "vpc-xxxxx",
"VPCName": "production",
"Description": "RDSNetworkInterface",
"InterfaceType": "interface",
"AvailabilityZone": "us-east-1c",
"EC2Associated": null,
"RDSAssociated": {
"Identifier": "rds-example",
"Engine": "postgres",
"DBInstanceClass": "db.m5.large",
"Endpoint": "rds-example.xxxxx.us-east-1.rds.amazonaws.com",
"Status": "available"
}
}
}
Expand Down
19 changes: 16 additions & 3 deletions cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,26 @@ func Execute() {
&cli.StringFlag{
Name: "ip",
Aliases: []string{"i"},
Usage: "The ip address to find",
Required: true,
Usage: "The ip address to find the resource associated",
Required: false,
},
&cli.StringFlag{
Name: "dns",
Aliases: []string{"d"},
Usage: "The dns address to find the resource associated, if return more than 1 ip, will be used the first",
Required: false,
},
},
Action: func(c *cli.Context) error {
var ip string
ip = c.String("ip")

if c.String("dns") != "" {
ips, _ := whois.ResolvDNS(c.String("dns"))
ip = ips[0]
}
for _, p := range c.StringSlice("profile") {
result, err := whois.FindIP(p, c.String("region"), c.String("ip"))
result, err := whois.FindIP(p, c.String("region"), ip)
if err != nil {
return err
}
Expand Down
177 changes: 145 additions & 32 deletions pkg/whois.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import (
"errors"
"fmt"
"net"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/rds"
)

type Result struct {
Expand All @@ -22,18 +24,70 @@ type ResultNotFond struct {
}

type DefaultOutput struct {
OwnerId *string `json:"OwnerId"`
Status *string `json:"Status"`
VPCId *string `json:"VPCId"`
VPCName *string `json:"VPCName"`
Description *string `json:"Description"`
InterfaceType *string `json:"InterfaceType"`
AvailabilityZone *string `json:"AvailabilityZone"`
EC2Associated *EC2Output
RDSAssociated *RDSOutput
}

type RDSEndpoint struct {
Identifier *string
Endpoint *string
}

type EC2Output struct {
VpcId *string `json:"VpcId"`
InstanceId *string `json:"InstanceId"`
InstanceType *string `json:"InstanceType"`
Tags []*ec2.Tag `json:"Tags"`
InstanceId *string `json:"InstanceId"`
InstanceState *string `json:"InstanceState"`
LaunchTime *time.Time `json:"LaunchTime"`
InstanceType *string `json:"InstanceType"`
Tags []*ec2.Tag `json:"Tags"`
}

type RDSOutput struct {
Identifier *string `json:"Identifier"`
Engine *string `json:"Engine"`
DBInstanceClass *string `json:"DBInstanceClass"`
Endpoint *string `json:"Endpoint"`
Status *string `json:"Status"`
}

func newSession(profile, region string) (*session.Session, error) {
sess, err := session.NewSessionWithOptions(session.Options{
Profile: profile,

Config: aws.Config{
Region: aws.String(region),
},

// Force enable Shared Config support
SharedConfigState: session.SharedConfigEnable,
})
return sess, err
}

func getVPCName(sess *session.Session, v *string) (*string, error) {
svc := ec2.New(sess)
var n *string

r, err := svc.DescribeVpcs(&ec2.DescribeVpcsInput{
VpcIds: []*string{
aws.String(*v),
},
})
if err != nil {
return nil, err
}
for _, t := range r.Vpcs[0].Tags {
if *t.Key == "Name" {
n = t.Value
break
}
}
return n, nil
}

func isPrivateIP(ip string) (bool, error) {
Expand All @@ -50,8 +104,46 @@ func isPrivateIP(ip string) (bool, error) {
return private, nil
}

func getInstanceInfo(sess *session.Session, i *string) (*EC2Output, error) {
func getRDSEndpoints(sess *session.Session) ([]RDSEndpoint, error) {
var e []RDSEndpoint
svc := rds.New(sess)

result, err := svc.DescribeDBInstances(&rds.DescribeDBInstancesInput{})
if err != nil {
if err != nil {
return nil, err
}
}
for _, r := range result.DBInstances {
e = append(e, RDSEndpoint{
Identifier: r.DBInstanceIdentifier,
Endpoint: r.Endpoint.Address,
})
}
return e, nil
}

func getRDSInfo(sess *session.Session, r *string) (*RDSOutput, error) {
svc := rds.New(sess)

result, err := svc.DescribeDBInstances(&rds.DescribeDBInstancesInput{
DBInstanceIdentifier: r,
})
if err != nil {
if err != nil {
return nil, err
}
}
return &RDSOutput{
Identifier: result.DBInstances[0].DBInstanceIdentifier,
Engine: result.DBInstances[0].Engine,
DBInstanceClass: result.DBInstances[0].DBInstanceClass,
Endpoint: result.DBInstances[0].Endpoint.Address,
Status: result.DBInstances[0].DBInstanceStatus,
}, nil
}

func getEC2Info(sess *session.Session, i *string) (*EC2Output, error) {
svc := ec2.New(sess)

result, err := svc.DescribeInstances(&ec2.DescribeInstancesInput{
Expand All @@ -60,30 +152,16 @@ func getInstanceInfo(sess *session.Session, i *string) (*EC2Output, error) {
if err != nil {
return nil, err
}

return &EC2Output{
VpcId: result.Reservations[0].Instances[0].VpcId,
InstanceId: result.Reservations[0].Instances[0].InstanceId,
InstanceType: result.Reservations[0].Instances[0].InstanceType,
Tags: result.Reservations[0].Instances[0].Tags,
InstanceId: result.Reservations[0].Instances[0].InstanceId,
InstanceType: result.Reservations[0].Instances[0].InstanceType,
InstanceState: result.Reservations[0].Instances[0].State.Name,
LaunchTime: result.Reservations[0].Instances[0].LaunchTime,
Tags: result.Reservations[0].Instances[0].Tags,
}, nil
}

func newSession(profile, region string) (*session.Session, error) {
sess, err := session.NewSessionWithOptions(session.Options{
Profile: profile,

Config: aws.Config{
Region: aws.String(region),
},

// Force enable Shared Config support
SharedConfigState: session.SharedConfigEnable,
})
return sess, err
}

func getInterfaceInfo(sess *session.Session, ip string) (*ec2.DescribeNetworkInterfacesOutput, error) {
func getENIInfo(sess *session.Session, ip string) (*ec2.DescribeNetworkInterfacesOutput, error) {
svc := ec2.New(sess)
filter := "association.public-ip"

Expand Down Expand Up @@ -114,15 +192,29 @@ func getInterfaceInfo(sess *session.Session, ip string) (*ec2.DescribeNetworkInt
return result, nil
}

func ResolvDNS(dns string) ([]string, error) {
var ips []string
i, err := net.LookupIP(dns)
if err != nil {
return nil, err
}

for _, i := range i {
ips = append(ips, i.String())
}
return ips, nil
}

func FindIP(profile, region, ip string) ([]byte, error) {
var e *EC2Output
var r *RDSOutput

sess, err := newSession(profile, region)
if err != nil {
return nil, err
}

result, err := getInterfaceInfo(sess, ip)
result, err := getENIInfo(sess, ip)
if err != nil {
return nil, err
}
Expand All @@ -138,22 +230,43 @@ func FindIP(profile, region, ip string) ([]byte, error) {
return b, nil
}

VPCName, err := getVPCName(sess, result.NetworkInterfaces[0].VpcId)
if err != nil {
return nil, err
}
if *result.NetworkInterfaces[0].Description == "RDSNetworkInterface" {
endpoints, err := getRDSEndpoints(sess)
if err != nil {
return nil, err
}

for _, e := range endpoints {
i, _ := ResolvDNS(string(*e.Endpoint))
if ip == string(i[0]) {
r, _ = getRDSInfo(sess, e.Identifier)
break
}
}
}

instanceID := result.NetworkInterfaces[0].Attachment.InstanceId
if instanceID != nil {
e, _ = getInstanceInfo(sess, instanceID)
e, _ = getEC2Info(sess, instanceID)
}

r := Result{
o := Result{
Profile: profile,
ENI: &DefaultOutput{
OwnerId: result.NetworkInterfaces[0].OwnerId,
Status: result.NetworkInterfaces[0].Status,
VPCId: result.NetworkInterfaces[0].VpcId,
VPCName: VPCName,
Description: result.NetworkInterfaces[0].Description,
InterfaceType: result.NetworkInterfaces[0].InterfaceType,
AvailabilityZone: result.NetworkInterfaces[0].AvailabilityZone,
EC2Associated: e,
RDSAssociated: r,
},
}
b, err := json.MarshalIndent(r, "", " ")
b, err := json.MarshalIndent(o, "", " ")
if err != nil {
return nil, err
}
Expand Down

0 comments on commit 1f965f4

Please sign in to comment.