miasma-aws 0.2.4 → 0.3.0

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
  SHA1:
3
- metadata.gz: 1cb99ed122c82e005f8d171610fff1b9d5709e58
4
- data.tar.gz: 1c9e9dc24dd0bbe188498a49c4f1255f82e11b94
3
+ metadata.gz: 9221ddb29c7683d78498567b552c6295078a97aa
4
+ data.tar.gz: e83a397c308eb038fdbaa14a0cc65da3b7da5441
5
5
  SHA512:
6
- metadata.gz: 1bc7a82669aad8364a8efb38ebd87ae97b5682d1449561f24f810549d16b4ebf74be2af02faaba072bf17650dd86c1f7f55703ce51d23cb90485a8c91e181688
7
- data.tar.gz: 11d1fc54c194c3401c5a1eb4c9a20c6d4be8679349b486386c54b86178ab3b3275941c9159111341bf2897a975c2b19533bdfae99a5e7f0b17e23e19ca2185d9
6
+ metadata.gz: 3e5f23aba1e47e832362638e817d66d6763b0422453fc78a8ec3814afd43130aa0762b8326b7111657d85d4106daf4791dfd20b93f8e596ccb836f7fe9ed0ee7
7
+ data.tar.gz: eeafe49ac5867ae0f24960ad25e7cb8a173bf0e5054535b5ec40efedd4700da84fa19aa1ab3aa96656d0be37d3fa5bbe2a27481628e63a0479ef737a26055721
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ # v0.3.0
2
+ * [feature] Add sts session token support (MFA #35)
3
+ * [enhancement] Load single bucket directly (remove bucket list permission requirement #33)
4
+
1
5
  # v0.2.4
2
6
  * [enhancement] Add aws credential file mappings for token value (#31)
3
7
  * [fix] Support quoted values within aws credentials/configuration files (#31)
data/README.md CHANGED
@@ -39,6 +39,9 @@ Miasma.api(
39
39
  * `aws_sts_role_arn` - Assume role
40
40
  * `aws_external_id` - Provide an external ID when assuming role
41
41
  * `aws_sts_role_session_name` - Provide custom session name when assuming role
42
+ * `aws_sts_session_token` - MFA related session token
43
+ * `aws_sts_session_token_code` - Six digit code from MFA device
44
+ * `aws_sts_mfa_serial_number` - Serial number or ARN of MFA device
42
45
 
43
46
  ### S3 related attributes
44
47
 
@@ -92,6 +92,27 @@ module Miasma
92
92
  bucket
93
93
  end
94
94
 
95
+ # Directly fetch bucket
96
+ #
97
+ # @param ident [String] identifier
98
+ # @return [Models::Storage::Bucket, NilClass]
99
+ def bucket_get(ident)
100
+ bucket = Bucket.new(self,
101
+ :id => ident,
102
+ :name => ident
103
+ )
104
+ begin
105
+ bucket.reload
106
+ bucket
107
+ rescue Error::ApiError::RequestError => e
108
+ if(e.response.status == 404)
109
+ nil
110
+ else
111
+ raise
112
+ end
113
+ end
114
+ end
115
+
95
116
  # Destroy bucket
96
117
  #
97
118
  # @param bucket [Models::Storage::Bucket]
@@ -337,6 +337,9 @@ module Miasma
337
337
  attribute :aws_sts_role_session_name, String
338
338
  attribute :aws_sts_region, String
339
339
  attribute :aws_sts_host, String
340
+ attribute :aws_sts_session_token, String
341
+ attribute :aws_sts_session_token_code, [String, Proc, Method]
342
+ attribute :aws_sts_mfa_serial_number, [String]
340
343
  attribute :aws_credentials_file, String, :required => true, :default => File.join(Dir.home, '.aws/credentials')
341
344
  attribute :aws_config_file, String, :required => true, :default => File.join(Dir.home, '.aws/config')
342
345
  attribute :aws_access_key_id, String, :required => true
@@ -349,9 +352,6 @@ module Miasma
349
352
  attribute :euca_compat, Symbol, :allowed_values => [:path, :dns], :coerce => lambda{|v| v.is_a?(String) ? v.to_sym : v}
350
353
  attribute :euca_dns_map, Smash, :coerce => lambda{|v| v.to_smash}, :default => Smash.new
351
354
  attribute :ssl_enabled, [TrueClass, FalseClass], :default => true
352
-
353
- # @return [Contrib::AwsApiCore::SignatureV4]
354
- attr_reader :signer
355
355
  end
356
356
 
357
357
  # AWS config file key remapping
@@ -360,7 +360,7 @@ module Miasma
360
360
  'region' => 'aws_region',
361
361
  'role_arn' => 'aws_sts_role_arn',
362
362
  'aws_security_token' => 'aws_sts_token',
363
- 'aws_session_token' => 'aws_sts_token'
363
+ 'aws_session_token' => 'aws_sts_session_token'
364
364
  )
365
365
  )
366
366
  klass.const_set(:INSTANCE_PROFILE_HOST, 'http://169.254.169.254')
@@ -405,9 +405,6 @@ module Miasma
405
405
  ).merge(creds)
406
406
  )
407
407
  end
408
- if(creds[:aws_sts_role_arn])
409
- sts_assume_role!(creds)
410
- end
411
408
  if(creds[:aws_iam_instance_profile])
412
409
  load_instance_credentials!(creds)
413
410
  end
@@ -472,24 +469,42 @@ module Miasma
472
469
  true
473
470
  end
474
471
 
472
+ def sts_mfa_session!(creds)
473
+ if(sts_mfa_session_update_required?(creds))
474
+ sts = Miasma::Contrib::Aws::Api::Sts.new(
475
+ :aws_access_key_id => creds[:aws_access_key_id],
476
+ :aws_secret_access_key => creds[:aws_secret_access_key],
477
+ :aws_region => creds.fetch(:aws_sts_region, 'us-east-1'),
478
+ :aws_credentials_file => creds.fetch(:aws_credentials_file, aws_credentials_file),
479
+ :aws_config_file => creds.fetch(:aws_config_file, aws_config_file),
480
+ :aws_profile_name => creds[:aws_profile_name],
481
+ :aws_host => creds[:aws_sts_host],
482
+ )
483
+ creds.merge!(
484
+ sts.mfa_session(
485
+ creds[:aws_sts_session_token_code],
486
+ :mfa_serial => creds[:aws_sts_mfa_serial_number]
487
+ )
488
+ )
489
+ end
490
+ true
491
+ end
492
+
475
493
  # Assume requested role and replace key id and secret
476
494
  #
477
495
  # @param creds [Hash]
478
496
  # @return [TrueClass]
479
497
  def sts_assume_role!(creds)
480
- unless(creds[:aws_access_key_id_original])
481
- creds[:aws_access_key_id_original] = creds[:aws_access_key_id]
482
- creds[:aws_secret_access_key_original] = creds[:aws_secret_access_key]
483
- end
484
- if(sts_update_required?(creds))
498
+ if(sts_assume_role_update_required?(creds))
485
499
  sts = Miasma::Contrib::Aws::Api::Sts.new(
486
- :aws_access_key_id => creds[:aws_access_key_id_original],
487
- :aws_secret_access_key => creds[:aws_secret_access_key_original],
500
+ :aws_access_key_id => get_credential(:access_key_id, creds),
501
+ :aws_secret_access_key => get_credential(:secret_access_key, creds),
488
502
  :aws_region => creds.fetch(:aws_sts_region, 'us-east-1'),
489
503
  :aws_credentials_file => creds.fetch(:aws_credentials_file, aws_credentials_file),
490
504
  :aws_config_file => creds.fetch(:aws_config_file, aws_config_file),
491
505
  :aws_profile_name => creds[:aws_profile_name],
492
- :aws_host => creds[:aws_sts_host]
506
+ :aws_host => creds[:aws_sts_host],
507
+ :aws_sts_token => creds[:aws_sts_session_token]
493
508
  )
494
509
  role_info = sts.assume_role(
495
510
  creds[:aws_sts_role_arn],
@@ -590,11 +605,33 @@ module Miasma
590
605
  ].join('.')
591
606
  end
592
607
  end
593
- @signer = Contrib::AwsApiCore::SignatureV4.new(
594
- aws_access_key_id, aws_secret_access_key, aws_region, self.class::API_SERVICE
608
+ end
609
+
610
+ # @return [Contrib::AwsApiCore::SignatureV4]
611
+ def signer
612
+ Contrib::AwsApiCore::SignatureV4.new(
613
+ get_credential(:access_key_id),
614
+ get_credential(:secret_access_key),
615
+ aws_region,
616
+ self.class::API_SERVICE
595
617
  )
596
618
  end
597
619
 
620
+ # Return correct credential value based on STS context
621
+ #
622
+ # @param key [String, Symbol] credential suffix
623
+ # @return [Object]
624
+ def get_credential(key, data_hash=nil)
625
+ data_hash = attributes if data_hash.nil?
626
+ if(data_hash[:aws_sts_token])
627
+ data_hash.fetch("aws_sts_#{key}", data_hash["aws_#{key}"])
628
+ elsif(data_hash[:aws_sts_session_token])
629
+ data_hash.fetch("aws_sts_session_#{key}", data_hash["aws_#{key}"])
630
+ else
631
+ data_hash["aws_#{key}"]
632
+ end
633
+ end
634
+
598
635
  # @return [String] custom escape for aws compat
599
636
  def uri_escape(string)
600
637
  signer.safe_escape(string)
@@ -637,8 +674,16 @@ module Miasma
637
674
  )
638
675
  end
639
676
  end
640
- if(aws_sts_token)
641
- sts_assume_role!(attributes) if sts_update_required?
677
+ if(aws_sts_session_token || aws_sts_session_token_code)
678
+ if(sts_mfa_session_update_required?)
679
+ sts_mfa_session!(data)
680
+ end
681
+ options.set(:headers, 'X-Amz-Security-Token', aws_sts_session_token)
682
+ end
683
+ if(aws_sts_token || aws_sts_role_arn)
684
+ if(sts_assume_role_update_required?)
685
+ sts_assume_role!(data)
686
+ end
642
687
  options.set(:headers, 'X-Amz-Security-Token', aws_sts_token)
643
688
  end
644
689
  signature = signer.generate(http_method, path, options)
@@ -649,10 +694,21 @@ module Miasma
649
694
 
650
695
  # @return [TrueClass, FalseClass]
651
696
  # @note update check only applied if assuming role
652
- def sts_update_required?(args={})
653
- if(args.fetch(:aws_sts_role_arn, data[:aws_sts_role_arn]))
654
- expiry = args.fetch(:aws_sts_token_expires, data[:aws_sts_token_expires])
655
- expiry.nil? || expiry <= Time.now - 1
697
+ def sts_assume_role_update_required?(args={})
698
+ if(args.fetch(:aws_sts_role_arn, attributes[:aws_sts_role_arn]))
699
+ expiry = args.fetch(:aws_sts_token_expires, attributes[:aws_sts_token_expires])
700
+ expiry.nil? || expiry <= Time.now - 15
701
+ else
702
+ false
703
+ end
704
+ end
705
+
706
+ # @return [TrueClass, FalseClass]
707
+ # @note update check only applied if assuming role
708
+ def sts_mfa_session_update_required?(args={})
709
+ if(args.fetch(:aws_sts_session_token_code, attributes[:aws_sts_session_token_code]))
710
+ expiry = args.fetch(:aws_sts_session_token_expires, attributes[:aws_sts_session_token_expires])
711
+ expiry.nil? || expiry <= Time.now - 15
656
712
  else
657
713
  false
658
714
  end
@@ -0,0 +1,49 @@
1
+ require 'miasma'
2
+
3
+ module Miasma
4
+ module Contrib
5
+ module Aws
6
+ module Api
7
+ class Iam < Miasma::Types::Api
8
+
9
+ # Service name of the API
10
+ API_SERVICE = 'iam'
11
+ # Supported version of the IAM API
12
+ API_VERSION = '2010-05-08'
13
+
14
+ include Contrib::AwsApiCore::ApiCommon
15
+ include Contrib::AwsApiCore::RequestUtils
16
+
17
+ def connect
18
+ super
19
+ service_name = self.class::API_SERVICE.downcase
20
+ self.aws_host = [
21
+ service_name,
22
+ api_endpoint
23
+ ].join('.')
24
+ end
25
+
26
+ # Fetch current user information
27
+ def user_info
28
+ result = request(
29
+ :path => '/',
30
+ :params => {
31
+ 'Action' => 'GetUser'
32
+ }
33
+ ).get(:body, 'GetUserResponse', 'GetUserResult', 'User')
34
+ Smash.new(
35
+ :user_id => result['UserId'],
36
+ :path => result['Path'],
37
+ :username => result['UserName'],
38
+ :arn => result['Arn'],
39
+ :created => result['CreateDate'],
40
+ :password_last_used => result['PasswordLastUsed'],
41
+ :account_id => result['Arn'].split(':')[4]
42
+ )
43
+ end
44
+
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -8,12 +8,38 @@ module Miasma
8
8
 
9
9
  # Service name of the API
10
10
  API_SERVICE = 'sts'
11
- # Supported version of the AutoScaling API
11
+ # Supported version of the STS API
12
12
  API_VERSION = '2011-06-15'
13
13
 
14
14
  include Contrib::AwsApiCore::ApiCommon
15
15
  include Contrib::AwsApiCore::RequestUtils
16
16
 
17
+ # Generate MFA session credentials
18
+ #
19
+ # @param token_code [String, Proc] Code from MFA device
20
+ # @param args [Hash]
21
+ # @option args [Integer] :duration life of session in seconds
22
+ # @option args [String] :mfa_serial MFA device identification number
23
+ # @return [Hash]
24
+ def mfa_session(token_code, args={})
25
+ req_params = Smash.new.tap do |params|
26
+ params['Action'] = 'GetSessionToken'
27
+ params['TokenCode'] = token_code.respond_to?(:call) ? token_code.call : token_code
28
+ params['DurationSeconds'] = args[:duration] if args[:duration]
29
+ params['SerialNumber'] = args[:mfa_serial].to_s.empty? ? default_mfa_serial : args[:mfa_serial]
30
+ end
31
+ result = request(
32
+ :path => '/',
33
+ :params => req_params
34
+ ).get(:body, 'GetSessionTokenResponse', 'GetSessionTokenResult', 'Credentials')
35
+ Smash.new(
36
+ :aws_sts_session_token => result['SessionToken'],
37
+ :aws_sts_session_secret_access_key => result['SecretAccessKey'],
38
+ :aws_sts_session_access_key_id => result['AccessKeyId'],
39
+ :aws_sts_session_token_expires => Time.parse(result['Expiration'])
40
+ )
41
+ end
42
+
17
43
  # Assume new role
18
44
  #
19
45
  # @param role_arn [String] IAM Role ARN
@@ -34,14 +60,26 @@ module Miasma
34
60
  ).get(:body, 'AssumeRoleResponse', 'AssumeRoleResult')
35
61
  Smash.new(
36
62
  :aws_sts_token => result.get('Credentials', 'SessionToken'),
37
- :aws_secret_access_key => result.get('Credentials', 'SecretAccessKey'),
38
- :aws_access_key_id => result.get('Credentials', 'AccessKeyId'),
63
+ :aws_sts_secret_access_key => result.get('Credentials', 'SecretAccessKey'),
64
+ :aws_sts_access_key_id => result.get('Credentials', 'AccessKeyId'),
39
65
  :aws_sts_token_expires => Time.parse(result.get('Credentials', 'Expiration')),
40
66
  :aws_sts_assumed_role_arn => result.get('AssumedRoleUser', 'Arn'),
41
67
  :aws_sts_assumed_role_id => result.get('AssumedRoleUser', 'AssumedRoleId')
42
68
  )
43
69
  end
44
70
 
71
+ # @return [String]
72
+ def default_mfa_serial
73
+ user_data = Iam.new(
74
+ Smash[
75
+ [:aws_access_key_id, :aws_secret_access_key, :aws_region].map do |key|
76
+ [key, attributes[key]]
77
+ end
78
+ ]
79
+ ).user_info
80
+ "arn:aws:iam::#{user_data[:account_id]}:mfa/#{user_data[:username]}"
81
+ end
82
+
45
83
  end
46
84
  end
47
85
  end
@@ -5,6 +5,7 @@ module Miasma
5
5
  module Aws
6
6
  module Api
7
7
  autoload :Sts, 'miasma-aws/api/sts'
8
+ autoload :Iam, 'miasma-aws/api/iam'
8
9
  end
9
10
  end
10
11
  end
@@ -1,4 +1,4 @@
1
1
  module MiasmaAws
2
2
  # Current library version
3
- VERSION = Gem::Version.new('0.2.4')
3
+ VERSION = Gem::Version.new('0.3.0')
4
4
  end
data/miasma-aws.gemspec CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |s|
10
10
  s.description = 'Smoggy AWS API'
11
11
  s.license = 'Apache 2.0'
12
12
  s.require_path = 'lib'
13
- s.add_development_dependency 'miasma', '>= 0.2.35'
13
+ s.add_runtime_dependency 'miasma', '>= 0.2.39', '< 0.5'
14
14
  s.add_development_dependency 'rake'
15
15
  s.add_development_dependency 'rubocop'
16
16
  s.add_development_dependency 'pry'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: miasma-aws
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.4
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Roberts
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-02-03 00:00:00.000000000 Z
11
+ date: 2016-02-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: miasma
@@ -16,14 +16,20 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 0.2.35
20
- type: :development
19
+ version: 0.2.39
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '0.5'
23
+ type: :runtime
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
23
26
  requirements:
24
27
  - - ">="
25
28
  - !ruby/object:Gem::Version
26
- version: 0.2.35
29
+ version: 0.2.39
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '0.5'
27
33
  - !ruby/object:Gem::Dependency
28
34
  name: rake
29
35
  requirement: !ruby/object:Gem::Requirement
@@ -147,6 +153,7 @@ files:
147
153
  - README.md
148
154
  - lib/miasma-aws.rb
149
155
  - lib/miasma-aws/api.rb
156
+ - lib/miasma-aws/api/iam.rb
150
157
  - lib/miasma-aws/api/sts.rb
151
158
  - lib/miasma-aws/version.rb
152
159
  - lib/miasma/contrib/aws.rb