aws_recon 0.2.5 → 0.2.10

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6a7f47022478c23d46ab98689197a4f986b4dd9b0c31f8b735196e7cb6367f43
4
- data.tar.gz: '09b8c94ee155aec24c9b33912cc2a547cb0be6e022e4a5bbdcce6cce3ed72877'
3
+ metadata.gz: 9ed0fd50b4c2a0542194d2844f4e95c98e2f09ce3802878c0463f78cec8a8f92
4
+ data.tar.gz: 5112bb3ec8e07f61a4ba0f673044486f8423fb61710893650c11cc0e793bd9e1
5
5
  SHA512:
6
- metadata.gz: 98f874c9b86214db1e351aeba46d23c7fbf1c33e88bd3164f1002a17b4643a3b46f65b6702efef41a90083c38631ac737661f98f867049165c8ba4c66a3b28dc
7
- data.tar.gz: c2d70e2d05ee8e9161a8f90d2d066744205e55b0c383c1c8246f827aa1a8f75fa4651745612471f2d6baf0474a46b8e28d1a50bb336f759f3e9437cf36f4ce91
6
+ metadata.gz: 2dc80a5605f4c8673efb9f026e7485a66f7d190fb57e9eb636fd67a8e31db39b857f1d2045b9e56b9ced86dd31f186290f94eac2ef87de3671e4709e604a2d20
7
+ data.tar.gz: a045657734baf60b898d8f78dc94eef3ae69645fcccae85288beddfb2cd46ed1d330edc773fd88e058b1ddaf5031e939df36523398c8b3cc8cdf42e17cb74e64
data/.gitignore CHANGED
@@ -1,5 +1,6 @@
1
1
  .DS_Store
2
2
  *.json
3
+ Gemfile.lock
3
4
  .rvmrc
4
5
  .ruby-gemset
5
6
  .ruby-version
@@ -0,0 +1,34 @@
1
+ ARG RUBY_VERSION=2.6.6
2
+ FROM ruby:${RUBY_VERSION}-alpine
3
+
4
+ LABEL maintainer="Darkbit <info@darkbit.io>"
5
+
6
+ ARG USER=recon
7
+ ARG GEM=aws_recon
8
+ ARG VERSION=0.2.9
9
+ ARG BUNDLER_VERSION=2.1.4
10
+
11
+ # Install new Bundler version
12
+ RUN rm /usr/local/lib/ruby/gems/*/specifications/default/bundler-*.gemspec && \
13
+ gem uninstall bundler && \
14
+ gem install bundler -v ${BUNDLER_VERSION}
15
+
16
+ # Install gem
17
+ RUN gem install ${GEM} -v ${VERSION}
18
+
19
+ # Create non-root user
20
+ RUN addgroup -S ${USER} && \
21
+ adduser -S ${USER} \
22
+ -G ${USER} \
23
+ -s /bin/ash \
24
+ -h /${USER}
25
+
26
+ # Copy binstub
27
+ COPY binstub/${GEM} /usr/local/bundle/bin/
28
+ RUN chmod +x /usr/local/bundle/bin/${GEM}
29
+
30
+ # Switch user
31
+ USER ${USER}
32
+ WORKDIR /${USER}
33
+
34
+ CMD ["ash"]
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2020 Darkbit
3
+ Copyright (c) 2020 Darkbit, LLC
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -28,7 +28,7 @@ Gem::Specification.new do |spec|
28
28
 
29
29
  spec.add_development_dependency 'bundler', '~> 1.17'
30
30
  spec.add_development_dependency 'gem-release', '~> 2.1'
31
- spec.add_development_dependency 'rake', '>= 12.3.3'
31
+ spec.add_development_dependency 'rake', '~> 12.3'
32
32
  spec.add_development_dependency 'minitest', '~> 5.0'
33
33
  spec.add_development_dependency 'solargraph', '~> 0.39.11'
34
34
  spec.add_development_dependency 'rubocop', '~> 0.87.1'
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #
4
+ # Manually generated binstub
5
+ #
6
+
7
+ require "rubygems"
8
+ require "bundler/setup"
9
+
10
+ load Gem.bin_path("aws_recon", "aws_recon")
@@ -44,7 +44,7 @@ module AwsRecon
44
44
  #
45
45
  def collect(service, region)
46
46
  mapper = Object.const_get(service.name)
47
- resources = mapper.new(service.name, region, @options)
47
+ resources = mapper.new(@account_id, service.name, region, @options)
48
48
 
49
49
  collection = resources.collect.map do |resource|
50
50
  if @options.output_format == 'custom'
@@ -0,0 +1,22 @@
1
+ class ElastiCache < Mapper
2
+ def collect
3
+ resources = []
4
+
5
+ #
6
+ # describe_cache_clusters
7
+ #
8
+ @client.describe_cache_clusters.each_with_index do |response, page|
9
+ log(response.context.operation_name, page)
10
+
11
+ response.cache_clusters.each do |cluster|
12
+ struct = OpenStruct.new(cluster.to_h)
13
+ struct.type = 'cluster'
14
+ struct.arn = cluster.arn
15
+
16
+ resources.push(struct.to_h)
17
+ end
18
+ end
19
+
20
+ resources
21
+ end
22
+ end
@@ -10,7 +10,10 @@ class IAM < Mapper
10
10
  # list_mfa_devices
11
11
  # list_ssh_public_keys
12
12
  #
13
- @client.get_account_authorization_details.each_with_index do |response, page|
13
+ opts = {
14
+ filter: %w[User Role Group LocalManagedPolicy AWSManagedPolicy]
15
+ }
16
+ @client.get_account_authorization_details(opts).each_with_index do |response, page|
14
17
  log(response.context.operation_name, page)
15
18
 
16
19
  # users
@@ -19,6 +22,14 @@ class IAM < Mapper
19
22
  struct.type = 'user'
20
23
  struct.mfa_devices = @client.list_mfa_devices({ user_name: user.user_name }).mfa_devices.map(&:to_h)
21
24
  struct.ssh_keys = @client.list_ssh_public_keys({ user_name: user.user_name }).ssh_public_keys.map(&:to_h)
25
+ struct.user_policy_list = if user.user_policy_list
26
+ user.user_policy_list.map do |p|
27
+ {
28
+ policy_name: p.policy_name,
29
+ policy_document: JSON.parse(CGI.unescape(p.policy_document))
30
+ }
31
+ end
32
+ end
22
33
 
23
34
  resources.push(struct.to_h)
24
35
  end
@@ -27,6 +38,14 @@ class IAM < Mapper
27
38
  response.group_detail_list.each do |group|
28
39
  struct = OpenStruct.new(group.to_h)
29
40
  struct.type = 'group'
41
+ struct.group_policy_list = if group.group_policy_list
42
+ group.group_policy_list.map do |p|
43
+ {
44
+ policy_name: p.policy_name,
45
+ policy_document: JSON.parse(CGI.unescape(p.policy_document))
46
+ }
47
+ end
48
+ end
30
49
 
31
50
  resources.push(struct.to_h)
32
51
  end
@@ -35,6 +54,15 @@ class IAM < Mapper
35
54
  response.role_detail_list.each do |role|
36
55
  struct = OpenStruct.new(role.to_h)
37
56
  struct.type = 'role'
57
+ struct.assume_role_policy_document = JSON.parse(CGI.unescape(role.assume_role_policy_document))
58
+ struct.role_policy_list = if role.role_policy_list
59
+ role.role_policy_list.map do |p|
60
+ {
61
+ policy_name: p.policy_name,
62
+ policy_document: JSON.parse(CGI.unescape(p.policy_document))
63
+ }
64
+ end
65
+ end
38
66
 
39
67
  resources.push(struct.to_h)
40
68
  end
@@ -43,6 +71,16 @@ class IAM < Mapper
43
71
  response.policies.each do |policy|
44
72
  struct = OpenStruct.new(policy.to_h)
45
73
  struct.type = 'policy'
74
+ struct.policy_version_list = if policy.policy_version_list
75
+ policy.policy_version_list.map do |p|
76
+ {
77
+ version_id: p.version_id,
78
+ document: JSON.parse(CGI.unescape(p.document)),
79
+ is_default_version: p.is_default_version,
80
+ create_date: p.create_date
81
+ }
82
+ end
83
+ end
46
84
 
47
85
  resources.push(struct.to_h)
48
86
  end
@@ -56,6 +94,7 @@ class IAM < Mapper
56
94
 
57
95
  struct = OpenStruct.new(response.password_policy.to_h)
58
96
  struct.type = 'password_policy'
97
+ struct.arn = "arn:aws:iam::#{@account}:account_password_policy/global"
59
98
 
60
99
  resources.push(struct.to_h)
61
100
  end
@@ -68,6 +107,7 @@ class IAM < Mapper
68
107
 
69
108
  struct = OpenStruct.new(response.summary_map)
70
109
  struct.type = 'account_summary'
110
+ struct.arn = "arn:aws:iam::#{@account}:account_summary/global"
71
111
 
72
112
  resources.push(struct.to_h)
73
113
  end
@@ -111,6 +151,7 @@ class IAM < Mapper
111
151
 
112
152
  struct = OpenStruct.new
113
153
  struct.type = 'credential_report'
154
+ struct.arn = "arn:aws:iam::#{@account}:credential_report/global"
114
155
  struct.content = CSV.parse(response.content, headers: :first_row).map(&:to_h)
115
156
  struct.report_format = response.report_format
116
157
  struct.generated_time = response.generated_time
@@ -31,6 +31,21 @@ class Organizations < Mapper
31
31
  end
32
32
  end
33
33
 
34
+ #
35
+ # list_policies
36
+ #
37
+ @client.list_policies({ filter: 'SERVICE_CONTROL_POLICY' }).each_with_index do |response, page|
38
+ log(response.context.operation_name, page)
39
+
40
+ response.policies.each do |policy|
41
+ struct = OpenStruct.new(policy.to_h)
42
+ struct.type = 'service_control_policy'
43
+ struct.content = JSON.parse(CGI.unescape(@client.describe_policy({ policy_id: policy.id }).policy.content))
44
+
45
+ resources.push(struct.to_h)
46
+ end
47
+ end
48
+
34
49
  resources
35
50
  end
36
51
  end
@@ -13,7 +13,7 @@ class Shield < Mapper
13
13
 
14
14
  struct = OpenStruct.new(response.subscription.to_h)
15
15
  struct.type = 'subscription'
16
- struct.arn = "arn:aws:shield:#{@region}:#{account}:subscription"
16
+ struct.arn = "arn:aws:shield:#{@region}:#{@account}:subscription"
17
17
 
18
18
  resources.push(struct.to_h)
19
19
  end
@@ -26,7 +26,7 @@ class Shield < Mapper
26
26
 
27
27
  struct = OpenStruct.new
28
28
  struct.type = 'contact_list'
29
- struct.arn = "arn:aws:shield:#{@region}:#{account}:contact_list"
29
+ struct.arn = "arn:aws:shield:#{@region}:#{@account}:contact_list"
30
30
  struct.contacts = response.emergency_contact_list.map(&:to_h)
31
31
 
32
32
  resources.push(struct.to_h)
@@ -18,6 +18,7 @@ class SQS < Mapper
18
18
  struct = OpenStruct.new(@client.get_queue_attributes({ queue_url: queue, attribute_names: ['All'] }).attributes.to_h)
19
19
  struct.type = 'queue'
20
20
  struct.arn = struct.QueueArn
21
+ struct.Policy = JSON.parse(CGI.unescape(struct.Policy))
21
22
 
22
23
  resources.push(struct.to_h)
23
24
  end
@@ -8,7 +8,7 @@ class Formatter
8
8
  def custom(account_id, region, service, resource)
9
9
  {
10
10
  account: account_id,
11
- name: resource[:arn] || "#{account_id}_#{region}_#{service.name}_#{resource[:type]}",
11
+ name: resource[:arn],
12
12
  service: service.name,
13
13
  region: region,
14
14
  asset_type: resource[:type],
@@ -22,7 +22,8 @@ class Mapper
22
22
  # S3 (unless the bucket was created in another region)
23
23
  SINGLE_REGION_SERVICES = %w[route53domains s3 shield support organizations].freeze
24
24
 
25
- def initialize(service, region, options)
25
+ def initialize(account, service, region, options)
26
+ @account = account
26
27
  @service = service
27
28
  @region = region
28
29
  @options = options
@@ -21,8 +21,6 @@
21
21
  alias: ec2
22
22
  - name: EKS
23
23
  alias: eks
24
- excluded_regions:
25
- - us-west-1
26
24
  - name: ECS
27
25
  alias: ecs
28
26
  - name: ElasticLoadBalancing
@@ -33,6 +31,8 @@
33
31
  alias: elbv2
34
32
  excluded_regions:
35
33
  - ap-southeast-1
34
+ - name: ElastiCache
35
+ alias: elasticache
36
36
  - name: IAM
37
37
  global: true
38
38
  alias: iam
@@ -1,3 +1,3 @@
1
1
  module AwsRecon
2
- VERSION = "0.2.5"
2
+ VERSION = "0.2.10"
3
3
  end
data/readme.md CHANGED
@@ -26,18 +26,20 @@ Ruby 2.5.x or 2.6.x (developed and tested with 2.6.5)
26
26
 
27
27
  ### Installation
28
28
 
29
- Install the gem:
29
+ AWS Recon can be run locally by installing the Ruby gem, or via a Docker container.
30
+
31
+ To run locally, first install the gem:
30
32
 
31
33
  ```
32
34
  $ gem install aws_recon
33
- Fetching aws_recon-0.2.2.gem
35
+ Fetching aws_recon-0.2.8.gem
34
36
  Fetching aws-sdk-resources-3.76.0.gem
35
37
  Fetching aws-sdk-3.0.1.gem
36
38
  Fetching parallel-1.19.2.gem
37
39
  ...
38
40
  Successfully installed aws-sdk-3.0.1
39
41
  Successfully installed parallel-1.19.2
40
- Successfully installed aws_recon-0.2.2
42
+ Successfully installed aws_recon-0.2.8
41
43
  ```
42
44
 
43
45
  Or add it to your Gemfile using `bundle`:
@@ -49,9 +51,23 @@ Resolving dependencies...
49
51
  ...
50
52
  Using aws-sdk 3.0.1
51
53
  Using parallel 1.19.2
52
- Using aws_recon 0.2.2
54
+ Using aws_recon 0.2.8
55
+ ```
56
+
57
+ To run via a Docker a container, pass the necessary AWS credentials into the Docker `run` command. For example:
58
+
59
+ ```
60
+ $ docker run -t --rm \
61
+ -e AWS_REGION \
62
+ -e AWS_ACCESS_KEY_ID \
63
+ -e AWS_SECRET_ACCESS_KEY \
64
+ -e AWS_SESSION_TOKEN \
65
+ -v $(pwd)/output.json:/recon/output.json \
66
+ darkbitio/aws_recon:latest \
67
+ aws_recon -v -s EC2 -r global,us-east-1,us-east-2
53
68
  ```
54
69
 
70
+
55
71
  ## Usage
56
72
 
57
73
  AWS Recon will leverage any AWS credentials currently available to the environment it runs in. If you are collecting from multiple accounts, you may want to leverage something like [aws-vault](https://github.com/99designs/aws-vault) to manage different credentials.
@@ -66,6 +82,39 @@ Plain environment variables will work fine too.
66
82
  $ AWS_PROFILE=<profile> aws_recon
67
83
  ```
68
84
 
85
+ To run from a Docker container using `aws-vault` managed credentials (output to stdout):
86
+
87
+ ```
88
+ $ aws-vault exec <vault_profile> -- docker run -t --rm \
89
+ -e AWS_REGION \
90
+ -e AWS_ACCESS_KEY_ID \
91
+ -e AWS_SECRET_ACCESS_KEY \
92
+ -e AWS_SESSION_TOKEN \
93
+ darkbitio/aws_recon:latest \
94
+ aws_recon -j -s EC2 -r global,us-east-1,us-east-2
95
+ ```
96
+
97
+ To run from a Docker container using `aws-vault` managed credentials and output to a file, you will need to satisfy a couple of requirements. First, Docker needs access to bind mount the path you specify (or a parent path above). Second, you need to create an empty file to save the output into (e.g. `output.json`). This is because we are only mounting that one file into the Docker container at run time. For example:
98
+
99
+ Create an empty file.
100
+
101
+ ```
102
+ $ touch output.json
103
+ ```
104
+
105
+ Run the `aws_recon` container, specifying the output file.
106
+
107
+ ```
108
+ $ aws-vault exec <vault_profile> -- docker run -t --rm \
109
+ -e AWS_REGION \
110
+ -e AWS_ACCESS_KEY_ID \
111
+ -e AWS_SECRET_ACCESS_KEY \
112
+ -e AWS_SESSION_TOKEN \
113
+ -v $(pwd)/output.json:/recon/output.json \
114
+ darkbitio/aws_recon:latest \
115
+ aws_recon -s EC2 -v -r global,us-east-1,us-east-2
116
+ ```
117
+
69
118
  You may want to use the `-v` or `--verbose` flag initially to see status and activity while collection is running.
70
119
 
71
120
  In verbose mode, the console output will show:
@@ -109,6 +158,12 @@ $ AWS_PROFILE=<profile> aws_recon -s S3,EC2 -r global,us-east-1,us-east-2
109
158
  $ AWS_PROFILE=<profile> aws_recon --services S3,EC2 --regions global,us-east-1,us-east-2
110
159
  ```
111
160
 
161
+ Example [OpenCSPM](https://github.com/OpenCSPM/opencspm) formatted output.
162
+
163
+ ```
164
+ $ AWS_PROFILE=<profile> aws_recon -s S3,EC2 -r global,us-east-1,us-east-2 -f custom > output.json
165
+ ```
166
+
112
167
  #### Errors
113
168
 
114
169
  An exception will be raised on `AccessDeniedException` errors. This typically means your user/role doesn't have the necessary permissions to get/list/describe for that service. These exceptions are raised so troubleshooting access issues is easier.
@@ -135,7 +190,7 @@ Most users will want to limit collection to relevant services and regions. Runni
135
190
  ```
136
191
  $ aws_recon -h
137
192
 
138
- AWS Recon - AWS Inventory Collector
193
+ AWS Recon - AWS Inventory Collector (0.2.8)
139
194
 
140
195
  Usage: aws_recon [options]
141
196
  -r, --regions [REGIONS] Regions to scan, separated by comma (default: all)
@@ -146,6 +201,7 @@ Usage: aws_recon [options]
146
201
  -o, --output [OUTPUT] Specify output file (default: output.json)
147
202
  -f, --format [FORMAT] Specify output format (default: aws)
148
203
  -t, --threads [THREADS] Specify max threads (default: 8, max: 128)
204
+ -u, --user-data Collect EC2 instance user data (default: false)
149
205
  -z, --skip-slow Skip slow operations (default: false)
150
206
  -j, --stream-output Stream JSON lines to stdout (default: false)
151
207
  -v, --verbose Output client progress and current operation
@@ -193,6 +249,7 @@ AWS Recon aims to collect all resources and metadata that are relevant in determ
193
249
  - [x] ELB
194
250
  - [x] EKS
195
251
  - [x] Elasticsearch
252
+ - [x] ElastiCache
196
253
  - [x] Firehose
197
254
  - [ ] FMS
198
255
  - [ ] Glacier
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aws_recon
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.5
4
+ version: 0.2.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josh Larsen
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-08-18 00:00:00.000000000 Z
12
+ date: 2020-11-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: aws-sdk
@@ -71,16 +71,16 @@ dependencies:
71
71
  name: rake
72
72
  requirement: !ruby/object:Gem::Requirement
73
73
  requirements:
74
- - - ">="
74
+ - - "~>"
75
75
  - !ruby/object:Gem::Version
76
- version: 12.3.3
76
+ version: '12.3'
77
77
  type: :development
78
78
  prerelease: false
79
79
  version_requirements: !ruby/object:Gem::Requirement
80
80
  requirements:
81
- - - ">="
81
+ - - "~>"
82
82
  - !ruby/object:Gem::Version
83
- version: 12.3.3
83
+ version: '12.3'
84
84
  - !ruby/object:Gem::Dependency
85
85
  name: minitest
86
86
  requirement: !ruby/object:Gem::Requirement
@@ -152,14 +152,15 @@ files:
152
152
  - ".gitignore"
153
153
  - ".rubocop.yml"
154
154
  - ".travis.yml"
155
+ - Dockerfile
155
156
  - Gemfile
156
- - Gemfile.lock
157
157
  - LICENSE.txt
158
158
  - Rakefile
159
159
  - aws_recon.gemspec
160
160
  - bin/aws_recon
161
161
  - bin/console
162
162
  - bin/setup
163
+ - binstub/aws_recon
163
164
  - lib/aws_recon.rb
164
165
  - lib/aws_recon/aws_recon.rb
165
166
  - lib/aws_recon/collectors.rb
@@ -185,6 +186,7 @@ files:
185
186
  - lib/aws_recon/collectors/ecs.rb
186
187
  - lib/aws_recon/collectors/efs.rb
187
188
  - lib/aws_recon/collectors/eks.rb
189
+ - lib/aws_recon/collectors/elasticache.rb
188
190
  - lib/aws_recon/collectors/elasticloadbalancing.rb
189
191
  - lib/aws_recon/collectors/elasticloadbalancingv2.rb
190
192
  - lib/aws_recon/collectors/elasticsearch.rb