aws_assume_role 0.0.2 → 0.0.3

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: 199475f5c68d701119ec803d1af1e677583341c1
4
- data.tar.gz: 925d022c466217e38ae89c9345607963156de362
3
+ metadata.gz: 55cb1bdbca27ae7dfaf53948e1ad746a66afcc25
4
+ data.tar.gz: 43b7ce33d786b7ebfabbedb921b2c9b3a5f28cf1
5
5
  SHA512:
6
- metadata.gz: 6d898f41a279403a627b7b16b80747c76402e643af7fcd22311898e116afa96747b22345938430d4621d1d1663f3eda67e237027b354f60806d497258f0b1ebc
7
- data.tar.gz: ca9f4c76a16552e30bbd94f66997675b99c60e1cea48fb0faca796bbf2e57973b1e533f5f07a5dd3865269a8ffa77200f25819555bd64fe3c7b5f42b19bdb1e4
6
+ metadata.gz: ee94a33205511ab48a1c1f0e458ef98a20267b496cc556fd5cc35d87dbb4311d0d305374839348966e5b981ca29dee5ab0848ffa344215f5e20abc4ae864c815
7
+ data.tar.gz: 636303eb8f232061473a6d3de6a5809cc8eea20f3579c2f0eef8cd2e4964e08c569cce83c7b2fe05014c0980e76926038c67a998d7a10fc0201c5194b8ccd194
data/README.md CHANGED
@@ -1,8 +1,35 @@
1
1
  # aws-assume-role
2
2
 
3
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.
4
+ credentials in environment variables then execute a provided command. It stores
5
+ the fetched credentials in Gnome Keyring or OSX Keychain so they are not
6
+ readable from disk.
7
+
8
+ ### Why?
9
+
10
+ This keeps your credentials safe in the keystore, and they are set as
11
+ environment variables for the duration and context of the executing command.
12
+ This helps prevent credential leaking and theft, and means they aren't stored on
13
+ disk as unencrypted files.
14
+
15
+ It allows easy credential management and roll assumption with a 2FA/MFA device.
16
+
17
+ For security and account management purposes we don't want to be managing users
18
+ in multiple accounts, just centrally then allowing them to assume roles in
19
+ other accounts.
20
+
21
+ ###
22
+
23
+ Assumptions:
24
+
25
+ - You have a parent/master account which you authenticate against with a 2FA
26
+ device.
27
+ - You then assume a role in another account.
28
+
29
+ This is easy to achieve in a web console, but you probably want to use tools
30
+ like Terraform of AWS Cli. This makes using those tools easy, without having to
31
+ constantly fetch and manage credentials for assumed roles, or provide
32
+ users/access keys for each account.
6
33
 
7
34
  ## Install
8
35
 
@@ -83,6 +110,31 @@ xx:
83
110
 
84
111
  ## How to use?
85
112
 
113
+ You need a key and secret for each `basic` role (a `parent`). You can set this
114
+ in the environment variable or in the `~/.aws/credentials` file.
115
+
116
+ It is recommended that you set this in the environment variable, the first time
117
+ aws-assume-role runs it will place these values in the keystore so they are
118
+ safe.
119
+
120
+ ### Add the basic/profile credentials to keystore
121
+
122
+ You can add the credentials that the system will use to assume roles to the
123
+ keystore. This is the recommended way of using `aws-assume-role`.
124
+
125
+ To add(or update) credentials use:
126
+
127
+ ```shell
128
+ $ aws-assume-role --profile scalefactory --add
129
+ Enter your AWS_ACCESS_KEY_ID:
130
+ 1234567890010
131
+ Enter your AWS_SECRET_ACCESS_KEY:
132
+ abcdefghijklmnopqrstuvwzyx1
133
+ Enter a AWS Region:
134
+ eu-west-1
135
+
136
+ ```
137
+
86
138
  ### In Environment variable
87
139
 
88
140
  ```
@@ -141,3 +193,13 @@ aws-assume-role --profile yy_mgmt -- aws ec2 describe-instances --query "Reserva
141
193
  10.254.0.10
142
194
  10.254.4.5
143
195
  ```
196
+
197
+
198
+ ## Deleting keystore values
199
+
200
+ Maybe you have a new keypair?
201
+
202
+ ```
203
+ aws-assume-role --profile yy_mgmt --delete
204
+ aws-assume-role --profile scalefactory --delete
205
+ ```
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
 
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = 'aws_assume_role'
7
- spec.version = '0.0.2'
7
+ spec.version = '0.0.3'
8
8
  spec.authors = ['Jon Topper', 'Jack Thomas']
9
9
  spec.email = ['jon@scalefactory.com', 'jack@scalefactory.com']
10
10
 
data/bin/aws-assume-role CHANGED
@@ -15,13 +15,25 @@ optparse = OptionParser.new do |opts|
15
15
  end
16
16
 
17
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|
18
+ opts.on('-p', '--profile name', 'Load the credentials for a profile into AWS ' \
19
+ 'environment variables.') do |c|
20
20
  options[:profile] = c
21
21
  end
22
22
 
23
+ options[:add] = false
24
+ opts.on('-a', '--add', 'Add basic/parent account credentials to keystore' \
25
+ 'environment variables. Must provide a profile name') do |c|
26
+ options[:add] = c
27
+ end
28
+
29
+ options[:delete] = false
30
+ opts.on('-d', '--delete', 'Delete credentials from keystore. Must ' \
31
+ 'provide a profile name.') do
32
+ options[:delete] = true
33
+ end
34
+
23
35
  options[:debug] = false
24
- opts.on('-d', '--debug', 'Enable debugging') do
36
+ opts.on('--debug', 'Enable debugging') do
25
37
  options[:debug] = true
26
38
  end
27
39
 
@@ -45,14 +57,20 @@ begin
45
57
  AWSAssumeRole::Profile.load_profiles
46
58
  AWSAssumeRole::Profile.load_config_file(options[:config])
47
59
  rescue Errno::ENOENT
48
- puts "No config file at options[:config]. Please create one!"
49
- exit 1
60
+ STDERR.puts "No config file at options[:config]. Please create one!"
61
+ exit -1 # rubocop:disable Lint/AmbiguousOperator
50
62
  end
51
63
 
52
-
53
64
  if options[:profile] != false
54
65
  profile = AWSAssumeRole::Profile.get_by_name(options[:profile] )
55
- profile.use
66
+
67
+ if options[:delete]
68
+ profile.remove
69
+ elsif options[:add]
70
+ profile.add
71
+ else
72
+ profile.use
73
+ end
56
74
 
57
75
  if options[:verbose]
58
76
  system('env | grep "AWS" | sort')
@@ -29,7 +29,7 @@ module AWSAssumeRole
29
29
  return nil
30
30
  end
31
31
 
32
- hash[:expiration] = Time.parse(hash[:expiration])
32
+ hash[:expiration] = Time.parse(hash[:expiration]) unless hash[:expiration].nil?
33
33
 
34
34
  logger.debug("Loaded #{hash}")
35
35
  AWSAssumeRole::Credentials.new(hash)
@@ -65,6 +65,10 @@ module AWSAssumeRole
65
65
  @credentials[:expiration]
66
66
  end
67
67
 
68
+ def region
69
+ @credentials[:region]
70
+ end
71
+
68
72
  def store_in_keyring(key)
69
73
  keyring = Keyring.new
70
74
  logger.debug("Keyring: store '#{key}' with #{@credentials.to_json}")
@@ -110,6 +110,16 @@ module AWSAssumeRole
110
110
  set_env if @options['set_environment']
111
111
  end
112
112
 
113
+ def remove
114
+ Credentials.new({}).delete_from_keyring(keyring_key)
115
+ end
116
+
117
+ def add
118
+ STDERR.puts "You can't add credentials to an assume_role "\
119
+ 'just basic/parent accounts.'
120
+ exit -1 # rubocop:disable Lint/AmbiguousOperator
121
+ end
122
+
113
123
  end
114
124
 
115
125
  end
@@ -26,30 +26,11 @@ module AWSAssumeRole
26
26
  end
27
27
 
28
28
  def sts_client
29
+ logger.debug("Calling STS client")
29
30
 
30
31
  return @sts_client unless @sts_client.nil?
31
32
 
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')
33
+ if @options.key?('profile')
53
34
 
54
35
  logger.info("Loading profile #{@options['profile']} from ~/.aws/credentials")
55
36
  # Attempt to load with profile name suplied
@@ -57,6 +38,18 @@ module AWSAssumeRole
57
38
  profile: @options['profile'],
58
39
  )
59
40
 
41
+ elsif access_key_id && secret_access_key
42
+
43
+ logger.debug("Access Key: #{access_key_id}")
44
+ logger.debug("Secret Key: #{secret_access_key}")
45
+ logger.debug("Region: #{region}")
46
+
47
+ @sts_client = Aws::STS::Client.new(
48
+ access_key_id: access_key_id,
49
+ secret_access_key: secret_access_key,
50
+ region: region,
51
+ )
52
+
60
53
  else
61
54
 
62
55
  @sts_client = Aws::STS::Client.new
@@ -73,18 +66,85 @@ module AWSAssumeRole
73
66
 
74
67
  end
75
68
 
69
+ def basic_credentials
70
+ logger.debug("Loading basic credentials")
71
+ # Check for profile creds in keyring first
72
+ @basic_credentials = Credentials.load_from_keyring("#{keyring_key}|basic")
73
+
74
+ return @basic_credentials unless @basic_credentials.nil?
75
+
76
+ creds = {
77
+ :access_key_id => ENV['AWS_ACCESS_KEY_ID'],
78
+ :secret_access_key => ENV['AWS_SECRET_ACCESS_KEY'],
79
+ }
80
+
81
+ if creds[:access_key_id].nil? or creds[:secret_access_key].nil?
82
+ STDERR.puts 'No AWS_ACCESS_KEY_ID or AWS_SECRET_ACCESS_KEY ' \
83
+ 'found in the environment.'
84
+ exit -1 # rubocop:disable Lint/AmbiguousOperator
85
+ end
86
+
87
+ unless @options['region'].nil?
88
+ creds[:region] = @options['region']
89
+ else
90
+ creds[:region] = ENV['AWS_DEFAULT_REGION']
91
+ end
92
+
93
+ @basic_credentials = AWSAssumeRole::Credentials.new(creds)
94
+
95
+ @basic_credentials
96
+ end
97
+
76
98
  def access_key_id
77
- @options['access_key_id']
99
+ @options['access_key_id'] || basic_credentials.access_key_id
78
100
  end
79
101
 
80
102
  def secret_access_key
81
- @options['secret_access_key']
103
+ @options['secret_access_key'] || basic_credentials.secret_access_key
82
104
  end
83
105
 
84
106
  def region
85
- @options['region']
107
+ @options['region'] || basic_credentials.region
108
+ end
109
+
110
+ def remove
111
+ Credentials.new({}).delete_from_keyring(keyring_key)
112
+ Credentials.new({}).delete_from_keyring("#{keyring_key}|basic")
86
113
  end
87
114
 
115
+ def add
116
+ if @options['profile']
117
+ puts "WARNING: Storing credentials but a profile is specified and used for #{@name}"
118
+ end
119
+ if @options['access_key_id'] or @options['secret_access_key']
120
+ puts "WARNING: Storing credentials but they are specified in config for #{@name}"
121
+ end
122
+
123
+ @basic_credentials = Credentials.load_from_keyring("#{keyring_key}|basic")
124
+ new_creds = get_credentials
125
+ @basic_credentials.delete_from_keyring(keyring_key) unless @basic_credentials.nil?
126
+ @basic_credentials = AWSAssumeRole::Credentials.new(new_creds)
127
+ @basic_credentials.store_in_keyring("#{keyring_key}|basic")
128
+ end
129
+
130
+ def get_credentials
131
+ puts "Enter your AWS_ACCESS_KEY_ID: "
132
+ id = STDIN.gets
133
+ id.chomp!
134
+ puts "Enter your AWS_SECRET_ACCESS_KEY: "
135
+ secret = STDIN.gets
136
+ secret.chomp!
137
+ puts "Enter a AWS Region:"
138
+ region = STDIN.gets
139
+ region.chomp!
140
+
141
+ creds = {
142
+ :access_key_id => id,
143
+ :secret_access_key => secret,
144
+ :region => region,
145
+ }
146
+ creds
147
+ end
88
148
  end
89
149
 
90
150
  end
@@ -119,6 +119,14 @@ module AWSAssumeRole
119
119
  raise NotImplementedError
120
120
  end
121
121
 
122
+ def add
123
+ raise NotImplementedError
124
+ end
125
+
126
+ def remove
127
+ raise NotImplementedError
128
+ end
129
+
122
130
  def token_code
123
131
  puts "Enter your MFA's time-based one time password: "
124
132
  token_code = STDIN.gets
@@ -158,17 +166,29 @@ module AWSAssumeRole
158
166
 
159
167
  end
160
168
 
161
- identity = sts_client.get_caller_identity
169
+ begin
170
+ identity = sts_client.get_caller_identity
171
+ rescue Aws::Errors::MissingCredentialsError
172
+ STDERR.puts "No credentials found. Set one in the credentials file "\
173
+ "or environment."
174
+ exit -1 # rubocop:disable Lint/AmbiguousOperator
175
+ end
162
176
  user_name = identity.arn.split('/')[1]
163
177
  mfa_arn = "arn:aws:iam::#{identity.account}:mfa/#{user_name}"
164
178
 
165
179
  mfa_token_code = token_code
166
180
 
167
- session = sts_client.get_session_token(
168
- duration_seconds: duration,
169
- serial_number: mfa_arn,
170
- token_code: mfa_token_code,
171
- )
181
+ begin
182
+ session = sts_client.get_session_token(
183
+ duration_seconds: duration,
184
+ serial_number: mfa_arn,
185
+ token_code: mfa_token_code,
186
+ )
187
+ rescue Aws::STS::Errors::AccessDenied
188
+ STDERR.puts 'MultiFactorAuthentication failed with invalid MFA ' \
189
+ 'one time pass code.'
190
+ exit -1 # rubocop:disable Lint/AmbiguousOperator
191
+ end
172
192
 
173
193
  @session = Credentials.create_from_sdk(session.credentials)
174
194
  logger.info("Storing session in keyring '#{keyring_key}'")
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aws_assume_role
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jon Topper
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-12-09 00:00:00.000000000 Z
12
+ date: 2016-12-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: aws-sdk