aws_assume_role 0.0.2

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 199475f5c68d701119ec803d1af1e677583341c1
4
+ data.tar.gz: 925d022c466217e38ae89c9345607963156de362
5
+ SHA512:
6
+ metadata.gz: 6d898f41a279403a627b7b16b80747c76402e643af7fcd22311898e116afa96747b22345938430d4621d1d1663f3eda67e237027b354f60806d497258f0b1ebc
7
+ data.tar.gz: ca9f4c76a16552e30bbd94f66997675b99c60e1cea48fb0faca796bbf2e57973b1e533f5f07a5dd3865269a8ffa77200f25819555bd64fe3c7b5f42b19bdb1e4
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ .bundle/
2
+ *.swp
3
+ vendor/
4
+ pkg/
5
+ Gemfile.lock
6
+ spec/reports/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,37 @@
1
+ ---
2
+ AllCops:
3
+ DisplayCopNames: true
4
+
5
+ Metrics/MethodLength:
6
+ Enabled: false
7
+
8
+ Style/IndentationWidth:
9
+ Width: 4
10
+
11
+ Style/TrailingCommaInArguments:
12
+ EnforcedStyleForMultiline: comma
13
+
14
+ Style/TrailingCommaInLiteral:
15
+ EnforcedStyleForMultiline: comma
16
+
17
+ Style/EmptyLinesAroundModuleBody:
18
+ EnforcedStyle: empty_lines
19
+
20
+ Style/EmptyLinesAroundClassBody:
21
+ EnforcedStyle: empty_lines
22
+
23
+ Style/EmptyLinesAroundMethodBody:
24
+ Enabled: false
25
+
26
+ Style/EmptyLinesAroundBlockBody:
27
+ Enabled: false
28
+
29
+ Metrics/ClassLength:
30
+ Enabled: false
31
+
32
+ Metrics/AbcSize:
33
+ Enabled: false
34
+
35
+ Metrics/PerceivedComplexity:
36
+ Enabled: false
37
+
data/Gemfile ADDED
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+ source 'https://rubygems.org'
3
+
4
+ gemspec
5
+
6
+ case Gem::Platform.local.os
7
+ when 'linux'
8
+ gem 'gir_ffi-gnome_keyring', '~> 0.0.3'
9
+ when 'darwin'
10
+ gem 'ruby-keychain', '~> 0.3.2'
11
+ end
12
+
13
+ # Development dependencies - didn't seem to get installed when
14
+ # referenced in the gemspec, find out why.
15
+ gem 'bundler'
16
+ gem 'rake'
17
+ gem 'rspec'
18
+ gem 'rubocop', require: false
data/LICENSE.md ADDED
@@ -0,0 +1,19 @@
1
+ The MIT License (MIT)
2
+ Copyright (c) 2016 The Scale Factory Limited
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
5
+ this software and associated documentation files (the "Software"), to deal in
6
+ the Software without restriction, including without limitation the rights to
7
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
8
+ the Software, and to permit persons to whom the Software is furnished to do so,
9
+ subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
16
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
17
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
18
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,143 @@
1
+ # aws-assume-role
2
+
3
+ This will get role credentials for you, managing 2FA devices, and set those
4
+ credentials in environments. It stores the fetched credentials in Gnome Keyring
5
+ or OSX Keychain so they are not readable from disk.
6
+
7
+ ## Install
8
+
9
+ `gem install aws_assume_role`
10
+
11
+ ### Platform notes
12
+
13
+ Gnome Keyring uses the [GirFFI](https://github.com/mvz/gir_ffi) bindings, which
14
+ requires the introspection bindings to be installed (as well as gnome-keyring).
15
+ `apt-get install gnome-keyring libgirepository1.0-dev` for Debian/Ubuntu.
16
+
17
+ ## Config file
18
+
19
+ Create a config file, the default is `~/.aws/assume.yaml`
20
+
21
+ ```yaml
22
+ ---
23
+ default:
24
+ set_environment: false
25
+ # credentials come from .aws/credentials default profile or environment
26
+
27
+ scalefactory:
28
+ # if this profile is passed don't set the environment credentials
29
+ set_environment: false
30
+ # load credentials from sf_sso profile in .aws/credentials
31
+ profile: sf_sso
32
+
33
+
34
+ # These use the scalefactory profile above
35
+ xx_mgmt:
36
+ parent: scalefactory
37
+ set_environment: true
38
+ type: assume_role
39
+ region: eu-west-1
40
+ role_arn: arn:aws:iam::123456789012:role/RoleNameHere
41
+
42
+ xx_test:
43
+ parent: scalefactory
44
+ set_environment: true
45
+ type: assume_role
46
+ role_arn: arn:aws:iam::123456789012:role/RoleNameHere
47
+
48
+ xx:
49
+ type: list
50
+ set_environment: true
51
+ list:
52
+ - name: xx_test
53
+ env_prefix: TEST_
54
+ - name: xx_mgmt
55
+ env_prefix: MGMT_
56
+
57
+
58
+ # These use the default above
59
+ yy_mgmt:
60
+ set_environment: true
61
+ type: assume_role
62
+ role_arn: arn:aws:iam::123456789012:role/RoleNameHere
63
+
64
+ yy_test:
65
+ set_environment: true
66
+ type: assume_role
67
+ role_arn: arn:aws:iam::123456789012:role/RoleNameHere
68
+
69
+ xx:
70
+ type: list
71
+ set_environment: true
72
+ list:
73
+ - name: xx_test
74
+ env_prefix: TEST_
75
+ - name: xx_mgmt
76
+ env_prefix: MGMT_
77
+
78
+
79
+ ```
80
+
81
+
82
+
83
+
84
+ ## How to use?
85
+
86
+ ### In Environment variable
87
+
88
+ ```
89
+ export AWS_ACCESS_KEY_ID=1234567890010
90
+ export AWS_SECRET_ACCESS_KEY=abcdefghijklmnopqrstuvwzyx1
91
+ export AWS_DEFAULT_REGION=eu-west-1
92
+ ```
93
+
94
+ Then run the `aws-assume-role` command.
95
+
96
+ ### in credentials file
97
+
98
+ I have the following entry in `~/.aws/credentials`:
99
+
100
+ ```
101
+ [sf_sso]
102
+ aws_access_key_id = 1234567890010
103
+ aws_secret_access_key = abcdefghijklmnopqrstuvwzyx1
104
+ region = eu-west-1
105
+ ```
106
+
107
+ ### Environment Variables Set
108
+
109
+ If `region` is defined for an `assume_role` type the environment variable
110
+ `AWS_DEFAULT_REGION` will be set with that value, otherwise it will be left
111
+ blank.
112
+
113
+ `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and `AWS_SESSION_TOKEN` will all
114
+ be set.
115
+
116
+ If a type of `list` is called the environment variables will all be set with the
117
+ provided prefix, e.g.
118
+
119
+ ```
120
+ MGMT_AWS_ACCESS_KEY_ID=122343534535435435
121
+ MGMT_AWS_DEFAULT_REGION=eu-west-1
122
+ MGMT_AWS_SECRET_ACCESS_KEY=+4324234234235454353535353535
123
+ MGMT_AWS_SESSION_TOKEN=F353453535345345345345345353534
124
+ TEST_AWS_ACCESS_KEY_ID=53454353453453453534
125
+ TEST_AWS_SECRET_ACCESS_KEY=3534534534534534534534
126
+ TEST_AWS_SESSION_TOKEN=5435345353453
127
+ ```
128
+
129
+ If you use the `-v` or `--verbose` flag it will print out any AWS environment
130
+ variables set at the end of the action.
131
+
132
+ ### Calling another application
133
+
134
+ You can call another application by passing a bare double dash followed by the
135
+ target command.
136
+
137
+ ```
138
+ aws-assume-role --profile yy_mgmt -- aws ec2 describe-instances --query "Reservations[*].Instances[*].PrivateIpAddress" --output=text
139
+ 10.254.4.20
140
+ 10.254.4.15
141
+ 10.254.0.10
142
+ 10.254.4.5
143
+ ```
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler/gem_tasks'
2
+ task default: :spec
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'aws_assume_role'
7
+ spec.version = '0.0.2'
8
+ spec.authors = ['Jon Topper', 'Jack Thomas']
9
+ spec.email = ['jon@scalefactory.com', 'jack@scalefactory.com']
10
+
11
+ spec.description = 'Used to fetch multiple AWS Role Credential '\
12
+ 'Keys using different Session Keys '\
13
+ 'and store them securely using Gnome Keyring '\
14
+ 'or OSX keychain'
15
+ spec.summary = 'Manage AWS STS credentials with MFA'
16
+ spec.homepage = 'https://github.com/scalefactory/aws_assume_role'
17
+ spec.license = 'MIT'
18
+
19
+ spec.files = `git ls-files -z`.split("\x0").reject { |f|
20
+ f.match(%r{^(test|spec|features)/})
21
+ }
22
+ spec.bindir = 'bin'
23
+ spec.executables = spec.files.grep(%r{^bin/aws}) { |f| File.basename(f) }
24
+ spec.require_paths = ['lib']
25
+
26
+ spec.add_runtime_dependency 'aws-sdk'
27
+ spec.add_runtime_dependency 'inifile'
28
+ spec.add_runtime_dependency 'keyring', '~> 0.4.1'
29
+
30
+ # spec.add_development_dependency 'bundler', '~> 1.12'
31
+ # spec.add_development_dependency 'rake', '~> 10.0'
32
+ end
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift File.expand_path('../lib/', __FILE__)
4
+
5
+ require 'optparse'
6
+ require 'aws_assume_role'
7
+
8
+ options = {}
9
+ optparse = OptionParser.new do |opts|
10
+
11
+ options[:config] = "#{ENV['HOME']}/.aws/assume.yaml"
12
+ opts.on('-c', '--config file', 'Config file. defaults to' \
13
+ '~/.aws/assume.yaml.') do |c|
14
+ options[:config] = c
15
+ end
16
+
17
+ options[:profile] = false
18
+ opts.on('--profile name', 'Load the credentials for a profile into AWS ' \
19
+ 'environment variables. If this is not specified the ') do |c|
20
+ options[:profile] = c
21
+ end
22
+
23
+ options[:debug] = false
24
+ opts.on('-d', '--debug', 'Enable debugging') do
25
+ options[:debug] = true
26
+ end
27
+
28
+ options[:verbose] = false
29
+ opts.on('-v', '--verbose', 'Get more words') do
30
+ options[:verbose] = true
31
+ end
32
+
33
+ end
34
+
35
+ optdata = optparse.parse!
36
+
37
+ if options[:debug]
38
+ AWSAssumeRole::Profile.logger.level = Logger::DEBUG
39
+ else
40
+ AWSAssumeRole::Profile.logger.level = Logger::WARN
41
+ end
42
+
43
+
44
+ begin
45
+ AWSAssumeRole::Profile.load_profiles
46
+ AWSAssumeRole::Profile.load_config_file(options[:config])
47
+ rescue Errno::ENOENT
48
+ puts "No config file at options[:config]. Please create one!"
49
+ exit 1
50
+ end
51
+
52
+
53
+ if options[:profile] != false
54
+ profile = AWSAssumeRole::Profile.get_by_name(options[:profile] )
55
+ profile.use
56
+
57
+ if options[:verbose]
58
+ system('env | grep "AWS" | sort')
59
+ end
60
+ end
61
+
62
+ unless optdata.empty?
63
+ cmd = optdata.join(' ')
64
+ AWSAssumeRole::Profile.logger.debug "Executing Command '#{cmd}'"
65
+ system(cmd)
66
+ end
data/bin/test.rb ADDED
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift File.expand_path('../lib/', __FILE__)
4
+
5
+ require 'aws_assume_role'
6
+
7
+ test_profiles_yaml = <<EOF
8
+ ---
9
+ default:
10
+ set_environment: false
11
+ # credentials come from .aws/credentials or environment
12
+
13
+ mgmt:
14
+ set_environment: true
15
+ type: assume_role
16
+ role_arn: arn:aws:iam::339253004131:role/TerraformUser
17
+
18
+ test:
19
+ set_environment: true
20
+ type: assume_role
21
+ role_arn: arn:aws:iam::542043528869:role/TerraformUser
22
+
23
+ tf_test:
24
+ type: list
25
+ list:
26
+ - name: test
27
+ env_prefix: TEST_
28
+ - name: mgmt
29
+ env_prefix: MGMT_
30
+
31
+ EOF
32
+
33
+ AWSAssumeRole::Profile.logger.level = Logger::DEBUG
34
+ AWSAssumeRole::Profile.parse_config(test_profiles_yaml)
35
+
36
+ p = AWSAssumeRole::Profile.get_by_name('tf_test')
37
+ p.use
38
+
39
+ system('env | grep "AWS" | sort')
@@ -0,0 +1,3 @@
1
+ require 'aws_assume_role/logging'
2
+ require 'aws_assume_role/profile'
3
+ require 'aws_assume_role/credentials'
@@ -0,0 +1,88 @@
1
+ # AWSAssumeRole
2
+ module AWSAssumeRole
3
+
4
+ require 'keyring'
5
+ require 'json'
6
+ require 'time'
7
+
8
+ # Represents credentials, used for serialising into keychain
9
+ class Credentials
10
+
11
+ include Logging
12
+
13
+ def self.load_from_keyring(key)
14
+
15
+ logger.debug("Keyring: load '#{key}'")
16
+
17
+ keyring = Keyring.new
18
+ json_session = keyring.get_password('AWSAssumeRole', key)
19
+
20
+ unless json_session
21
+ logger.info('No JSON session data in keyring')
22
+ return nil
23
+ end
24
+
25
+ hash = JSON.parse(json_session, symbolize_names: true)
26
+
27
+ unless hash
28
+ logger.info('Couldn\'t parse keyring data as JSON')
29
+ return nil
30
+ end
31
+
32
+ hash[:expiration] = Time.parse(hash[:expiration])
33
+
34
+ logger.debug("Loaded #{hash}")
35
+ AWSAssumeRole::Credentials.new(hash)
36
+
37
+ end
38
+
39
+ def self.create_from_sdk(object)
40
+
41
+ raise TypeError unless object.is_a?(Aws::STS::Types::Credentials)
42
+ AWSAssumeRole::Credentials.new(object.to_h)
43
+
44
+ end
45
+
46
+ @credentials = nil
47
+
48
+ def initialize(hash)
49
+ @credentials = hash
50
+ end
51
+
52
+ def secret_access_key
53
+ @credentials[:secret_access_key]
54
+ end
55
+
56
+ def access_key_id
57
+ @credentials[:access_key_id]
58
+ end
59
+
60
+ def session_token
61
+ @credentials[:session_token]
62
+ end
63
+
64
+ def expiration
65
+ @credentials[:expiration]
66
+ end
67
+
68
+ def store_in_keyring(key)
69
+ keyring = Keyring.new
70
+ logger.debug("Keyring: store '#{key}' with #{@credentials.to_json}")
71
+ keyring.set_password('AWSAssumeRole', key, @credentials.to_json)
72
+ end
73
+
74
+ def delete_from_keyring(key)
75
+ keyring = Keyring.new
76
+ logger.debug("Keyring: delete '#{key}'")
77
+ keyring.delete_password('AWSAssumeRole', key)
78
+ end
79
+
80
+ def expired?
81
+ logger.debug("Checking expiry: #{@credentials[:expiration]} "\
82
+ '<= Time.now')
83
+ @credentials[:expiration] <= Time.now
84
+ end
85
+
86
+ end
87
+
88
+ end
@@ -0,0 +1,36 @@
1
+ # Mixin to provide global logging object
2
+ module AWSAssumeRole
3
+
4
+ module Logging
5
+
6
+ require 'logger'
7
+
8
+ class << self
9
+
10
+ def logger
11
+ @logger ||= Logger.new($stderr)
12
+ end
13
+
14
+ attr_writer :logger
15
+
16
+ end
17
+
18
+ def self.included(base)
19
+
20
+ class << base
21
+
22
+ def logger # rubocop:disable Lint/NestedMethodDefinition
23
+ Logging.logger
24
+ end
25
+
26
+ end
27
+
28
+ end
29
+
30
+ def logger
31
+ Logging.logger
32
+ end
33
+
34
+ end
35
+
36
+ end
@@ -0,0 +1,183 @@
1
+ require 'keyring'
2
+
3
+ module AWSAssumeRole
4
+
5
+ # Base Profile superclass
6
+ class Profile
7
+
8
+ include Logging
9
+
10
+ # Class methods for dispatch to individual Profile strategy
11
+
12
+ @implementations = {}
13
+ @named_profiles = {}
14
+ @config_file = '-'
15
+
16
+ class << self
17
+
18
+ attr_accessor :implementations
19
+ attr_accessor :named_profiles
20
+ attr_accessor :config_file
21
+
22
+ end
23
+
24
+ def self.register_implementation(type, impl)
25
+ logger.info('Registering implementation ' \
26
+ "for type '#{type}': #{impl}")
27
+ AWSAssumeRole::Profile.implementations[type] = impl
28
+ end
29
+
30
+ def self.load_profiles
31
+ Dir.glob(
32
+ File.expand_path('profile/*.rb', File.dirname(__FILE__)),
33
+ ).each do |profile_class|
34
+ require profile_class
35
+ end
36
+ end
37
+
38
+ def self.create(name, options)
39
+
40
+ options['type'] = 'basic' unless options.key?('type')
41
+
42
+ if implementations.key?(options['type'])
43
+ logger.info("Creating profile '#{name}' "\
44
+ "with type #{options['type']}")
45
+ i = implementations[options['type']].new(name, options)
46
+ named_profiles[name] = i
47
+ return i
48
+ end
49
+
50
+ STDERR.puts 'No implementation for profiles of type '\
51
+ "'#{options['type']}'"
52
+ exit -1 # rubocop:disable Lint/AmbiguousOperator
53
+
54
+ end
55
+
56
+ def self.get_by_name(name)
57
+ unless named_profiles.key?(name)
58
+ STDERR.puts "No profile '#{name}' found"
59
+ exit -1 # rubocop:disable Lint/AmbiguousOperator
60
+ end
61
+ named_profiles[name]
62
+ end
63
+
64
+ def self.load_config_file(config_path)
65
+ @config_file = config_path
66
+ logger.info("Loading configuration from #{config_path}")
67
+ parse_config(File.open(config_path))
68
+ end
69
+
70
+ def self.parse_config(yaml)
71
+
72
+ require 'yaml'
73
+
74
+ profiles = YAML.load(yaml)
75
+ profiles.each do |name, options|
76
+ options['config_file'] = config_file
77
+ options['name'] = name
78
+ AWSAssumeRole::Profile.create(name, options)
79
+ end
80
+
81
+ end
82
+
83
+ # Superclass for Profile strategies
84
+
85
+ def set_env(prefix = '') # rubocop:disable Style/AccessorMethodName
86
+
87
+ logger.info("Setting environment with prefix '#{prefix}'")
88
+
89
+ ENV["#{prefix}AWS_ACCESS_KEY_ID"] = access_key_id
90
+ ENV["#{prefix}AWS_SECRET_ACCESS_KEY"] = secret_access_key
91
+ ENV["#{prefix}AWS_DEFAULT_REGION"] = region
92
+
93
+ return unless respond_to?(:session_token)
94
+
95
+ logger.info(':session_token available, setting environment')
96
+ ENV["#{prefix}AWS_SESSION_TOKEN"] = session_token
97
+
98
+ end
99
+
100
+ def access_key_id
101
+ raise NotImplementedError
102
+ end
103
+
104
+ def secret_access_key
105
+ raise NotImplementedError
106
+ end
107
+
108
+ def region
109
+ raise NotImplementedError
110
+ end
111
+
112
+ def sts_client
113
+ raise NotImplementedError
114
+ end
115
+
116
+ attr_reader :name
117
+
118
+ def use
119
+ raise NotImplementedError
120
+ end
121
+
122
+ def token_code
123
+ puts "Enter your MFA's time-based one time password: "
124
+ token_code = STDIN.gets
125
+ token_code.chomp!
126
+ end
127
+
128
+ def keyring_key
129
+ "#{@options['config_file']}|#{@options['name']}"
130
+ end
131
+
132
+ def session(duration = 3600)
133
+
134
+ # See if we already have a non-expired session cached in this
135
+ # object.
136
+
137
+ unless @session.nil?
138
+
139
+ logger.info('Found session cached in object')
140
+ return @session unless @session.expired?
141
+ logger.info('Session expired, deleting keyring key '\
142
+ "'#{keyring_key}'")
143
+ @session.delete_from_keyring(keyring_key)
144
+
145
+ end
146
+
147
+ # See if there's a non-exipred session cached in the keyring
148
+
149
+ @session = AWSAssumeRole::Credentials.load_from_keyring(keyring_key)
150
+
151
+ unless @session.nil?
152
+
153
+ logger.info("Found session in keyring for '#{keyring_key}'")
154
+ return @session unless @session.expired?
155
+ logger.info('Session expired, deleting keyring key '\
156
+ "'#{keyring_key}'")
157
+ @session.delete_from_keyring(keyring_key)
158
+
159
+ end
160
+
161
+ identity = sts_client.get_caller_identity
162
+ user_name = identity.arn.split('/')[1]
163
+ mfa_arn = "arn:aws:iam::#{identity.account}:mfa/#{user_name}"
164
+
165
+ mfa_token_code = token_code
166
+
167
+ session = sts_client.get_session_token(
168
+ duration_seconds: duration,
169
+ serial_number: mfa_arn,
170
+ token_code: mfa_token_code,
171
+ )
172
+
173
+ @session = Credentials.create_from_sdk(session.credentials)
174
+ logger.info("Storing session in keyring '#{keyring_key}'")
175
+ @session.store_in_keyring(keyring_key)
176
+
177
+ @session
178
+
179
+ end
180
+
181
+ end
182
+
183
+ end
@@ -0,0 +1,117 @@
1
+ # AWSAssumeRole
2
+ module AWSAssumeRole
3
+
4
+ class Profile
5
+
6
+ # A Profile implementation for assuming roles using STS
7
+ class AssumeRole < Profile
8
+
9
+ include Logging
10
+
11
+ register_implementation('assume_role', self)
12
+
13
+ @sts_client = nil
14
+ @role = nil
15
+ @options = nil
16
+ @name = nil
17
+
18
+ def default_options
19
+ {
20
+ 'parent' => 'default',
21
+ 'duration' => 3600,
22
+ }
23
+ end
24
+
25
+ def initialize(name, options = {})
26
+
27
+ require 'aws-sdk'
28
+
29
+ @options = default_options.merge(options)
30
+ @name = name
31
+
32
+ end
33
+
34
+ def sts_client
35
+
36
+ return @sts_client unless @sts_client.nil?
37
+
38
+ parent = AWSAssumeRole::Profile.get_by_name(@options['parent'])
39
+
40
+ @sts_client = Aws::STS::Client.new(
41
+ access_key_id: parent.session.access_key_id,
42
+ secret_access_key: parent.session.secret_access_key,
43
+ session_token: parent.session.session_token,
44
+ )
45
+
46
+ @sts_client
47
+
48
+ rescue Aws::Errors::MissingRegionError
49
+
50
+ STDERR.puts 'No region was given. \
51
+ Set one in the credentials file or environment'
52
+ exit -1 # rubocop:disable Lint/AmbiguousOperator
53
+
54
+ end
55
+
56
+ def role_credentials
57
+
58
+ # Check for non-expired session cached here
59
+
60
+ unless @role_credentials.nil?
61
+
62
+ return @role_credentials unless @role_credentials.expired?
63
+ @role_credentials.delete_from_keyring(keyring_key)
64
+
65
+ end
66
+
67
+ # See if here's a non-exipred session in the keyring
68
+
69
+ @role_credentials = Credentials.load_from_keyring(keyring_key)
70
+
71
+ unless @role_credentials.nil?
72
+
73
+ return @role_credentials unless @role_credentials.expired?
74
+ @role_credentials.delete_from_keyring(keyring_key)
75
+
76
+ end
77
+
78
+ role = sts_client.assume_role(
79
+ role_arn: @options['role_arn'],
80
+ role_session_name: name, # use something else?
81
+ duration_seconds: @options['duration'],
82
+ )
83
+
84
+ @role_credentials =
85
+ Credentials.create_from_sdk(role.credentials)
86
+
87
+ @role_credentials.store_in_keyring(keyring_key)
88
+
89
+ @role_credentials
90
+
91
+ end
92
+
93
+ def access_key_id
94
+ role_credentials.access_key_id
95
+ end
96
+
97
+ def secret_access_key
98
+ role_credentials.secret_access_key
99
+ end
100
+
101
+ def session_token
102
+ role_credentials.session_token
103
+ end
104
+
105
+ def region
106
+ @options['region']
107
+ end
108
+
109
+ def use
110
+ set_env if @options['set_environment']
111
+ end
112
+
113
+ end
114
+
115
+ end
116
+
117
+ end
@@ -0,0 +1,92 @@
1
+ # AWSAssumeRole
2
+ module AWSAssumeRole
3
+
4
+ class Profile
5
+
6
+ # Profile implementation which takes credentials from either
7
+ # passed options, from the environment, or from .aws/credentials
8
+ # file (per the standard behaviour of Aws::STS::Client)
9
+ class Basic < Profile
10
+
11
+ include Logging
12
+
13
+ register_implementation('basic', self)
14
+
15
+ @sts_client = nil
16
+ @options = nil
17
+ @name = nil
18
+
19
+ def initialize(name, options = {})
20
+
21
+ require 'aws-sdk'
22
+
23
+ @options = options
24
+ @name = name
25
+
26
+ end
27
+
28
+ def sts_client
29
+
30
+ return @sts_client unless @sts_client.nil?
31
+
32
+ if @options.key?('access_key_id') &&
33
+ @options.key?('secret_access_key')
34
+
35
+ if @options.key?('region')
36
+
37
+ @sts_client = Aws::STS::Client.new(
38
+ access_key_id: @options['access_key_id'],
39
+ secret_access_key: @options['secret_access_key'],
40
+ region: @options['region'],
41
+ )
42
+
43
+ else
44
+
45
+ @sts_client = Aws::STS::Client.new(
46
+ access_key_id: @options['access_key_id'],
47
+ secret_access_key: @options['secret_access_key'],
48
+ )
49
+
50
+ end
51
+
52
+ elsif @options.key?('profile')
53
+
54
+ logger.info("Loading profile #{@options['profile']} from ~/.aws/credentials")
55
+ # Attempt to load with profile name suplied
56
+ @sts_client = Aws::STS::Client.new(
57
+ profile: @options['profile'],
58
+ )
59
+
60
+ else
61
+
62
+ @sts_client = Aws::STS::Client.new
63
+
64
+ end
65
+
66
+ @sts_client
67
+
68
+ rescue Aws::Errors::MissingRegionError
69
+
70
+ STDERR.puts 'No region was given. \
71
+ Set one in the credentials file or environment'
72
+ exit -1 # rubocop:disable Lint/AmbiguousOperator
73
+
74
+ end
75
+
76
+ def access_key_id
77
+ @options['access_key_id']
78
+ end
79
+
80
+ def secret_access_key
81
+ @options['secret_access_key']
82
+ end
83
+
84
+ def region
85
+ @options['region']
86
+ end
87
+
88
+ end
89
+
90
+ end
91
+
92
+ end
@@ -0,0 +1,57 @@
1
+ # AWSAssumeRole
2
+ module AWSAssumeRole
3
+
4
+ class Profile
5
+
6
+ # A Profile implementation which aggregates other profiles.
7
+ # Used to setenv for multiple credentials, but with different
8
+ # prefixed.
9
+ class List < Profile
10
+
11
+ include Logging
12
+
13
+ register_implementation('list', self)
14
+
15
+ @options = nil
16
+ @name = nil
17
+
18
+ def initialize(name, options)
19
+
20
+ # TODO: validate options
21
+
22
+ @options = options
23
+ @name = name
24
+
25
+ end
26
+
27
+ def use
28
+
29
+ @options['list'].each do |i|
30
+
31
+ profile = Profile.get_by_name(i['name'])
32
+
33
+ next unless @options['set_environment']
34
+
35
+ if i['env_prefix']
36
+ profile.set_env(i['env_prefix'])
37
+ end
38
+
39
+ if i['map_names']
40
+ i['map_names'].each do |name,env|
41
+ call = name.to_sym
42
+ if profile.respond_to?(call)
43
+ ENV[env] = profile.send(call)
44
+ end
45
+ end
46
+ end
47
+
48
+ end
49
+
50
+
51
+ end
52
+
53
+ end
54
+
55
+ end
56
+
57
+ end
metadata ADDED
@@ -0,0 +1,107 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: aws_assume_role
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Jon Topper
8
+ - Jack Thomas
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2016-12-09 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: aws-sdk
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '0'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: '0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: inifile
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: keyring
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: 0.4.1
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: 0.4.1
56
+ description: Used to fetch multiple AWS Role Credential Keys using different Session
57
+ Keys and store them securely using Gnome Keyring or OSX keychain
58
+ email:
59
+ - jon@scalefactory.com
60
+ - jack@scalefactory.com
61
+ executables:
62
+ - aws-assume-role
63
+ extensions: []
64
+ extra_rdoc_files: []
65
+ files:
66
+ - ".gitignore"
67
+ - ".rspec"
68
+ - ".rubocop.yml"
69
+ - Gemfile
70
+ - LICENSE.md
71
+ - README.md
72
+ - Rakefile
73
+ - aws_assume_role.gemspec
74
+ - bin/aws-assume-role
75
+ - bin/test.rb
76
+ - lib/aws_assume_role.rb
77
+ - lib/aws_assume_role/credentials.rb
78
+ - lib/aws_assume_role/logging.rb
79
+ - lib/aws_assume_role/profile.rb
80
+ - lib/aws_assume_role/profile/assume_role.rb
81
+ - lib/aws_assume_role/profile/basic.rb
82
+ - lib/aws_assume_role/profile/list.rb
83
+ homepage: https://github.com/scalefactory/aws_assume_role
84
+ licenses:
85
+ - MIT
86
+ metadata: {}
87
+ post_install_message:
88
+ rdoc_options: []
89
+ require_paths:
90
+ - lib
91
+ required_ruby_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ required_rubygems_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ requirements: []
102
+ rubyforge_project:
103
+ rubygems_version: 2.5.1
104
+ signing_key:
105
+ specification_version: 4
106
+ summary: Manage AWS STS credentials with MFA
107
+ test_files: []