awskeyring 0.0.5 → 0.0.6
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 +4 -4
- data/.rubocop.yml +2 -1
- data/.travis.yml +12 -2
- data/CHANGELOG.md +13 -0
- data/README.md +1 -0
- data/Rakefile +15 -1
- data/lib/awskeyring.rb +9 -1
- data/lib/awskeyring/validate.rb +32 -0
- data/lib/awskeyring/version.rb +1 -1
- data/lib/awskeyring_command.rb +84 -15
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2aadd847341fb6a4dd7016dfca3fdd1ba60fe4c9
|
|
4
|
+
data.tar.gz: a8e548fb70b847b3fc7d7e1a277a4d12a054029c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 60a505a585398dc8e6034bbc51c4deef834f650fb76a042bc248f338dfbdb3e6354dab42d2536f404f93bee4785f21dfe5bc9fd4fb2c2a3b7cad52a297fc1414
|
|
7
|
+
data.tar.gz: 8a282201ceb09a170e1beeb2dd3ecc57dc4058ec669d5b3cb9cc1d9ce33a99ad6b3c09ad42e5c1146f504ee6559eb4c258857961a5e864709a8a5741d5bd15e0
|
data/.rubocop.yml
CHANGED
data/.travis.yml
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
|
+
---
|
|
1
2
|
language: ruby
|
|
2
3
|
os: osx
|
|
3
4
|
rvm:
|
|
4
|
-
- 2.3.3
|
|
5
|
+
- 2.3.3
|
|
5
6
|
before_install: gem install bundler
|
|
6
7
|
notifications:
|
|
7
8
|
slack:
|
|
8
|
-
secure:
|
|
9
|
+
secure: "okKLlQ93ogj8ut18MZazQD63XTIzCpTfneYQMlKPaLU5HnooAkUFmEpSN8ig\
|
|
10
|
+
TJD0GuEQ7Jf8+BkFlhECWl1FwtzIW6z/yMadiiLwY6qA/O1ZVVYZmkS4kpcKtCMrvO0Hf3iPL\
|
|
11
|
+
XpJX0sQGfGsMsho6NZuNs0dzlrr4+HX/cEGmDXBocDDxdv2d25HzHtqt4l+4axeJ+PJdHDmYl\
|
|
12
|
+
DzhtMAXhkGoPfzws7MPvkVcqY0eZRW2WqccO52zlQrBNcphp7BI8mLTW/BwkEY9YndJf2xoBa\
|
|
13
|
+
oEOuaIJbGgHbmskGMRui3vZnd08/fiWkNsCI/EhB5BDJ41bwobsHEtuqu+Qx92kI+hzjtU4D1\
|
|
14
|
+
6k1v/6HDm61T6gh6BTp0QEXJeOkiecjVGyzrMlBf5BEyIB7JfLvGPa7RUbvvvUxFGcEtnSbUZ\
|
|
15
|
+
49cXAG7cKiiSgcNOocCrMNIRAe864Tm606Mud1RbOv8tEiS9BJ96ktOpOfvrYPyYMRFTte2gR\
|
|
16
|
+
LwNDwcpXIWTI39CXD2EBxSfRK/OqmggLYMG5AKhxyS/Vl7E2p9gii6syB0LnpFBIZ6t+OSve7\
|
|
17
|
+
fZtCU/wruC5IrI/vHtWfApvADYKsXG6FEYJoJ1LsCDjIRlrkwV1dEVsp/HSSO6zRh9KhJ3b7y\
|
|
18
|
+
zL97Dyi6dCi+nM7aKGrox/8kvcn9alrdRt8sz3c="
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## [v0.0.6](https://github.com/vibrato/awskeyring/tree/v0.0.6) (2018-03-01)
|
|
4
|
+
[Full Changelog](https://github.com/vibrato/awskeyring/compare/v0.0.5...v0.0.6)
|
|
5
|
+
|
|
6
|
+
**Implemented enhancements:**
|
|
7
|
+
|
|
8
|
+
- Credential Rotation Feature [\#4](https://github.com/vibrato/awskeyring/issues/4)
|
|
9
|
+
- Rotate credentials feature. [\#11](https://github.com/vibrato/awskeyring/pull/11) ([tristanmorgan](https://github.com/tristanmorgan))
|
|
10
|
+
|
|
11
|
+
**Merged pull requests:**
|
|
12
|
+
|
|
13
|
+
- Input validation [\#10](https://github.com/vibrato/awskeyring/pull/10) ([tristanmorgan](https://github.com/tristanmorgan))
|
|
14
|
+
- Adding a check for incorrect file modes. [\#9](https://github.com/vibrato/awskeyring/pull/9) ([tristanmorgan](https://github.com/tristanmorgan))
|
|
15
|
+
|
|
3
16
|
## [v0.0.5](https://github.com/vibrato/awskeyring/tree/v0.0.5) (2018-02-15)
|
|
4
17
|
[Full Changelog](https://github.com/vibrato/awskeyring/compare/v0.0.4...v0.0.5)
|
|
5
18
|
|
data/README.md
CHANGED
|
@@ -56,6 +56,7 @@ The CLI is using [Thor](http://whatisthor.com) with help provided interactively.
|
|
|
56
56
|
awskeyring remove ACCOUNT # Removes an ACCOUNT from the keyring
|
|
57
57
|
awskeyring remove-role ROLE # Removes a ROLE from the keyring
|
|
58
58
|
awskeyring remove-token ACCOUNT # Removes a token for ACCOUNT from the keyring
|
|
59
|
+
awskeyring rotate ACCOUNT # Rotate access keys for an ACCOUNT
|
|
59
60
|
awskeyring token ACCOUNT [ROLE] [MFA] # Create an STS Token from a ROLE or an MFA code
|
|
60
61
|
|
|
61
62
|
and autocomplete that can be installed with:
|
data/Rakefile
CHANGED
|
@@ -13,4 +13,18 @@ end
|
|
|
13
13
|
|
|
14
14
|
RSpec::Core::RakeTask.new(:spec)
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
desc 'Check filemode bits'
|
|
17
|
+
task :filemode do
|
|
18
|
+
files = Dir.glob('**/*')
|
|
19
|
+
failure = false
|
|
20
|
+
files.each do |file|
|
|
21
|
+
mode = File.stat(file).mode
|
|
22
|
+
if (mode & 0x7) != (mode >> 3 & 0x7)
|
|
23
|
+
puts file
|
|
24
|
+
failure = true
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
abort 'Error: Incorrect file mode found' if failure
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
task default: %i[filemode rubocop spec]
|
data/lib/awskeyring.rb
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
require 'keychain'
|
|
2
2
|
require 'aws-sdk-iam'
|
|
3
|
+
require 'awskeyring/validate'
|
|
3
4
|
|
|
4
5
|
# Aws Key-ring logical object,
|
|
5
6
|
# gives you an interface to access keychains and items.
|
|
6
|
-
module Awskeyring
|
|
7
|
+
module Awskeyring # rubocop:disable Metrics/ModuleLength
|
|
7
8
|
PREFS_FILE = (File.expand_path '~/.awskeyring').freeze
|
|
8
9
|
ROLE_PREFIX = 'role '.freeze
|
|
9
10
|
ACCOUNT_PREFIX = 'account '.freeze
|
|
@@ -65,6 +66,13 @@ module Awskeyring
|
|
|
65
66
|
)
|
|
66
67
|
end
|
|
67
68
|
|
|
69
|
+
def self.update_item(account:, key:, secret:)
|
|
70
|
+
item = get_item(account)
|
|
71
|
+
item.attributes[:account] = key
|
|
72
|
+
item.password = secret
|
|
73
|
+
item.save!
|
|
74
|
+
end
|
|
75
|
+
|
|
68
76
|
def self.add_role(role:, arn:, account:)
|
|
69
77
|
all_items.create(
|
|
70
78
|
label: "#{ROLE_PREFIX}#{role}",
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Validation methods
|
|
2
|
+
module Awskeyring
|
|
3
|
+
def self.account_name(account_name)
|
|
4
|
+
raise 'Invalid Account Name' unless account_name =~ /\S+/
|
|
5
|
+
account_name
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def self.access_key(aws_access_key)
|
|
9
|
+
raise 'Invalid Access Key' unless aws_access_key =~ /\AAKIA[A-Z0-9]{12,16}\z/
|
|
10
|
+
aws_access_key
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.secret_access_key(aws_secret_access_key)
|
|
14
|
+
raise 'Secret Access Key is not 40 chars' if aws_secret_access_key.length != 40
|
|
15
|
+
aws_secret_access_key
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.mfa_arn(mfa_arn)
|
|
19
|
+
raise 'Invalid MFA ARN' unless mfa_arn =~ %r(\Aarn:aws:iam::[0-9]{12}:mfa\/\S*\z)
|
|
20
|
+
mfa_arn
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.role_name(account_name)
|
|
24
|
+
raise 'Invalid Role Name' unless account_name =~ /\S+/
|
|
25
|
+
account_name
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.role_arn(role_arn)
|
|
29
|
+
raise 'Invalid Role ARN' unless role_arn =~ %r(\Aarn:aws:iam::[0-9]{12}:role\/\S*\z)
|
|
30
|
+
role_arn
|
|
31
|
+
end
|
|
32
|
+
end
|
data/lib/awskeyring/version.rb
CHANGED
data/lib/awskeyring_command.rb
CHANGED
|
@@ -37,11 +37,13 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
|
37
37
|
puts 'Creating a new Keychain, you will be prompted for a password for it.'
|
|
38
38
|
Awskeyring.init_keychain(awskeyring: keychain)
|
|
39
39
|
|
|
40
|
+
exec_name = File.basename($PROGRAM_NAME)
|
|
41
|
+
|
|
40
42
|
puts 'Your keychain has been initialised. It will auto-lock after 5 minutes'
|
|
41
43
|
puts 'and when sleeping. Use Keychain Access to adjust.'
|
|
42
44
|
puts
|
|
43
45
|
puts "Add accounts to your #{keychain} keychain with:"
|
|
44
|
-
puts " #{
|
|
46
|
+
puts " #{exec_name} add"
|
|
45
47
|
end
|
|
46
48
|
|
|
47
49
|
desc 'list', 'Prints a list of accounts in the keyring'
|
|
@@ -57,7 +59,7 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
|
57
59
|
|
|
58
60
|
desc 'env ACCOUNT', 'Outputs bourne shell environment exports for an ACCOUNT'
|
|
59
61
|
def env(account = nil)
|
|
60
|
-
account =
|
|
62
|
+
account = ask_check(existing: account, message: 'account name', validator: Awskeyring.method(:account_name))
|
|
61
63
|
cred, temp_cred = get_valid_item_pair(account: account)
|
|
62
64
|
token = temp_cred.password unless temp_cred.nil?
|
|
63
65
|
put_env_string(
|
|
@@ -86,11 +88,16 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
|
86
88
|
method_option :key, type: :string, aliases: '-k', desc: 'AWS account key id.'
|
|
87
89
|
method_option :secret, type: :string, aliases: '-s', desc: 'AWS account secret.'
|
|
88
90
|
method_option :mfa, type: :string, aliases: '-m', desc: 'AWS virtual mfa arn.'
|
|
89
|
-
def add(account = nil)
|
|
90
|
-
account =
|
|
91
|
-
key =
|
|
92
|
-
secret =
|
|
93
|
-
|
|
91
|
+
def add(account = nil) # rubocop:disable Metrics/AbcSize
|
|
92
|
+
account = ask_check(existing: account, message: 'account name', validator: Awskeyring.method(:account_name))
|
|
93
|
+
key = ask_check(existing: options[:key], message: 'access key id', validator: Awskeyring.method(:access_key))
|
|
94
|
+
secret = ask_check(
|
|
95
|
+
existing: options[:secret], message: 'secret access key',
|
|
96
|
+
secure: true, validator: Awskeyring.method(:secret_access_key)
|
|
97
|
+
)
|
|
98
|
+
mfa = ask_check(
|
|
99
|
+
existing: options[:mfa], message: 'mfa arn', optional: true, validator: Awskeyring.method(:mfa_arn)
|
|
100
|
+
)
|
|
94
101
|
|
|
95
102
|
Awskeyring.add_item(
|
|
96
103
|
account: account,
|
|
@@ -98,33 +105,37 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
|
98
105
|
secret: secret,
|
|
99
106
|
comment: mfa
|
|
100
107
|
)
|
|
108
|
+
puts "# Added account #{account}"
|
|
101
109
|
end
|
|
102
110
|
|
|
103
111
|
map 'add-role' => :add_role
|
|
104
112
|
desc 'add-role ROLE', 'Adds a ROLE to the keyring'
|
|
105
113
|
method_option :arn, type: :string, aliases: '-a', desc: 'AWS role arn.'
|
|
106
114
|
def add_role(role = nil)
|
|
107
|
-
role =
|
|
108
|
-
arn =
|
|
109
|
-
account =
|
|
115
|
+
role = ask_check(existing: role, message: 'role name', validator: Awskeyring.method(:role_name))
|
|
116
|
+
arn = ask_check(existing: options[:arn], message: 'role arn', validator: Awskeyring.method(:role_arn))
|
|
117
|
+
account = ask_check(
|
|
118
|
+
existing: account, message: 'account', optional: true, validator: Awskeyring.method(:account_name)
|
|
119
|
+
)
|
|
110
120
|
|
|
111
121
|
Awskeyring.add_role(
|
|
112
122
|
role: role,
|
|
113
123
|
arn: arn,
|
|
114
124
|
account: account
|
|
115
125
|
)
|
|
126
|
+
puts "# Added role #{role}"
|
|
116
127
|
end
|
|
117
128
|
|
|
118
129
|
desc 'remove ACCOUNT', 'Removes an ACCOUNT from the keyring'
|
|
119
130
|
def remove(account = nil)
|
|
120
|
-
account =
|
|
131
|
+
account = ask_check(existing: account, message: 'account name', validator: Awskeyring.method(:account_name))
|
|
121
132
|
cred, temp_cred = get_valid_item_pair(account: account)
|
|
122
133
|
Awskeyring.delete_pair(cred, temp_cred, "# Removing account #{account}")
|
|
123
134
|
end
|
|
124
135
|
|
|
125
136
|
desc 'remove-token ACCOUNT', 'Removes a token for ACCOUNT from the keyring'
|
|
126
137
|
def remove_token(account = nil)
|
|
127
|
-
account =
|
|
138
|
+
account = ask_check(existing: account, message: 'account name', validator: Awskeyring.method(:account_name))
|
|
128
139
|
session_key, session_token = Awskeyring.get_pair(account)
|
|
129
140
|
session_key, session_token = Awskeyring.delete_expired(session_key, session_token) if session_key
|
|
130
141
|
Awskeyring.delete_pair(session_key, session_token, "# Removing token for account #{account}") if session_key
|
|
@@ -133,17 +144,47 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
|
133
144
|
map 'remove-role' => :remove_role
|
|
134
145
|
desc 'remove-role ROLE', 'Removes a ROLE from the keyring'
|
|
135
146
|
def remove_role(role = nil)
|
|
136
|
-
role =
|
|
147
|
+
role = ask_check(existing: role, message: 'role name', validator: Awskeyring.method(:role_name))
|
|
137
148
|
item_role = Awskeyring.get_role(role)
|
|
138
149
|
Awskeyring.delete_pair(item_role, nil, "# Removing role #{role}")
|
|
139
150
|
end
|
|
140
151
|
|
|
152
|
+
desc 'rotate ACCOUNT', 'Rotate access keys for an ACCOUNT'
|
|
153
|
+
def rotate(account = nil) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
154
|
+
account = ask_check(existing: account, message: 'account name', validator: Awskeyring.method(:account_name))
|
|
155
|
+
item = Awskeyring.get_item(account)
|
|
156
|
+
iam = Aws::IAM::Client.new(access_key_id: item.attributes[:account], secret_access_key: item.password)
|
|
157
|
+
|
|
158
|
+
if iam.list_access_keys[:access_key_metadata].length > 1
|
|
159
|
+
warn "You have two access keys for account #{account}"
|
|
160
|
+
exit 1
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
new_key = iam.create_access_key
|
|
164
|
+
iam = Aws::IAM::Client.new(
|
|
165
|
+
access_key_id: new_key[:access_key][:access_key_id],
|
|
166
|
+
secret_access_key: new_key[:access_key][:secret_access_key]
|
|
167
|
+
)
|
|
168
|
+
retry_backoff do
|
|
169
|
+
iam.delete_access_key(
|
|
170
|
+
access_key_id: item.attributes[:account]
|
|
171
|
+
)
|
|
172
|
+
end
|
|
173
|
+
Awskeyring.update_item(
|
|
174
|
+
account: account,
|
|
175
|
+
key: new_key[:access_key][:access_key_id],
|
|
176
|
+
secret: new_key[:access_key][:secret_access_key]
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
puts "# Updated account #{account}"
|
|
180
|
+
end
|
|
181
|
+
|
|
141
182
|
desc 'token ACCOUNT [ROLE] [MFA]', 'Create an STS Token from a ROLE or an MFA code'
|
|
142
183
|
method_option :role, type: :string, aliases: '-r', desc: 'The ROLE to assume.'
|
|
143
184
|
method_option :code, type: :string, aliases: '-c', desc: 'Virtual mfa CODE.'
|
|
144
185
|
method_option :duration, type: :string, aliases: '-d', desc: 'Session DURATION in seconds.'
|
|
145
186
|
def token(account = nil, role = nil, code = nil) # rubocop:disable all
|
|
146
|
-
account =
|
|
187
|
+
account = ask_check(existing: account, message: 'account name', validator: Awskeyring.method(:account_name))
|
|
147
188
|
role ||= options[:role]
|
|
148
189
|
code ||= options[:code]
|
|
149
190
|
duration = options[:duration]
|
|
@@ -206,7 +247,7 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
|
206
247
|
desc 'console ACCOUNT', 'Open the AWS Console for the ACCOUNT'
|
|
207
248
|
method_option :path, type: :string, aliases: '-p', desc: 'The service PATH to open.'
|
|
208
249
|
def console(account = nil) # rubocop:disable all
|
|
209
|
-
account =
|
|
250
|
+
account = ask_check(existing: account, message: 'account name', validator: Awskeyring.method(:account_name))
|
|
210
251
|
cred, temp_cred = get_valid_item_pair(account: account)
|
|
211
252
|
token = temp_cred.password unless temp_cred.nil?
|
|
212
253
|
|
|
@@ -334,6 +375,34 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
|
334
375
|
puts 'unset AWS_SESSION_TOKEN' unless token
|
|
335
376
|
end
|
|
336
377
|
|
|
378
|
+
def ask_check(existing:, message:, secure: false, optional: false, validator: nil)
|
|
379
|
+
retries ||= 3
|
|
380
|
+
begin
|
|
381
|
+
value = ask_missing(existing: existing, message: message, secure: secure, optional: optional)
|
|
382
|
+
value = validator.call(value) unless value.empty? && optional
|
|
383
|
+
rescue RuntimeError => e
|
|
384
|
+
warn e.message
|
|
385
|
+
retry unless (retries -= 1).zero?
|
|
386
|
+
exit 1
|
|
387
|
+
end
|
|
388
|
+
value
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
def retry_backoff(&block)
|
|
392
|
+
retries ||= 1
|
|
393
|
+
begin
|
|
394
|
+
yield block
|
|
395
|
+
rescue Aws::IAM::Errors::InvalidClientTokenId => e
|
|
396
|
+
if retries < 4
|
|
397
|
+
sleep 2**retries
|
|
398
|
+
retries += 1
|
|
399
|
+
retry
|
|
400
|
+
end
|
|
401
|
+
warn e.message
|
|
402
|
+
exit 1
|
|
403
|
+
end
|
|
404
|
+
end
|
|
405
|
+
|
|
337
406
|
def ask_missing(existing:, message:, secure: false, optional: false)
|
|
338
407
|
existing || ask(message: message, secure: secure, optional: optional)
|
|
339
408
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: awskeyring
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tristan Morgan
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2018-
|
|
11
|
+
date: 2018-03-01 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: aws-sdk-iam
|
|
@@ -157,6 +157,7 @@ files:
|
|
|
157
157
|
- awskeyring.gemspec
|
|
158
158
|
- exe/awskeyring
|
|
159
159
|
- lib/awskeyring.rb
|
|
160
|
+
- lib/awskeyring/validate.rb
|
|
160
161
|
- lib/awskeyring/version.rb
|
|
161
162
|
- lib/awskeyring_command.rb
|
|
162
163
|
homepage: https://github.com/vibrato/awskeyring
|