aws-mfa 0.1.0
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.
- data/bin/aws-mfa +120 -0
- metadata +48 -0
data/bin/aws-mfa
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
class AwsMfa
|
6
|
+
def initialize
|
7
|
+
@aws_config_dir = validate_aws_install
|
8
|
+
end
|
9
|
+
|
10
|
+
def validate_aws_install
|
11
|
+
unless which('aws')
|
12
|
+
puts 'Could not find the aws command'
|
13
|
+
exit 1
|
14
|
+
end
|
15
|
+
|
16
|
+
if ENV['AWS_CREDENTIAL_FILE']
|
17
|
+
aws_config_file = ENV['AWS_CREDENTIAL_FILE']
|
18
|
+
aws_config_dir = File.dirname(aws_config_file)
|
19
|
+
else
|
20
|
+
aws_config_dir = File.join(ENV['HOME'], '.aws')
|
21
|
+
aws_config_file = File.join(aws_config_dir, 'config')
|
22
|
+
end
|
23
|
+
|
24
|
+
unless File.readable?(aws_config_file)
|
25
|
+
puts 'Aws configuration not found. You must run `aws cli configure`'
|
26
|
+
exit 1
|
27
|
+
end
|
28
|
+
|
29
|
+
aws_config_dir
|
30
|
+
end
|
31
|
+
|
32
|
+
# http://stackoverflow.com/questions/2108727/which-in-ruby-checking-if-program-exists-in-path-from-ruby
|
33
|
+
def which(cmd)
|
34
|
+
exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
|
35
|
+
ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
|
36
|
+
exts.each { |ext|
|
37
|
+
exe = File.join(path, "#{cmd}#{ext}")
|
38
|
+
return exe if File.executable? exe
|
39
|
+
}
|
40
|
+
end
|
41
|
+
return nil
|
42
|
+
end
|
43
|
+
|
44
|
+
def load_arn
|
45
|
+
arn_file = File.join(@aws_config_dir, 'mfa_device')
|
46
|
+
|
47
|
+
if File.readable?(arn_file)
|
48
|
+
arn = File.read(arn_file)
|
49
|
+
else
|
50
|
+
puts 'Fetching MFA devices for your account...'
|
51
|
+
mfa_devices = JSON.parse(`aws iam list-mfa-devices`).fetch('MFADevices')
|
52
|
+
if mfa_devices.any?
|
53
|
+
arn = mfa_devices.first.fetch('SerialNumber')
|
54
|
+
puts "Using MFA device #{arn}. To change this in the future edit #{arn_file}."
|
55
|
+
File.open(arn_file, 'w') { |f| f.print arn }
|
56
|
+
else
|
57
|
+
abort 'No MFA devices were found for your account'
|
58
|
+
end
|
59
|
+
end
|
60
|
+
arn
|
61
|
+
end
|
62
|
+
|
63
|
+
def get_credentials(arn)
|
64
|
+
credentials_file = File.join(@aws_config_dir, 'mfa_credentials')
|
65
|
+
|
66
|
+
if File.readable?(credentials_file) && token_not_expired?(credentials_file)
|
67
|
+
credentials = JSON.parse(File.read(credentials_file))
|
68
|
+
else
|
69
|
+
puts 'Enter the 6-digit code from your MFA device:'
|
70
|
+
code = $stdin.gets.chomp
|
71
|
+
unless code =~ /^\d{6}$/
|
72
|
+
puts 'That is an invalid MFA code'
|
73
|
+
exit 1
|
74
|
+
end
|
75
|
+
credentials_raw = `aws sts get-session-token --serial-number #{arn} --token-code #{code}`
|
76
|
+
credentials = JSON.parse(credentials_raw)
|
77
|
+
File.open(credentials_file, 'w') { |f| f.print credentials_raw }
|
78
|
+
end
|
79
|
+
|
80
|
+
credentials['Credentials']
|
81
|
+
end
|
82
|
+
|
83
|
+
def token_not_expired?(credentials_file)
|
84
|
+
# default is 12 hours
|
85
|
+
expiration_period = 12 * 60 * 60
|
86
|
+
mtime = File.stat(credentials_file).mtime
|
87
|
+
now = Time.new
|
88
|
+
if now - mtime < expiration_period
|
89
|
+
true
|
90
|
+
else
|
91
|
+
false
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def print_credentials(credentials)
|
96
|
+
puts "export AWS_SECRET_ACCESS_KEY='#{credentials['SecretAccessKey']}'"
|
97
|
+
puts "export AWS_ACCESS_KEY_ID='#{credentials['AccessKeyId']}'"
|
98
|
+
puts "export AWS_SESSION_TOKEN='#{credentials['SessionToken']}'"
|
99
|
+
end
|
100
|
+
|
101
|
+
def export_credentials(credentials)
|
102
|
+
ENV['AWS_SECRET_ACCESS_KEY'] = credentials['SecretAccessKey']
|
103
|
+
ENV['AWS_ACCESS_KEY_ID'] = credentials['AccessKeyId']
|
104
|
+
ENV['AWS_SESSION_TOKEN'] = credentials['SessionToken']
|
105
|
+
end
|
106
|
+
|
107
|
+
def execute
|
108
|
+
arn = load_arn
|
109
|
+
credentials = get_credentials(arn)
|
110
|
+
if ARGV.empty?
|
111
|
+
print_credentials(credentials)
|
112
|
+
else
|
113
|
+
export_credentials(credentials)
|
114
|
+
exec(*ARGV)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
aws_mfa = AwsMfa.new
|
120
|
+
aws_mfa.execute
|
metadata
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: aws-mfa
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Brian Pitts
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-09-30 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: Run AWS commands with MFA
|
15
|
+
email: brian.pitts@lonelyplanet.com
|
16
|
+
executables:
|
17
|
+
- aws-mfa
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- bin/aws-mfa
|
22
|
+
homepage: http://www.github.com/lonelyplanet/aws-mfa
|
23
|
+
licenses:
|
24
|
+
- Apache-2.0
|
25
|
+
post_install_message:
|
26
|
+
rdoc_options: []
|
27
|
+
require_paths:
|
28
|
+
- lib
|
29
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
30
|
+
none: false
|
31
|
+
requirements:
|
32
|
+
- - ! '>='
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0'
|
35
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
36
|
+
none: false
|
37
|
+
requirements:
|
38
|
+
- - ! '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
requirements:
|
42
|
+
- aws-cli
|
43
|
+
rubyforge_project:
|
44
|
+
rubygems_version: 1.8.23
|
45
|
+
signing_key:
|
46
|
+
specification_version: 3
|
47
|
+
summary: Run AWS commands with MFA
|
48
|
+
test_files: []
|