From 72f19ad839968d147172da11373d097cafc36e8a Mon Sep 17 00:00:00 2001 From: luizm Date: Mon, 20 Apr 2020 19:07:18 -0300 Subject: [PATCH] Initial version --- .github/workflows/release.yml | 27 ++++++++++++ .goreleaser.yml | 10 +++++ README.md | 60 ++++++++++++++++++------- cmd/cmd.go | 12 ++--- go.sum | 1 + pkg/whois.go | 83 +++++++++++++++++++++++++++-------- 6 files changed, 152 insertions(+), 41 deletions(-) create mode 100644 .github/workflows/release.yml create mode 100644 .goreleaser.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..3ad3ca0 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,27 @@ +name: goreleaser +on: + push: + branches: + - "!*" + tags: + - "v*.*.*" +jobs: + build: + name: goreleser + runs-on: ubuntu-latest + steps: + - name: Set up Go 1.14 + uses: actions/setup-go@v1 + with: + go-version: 1.14 + id: go + - name: Check out code into the Go module directory + uses: actions/checkout@v1 + with: + fetch-depth: 1 + - name: Run goreleaser + uses: goreleaser/goreleaser-action@master + with: + args: release + env: + GITHUB_TOKEN: ${{ secrets.CUSTOM_GITHUB_TOKEN }} diff --git a/.goreleaser.yml b/.goreleaser.yml new file mode 100644 index 0000000..ff07c49 --- /dev/null +++ b/.goreleaser.yml @@ -0,0 +1,10 @@ +brews: + - name: aws-whois + github: + owner: luizm + name: homebrew-tap + homepage: https://github.com/luizm/aws-whois + description: CLI to find which aws resource are using a specific IP + folder: Formula + test: | + system "#{bin}/aws-whois -h" diff --git a/README.md b/README.md index 0c96fac..d4f4441 100644 --- a/README.md +++ b/README.md @@ -1,31 +1,57 @@ -# aws-whois +## Description -WIP - Just for fun +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. -`aws-whois --profile example --ip 172.20.202.101` +## Install + +```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` ```log { - "OwnerId": "xxxxxxxx", - "Description": "Interface for NAT Gateway nat-xxxxxxxxxxxx", - "InterfaceType": "nat_gateway", - "AvailabilityZone": "us-east-1c" + "Profile": "example", + "ENI": { + "OwnerId": "xxxxxx", + "Description": "Interface for NAT Gateway nat-xxxxxx", + "InterfaceType": "nat_gateway", + "AvailabilityZone": "us-east-1c", + } + "EC2Associated": null } ``` -`aws-whois --profile example --ip 172.20.202.10` +`$ aws-whois --profile example --profile example2 --ip 54.X.X.X` ```log { - "OwnerId": "xxxxxxxx", - "VpcId": "vpc-xxxxxxxx", - "InstanceId": "i-xxxxxxxxxx", - "InstanceType": "t3.micro", - "Tags": [ - { - "Key": "Name", - "Value": "instance-example.local" + "Profile": "example", + "Message": "ip 54.X.X.X not found" +} +{ + "Profile": "example2", + "ENI": { + "OwnerId": "xxxxxx", + "Description": "Primary network interface", + "InterfaceType": "interface", + "AvailabilityZone": "us-east-1a", + "EC2Associated": { + "VpcId": "vpc-xxxxxx", + "InstanceId": "i-xxxxxx", + "InstanceType": "t3.micro", + "Tags": [ + { + "Key": "Name", + "Value": "example.local" + } + ] + } } - ] } ``` diff --git a/cmd/cmd.go b/cmd/cmd.go index 45c09ba..e7cc2bb 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -19,7 +19,7 @@ func Execute() { DefaultText: "us-east-1", Required: false, }, - &cli.StringFlag{ + &cli.StringSliceFlag{ Name: "profile", Aliases: []string{"p"}, Usage: "Use a specific profile from your credential file", @@ -33,11 +33,13 @@ func Execute() { }, }, Action: func(c *cli.Context) error { - result, err := whois.Find(c.String("profile"), c.String("region"), c.String("ip")) - if err != nil { - return err + for _, p := range c.StringSlice("profile") { + result, err := whois.FindIP(p, c.String("region"), c.String("ip")) + if err != nil { + return err + } + fmt.Println(string(result)) } - fmt.Println(string(result)) return nil }, } diff --git a/go.sum b/go.sum index 10c1a1a..a1187d9 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,4 @@ +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/aws/aws-sdk-go v1.30.7 h1:IaXfqtioP6p9SFAnNfsqdNczbR5UNbYqvcZUSsCAdTY= github.com/aws/aws-sdk-go v1.30.7/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= diff --git a/pkg/whois.go b/pkg/whois.go index 5d6bf30..b53f4c6 100644 --- a/pkg/whois.go +++ b/pkg/whois.go @@ -2,29 +2,55 @@ package whois import ( "encoding/json" + "errors" "fmt" + "net" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/ec2" ) +type Result struct { + Profile string `json:"Profile"` + ENI *DefaultOutput +} + +type ResultNotFond struct { + Profile string `json:"Profile"` + Message string +} + type DefaultOutput struct { OwnerId *string `json:"OwnerId"` Description *string `json:"Description"` InterfaceType *string `json:"InterfaceType"` AvailabilityZone *string `json:"AvailabilityZone"` + EC2Associated *EC2Output } type EC2Output struct { - OwnerId *string `json:"OwnerId"` VpcId *string `json:"VpcId"` InstanceId *string `json:"InstanceId"` InstanceType *string `json:"InstanceType"` Tags []*ec2.Tag `json:"Tags"` } -func describeInstace(sess *session.Session, i *string) (*EC2Output, error) { +func isPrivateIP(ip string) (bool, error) { + private := false + IP := net.ParseIP(ip) + if IP == nil { + return false, errors.New("invalid IP address") + } + _, prefix08, _ := net.ParseCIDR("10.0.0.0/8") + _, prefix12, _ := net.ParseCIDR("172.16.0.0/12") + _, prefix16, _ := net.ParseCIDR("192.168.0.0/16") + private = prefix08.Contains(IP) || prefix12.Contains(IP) || prefix16.Contains(IP) + + return private, nil +} + +func getInstanceInfo(sess *session.Session, i *string) (*EC2Output, error) { svc := ec2.New(sess) @@ -36,7 +62,6 @@ func describeInstace(sess *session.Session, i *string) (*EC2Output, error) { } return &EC2Output{ - OwnerId: result.Reservations[0].OwnerId, VpcId: result.Reservations[0].Instances[0].VpcId, InstanceId: result.Reservations[0].Instances[0].InstanceId, InstanceType: result.Reservations[0].Instances[0].InstanceType, @@ -58,13 +83,23 @@ func newSession(profile, region string) (*session.Session, error) { return sess, err } -func describeNetworkInterface(sess *session.Session, ip string) (*ec2.DescribeNetworkInterfacesOutput, error) { +func getInterfaceInfo(sess *session.Session, ip string) (*ec2.DescribeNetworkInterfacesOutput, error) { svc := ec2.New(sess) + filter := "association.public-ip" + + isPrivate, err := isPrivateIP(ip) + if err != nil { + return nil, err + } + + if isPrivate { + filter = "private-ip-address" + } input := &ec2.DescribeNetworkInterfacesInput{ Filters: []*ec2.Filter{ { - Name: aws.String("private-ip-address"), + Name: aws.String(filter), Values: []*string{ aws.String(ip), }, @@ -79,36 +114,46 @@ func describeNetworkInterface(sess *session.Session, ip string) (*ec2.DescribeNe return result, nil } -func Find(profile, region, ip string) ([]byte, error) { +func FindIP(profile, region, ip string) ([]byte, error) { + var e *EC2Output + sess, err := newSession(profile, region) if err != nil { return nil, err } - result, err := describeNetworkInterface(sess, ip) + result, err := getInterfaceInfo(sess, ip) if err != nil { return nil, err } if result.NetworkInterfaces == nil { - return []byte(fmt.Sprintf("Check in the %v profile, ip %v not found", profile, ip)), nil - } - - instanceID := result.NetworkInterfaces[0].Attachment.InstanceId - if instanceID != nil { - r, _ := describeInstace(sess, instanceID) - b, err := json.MarshalIndent(r, " ", " ") + r := &ResultNotFond{ + Profile: profile, + Message: fmt.Sprintf("ip %v not found", ip), + } + b, err := json.MarshalIndent(r, "", " ") if err != nil { return nil, err } return b, nil } - b, err := json.MarshalIndent(&DefaultOutput{ - OwnerId: result.NetworkInterfaces[0].OwnerId, - Description: result.NetworkInterfaces[0].Description, - InterfaceType: result.NetworkInterfaces[0].InterfaceType, - AvailabilityZone: result.NetworkInterfaces[0].AvailabilityZone}, " ", " ") + instanceID := result.NetworkInterfaces[0].Attachment.InstanceId + if instanceID != nil { + e, _ = getInstanceInfo(sess, instanceID) + } + r := Result{ + Profile: profile, + ENI: &DefaultOutput{ + OwnerId: result.NetworkInterfaces[0].OwnerId, + Description: result.NetworkInterfaces[0].Description, + InterfaceType: result.NetworkInterfaces[0].InterfaceType, + AvailabilityZone: result.NetworkInterfaces[0].AvailabilityZone, + EC2Associated: e, + }, + } + b, err := json.MarshalIndent(r, "", " ") if err != nil { return nil, err }