Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
arkadiyt committed May 14, 2018
1 parent 3c31ed0 commit 75d7958
Show file tree
Hide file tree
Showing 59 changed files with 2,414 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
*.swp
.bundle
coverage/
Gemfile.lock
vendor/bundle
3 changes: 3 additions & 0 deletions .rspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
--color
--order random
--require spec_helper
26 changes: 26 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
inherit_from:
- .rubocop_todo.yml

Layout/IndentArray:
EnforcedStyle: consistent

Layout/IndentHash:
EnforcedStyle: consistent

Layout/MultilineMethodCallIndentation:
EnforcedStyle: indented

Layout/SpaceInsideHashLiteralBraces:
EnforcedStyle: no_space

Metrics/AbcSize:
Enabled: false

Metrics/LineLength:
Max: 120

Metrics/MethodLength:
Enabled: false

Style/Documentation:
Enabled: false
7 changes: 7 additions & 0 deletions .rubocop_todo.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# This configuration was generated by
# `rubocop --auto-gen-config`
# on 2018-05-11 18:49:11 -0700 using RuboCop version 0.55.0.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again.
13 changes: 13 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
language: ruby
dist: trusty
rvm:
- ruby-2.1
- ruby-2.2
- ruby-2.3
- ruby-2.4
- ruby-2.5
- ruby-head
script:
- bundle exec bundler-audit
- bundle exec rubocop
- bundle exec rspec
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
### 1.0.0 (05/13/2018)
* Initial release
1 change: 1 addition & 0 deletions CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Treat everyone with respect.
31 changes: 31 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# How to contribute

Thank you for your interest in contributing to aws_public_ips!

### Code of conduct

Please adhere to the [code of conduct](https://github.com/arkadiyt/aws_public_ips/blob/master/CODE_OF_CONDUCT.md).

### Bugs

**Known issues:** Before reporting new bugs, search if your issue already exists in the [open issues](https://github.com/arkadiyt/aws_public_ips/issues).

**Reporting new issues:** Provide a reduced test case with clear reproduction steps.

**Security issues:** If you've found a security vulnerability, please disclose it privately first by direct messaging me on [twitter](https://twitter.com/arkadiyt).

### Proposing a change

If you plan on making large changes, please file an issue before submitting a pull request so we can reach agreement on your proposal.

### Sending a pull request

1. Fork this repository
2. Check out a feature branch: `git checkout -b your-feature-branch`
3. Make changes on your branch
4. Add/update tests - this project maintains 100% code coverage
5. Make sure all status checks pass locally:
- `bundle exec bundler-audit`
- `bundle exec rubocop`
- `bundle exec rspec`
6. Submit a pull request with a description of your changes
4 changes: 4 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# frozen_string_literal: true

source 'https://rubygems.org'
gemspec
7 changes: 7 additions & 0 deletions LICENSE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Copyright (c) 2018 Arkadiy Tetelman

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
111 changes: 111 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# aws_public_ips [![Gem](https://img.shields.io/gem/v/aws_public_ips.svg)](https://rubygems.org/gems/aws_public_ips) [![TravisCI](https://travis-ci.org/arkadiyt/aws_public_ips.svg?branch=master)](https://travis-ci.org/arkadiyt/aws_public_ips/) [![Coverage Status](https://coveralls.io/repos/github/arkadiyt/aws_public_ips/badge.svg?branch=master)](https://coveralls.io/github/arkadiyt/aws_public_ips?branch=master) [![License](https://img.shields.io/github/license/arkadiyt/aws_public_ips.svg)](https://github.com/arkadiyt/aws_public_ips/blob/master/LICENSE.md)

## Table of Contents
- [What's it for](https://github.com/arkadiyt/aws_public_ips#whats-it-for)
- [Quick start](https://github.com/arkadiyt/aws_public_ips#quick-start)
- [CLI reference](https://github.com/arkadiyt/aws_public_ips#cli-reference)
- [Configuration](https://github.com/arkadiyt/aws_public_ips#configuration)
- [IAM permissions](https://github.com/arkadiyt/aws_public_ips#iam-permissions)
- [Changelog](https://github.com/arkadiyt/aws_public_ips#changelog)
- [Contributing](https://github.com/arkadiyt/aws_public_ips#contributing)
- [Getting in touch](https://github.com/arkadiyt/aws_public_ips#getting-in-touch)

### What's it for

aws_public_ips is a tool to fetch all public IP addresses (both IPv4/IPv6) associated with an AWS account.

It can be used as a library and as a CLI, and supports the following AWS services (all with both Classic & VPC flavors):

- APIGateway
- CloudFront
- EC2 (and as a result: ECS, EKS, Beanstalk, Fargate, Batch, & NAT Instances)
- ElasticSearch
- ELB (Classic ELB)
- ELBv2 (ALB/NLB)
- Lightsail
- RDS
- Redshift

If a service isn't listed (S3, ElastiCache, etc) it's most like because it doesn't have anything to support (i.e. it might not be deployable publicly, it might have all ip addresses resolve to global AWS infrastructure, etc).

### Quick start

- Install the gem and run it:
```
$ gem install aws_public_ips
$ aws_public_ips # Uses default ~/.aws/credentials
52.84.11.13
52.84.11.83
52.84.11.159
52.84.11.104
2600:9000:2039:ba00:1a:cd27:1440:93a1
2600:9000:2039:6e00:1a:cd27:1440:93a1
2600:9000:2039:1200:1a:cd27:1440:93a1
2600:9000:2039:cc00:1a:cd27:1440:93a1
2600:9000:2039:2a00:1a:cd27:1440:93a1
2600:9000:2039:2400:1a:cd27:1440:93a1
2600:9000:2039:2e00:1a:cd27:1440:93a1
2600:9000:2039:ae00:1a:cd27:1440:93a1
```

### CLI reference

```
$ aws_public_ips --help
Usage: aws_public_ips [options]
-s, --services <s1>,<s2>,<s3> List of AWS services to check. Available services: apigateway,cloudfront,ec2,elasticsearch,elb,elbv2,lightsail,rds,redshift. Defaults to all.
-f, --format <format> Set output format. Available formats: json,prettyjson,text. Defaults to text.
-v, --[no-]verbose Enable debug/trace output
```

### Configuration

For authentication aws_public_ips uses the default [aws-sdk-ruby](https://github.com/aws/aws-sdk-ruby) configuration, meaning that the following are checked in order:
1. Environment variables:
- `AWS_ACCESS_KEY_ID`
- `AWS_SECRET_ACCESS_KEY`
- `AWS_REGION`
- `AWS_PROFILE`
2. Shared credentials files:
- `~/.aws/credentials`
- `~/.aws/config`
3. Instance profile via metadata endpoint (if running on EC2, ECS, EKS, or Fargate)

For more information see the AWS SDK [documentation on configuration](https://github.com/aws/aws-sdk-ruby#configuration).

### IAM permissions

To find the public IPs from all AWS services, the minimal IAM policy needed is:
```json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"apigateway:GET",
"cloudfront:ListDistributions",
"ec2:DescribeInstances",
"elasticloadbalancing:DescribeLoadBalancers",
"lightsail:GetInstances",
"lightsail:GetLoadBalancers",
"rds:DescribeDBInstances",
"redshift:DescribeClusters"
],
"Resource": "*"
}
]
}
```

### Changelog

Please see [CHANGELOG.md](https://github.com/arkadiyt/aws_public_ips/blob/master/CHANGELOG.md). This project follows [semantic versioning](https://semver.org/).

### Contributing

Please see [CONTRIBUTING.md](https://github.com/arkadiyt/aws_public_ips/blob/master/CONTRIBUTING.md).

### Getting in touch

Feel free to tweet or direct message me: [@arkadiyt](https://twitter.com/arkadiyt)
33 changes: 33 additions & 0 deletions aws_public_ips.gemspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# frozen_string_literal: true

$LOAD_PATH.unshift File.expand_path('lib', __dir__)
require 'aws_public_ips/version'

Gem::Specification.new do |gem|
gem.name = 'aws_public_ips'
gem.platform = Gem::Platform::RUBY
gem.version = AwsPublicIps::VERSION
gem.authors = ['Arkadiy Tetelman']
gem.required_ruby_version = '>= 2.1.0'
gem.summary = 'A library/cli to fetch all public IP addresses associated with an AWS account'
gem.description = gem.summary
gem.homepage = 'https://github.com/arkadiyt/aws_public_ips'
gem.license = 'MIT'
gem.files = Dir['lib/**/*.rb'] + Dir['bin/*']

gem.add_dependency('aws-sdk-apigateway', '~> 1.10.0')
gem.add_dependency('aws-sdk-cloudfront', '~> 1.2.0')
gem.add_dependency('aws-sdk-ec2', '~> 1.33.0')
gem.add_dependency('aws-sdk-elasticloadbalancing', '~> 1.2.0')
gem.add_dependency('aws-sdk-elasticloadbalancingv2', '~> 1.8.0')
gem.add_dependency('aws-sdk-elasticsearchservice', '~> 1.5.0')
gem.add_dependency('aws-sdk-lightsail', '~> 1.4.0')
gem.add_dependency('aws-sdk-rds', '~> 1.18.0')
gem.add_dependency('aws-sdk-redshift', '~> 1.2.0')

gem.add_development_dependency('bundler-audit', '~> 0.6.0')
gem.add_development_dependency('coveralls', '~> 0.8.12')
gem.add_development_dependency('rspec', '~> 3.7.0')
gem.add_development_dependency('rubocop', '~> 0.55.0')
gem.add_development_dependency('webmock', '~> 3.4.1')
end
9 changes: 9 additions & 0 deletions bin/aws_public_ips
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

$LOAD_PATH.unshift(File.expand_path(File.join('..', '..', 'lib'), __FILE__))

require 'bundler/setup'
require 'aws_public_ips/cli'

AwsPublicIps::CLI.new.run(ARGV)
10 changes: 10 additions & 0 deletions lib/aws_public_ips.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# frozen_string_literal: true

module AwsPublicIps
end

require 'aws_public_ips/checks'
require 'aws_public_ips/cli'
require 'aws_public_ips/formatters'
require 'aws_public_ips/utils'
require 'aws_public_ips/version'
16 changes: 16 additions & 0 deletions lib/aws_public_ips/checks.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# frozen_string_literal: true

module AwsPublicIps
module Checks
end
end

require 'aws_public_ips/checks/apigateway'
require 'aws_public_ips/checks/cloudfront'
require 'aws_public_ips/checks/ec2'
require 'aws_public_ips/checks/elasticsearch'
require 'aws_public_ips/checks/elb'
require 'aws_public_ips/checks/elbv2'
require 'aws_public_ips/checks/lightsail'
require 'aws_public_ips/checks/rds'
require 'aws_public_ips/checks/redshift'
29 changes: 29 additions & 0 deletions lib/aws_public_ips/checks/apigateway.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# frozen_string_literal: true

require 'aws-sdk-apigateway'
require 'aws_public_ips/utils'

module AwsPublicIps
module Checks
module Apigateway
def self.run
client = Aws::APIGateway::Client.new

# TODO(arkadiy) https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-private-integration.html

# APIGateway doesn't return the full domain in the response, we have to build
# it using the api id and region
client.get_rest_apis.flat_map do |response|
response.items.map do |api|
hostname = "#{api.id}.execute-api.#{client.config.region}.amazonaws.com"
{
id: api.id,
hostname: hostname,
ip_addresses: Utils.resolve_hostname(hostname)
}
end
end
end
end
end
end
24 changes: 24 additions & 0 deletions lib/aws_public_ips/checks/cloudfront.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# frozen_string_literal: true

require 'aws-sdk-cloudfront'
require 'aws_public_ips/utils'

module AwsPublicIps
module Checks
module Cloudfront
def self.run
client = Aws::CloudFront::Client.new

client.list_distributions.flat_map do |response|
response.distribution_list.items.flat_map do |distribution|
{
id: distribution.id,
hostname: distribution.domain_name,
ip_addresses: Utils.resolve_hostname(distribution.domain_name)
}
end
end
end
end
end
end
39 changes: 39 additions & 0 deletions lib/aws_public_ips/checks/ec2.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# frozen_string_literal: true

require 'aws-sdk-ec2'

module AwsPublicIps
module Checks
module Ec2
def self.run
client = Aws::EC2::Client.new

# TODO(arkadiy) confirm this covers Batch, Fargate
# Iterate over all EC2 instances. This will include those from EC2, ECS, EKS, Fargate, Batch,
# Beanstalk, and NAT Instances
# It will not include NAT Gateways (IPv4) or Egress Only Internet Gateways (IPv6), but they do not allow
# ingress traffic so we skip them anyway
client.describe_instances.flat_map do |response|
response.reservations.flat_map do |reservation|
reservation.instances.map do |instance|
# EC2-Classic instances have a `public_ip_address` and no `network_interfaces`
# EC2-VPC instances both set, so we uniq the ip addresses
ip_addresses = [instance.public_ip_address].compact + instance.network_interfaces.flat_map do |interface|
public_ip = interface.association ? [interface.association.public_ip].compact : []
public_ip + interface.ipv_6_addresses.map(&:ipv_6_address)
end

# If hostname is empty string, canonicalize to nil
hostname = instance.public_dns_name.empty? ? nil : instance.public_dns_name
{
id: instance.instance_id,
hostname: hostname,
ip_addresses: ip_addresses.uniq
}
end
end
end
end
end
end
end
Loading

0 comments on commit 75d7958

Please sign in to comment.