aws_recon 0.2.20 → 0.2.25

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: 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