aws_recon 0.2.20 → 0.2.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6b945e5c44370658e13ee470fee26d7e0b46fb6102a661485be9513af305f45d
4
- data.tar.gz: 969330b10b9a8264bdb5759b1c910b74ccf05e855f8f380cdebdac8b3694fb11
3
+ metadata.gz: 8786af60d08967c2203b1f4417480b135ae32b9a40ec05810afc9c2ae126f49d
4
+ data.tar.gz: fbd4741002a54dc15f2b2716dee6e1b6efcec48edfdf3e101d9c7bcb19105a4e
5
5
  SHA512:
6
- metadata.gz: a7d26039b60a21370bd7000d7ccaca93cfc5efc7c0dba88ae5d7ea12d63d3a7b8f973b06d3a1a2633fb82804a716f163ffda23f96a00ee73251eb85956f14dfd
7
- data.tar.gz: 61b1aa696e9ec44fd6b93d4ced975be8a2bb514537ed232db8a11cb840045bc52132a215a21a99e573f4df0b557b1612eb4dda458c249c0e5f86939b83049680
6
+ metadata.gz: 2c11e60d61cf9fcf894b8e38cb136e934b03fd7e9ef1bd349ce27c41c9943e75cb146893d14374167d7f2eb73b40d97a901fbaebd6e6aee5ac93ef8095b1d0b4
7
+ data.tar.gz: 9510a49029fbc382fefc50ad8ae0de0351b6f8648a1044abbebdf695459ef5a4845014057618387549546876c10ea1eb09cba55f4692a0405f41b96810c0dce5
@@ -0,0 +1,25 @@
1
+ class Backup < Mapper
2
+ #
3
+ # Returns an array of resources.
4
+ #
5
+ def collect
6
+ resources = []
7
+
8
+ #
9
+ # list_backup_plans
10
+ #
11
+ @client.list_protected_resources.each_with_index do |response, page|
12
+ log(response.context.operation_name, page)
13
+
14
+ response.results.each do |resource|
15
+ struct = OpenStruct.new(resource.to_h)
16
+ struct.type = 'protected_resource'
17
+ struct.arn = resource.resource_arn
18
+
19
+ resources.push(struct.to_h)
20
+ end
21
+ end
22
+
23
+ resources
24
+ end
25
+ end
@@ -8,20 +8,37 @@ class CodePipeline < Mapper
8
8
  #
9
9
  # list_pipelines
10
10
  #
11
- @client.list_pipelines.each_with_index do |response, page|
12
- log(response.context.operation_name, page)
11
+ begin
12
+ @client.list_pipelines.each_with_index do |response, page|
13
+ log(response.context.operation_name, page)
13
14
 
14
- # get_pipeline
15
- response.pipelines.each do |pipeline|
16
- resp = @client.get_pipeline(name: pipeline.name)
17
- struct = OpenStruct.new(resp.pipeline.to_h)
18
- struct.type = 'pipeline'
19
- struct.arn = resp.metadata.pipeline_arn
15
+ # get_pipeline
16
+ response.pipelines.each do |pipeline|
17
+ resp = @client.get_pipeline(name: pipeline.name)
18
+ struct = OpenStruct.new(resp.pipeline.to_h)
19
+ struct.type = 'pipeline'
20
+ struct.arn = resp.metadata.pipeline_arn
20
21
 
21
- resources.push(struct.to_h)
22
+ resources.push(struct.to_h)
23
+ end
24
+ end
25
+ rescue Aws::CodePipeline::Errors::ServiceError => e
26
+ log_error(e.code)
27
+
28
+ unless suppressed_errors.include?(e.code) && !@options.quit_on_exception
29
+ raise e
22
30
  end
23
31
  end
24
32
 
25
33
  resources
26
34
  end
35
+
36
+ private
37
+
38
+ # not an error
39
+ def suppressed_errors
40
+ %w[
41
+ AccessDeniedException
42
+ ]
43
+ end
27
44
  end
@@ -14,7 +14,7 @@ class DatabaseMigrationService < Mapper
14
14
  response.replication_instances.each do |instance|
15
15
  struct = OpenStruct.new(instance.to_h)
16
16
  struct.type = 'replication_instance'
17
- struct.arb = "arn:aws:#{@service}:#{@region}::replication_instance/#{instance.replication_instance_identifier}"
17
+ struct.arn = "arn:aws:#{@service}:#{@region}::replication_instance/#{instance.replication_instance_identifier}"
18
18
 
19
19
  resources.push(struct.to_h)
20
20
  end
@@ -29,6 +29,7 @@ class DynamoDB < Mapper
29
29
  struct = OpenStruct.new(@client.describe_table({ table_name: table_name }).table.to_h)
30
30
  struct.type = 'table'
31
31
  struct.arn = struct.table_arn
32
+ struct.continuous_backups_description = @client.describe_continuous_backups({ table_name: table_name }).continuous_backups_description.to_h
32
33
 
33
34
  resources.push(struct.to_h)
34
35
  end
@@ -16,10 +16,14 @@ class ECR < Mapper
16
16
  struct.type = 'repository'
17
17
  struct.arn = repo.repository_arn
18
18
  struct.policy = @client
19
- .get_repository_policy({ repository_name: repo.repository_name }).to_h
19
+ .get_repository_policy({ repository_name: repo.repository_name }).policy_text.parse_policy
20
20
 
21
21
  rescue Aws::ECR::Errors::ServiceError => e
22
- raise e unless suppressed_errors.include?(e.code)
22
+ log_error(e.code)
23
+
24
+ unless suppressed_errors.include?(e.code) && !@options.quit_on_exception
25
+ raise e
26
+ end
23
27
  ensure
24
28
  resources.push(struct.to_h)
25
29
  end
@@ -0,0 +1,39 @@
1
+ class EMR < Mapper
2
+ #
3
+ # Returns an array of resources.
4
+ #
5
+ def collect
6
+ resources = []
7
+
8
+ #
9
+ # get_block_public_access_configuration
10
+ #
11
+ @client.get_block_public_access_configuration.each do |response|
12
+ log(response.context.operation_name)
13
+
14
+ struct = OpenStruct.new(response.block_public_access_configuration.to_h)
15
+ struct.type = 'configuration'
16
+
17
+ resources.push(struct.to_h)
18
+ end
19
+
20
+ #
21
+ # list_clusters
22
+ #
23
+ @client.list_clusters.each_with_index do |response, page|
24
+ log(response.context.operation_name, page)
25
+
26
+ response.clusters.each do |cluster|
27
+ log(response.context.operation_name, cluster.id)
28
+
29
+ struct = OpenStruct.new(@client.describe_cluster({ cluster_id: cluster.id }).cluster.to_h)
30
+ struct.type = 'cluster'
31
+ struct.arn = cluster.cluster_arn
32
+
33
+ resources.push(struct.to_h)
34
+ end
35
+ end
36
+
37
+ resources
38
+ end
39
+ end
@@ -89,14 +89,22 @@ class IAM < Mapper
89
89
  #
90
90
  # get_account_password_policy
91
91
  #
92
- @client.get_account_password_policy.each do |response|
93
- log(response.context.operation_name)
92
+ begin
93
+ @client.get_account_password_policy.each do |response|
94
+ log(response.context.operation_name)
94
95
 
95
- struct = OpenStruct.new(response.password_policy.to_h)
96
- struct.type = 'password_policy'
97
- struct.arn = "arn:aws:iam::#{@account}:account_password_policy/global"
96
+ struct = OpenStruct.new(response.password_policy.to_h)
97
+ struct.type = 'password_policy'
98
+ struct.arn = "arn:aws:iam::#{@account}:account_password_policy/global"
98
99
 
99
- resources.push(struct.to_h)
100
+ resources.push(struct.to_h)
101
+ end
102
+ rescue Aws::IAM::Errors::ServiceError => e
103
+ log_error(e.code)
104
+
105
+ unless suppressed_errors.include?(e.code) && !@options.quit_on_exception
106
+ raise e
107
+ end
100
108
  end
101
109
 
102
110
  #
@@ -178,7 +186,10 @@ class IAM < Mapper
178
186
  end
179
187
  rescue Aws::IAM::Errors::ServiceError => e
180
188
  log_error(e.code)
181
- raise e unless suppressed_errors.include?(e.code)
189
+
190
+ unless suppressed_errors.include?(e.code) && !@options.quit_on_exception
191
+ raise e
192
+ end
182
193
  end
183
194
 
184
195
  resources
@@ -190,6 +201,7 @@ class IAM < Mapper
190
201
  def suppressed_errors
191
202
  %w[
192
203
  ReportNotPresent
204
+ NoSuchEntity
193
205
  ]
194
206
  end
195
207
  end
@@ -29,7 +29,10 @@ class KMS < Mapper
29
29
  .key_rotation_enabled
30
30
  rescue Aws::KMS::Errors::ServiceError => e
31
31
  log_error(e.code)
32
- raise e unless suppressed_errors.include?(e.code)
32
+
33
+ unless suppressed_errors.include?(e.code) && !@options.quit_on_exception
34
+ raise e
35
+ end
33
36
  end
34
37
 
35
38
  # list_grants
@@ -34,18 +34,35 @@ class Organizations < Mapper
34
34
  #
35
35
  # list_policies
36
36
  #
37
- @client.list_policies({ filter: 'SERVICE_CONTROL_POLICY' }).each_with_index do |response, page|
38
- log(response.context.operation_name, page)
37
+ begin
38
+ @client.list_policies({ filter: 'SERVICE_CONTROL_POLICY' }).each_with_index do |response, page|
39
+ log(response.context.operation_name, page)
39
40
 
40
- response.policies.each do |policy|
41
- struct = OpenStruct.new(policy.to_h)
42
- struct.type = 'service_control_policy'
43
- struct.content = @client.describe_policy({ policy_id: policy.id }).policy.content.parse_policy
41
+ response.policies.each do |policy|
42
+ struct = OpenStruct.new(policy.to_h)
43
+ struct.type = 'service_control_policy'
44
+ struct.content = @client.describe_policy({ policy_id: policy.id }).policy.content.parse_policy
44
45
 
45
- resources.push(struct.to_h)
46
+ resources.push(struct.to_h)
47
+ end
48
+ end
49
+ rescue Aws::Organizations::Errors::ServiceError => e
50
+ log_error(e.code)
51
+
52
+ unless suppressed_errors.include?(e.code) && !@options.quit_on_exception
53
+ raise e
46
54
  end
47
55
  end
48
56
 
49
57
  resources
50
58
  end
59
+
60
+ private
61
+
62
+ # not an error
63
+ def suppressed_errors
64
+ %w[
65
+ AccessDeniedException
66
+ ]
67
+ end
51
68
  end
@@ -38,6 +38,8 @@ class RDS < Mapper
38
38
  struct.arn = instance.db_instance_arn
39
39
  struct.parent_id = instance.db_cluster_identifier
40
40
 
41
+ # TODO: describe_db_snapshots here (with public flag)
42
+
41
43
  resources.push(struct.to_h)
42
44
  end
43
45
  end
@@ -15,6 +15,7 @@ class Redshift < Mapper
15
15
  struct = OpenStruct.new(cluster.to_h)
16
16
  struct.type = 'cluster'
17
17
  struct.arn = cluster.cluster_identifier
18
+ struct.logging_status = @client.describe_logging_status({ cluster_identifier: cluster.cluster_identifier }).to_h
18
19
 
19
20
  resources.push(struct.to_h)
20
21
  end
@@ -61,7 +61,11 @@ class S3 < Mapper
61
61
  end
62
62
 
63
63
  rescue Aws::S3::Errors::ServiceError => e
64
- raise e unless suppressed_errors.include?(e.code)
64
+ log_error(e.code)
65
+
66
+ unless suppressed_errors.include?(e.code) && !@options.quit_on_exception
67
+ raise e
68
+ end
65
69
  end
66
70
 
67
71
  resources.push(struct.to_h)
@@ -8,16 +8,33 @@ class SecurityHub < Mapper
8
8
  #
9
9
  # describe_hub
10
10
  #
11
- @client.describe_hub.each do |response|
12
- log(response.context.operation_name)
11
+ begin
12
+ @client.describe_hub.each do |response|
13
+ log(response.context.operation_name)
13
14
 
14
- struct = OpenStruct.new(response.to_h)
15
- struct.type = 'hub'
16
- struct.arn = response.hub_arn
15
+ struct = OpenStruct.new(response.to_h)
16
+ struct.type = 'hub'
17
+ struct.arn = response.hub_arn
17
18
 
18
- resources.push(struct.to_h)
19
+ resources.push(struct.to_h)
20
+ end
21
+ rescue Aws::SecurityHub::Errors::ServiceError => e
22
+ log_error(e.code)
23
+
24
+ unless suppressed_errors.include?(e.code) && !@options.quit_on_exception
25
+ raise e
26
+ end
19
27
  end
20
28
 
21
29
  resources
22
30
  end
31
+
32
+ private
33
+
34
+ # not an error
35
+ def suppressed_errors
36
+ %w[
37
+ InvalidAccessException
38
+ ]
39
+ end
23
40
  end
@@ -27,7 +27,10 @@ class ServiceQuotas < Mapper
27
27
  end
28
28
  rescue Aws::ServiceQuotas::Errors::ServiceError => e
29
29
  log_error(e.code, service)
30
- raise e unless suppressed_errors.include?(e.code)
30
+
31
+ unless suppressed_errors.include?(e.code) && !@options.quit_on_exception
32
+ raise e
33
+ end
31
34
  end
32
35
 
33
36
  resources
@@ -51,7 +51,10 @@ class Shield < Mapper
51
51
  resources
52
52
  rescue Aws::Shield::Errors::ServiceError => e
53
53
  log_error(e.code)
54
- raise e unless suppressed_errors.include?(e.code)
54
+
55
+ unless suppressed_errors.include?(e.code) && !@options.quit_on_exception
56
+ raise e
57
+ end
55
58
 
56
59
  [] # no access or service isn't enabled
57
60
  end
@@ -27,7 +27,10 @@ class Support < Mapper
27
27
  resources
28
28
  rescue Aws::Support::Errors::ServiceError => e
29
29
  log_error(e.code)
30
- raise e unless suppressed_errors.include?(e.code)
30
+
31
+ unless suppressed_errors.include?(e.code) && !@options.quit_on_exception
32
+ raise e
33
+ end
31
34
 
32
35
  [] # no Support subscription
33
36
  end
@@ -37,6 +40,7 @@ class Support < Mapper
37
40
  # not an error
38
41
  def suppressed_errors
39
42
  %w[
43
+ AccessDeniedException
40
44
  SubscriptionRequiredException
41
45
  ]
42
46
  end
@@ -20,6 +20,7 @@ class Parser
20
20
  :skip_credential_report,
21
21
  :stream_output,
22
22
  :verbose,
23
+ :quit_on_exception,
23
24
  :debug
24
25
  )
25
26
 
@@ -47,6 +48,7 @@ class Parser
47
48
  false,
48
49
  false,
49
50
  false,
51
+ false,
50
52
  false
51
53
  )
52
54
 
@@ -135,6 +137,11 @@ class Parser
135
137
  args.verbose = true unless args.stream_output
136
138
  end
137
139
 
140
+ # re-raise exceptions
141
+ opts.on('-q', '--quit-on-exception', 'Stop collection if an API error is encountered (default: false)') do
142
+ args.quit_on_exception = true
143
+ end
144
+
138
145
  # debug
139
146
  opts.on('-d', '--debug', 'Output debug with wire trace info') do
140
147
  unless args.stream_output
@@ -6,12 +6,19 @@
6
6
  alias: aa
7
7
  - name: ApplicationAutoScaling
8
8
  alias: aas
9
+ - name: Backup
10
+ alias: backup
9
11
  - name: ConfigService
10
12
  alias: config
11
13
  - name: CodeBuild
12
14
  alias: codebuild
15
+ excluded_regions:
16
+ - af-south-1
13
17
  - name: CodePipeline
14
18
  alias: codepipeline
19
+ excluded_regions:
20
+ - af-south-1
21
+ - me-south-1
15
22
  - name: AutoScaling
16
23
  alias: autoscaling
17
24
  - name: CloudTrail
@@ -37,6 +44,13 @@
37
44
  - ap-southeast-1
38
45
  - name: ElastiCache
39
46
  alias: elasticache
47
+ - name: EMR
48
+ alias: emr
49
+ excluded_regions:
50
+ - ap-east-1
51
+ - af-south-1
52
+ - eu-south-1
53
+ - me-south-1
40
54
  - name: IAM
41
55
  global: true
42
56
  alias: iam
@@ -85,12 +99,17 @@
85
99
  - eu-north-1
86
100
  - eu-west-3
87
101
  - us-west-1
102
+ - ap-east-1
103
+ - af-south-1
104
+ - eu-south-1
88
105
  - name: CloudWatch
89
106
  alias: cloudwatch
90
107
  - name: CloudWatchLogs
91
108
  alias: cloudwatchlogs
92
109
  - name: Kafka
93
110
  alias: kafka
111
+ excluded_regions:
112
+ - af-south-1
94
113
  - name: SecretsManager
95
114
  alias: sm
96
115
  - name: SecurityHub
@@ -116,6 +135,10 @@
116
135
  - eu-north-1
117
136
  - us-west-1
118
137
  - sa-east-1
138
+ - ap-east-1
139
+ - af-south-1
140
+ - eu-south-1
141
+ - me-south-1
119
142
  - name: WorkSpaces
120
143
  alias: workspaces
121
144
  excluded_regions:
@@ -124,12 +147,21 @@
124
147
  - eu-west-3
125
148
  - us-east-2
126
149
  - us-west-1
150
+ - ap-east-1
151
+ - af-south-1
152
+ - eu-south-1
153
+ - me-south-1
127
154
  - name: SageMaker
128
155
  alias: sagemaker
129
156
  - name: ServiceQuotas
130
157
  alias: servicequotas
131
158
  - name: Transfer
132
159
  alias: transfer
160
+ excluded_regions:
161
+ - ap-east-1
162
+ - af-south-1
163
+ - eu-south-1
164
+ - me-south-1
133
165
  - name: DirectConnect
134
166
  alias: dc
135
167
  - name: DirectoryService
@@ -1,3 +1,3 @@
1
1
  module AwsRecon
2
- VERSION = "0.2.20"
2
+ VERSION = "0.2.25"
3
3
  end
data/readme.md CHANGED
@@ -215,6 +215,20 @@ Usage: aws_recon [options]
215
215
 
216
216
  Output is always some form of JSON - either JSON lines or plain JSON. The output is either written to a file (the default), or written to stdout (with `-j`).
217
217
 
218
+ ## Support for Manually Enabled Regions
219
+
220
+ If you have enabled manually enabled regions:
221
+
222
+ - me-south-1 - Middle East (Bahrain)
223
+ - af-south-1 - Africa (Cape Town)
224
+ - ap-east-1 - Asia Pacific (Hong Kong)
225
+ - eu-south-1 - Europe (Milan)
226
+
227
+ and you are using STS to assume a role into an account, you will need to [enable v2 STS tokens](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html) in the account you are assuming the role **from** to be able to run AWS Recon against those regions.
228
+
229
+ > Version 1 tokens are valid only in AWS Regions that are available by default. These tokens do not work in manually enabled Regions, such as Asia Pacific (Hong Kong). Version 2 tokens are valid in all Regions. However, version 2 tokens are longer and might affect systems where you temporarily store tokens.
230
+
231
+ If you are using a static access key/secret, you can collect from these regions with either `v1` or `v2` STS tokens.
218
232
 
219
233
  ## Supported Services & Resources
220
234
 
@@ -226,6 +240,7 @@ AWS Recon aims to collect all resources and metadata that are relevant in determ
226
240
  - [x] AdvancedShield
227
241
  - [x] ApplicationAutoScaling
228
242
  - [x] Athena
243
+ - [x] Backup
229
244
  - [x] GuardDuty
230
245
  - [ ] Macie
231
246
  - [x] Systems Manager
@@ -249,8 +264,9 @@ AWS Recon aims to collect all resources and metadata that are relevant in determ
249
264
  - [x] ECR
250
265
  - [x] ECS
251
266
  - [x] EFS
252
- - [x] ELB
253
267
  - [x] EKS
268
+ - [x] ELB
269
+ - [x] EMR
254
270
  - [x] Elasticsearch
255
271
  - [x] ElastiCache
256
272
  - [x] Firehose
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.20
4
+ version: 0.2.25
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-11-26 00:00:00.000000000 Z
12
+ date: 2020-12-06 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: aws-sdk
@@ -187,6 +187,7 @@ files:
187
187
  - lib/aws_recon/collectors/applicationautoscaling.rb
188
188
  - lib/aws_recon/collectors/athena.rb
189
189
  - lib/aws_recon/collectors/autoscaling.rb
190
+ - lib/aws_recon/collectors/backup.rb
190
191
  - lib/aws_recon/collectors/cloudformation.rb
191
192
  - lib/aws_recon/collectors/cloudfront.rb
192
193
  - lib/aws_recon/collectors/cloudtrail.rb
@@ -208,6 +209,7 @@ files:
208
209
  - lib/aws_recon/collectors/elasticloadbalancing.rb
209
210
  - lib/aws_recon/collectors/elasticloadbalancingv2.rb
210
211
  - lib/aws_recon/collectors/elasticsearch.rb
212
+ - lib/aws_recon/collectors/emr.rb
211
213
  - lib/aws_recon/collectors/firehose.rb
212
214
  - lib/aws_recon/collectors/guardduty.rb
213
215
  - lib/aws_recon/collectors/iam.rb