aws-session-credentials 0.1.1 → 1.0.0.pre.1
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 +8 -8
- data/.travis.yml +5 -2
- data/README.md +49 -23
- data/aws-session-credentials.gemspec +3 -0
- data/lib/aws/session/credentials/cache.rb +31 -0
- data/lib/aws/session/credentials/cli.rb +117 -20
- data/lib/aws/session/credentials/config.rb +13 -77
- data/lib/aws/session/credentials/credential_file.rb +18 -28
- data/lib/aws/session/credentials/file_provider/ini_file_provider.rb +30 -0
- data/lib/aws/session/credentials/file_provider/yaml_file_provider.rb +34 -0
- data/lib/aws/session/credentials/mfa_device/generic_mfa_device.rb +19 -0
- data/lib/aws/session/credentials/mfa_device/yubikey_mfa_device.rb +61 -0
- data/lib/aws/session/credentials/profile.rb +13 -0
- data/lib/aws/session/credentials/profile_storage.rb +41 -0
- data/lib/aws/session/credentials/session_builder.rb +41 -30
- data/lib/aws/session/credentials/session_manager.rb +90 -0
- data/lib/aws/session/credentials/version.rb +1 -1
- data/lib/aws/session/credentials.rb +15 -0
- metadata +54 -4
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
ZThmODRmYTdkNDdmN2IzNThlMGZjNzVjOTg3MzI1MDhmMzM5NDUwZg==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
OWZmM2I4YjQxM2E1NTVjZjc4YjlkNmE1NGFkZTc3NDUwNGE3Y2ZiMg==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
ZTA0MWMzNTY1NjEyODg1NmE3MWQ1MjE4OGU5YjU3YjMwZGVjYzQyNDZhNmY4
|
10
|
+
MWJiMjI3NTMzZGVjMTNkYTI4NjcwZDA0ZTBiZmY3ZGRmODNiMWVhZmU2NzNl
|
11
|
+
NDJjZWI2NmFmMzgyYzkyZGU1YTM3NjQxMDU4YmUzYjQzNjNhOGE=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
OGFmN2YzZjZmNjhkNjcyMTFlMzkxZTcyOGZlOGY0YmQ3NDcyZjkxOWIzYmUx
|
14
|
+
NWY4YWMyNTFkNTZlM2M4N2ZiOTFiZGMyMzBkYjg1MDlmNWY5YjJmYjNiNzBm
|
15
|
+
OWY1MTMxMDNiOTQ1MWFlM2VkNjY3ODY4ODNhMzM3ZjJhNGQzNWY=
|
data/.travis.yml
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
language: ruby
|
2
2
|
rvm:
|
3
|
-
- 2.2.3
|
4
|
-
before_install:
|
3
|
+
- 2.2.3
|
4
|
+
before_install:
|
5
|
+
- gem install bundler -v 1.10.6
|
6
|
+
- sudo apt-get update -qq
|
7
|
+
- sudo apt-get install -y libccid libpcsclite-dev pcscd pcsc-tools
|
5
8
|
deploy:
|
6
9
|
provider: rubygems
|
7
10
|
api_key:
|
data/README.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# Aws::Session::Credentials
|
2
2
|
|
3
3
|
[](https://travis-ci.org/zl4bv/aws-session-credentials)
|
4
|
+
[](https://badge.fury.io/rb/aws-session-credentials)
|
4
5
|
|
5
6
|
Command-line tool to generate AWS session credentials.
|
6
7
|
|
@@ -26,6 +27,8 @@ Or install it yourself as:
|
|
26
27
|
|
27
28
|
## Usage
|
28
29
|
|
30
|
+
### Generating new session credentials
|
31
|
+
|
29
32
|
Example:
|
30
33
|
|
31
34
|
```
|
@@ -41,35 +44,58 @@ Usage:
|
|
41
44
|
aws-session
|
42
45
|
|
43
46
|
Options:
|
44
|
-
|
45
|
-
|
46
|
-
[--region=REGION] # AWS region to connect to
|
47
|
-
[--config-file=CONFIG-FILE] # YAML file to load config from
|
48
|
-
# Default: ~/.aws/credentials.yml
|
49
|
-
[--credential-file=CREDENTIAL-FILE] # INI file to save session credentials to
|
50
|
-
# Default: ~/.aws/credentials
|
51
|
-
[--profile=PROFILE] # Profile that session token will be loaded into
|
52
|
-
# Default: default
|
53
|
-
[--duration=N] # Duration, in seconds, that credentials should remain valid
|
54
|
-
# Default: 1
|
55
|
-
[--mfa-device=MFA-DEVICE] # ARN of MFA device
|
56
|
-
[--mfa-code=MFA-CODE] # Six digit code from MFA device
|
57
|
-
```
|
47
|
+
Usage:
|
48
|
+
aws-session new
|
58
49
|
|
59
|
-
|
50
|
+
Options:
|
51
|
+
[--aws-access-key-id=AWS-ACCESS-KEY-ID] # Access key used to generate session token
|
52
|
+
[--aws-secret-access-key=AWS-SECRET-ACCESS-KEY] # Secret key used to generate session token
|
53
|
+
[--aws-region=AWS-REGION] # AWS region to connect to
|
54
|
+
[--config-file=CONFIG-FILE] # YAML file to load config from
|
55
|
+
# Default: ~/.aws/aws-session-config.yml
|
56
|
+
[--source-profile=SOURCE-PROFILE] # Profile in config file that user credentials will be loaded from
|
57
|
+
# Default: default
|
58
|
+
[--profile=PROFILE] # Profile that session token will be loaded into
|
59
|
+
# Default: default
|
60
|
+
[--duration=N] # Duration, in seconds, that credentials should remain valid
|
61
|
+
[--mfa-device=MFA-DEVICE] # ARN of MFA device
|
62
|
+
[--mfa-code=MFA-CODE] # Six digit code from MFA device
|
63
|
+
[--yubikey-name=YUBIKEY-NAME] # Name of yubikey device
|
64
|
+
# Default: Yubikey
|
65
|
+
[--oath-credential=OATH-CREDENTIAL] # Name of OATH credential
|
66
|
+
```
|
60
67
|
|
61
|
-
|
68
|
+
### Assuming a role
|
62
69
|
|
63
70
|
Example:
|
64
71
|
|
65
|
-
```yaml
|
66
|
-
---
|
67
|
-
aws_access_key_id: AKIAIOSFODNN7EXAMPLE
|
68
|
-
aws_secret_access_key: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
|
69
|
-
region: ap-southeast-2
|
70
|
-
duration: 86400
|
71
|
-
mfa_device: arn:aws:iam::000000000000:mfa/user.name@example.com
|
72
72
|
```
|
73
|
+
$ aws-session assume-role --role-arn=ROLE_ARN --role-session-name=ROLE_SESSION_NAME
|
74
|
+
```
|
75
|
+
|
76
|
+
### Source profiles
|
77
|
+
|
78
|
+
Instead of specifying all of the options via the command line each time you
|
79
|
+
want to generate new session credentials, you can store the options in a
|
80
|
+
*source profile*.
|
81
|
+
|
82
|
+
```
|
83
|
+
$ aws-session configure
|
84
|
+
Source profile (leave blank for "default"):
|
85
|
+
AWS Access Key ID: AKIAIOSFODNN7EXAMPLE
|
86
|
+
AWS Secret Access Key:
|
87
|
+
AWS region: ap-southeast-2
|
88
|
+
Session duration (in seconds): 86400
|
89
|
+
|
90
|
+
Configure MFA? y
|
91
|
+
MFA device ARN: arn:aws:iam::000000000000:mfa/user.name@example.com
|
92
|
+
|
93
|
+
Configure Yubikey? y
|
94
|
+
OATH credential name: user.name@example.com@accountalias
|
95
|
+
```
|
96
|
+
|
97
|
+
See `aws-session --help configure` for more info. By default, the configuration
|
98
|
+
is stored in `~/.aws/aws-session-config.yml`.
|
73
99
|
|
74
100
|
## Contributing
|
75
101
|
|
@@ -24,7 +24,10 @@ Gem::Specification.new do |spec|
|
|
24
24
|
spec.add_development_dependency 'rspec'
|
25
25
|
spec.add_development_dependency 'rspec-its'
|
26
26
|
|
27
|
+
spec.add_runtime_dependency 'activesupport'
|
27
28
|
spec.add_runtime_dependency 'aws-sdk', '~> 2.1'
|
28
29
|
spec.add_runtime_dependency 'inifile', '~> 3.0'
|
30
|
+
spec.add_runtime_dependency 'smartcard'
|
29
31
|
spec.add_runtime_dependency 'thor', '~> 0.19'
|
32
|
+
spec.add_runtime_dependency 'yubioath'
|
30
33
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Aws
|
2
|
+
module Session
|
3
|
+
module Credentials
|
4
|
+
# Holds session credentials
|
5
|
+
class Cache
|
6
|
+
include ProfileStorage
|
7
|
+
include FileProvider::YamlFileProvider
|
8
|
+
|
9
|
+
attr_reader :path
|
10
|
+
|
11
|
+
def initialize(options = {})
|
12
|
+
@path = File.expand_path(options[:path] || default_path)
|
13
|
+
end
|
14
|
+
|
15
|
+
def default_path
|
16
|
+
File.join(%w(~ .aws aws-session-cache.yml))
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [Hash<String,Hash>]
|
20
|
+
def profiles_hash
|
21
|
+
self[:profiles] || {}
|
22
|
+
end
|
23
|
+
|
24
|
+
# @param [Hash] hsh
|
25
|
+
def profiles_hash=(hsh)
|
26
|
+
self[:profiles] = hsh
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -6,26 +6,26 @@ module Aws
|
|
6
6
|
module Credentials
|
7
7
|
# Command line interface
|
8
8
|
class Cli < Thor
|
9
|
-
method_option 'access-key-id',
|
9
|
+
method_option 'aws-access-key-id',
|
10
10
|
type: :string,
|
11
11
|
desc: 'Access key used to generate session token',
|
12
12
|
default: nil
|
13
|
-
method_option 'secret-access-key',
|
13
|
+
method_option 'aws-secret-access-key',
|
14
14
|
type: :string,
|
15
15
|
desc: 'Secret key used to generate session token',
|
16
16
|
default: nil
|
17
|
-
method_option 'region',
|
17
|
+
method_option 'aws-region',
|
18
18
|
type: :string,
|
19
19
|
desc: 'AWS region to connect to',
|
20
20
|
default: nil
|
21
21
|
method_option 'config-file',
|
22
22
|
type: :string,
|
23
23
|
desc: 'YAML file to load config from',
|
24
|
-
default: '~/.aws/
|
25
|
-
method_option '
|
24
|
+
default: '~/.aws/aws-session-config.yml'
|
25
|
+
method_option 'source-profile',
|
26
26
|
type: :string,
|
27
|
-
desc: '
|
28
|
-
default: '
|
27
|
+
desc: 'Profile in config file that user credentials will be loaded from',
|
28
|
+
default: 'default'
|
29
29
|
method_option 'profile',
|
30
30
|
type: :string,
|
31
31
|
desc: 'Profile that session token will be loaded into',
|
@@ -33,7 +33,7 @@ module Aws
|
|
33
33
|
method_option 'duration',
|
34
34
|
type: :numeric,
|
35
35
|
desc: 'Duration, in seconds, that credentials should remain valid',
|
36
|
-
default:
|
36
|
+
default: nil
|
37
37
|
method_option 'mfa-device',
|
38
38
|
type: :string,
|
39
39
|
desc: 'ARN of MFA device',
|
@@ -42,21 +42,118 @@ module Aws
|
|
42
42
|
type: :string,
|
43
43
|
desc: 'Six digit code from MFA device',
|
44
44
|
default: nil
|
45
|
+
method_option 'yubikey-name',
|
46
|
+
type: :string,
|
47
|
+
desc: 'Name of yubikey device',
|
48
|
+
default: 'Yubikey'
|
49
|
+
method_option 'oath-credential',
|
50
|
+
type: :string,
|
51
|
+
desc: 'Name of OATH credential',
|
52
|
+
default: nil
|
45
53
|
desc 'new', 'Generates new AWS session credentials'
|
46
54
|
def new
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
55
|
+
cli_opts = options.transform_keys { |key| key.sub(/-/, '_') }
|
56
|
+
SessionManager.new.new_session(cli_opts)
|
57
|
+
end
|
58
|
+
|
59
|
+
method_option 'role_arn',
|
60
|
+
type: :string,
|
61
|
+
desc: 'The ARN of the role to assume',
|
62
|
+
required: true
|
63
|
+
method_option 'role_session_name',
|
64
|
+
type: :string,
|
65
|
+
desc: 'An identifier for the assumed role session',
|
66
|
+
required: true
|
67
|
+
method_option 'profile',
|
68
|
+
type: :string,
|
69
|
+
desc: 'Profile that session token will be loaded into',
|
70
|
+
default: 'default'
|
71
|
+
method_option 'duration',
|
72
|
+
type: :numeric,
|
73
|
+
desc: 'Duration, in seconds, that credentials should remain valid',
|
74
|
+
default: nil
|
75
|
+
method_option 'mfa-device',
|
76
|
+
type: :string,
|
77
|
+
desc: 'ARN of MFA device',
|
78
|
+
default: nil
|
79
|
+
method_option 'mfa-code',
|
80
|
+
type: :string,
|
81
|
+
desc: 'Six digit code from MFA device',
|
82
|
+
default: nil
|
83
|
+
method_option 'yubikey-name',
|
84
|
+
type: :string,
|
85
|
+
desc: 'Name of yubikey device',
|
86
|
+
default: 'Yubikey'
|
87
|
+
method_option 'oath-credential',
|
88
|
+
type: :string,
|
89
|
+
desc: 'Name of OATH credential',
|
90
|
+
default: nil
|
91
|
+
desc 'assume-role', 'Assumes a role'
|
92
|
+
def assume_role
|
93
|
+
cli_opts = options.transform_keys { |key| key.sub(/-/, '_') }
|
94
|
+
SessionManager.new.assume_role(cli_opts)
|
95
|
+
end
|
96
|
+
|
97
|
+
method_option 'aws-access-key-id',
|
98
|
+
type: :string,
|
99
|
+
desc: 'Access key used to generate session token',
|
100
|
+
default: nil
|
101
|
+
method_option 'aws-secret-access-key',
|
102
|
+
type: :string,
|
103
|
+
desc: 'Secret key used to generate session token',
|
104
|
+
default: nil
|
105
|
+
method_option 'aws-region',
|
106
|
+
type: :string,
|
107
|
+
desc: 'AWS region to connect to',
|
108
|
+
default: nil
|
109
|
+
method_option 'config-file',
|
110
|
+
type: :string,
|
111
|
+
desc: 'YAML file to load config from',
|
112
|
+
default: '~/.aws/aws-session-config.yml'
|
113
|
+
method_option 'source-profile',
|
114
|
+
type: :string,
|
115
|
+
desc: 'Profile in config file that user credentials will be loaded from',
|
116
|
+
default: nil
|
117
|
+
method_option 'duration',
|
118
|
+
type: :numeric,
|
119
|
+
desc: 'Duration, in seconds, that credentials should remain valid',
|
120
|
+
default: nil
|
121
|
+
method_option 'mfa-device',
|
122
|
+
type: :string,
|
123
|
+
desc: 'ARN of MFA device',
|
124
|
+
default: nil
|
125
|
+
method_option 'yubikey-name',
|
126
|
+
type: :string,
|
127
|
+
desc: 'Name of yubikey device',
|
128
|
+
default: 'Yubikey'
|
129
|
+
method_option 'oath-credential',
|
130
|
+
type: :string,
|
131
|
+
desc: 'Name of OATH credential',
|
132
|
+
default: nil
|
133
|
+
desc 'configure', 'Configures a new source profile'
|
134
|
+
def configure
|
135
|
+
cli_opts = options.transform_keys { |key| key.sub(/-/, '_') }
|
136
|
+
cli_opts['source_profile'] ||= ask('Source profile (leave blank for "default"):')
|
137
|
+
cli_opts['aws_access_key_id'] ||= ask('AWS Access Key ID:')
|
138
|
+
cli_opts['aws_secret_access_key'] ||= ask('AWS Secret Access Key:', echo: false)
|
139
|
+
puts '' # BUG: No LF printed when echo is set to false
|
140
|
+
cli_opts['aws_region'] ||= ask('AWS region:')
|
141
|
+
cli_opts['duration'] ||= ask('Session duration (in seconds):')
|
142
|
+
|
143
|
+
puts ''
|
144
|
+
if yes?('Configure MFA?')
|
145
|
+
cli_opts['mfa_device'] ||= ask('MFA device ARN:')
|
146
|
+
puts ''
|
147
|
+
if yes?('Configure Yubikey?')
|
148
|
+
cli_opts['oath_credential'] ||= ask('OATH credential name:')
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
cli_opts['source_profile'] = 'default' if cli_opts['source_profile'].empty?
|
56
153
|
|
57
|
-
|
58
|
-
|
59
|
-
|
154
|
+
prof = Profile.new(cli_opts.except('config_file', 'source_profile'))
|
155
|
+
cf = Config.new(path: cli_opts['config_file'])
|
156
|
+
cf.set_profile(cli_opts[:source_profile], prof)
|
60
157
|
end
|
61
158
|
|
62
159
|
default_task :new
|
@@ -3,91 +3,27 @@ module Aws
|
|
3
3
|
module Credentials
|
4
4
|
# Holds configuration
|
5
5
|
class Config
|
6
|
-
|
7
|
-
|
8
|
-
@config = config || load_file
|
9
|
-
end
|
10
|
-
|
11
|
-
def [](key)
|
12
|
-
@config[key]
|
13
|
-
end
|
14
|
-
|
15
|
-
def []=(key, value)
|
16
|
-
@config[key] = value
|
17
|
-
end
|
18
|
-
|
19
|
-
def aws_access_key_id
|
20
|
-
self['aws_access_key_id']
|
21
|
-
end
|
22
|
-
|
23
|
-
def aws_access_key_id=(value)
|
24
|
-
self['aws_access_key_id'] = value
|
25
|
-
end
|
26
|
-
|
27
|
-
def aws_secret_access_key
|
28
|
-
self['aws_secret_access_key']
|
29
|
-
end
|
30
|
-
|
31
|
-
def aws_secret_access_key=(value)
|
32
|
-
self['aws_secret_access_key'] = value
|
33
|
-
end
|
34
|
-
|
35
|
-
def credential_file
|
36
|
-
self['credential_file']
|
37
|
-
end
|
38
|
-
|
39
|
-
def credential_file=(value)
|
40
|
-
self['credential_file'] = value
|
41
|
-
end
|
42
|
-
|
43
|
-
def duration
|
44
|
-
self['duration']
|
45
|
-
end
|
46
|
-
|
47
|
-
def duration=(value)
|
48
|
-
self['duration'] = value
|
49
|
-
end
|
50
|
-
|
51
|
-
# @api private
|
52
|
-
def load_file
|
53
|
-
return {} unless File.exist?(@path)
|
54
|
-
YAML.load(File.read(@path))
|
55
|
-
end
|
56
|
-
|
57
|
-
def mfa_code
|
58
|
-
self['mfa_code']
|
59
|
-
end
|
60
|
-
|
61
|
-
def mfa_code=(value)
|
62
|
-
self['mfa_code'] = value
|
63
|
-
end
|
64
|
-
|
65
|
-
def mfa_device
|
66
|
-
self['mfa_device']
|
67
|
-
end
|
68
|
-
|
69
|
-
def mfa_device=(value)
|
70
|
-
self['mfa_device'] = value
|
71
|
-
end
|
6
|
+
include ProfileStorage
|
7
|
+
include FileProvider::YamlFileProvider
|
72
8
|
|
73
|
-
|
74
|
-
self['profile']
|
75
|
-
end
|
9
|
+
attr_reader :path
|
76
10
|
|
77
|
-
def
|
78
|
-
|
11
|
+
def initialize(options = {})
|
12
|
+
@path = File.expand_path(options[:path] || default_path)
|
79
13
|
end
|
80
14
|
|
81
|
-
def
|
82
|
-
|
15
|
+
def default_path
|
16
|
+
File.join(%w(~ .aws aws-session-config.yml))
|
83
17
|
end
|
84
18
|
|
85
|
-
|
86
|
-
|
19
|
+
# @return [Hash<String,Hash>]
|
20
|
+
def profiles_hash
|
21
|
+
self[:profiles] || {}
|
87
22
|
end
|
88
23
|
|
89
|
-
|
90
|
-
|
24
|
+
# @param [Hash] hsh
|
25
|
+
def profiles_hash=(hsh)
|
26
|
+
self[:profiles] = hsh
|
91
27
|
end
|
92
28
|
end
|
93
29
|
end
|
@@ -1,39 +1,29 @@
|
|
1
1
|
module Aws
|
2
2
|
module Session
|
3
3
|
module Credentials
|
4
|
-
#
|
4
|
+
# Holds credentials that are read by AWS SDKs
|
5
5
|
class CredentialFile
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
6
|
+
include FileProvider::IniFileProvider
|
7
|
+
include ProfileStorage
|
8
|
+
|
9
|
+
attr_reader :path
|
10
|
+
|
11
|
+
def initialize(options = {})
|
12
|
+
@path = File.expand_path(options[:path] || default_path)
|
13
|
+
end
|
14
|
+
|
15
|
+
def default_path
|
16
|
+
File.join(%w(~ .aws credentials))
|
11
17
|
end
|
12
18
|
|
13
|
-
# @
|
14
|
-
def
|
15
|
-
|
16
|
-
IniFile.load(@path)
|
17
|
-
else
|
18
|
-
path_dir = File.dirname(@path)
|
19
|
-
FileUtils.mkdir_p(path_dir) unless File.exist?(path_dir)
|
20
|
-
IniFile.new(filename: @path, encoding: 'UTF-8')
|
21
|
-
end
|
19
|
+
# @return [Hash<String,Hash>]
|
20
|
+
def profiles_hash
|
21
|
+
read.to_h
|
22
22
|
end
|
23
23
|
|
24
|
-
#
|
25
|
-
|
26
|
-
|
27
|
-
# profile. Does not override options if they are not provided. Set an
|
28
|
-
# option to +nil+ to explicitly unset an existing option.
|
29
|
-
# @param [String] profile name of profile to set credentials for
|
30
|
-
# @param [Hash] options settings to set
|
31
|
-
# @option options [String] :access_key_id Access key
|
32
|
-
# @option options [String] :secret_access_key Secret key
|
33
|
-
# @option options [String] :session_token Session token
|
34
|
-
def set_credentials(profile, options = {})
|
35
|
-
@ini_file[profile] = @ini_file[profile].merge(options)
|
36
|
-
@ini_file.write
|
24
|
+
# @param [Hash<String,Hash>] prfs
|
25
|
+
def profiles_hash=(hsh)
|
26
|
+
hsh.each { |key, value| self[key] = value }
|
37
27
|
end
|
38
28
|
end
|
39
29
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Aws
|
2
|
+
module Session
|
3
|
+
module Credentials
|
4
|
+
module FileProvider
|
5
|
+
# Mixin to store configuration in an INI file
|
6
|
+
module IniFileProvider
|
7
|
+
def [](key)
|
8
|
+
read[key.to_s]
|
9
|
+
end
|
10
|
+
|
11
|
+
def []=(key, value)
|
12
|
+
ini_file = read
|
13
|
+
ini_file[key.to_s] = value
|
14
|
+
ini_file.save
|
15
|
+
end
|
16
|
+
|
17
|
+
# @api private
|
18
|
+
# @return [IniFile]
|
19
|
+
def read
|
20
|
+
if File.exist?(path)
|
21
|
+
IniFile.load(path)
|
22
|
+
else
|
23
|
+
IniFile.new(filename: path, encoding: 'UTF-8')
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Aws
|
2
|
+
module Session
|
3
|
+
module Credentials
|
4
|
+
module FileProvider
|
5
|
+
# Mixin to store configuration in a YAML file
|
6
|
+
module YamlFileProvider
|
7
|
+
def [](key)
|
8
|
+
read[key]
|
9
|
+
end
|
10
|
+
|
11
|
+
def []=(key, value)
|
12
|
+
hash = read.dup
|
13
|
+
hash[key] = value
|
14
|
+
write(hash)
|
15
|
+
end
|
16
|
+
|
17
|
+
# @api private
|
18
|
+
# @return [Hash]
|
19
|
+
def read
|
20
|
+
return {} unless File.exist?(path)
|
21
|
+
YAML.load(File.read(path)).deep_symbolize_keys
|
22
|
+
end
|
23
|
+
|
24
|
+
# @api private
|
25
|
+
# @param [Hash] hash
|
26
|
+
def write(hash)
|
27
|
+
hsh = hash.deep_stringify_keys
|
28
|
+
File.open(path, 'w') { |file| file.write(YAML.dump(hsh)) }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Aws
|
2
|
+
module Session
|
3
|
+
module Credentials
|
4
|
+
module MfaDevice
|
5
|
+
# Represents generic MFA device that generates codes. Class must be
|
6
|
+
# initialized with the code.
|
7
|
+
class GenericMfaDevice
|
8
|
+
attr_reader :code
|
9
|
+
attr_reader :device_arn
|
10
|
+
|
11
|
+
def initialize(options = {})
|
12
|
+
@code = options[:code]
|
13
|
+
@device_arn = options[:device_arn]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Aws
|
2
|
+
module Session
|
3
|
+
module Credentials
|
4
|
+
module MfaDevice
|
5
|
+
# Gets MFA codes from a Yubikey using YubiOATH
|
6
|
+
class YubikeyMfaDevice
|
7
|
+
attr_reader :device_arn
|
8
|
+
|
9
|
+
def initialize(options = {})
|
10
|
+
@yubikey_name = options.fetch(:yubikey_name) { 'Yubikey' }
|
11
|
+
@oath_credential = options[:oath_credential]
|
12
|
+
@device_arn = options[:device_arn]
|
13
|
+
end
|
14
|
+
|
15
|
+
def code
|
16
|
+
card_names.each do |card_name|
|
17
|
+
card(card_name) do |crd|
|
18
|
+
oath = YubiOATH.new(crd)
|
19
|
+
codes = oath.calculate_all(timestamp: Time.now)
|
20
|
+
# Credential names are returned as ASCII-8BIT
|
21
|
+
codes.transform_keys! { |key| key.force_encoding('UTF-8') }
|
22
|
+
return codes[@oath_credential]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
nil
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
# @param [String] name
|
31
|
+
# @yieldparam card [Smartcard::PCSC::Card]
|
32
|
+
def card(name)
|
33
|
+
context do |cxt|
|
34
|
+
begin
|
35
|
+
crd = cxt.card(name, :shared)
|
36
|
+
yield crd
|
37
|
+
ensure
|
38
|
+
crd.disconnect unless crd.nil?
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# @return [Array<String>]
|
44
|
+
def card_names
|
45
|
+
context do |cxt|
|
46
|
+
cxt.readers.select { |name| name.include?(@yubikey_name) }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# @yieldparam context [Smartcard::PCSC::Context]
|
51
|
+
def context
|
52
|
+
context = Smartcard::PCSC::Context.new
|
53
|
+
yield context
|
54
|
+
ensure
|
55
|
+
context.release
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Aws
|
2
|
+
module Session
|
3
|
+
module Credentials
|
4
|
+
# Mixin to store profiles
|
5
|
+
module ProfileStorage
|
6
|
+
# @return [Hash<String,Profile>]
|
7
|
+
def profiles
|
8
|
+
prfs = {}
|
9
|
+
profiles_hash.each do |name, options|
|
10
|
+
prfs[name] = Profile.new(options)
|
11
|
+
end
|
12
|
+
prfs
|
13
|
+
end
|
14
|
+
|
15
|
+
# @param [Hash<String,Profile>] prfs
|
16
|
+
def profiles=(prfs)
|
17
|
+
hash = {}
|
18
|
+
prfs.each do |name, prof|
|
19
|
+
hash[name] = prof.to_h
|
20
|
+
end
|
21
|
+
self.profiles_hash = hash
|
22
|
+
end
|
23
|
+
|
24
|
+
# @param [String] name
|
25
|
+
# @return [Profile]
|
26
|
+
def profile(name)
|
27
|
+
profiles[name]
|
28
|
+
end
|
29
|
+
|
30
|
+
# @param [String] name
|
31
|
+
# @param [Profile] prof
|
32
|
+
def set_profile(name, prof)
|
33
|
+
profs = profiles.dup
|
34
|
+
profs[name] = prof
|
35
|
+
self.profiles = profs
|
36
|
+
prof
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -3,44 +3,55 @@ module Aws
|
|
3
3
|
module Credentials
|
4
4
|
# Builds AWS session
|
5
5
|
class SessionBuilder
|
6
|
-
# @param [Hash]
|
7
|
-
|
8
|
-
|
9
|
-
@
|
10
|
-
@
|
6
|
+
# @param [Hash] options
|
7
|
+
def initialize(options)
|
8
|
+
@mfa_device = options[:mfa_device]
|
9
|
+
@session_duration_seconds = options[:session_duration_seconds]
|
10
|
+
@role_duration_seconds = options[:role_duration_seconds]
|
11
|
+
@role_arn = options[:role_arn]
|
12
|
+
@role_session_name = options[:role_session_name]
|
13
|
+
@source_profile = options[:source_profile]
|
14
|
+
@sts_client = options[:sts_client]
|
11
15
|
end
|
12
16
|
|
13
|
-
# @
|
14
|
-
def
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
17
|
+
# @return [Profile]
|
18
|
+
def role_profile
|
19
|
+
resp = sts_client.assume_role(
|
20
|
+
role_arn: @role_arn,
|
21
|
+
role_session_name: @role_session_name,
|
22
|
+
duration_seconds: @role_duration_seconds,
|
23
|
+
serial_number: @mfa_device.device_arn,
|
24
|
+
token_code: @mfa_device.code
|
19
25
|
)
|
26
|
+
return Profile.new(
|
27
|
+
aws_access_key_id: resp.credentials['access_key_id'],
|
28
|
+
aws_secret_access_key: resp.credentials['secret_access_key'],
|
29
|
+
aws_session_token: resp.credentials['session_token'],
|
30
|
+
aws_region: @source_profile.aws_region
|
31
|
+
) if resp
|
20
32
|
end
|
21
33
|
|
22
|
-
#
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
serial_number: @config['mfa_device'],
|
29
|
-
token_code: @config['mfa_code']
|
34
|
+
# @return [Profile]
|
35
|
+
def session_profile
|
36
|
+
resp = sts_client.get_session_token(
|
37
|
+
duration_seconds: @session_duration_seconds,
|
38
|
+
serial_number: @mfa_device.device_arn,
|
39
|
+
token_code: @mfa_device.code
|
30
40
|
)
|
31
|
-
return
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
41
|
+
return Profile.new(
|
42
|
+
aws_access_key_id: resp.credentials['access_key_id'],
|
43
|
+
aws_secret_access_key: resp.credentials['secret_access_key'],
|
44
|
+
aws_session_token: resp.credentials['session_token'],
|
45
|
+
aws_region: @source_profile.aws_region
|
46
|
+
) if resp
|
36
47
|
end
|
37
48
|
|
38
|
-
#
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
49
|
+
# @api private
|
50
|
+
def sts_client
|
51
|
+
@client ||= Aws::STS::Client.new(
|
52
|
+
region: @source_profile.aws_region,
|
53
|
+
credentials: @source_profile.aws_credentials
|
54
|
+
)
|
44
55
|
end
|
45
56
|
end
|
46
57
|
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module Aws
|
2
|
+
module Session
|
3
|
+
module Credentials
|
4
|
+
# Manages sessions
|
5
|
+
class SessionManager
|
6
|
+
# Assumes a role from provided options
|
7
|
+
# @param [Hash] options
|
8
|
+
# @option options [String] :profile
|
9
|
+
# @option options [String] :role_arn
|
10
|
+
# @option options [String] :duration
|
11
|
+
def assume_role(options)
|
12
|
+
options[:profile] = options[:profile].to_sym
|
13
|
+
|
14
|
+
session_prof = Cache.new.profile(options[:profile])
|
15
|
+
options = session_prof.to_h.deep_merge(options).deep_symbolize_keys
|
16
|
+
|
17
|
+
sb = SessionBuilder.new(
|
18
|
+
mfa_device: mfa_device(options),
|
19
|
+
role_duration_seconds: options[:duration],
|
20
|
+
role_arn: options[:role_arn],
|
21
|
+
role_session_name: options[:role_session_name],
|
22
|
+
source_profile: session_prof
|
23
|
+
)
|
24
|
+
role_profile = sb.role_profile
|
25
|
+
|
26
|
+
CredentialFile.new.set_profile(options[:profile], profile)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Creates a new session from provided options
|
30
|
+
# @param [Hash] options
|
31
|
+
# @option options [String] :aws_access_key_id
|
32
|
+
# @option options [String] :aws_secret_access_key
|
33
|
+
# @option options [String] :aws_session_token
|
34
|
+
# @option options [String] :aws_region
|
35
|
+
# @option options [String] :source_profile
|
36
|
+
# @option options [String] :profile
|
37
|
+
# @option options [String] :duration
|
38
|
+
# @option options [String] :mfa_device
|
39
|
+
# @option options [String] :mfa_code
|
40
|
+
# @option options [String] :yubikey_name
|
41
|
+
# @option options [String] :oath_credential
|
42
|
+
# @option options [String] :config_file
|
43
|
+
def new_session(options)
|
44
|
+
options[:source_profile] = options[:source_profile].to_sym
|
45
|
+
options[:profile] = options[:profile].to_sym
|
46
|
+
|
47
|
+
user_prof = user_profile(options[:source_profile], options[:config_file])
|
48
|
+
options = user_prof.to_h.deep_merge(options).deep_symbolize_keys
|
49
|
+
|
50
|
+
sb = SessionBuilder.new(
|
51
|
+
mfa_device: mfa_device(options),
|
52
|
+
session_duration_seconds: options[:duration],
|
53
|
+
source_profile: user_prof
|
54
|
+
)
|
55
|
+
set_user_session_profile(options[:profile], sb.session_profile)
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def mfa_device(options)
|
61
|
+
opts = {
|
62
|
+
device_arn: options[:mfa_device],
|
63
|
+
yubikey_name: options[:yubikey_name],
|
64
|
+
oath_credential: options[:oath_credential],
|
65
|
+
code: options[:mfa_code]
|
66
|
+
}
|
67
|
+
if options.key?(:oath_credential)
|
68
|
+
MfaDevice::YubikeyMfaDevice.new(opts)
|
69
|
+
else
|
70
|
+
MfaDevice::GenericMfaDevice.new(opts)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def set_user_session_profile(name, profile)
|
75
|
+
[Cache.new, CredentialFile.new].each do |profile_store|
|
76
|
+
profile_store.set_profile(name, profile)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def user_profile(name, path)
|
81
|
+
Config.new(path: path).profile(name)
|
82
|
+
end
|
83
|
+
|
84
|
+
def user_session_profile(name)
|
85
|
+
Cache.new.profile(name)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -1,10 +1,25 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
require 'active_support/core_ext/hash'
|
1
3
|
require 'aws-sdk'
|
2
4
|
require 'fileutils'
|
3
5
|
require 'inifile'
|
6
|
+
require 'ostruct'
|
7
|
+
require 'smartcard'
|
4
8
|
require 'yaml'
|
9
|
+
require 'yubioath'
|
5
10
|
|
11
|
+
require 'aws/session/credentials/file_provider/ini_file_provider'
|
12
|
+
require 'aws/session/credentials/file_provider/yaml_file_provider'
|
13
|
+
require 'aws/session/credentials/mfa_device/generic_mfa_device'
|
14
|
+
require 'aws/session/credentials/mfa_device/yubikey_mfa_device'
|
15
|
+
|
16
|
+
require 'aws/session/credentials/profile_storage'
|
17
|
+
|
18
|
+
require 'aws/session/credentials/cache'
|
6
19
|
require 'aws/session/credentials/config'
|
7
20
|
require 'aws/session/credentials/credential_file'
|
21
|
+
require 'aws/session/credentials/profile'
|
8
22
|
require 'aws/session/credentials/session_builder'
|
23
|
+
require 'aws/session/credentials/session_manager'
|
9
24
|
|
10
25
|
require 'aws/session/credentials/version'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: aws-session-credentials
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0.pre.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Vidulich
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-11-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -66,6 +66,20 @@ dependencies:
|
|
66
66
|
- - ! '>='
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: activesupport
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ! '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ! '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
84
|
name: aws-sdk
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -94,6 +108,20 @@ dependencies:
|
|
94
108
|
- - ~>
|
95
109
|
- !ruby/object:Gem::Version
|
96
110
|
version: '3.0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: smartcard
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ! '>='
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
97
125
|
- !ruby/object:Gem::Dependency
|
98
126
|
name: thor
|
99
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -108,6 +136,20 @@ dependencies:
|
|
108
136
|
- - ~>
|
109
137
|
- !ruby/object:Gem::Version
|
110
138
|
version: '0.19'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: yubioath
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ! '>='
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :runtime
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ! '>='
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
111
153
|
description: Command-line tool to generate AWS session credentials.
|
112
154
|
email:
|
113
155
|
- ben@vidulich.co.nz
|
@@ -129,10 +171,18 @@ files:
|
|
129
171
|
- bin/setup
|
130
172
|
- exe/aws-session
|
131
173
|
- lib/aws/session/credentials.rb
|
174
|
+
- lib/aws/session/credentials/cache.rb
|
132
175
|
- lib/aws/session/credentials/cli.rb
|
133
176
|
- lib/aws/session/credentials/config.rb
|
134
177
|
- lib/aws/session/credentials/credential_file.rb
|
178
|
+
- lib/aws/session/credentials/file_provider/ini_file_provider.rb
|
179
|
+
- lib/aws/session/credentials/file_provider/yaml_file_provider.rb
|
180
|
+
- lib/aws/session/credentials/mfa_device/generic_mfa_device.rb
|
181
|
+
- lib/aws/session/credentials/mfa_device/yubikey_mfa_device.rb
|
182
|
+
- lib/aws/session/credentials/profile.rb
|
183
|
+
- lib/aws/session/credentials/profile_storage.rb
|
135
184
|
- lib/aws/session/credentials/session_builder.rb
|
185
|
+
- lib/aws/session/credentials/session_manager.rb
|
136
186
|
- lib/aws/session/credentials/version.rb
|
137
187
|
homepage: https://github.com/zl4bv/aws-session-credentials
|
138
188
|
licenses:
|
@@ -149,9 +199,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
149
199
|
version: '0'
|
150
200
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
151
201
|
requirements:
|
152
|
-
- - ! '
|
202
|
+
- - ! '>'
|
153
203
|
- !ruby/object:Gem::Version
|
154
|
-
version:
|
204
|
+
version: 1.3.1
|
155
205
|
requirements: []
|
156
206
|
rubyforge_project:
|
157
207
|
rubygems_version: 2.4.5
|