miasma-aws 0.2.4 → 0.3.0

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