aws_assume_role 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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: []