fizx-aws-keychain-util 0.0.9

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 27ea92631c903ab8bea76497923363e481dc0fb2
4
+ data.tar.gz: a73afa0e9a6a2db23ce668cfcad199b796a3bbe9
5
+ SHA512:
6
+ metadata.gz: 1d61a6d2e2d65be497ea5f1d5a2227bce85791db4aa26f5a10254f402d747b11098dbf131e8e0712084349d5e38579fa015b309030590bfc5529cc25da446277
7
+ data.tar.gz: 28a1ab76661c941f4c64c89ef0f90658e6aac27ba9387fdedc221c347abdadbc265765afa745ac6f0fb8cfd53a90e977ecd88d595d6cd2a60fc4d98f22544546
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in aws-keychain-util.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Zach Wily
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,106 @@
1
+ # AWS Keychain Util
2
+
3
+ This gem provides a small command line utility that helps
4
+ manage AWS credentials in an OS X keychain, keeping them out
5
+ of your dotfiles.
6
+
7
+ This will create a keychain for you which automatically locks
8
+ after 5 minutes and on sleep, for some extra security for your
9
+ precious AWS secrets.
10
+
11
+ Once you've added your credentials, you can start a shell with
12
+ the credentials in the environment.
13
+
14
+ ## Installation
15
+
16
+ To install:
17
+
18
+ gem install aws-keychain-util
19
+
20
+ ## Usage
21
+
22
+ To create your keychain:
23
+
24
+ $ aws-creds init
25
+
26
+ Here you can choose a name for your new keychain, or use the
27
+ default 'aws'.
28
+
29
+ To add an item to your aws keychain:
30
+
31
+ $ aws-creds add
32
+
33
+ This will prompt for a friendly name, the access key id,
34
+ and the secret access key. This also prompts for an optional
35
+ MFA arn, which is necessary if you're going to use multifactor
36
+ auth with AWS.
37
+
38
+ To list items in the keychain:
39
+
40
+ $ aws-creds ls
41
+
42
+ To show some saved credentials:
43
+
44
+ $ aws-creds cat <name>
45
+
46
+ To start a shell with `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`
47
+ set in the environment:
48
+
49
+ $ aws-creds shell <name>
50
+
51
+ To emit the (bourne shell style) environment variable exports that
52
+ you can source into your shell:
53
+
54
+ $ aws-creds env <name>
55
+
56
+ To always load the given environment in your shell, add the following to
57
+ your .bashrc or .zshrc
58
+
59
+ source `aws-creds env <name>`
60
+
61
+ To automatically grab AWS credentials from your keychain when using
62
+ the aws-sdk gem, add the following code:
63
+
64
+ AWS.config(:credential_provider => AwsKeychainUtil::CredentialProvider.new('<name>', 'keychain name'))
65
+
66
+ ## AWS Multi-Factor Authentication (MFA)
67
+
68
+ To increase AWS security, it's possible to use MFA (multi-factor) authentication with the amazon APIs.
69
+ Managing temporary credentials is a serious challenge, as by definition the credentials expire after a
70
+ fixed period of time.
71
+
72
+ You then need to associate a multifactor authentication device with the IAM user.
73
+ [Amazon Directions for MFA Setup](http://docs.aws.amazon.com/IAM/latest/UserGuide/GenerateMFAConfig.html)
74
+
75
+ Configuring MFA into your IAM policies for API access is a complex process, the
76
+ documentation for which is [Here](http://docs.aws.amazon.com/IAM/latest/UserGuide/MFAProtectedAPI.html#ExampleMFAforResource).
77
+
78
+ In order to do a multifactor authentication, you need to run:
79
+
80
+ $ aws-creds mfa <name> <code>
81
+
82
+ Where `<code>` is the numeric code on your multifactor auth device. Then you just need to either open a
83
+ fresh shell for the `<name>` key or re-source your environment.
84
+
85
+ The tool also tracks mfa expiration, and automatically removes expired tokens when you open a new shell
86
+ or source your env.
87
+
88
+
89
+ ## Security
90
+
91
+ Unfortunately, when Keychain whitelists either the `aws-creds` script
92
+ or a ruby application that uses the CredentialProvider for aws-sdk,
93
+ it whitelists `ruby` as a whole. This means *any* ruby script will
94
+ be able to access your AWS credentials. We recommend that you either
95
+ do not whitelist your script at all (don't click "Always Allow"), or
96
+ use a dedicated keychain with an auto-lock interval of less than five
97
+ minutes. Keychains created with `aws-creds` will automatically be
98
+ configured to auto-lock at 5 minutes.
99
+
100
+ ## Contributing
101
+
102
+ 1. Fork it
103
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
104
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
105
+ 4. Push to the branch (`git push origin my-new-feature`)
106
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/aws-creds ADDED
@@ -0,0 +1,209 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ if $0 == "bin/aws-creds"
4
+ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__)) + "/../lib")
5
+ end
6
+
7
+ require 'rubygems'
8
+ require 'highline'
9
+ require 'keychain'
10
+ require 'json'
11
+ require 'aws-keychain-util'
12
+ require 'aws'
13
+
14
+ def ask(question)
15
+ HighLine.new.ask(question)
16
+ end
17
+
18
+ def ask_secure(question)
19
+ HighLine.new.ask(question) { |q| q.echo = '*' }
20
+ end
21
+
22
+ def load_keychain
23
+ keychain = AwsKeychainUtil.load_keychain
24
+ if keychain && keychain.lock_interval > 300
25
+ $stderr.puts "Your keychain is *not* set to lock automatically in under five minutes. This could be dangerous."
26
+ if !File.exist? AwsKeychainUtil::PREFS_FILE
27
+ $stderr.puts "You should probably run `#{$0} init` to create a new, secure keychain."
28
+ end
29
+ end
30
+ keychain
31
+ end
32
+
33
+ def get_item(name)
34
+ load_keychain.generic_passwords.where(:label => name).first
35
+ end
36
+
37
+ def get_name_from_args_for_command(command)
38
+ if ARGV.length < 1
39
+ puts "Usage: #{$0} #{command} <name>"
40
+ exit 1
41
+ end
42
+ ARGV.shift
43
+ end
44
+
45
+ def get_item_from_args_for_command(command)
46
+ name = get_name_from_args_for_command(command)
47
+ item_mfa = get_item("#{name} mfa")
48
+ item_token = get_item("#{name} token")
49
+ if item_mfa
50
+ if item_mfa.attributes[:comment]
51
+ expires_at = Time.at(item_mfa.attributes[:comment].to_i)
52
+ if expires_at < Time.now
53
+ puts "# Removing expired STS credentials"
54
+ item_token.delete
55
+ item_token = nil
56
+ item_mfa.delete
57
+ item_mfa = nil
58
+ end
59
+ end
60
+ end
61
+ if item_mfa and item_token
62
+ puts "# Using temporary STS credentials"
63
+ return item_mfa, item_token
64
+ end
65
+
66
+ item = get_item(name)
67
+ unless item
68
+ puts "Could not find item with name #{name}"
69
+ exit 1
70
+ end
71
+ return item, nil
72
+ end
73
+
74
+ command = ARGV.shift
75
+
76
+ case command
77
+ when 'init'
78
+ if File.exist? AwsKeychainUtil::PREFS_FILE
79
+ puts "#{AwsKeychainUtil::PREFS_FILE} already exists. Please remove it to run init again."
80
+ exit 1
81
+ end
82
+
83
+ name = ask("Name for AWS keychain (default: 'aws'): ")
84
+ name = "aws" if name == ""
85
+
86
+ puts "The OS will now ask you for a password to protect your keychain. Choose wisely."
87
+ keychain = Keychain.create(name)
88
+ keychain.lock_interval = 300
89
+ keychain.lock_on_sleep = true
90
+
91
+ $prefs = { 'aws_keychain_name' => name }
92
+ File.new(AwsKeychainUtil::PREFS_FILE, "w").write JSON.dump($prefs)
93
+
94
+ puts "Your AWS keychain has been created and configured to auto-lock after"
95
+ puts "5 minutes, and when sleeping. You can change those options in"
96
+ puts "Keychain Access."
97
+ puts
98
+ puts "You can now add accounts to the keychain with:"
99
+ puts " #{$0} add"
100
+
101
+ when 'ls'
102
+ keychain = load_keychain
103
+ keychain.generic_passwords.all.sort {|a,b|
104
+ a.attributes[:label] <=> b.attributes[:label]
105
+ }.each do |item|
106
+ puts " #{item.attributes[:label]}"
107
+ end
108
+
109
+ when 'add'
110
+ keychain = load_keychain
111
+ name = ask(" account name: ")
112
+ account = ask(" access key id: ")
113
+ password = ask_secure(" secret_access_key: ")
114
+ arn = ask(" mfa arn: ")
115
+
116
+ item = keychain.generic_passwords.create(
117
+ :label => name,
118
+ :account => account,
119
+ :password => password,
120
+ :comment => arn
121
+ )
122
+
123
+ when 'cat'
124
+ item, token = get_item_from_args_for_command('cat')
125
+ puts "AWS_ACCESS_KEY_ID=#{item.attributes[:account]}"
126
+ puts "AWS_SECRET_ACCESS_KEY=#{item.password}"
127
+ if token
128
+ puts "AWS_SECURITY_TOKEN=#{token.password}"
129
+ end
130
+
131
+ when 'rm'
132
+ item, token = get_item_from_args_for_command('rm')
133
+ item.delete
134
+
135
+ when 'mfa'
136
+ keychain = load_keychain
137
+ item_name = ARGV.shift
138
+ code = ARGV.shift
139
+ if not item_name or not code
140
+ puts "Usage: aws-creds mfa <item-name> <mfa-code>"
141
+ exit(1)
142
+ end
143
+
144
+ sts_item = get_item("#{item_name} mfa")
145
+ sts_token = get_item("#{item_name} token")
146
+ if sts_item
147
+ puts "Removing existing STS credentials"
148
+ sts_item.delete
149
+ sts_token.delete if sts_token
150
+ end
151
+
152
+ item, token = get_item(item_name)
153
+ sts = AWS::STS.new(:access_key_id => item.attributes[:account], :secret_access_key => item.password)
154
+ begin
155
+ response = sts.new_session(:duration => (60 * 60 * 12), :serial_number => item.attributes[:comment], :token_code => code)
156
+ temp_item = keychain.generic_passwords.create(:label => "#{item_name} mfa",
157
+ :account => response.credentials[:access_key_id],
158
+ :password=> response.credentials[:secret_access_key],
159
+ :comment => response.expires_at.to_i.to_s)
160
+ temp_token = keychain.generic_passwords.create(:label => "#{item_name} token",
161
+ :account => "#{response.credentials[:access_key_id]}_token",
162
+ :password=> response.credentials[:session_token],
163
+ :comment => response.expires_at.to_i.to_s)
164
+
165
+ puts "MultiFactorAuthentication succeeded, expiration is #{response.expires_at}"
166
+ rescue AWS::STS::Errors::AccessDenied => e
167
+ puts e.to_s
168
+ end
169
+
170
+ when 'env'
171
+ item, token = get_item_from_args_for_command('shell')
172
+ puts "export AWS_ACCESS_KEY_ID=\"#{item.attributes[:account]}\""
173
+ puts "export AWS_ACCESS_KEY=\"#{item.attributes[:account]}\""
174
+ puts "export AWS_SECRET_ACCESS_KEY=\"#{item.password}\""
175
+ puts "export AWS_SECRET_KEY=\"#{item.password}\""
176
+ puts "export RPROMPT=\"(aws #{item.attributes[:label]})\""
177
+ puts "export AWS_CREDS_NAME=\"#{item.attributes[:label]}\""
178
+ if token
179
+ puts "export AWS_SECURITY_TOKEN=\"#{token.password}\""
180
+ end
181
+
182
+ when 'shell'
183
+ if ENV['AWS_CREDS_NAME']
184
+ puts "Already in aws-creds shell (AWS_CREDS_NAME is #{ENV['AWS_CREDS_NAME']})"
185
+ exit 1
186
+ end
187
+
188
+ item, token = get_item_from_args_for_command('shell')
189
+ aws_env = {}
190
+ aws_env['AWS_ACCESS_KEY_ID'] = aws_env['AWS_ACCESS_KEY'] = item.attributes[:account]
191
+ aws_env['AWS_SECRET_ACCESS_KEY'] = aws_env['AWS_SECRET_KEY'] = item.password
192
+ aws_env['AWS_CREDS_NAME'] = item.attributes[:label]
193
+ if token
194
+ aws_env['AWS_SECURITY_TOKEN'] = token.password
195
+ end
196
+
197
+ if ARGV.empty?
198
+ aws_env['RPROMPT'] = "(aws #{item.attributes[:label]})" # zsh only
199
+
200
+ exec(aws_env, ENV['SHELL'])
201
+ else
202
+ exec(aws_env, *ARGV)
203
+ end
204
+
205
+ else
206
+ puts "Usage: #{$0} <command> <arguments>"
207
+ puts " Commands: init, ls, add, cat, env, mfa, rm, shell"
208
+ end
209
+
@@ -0,0 +1,22 @@
1
+ # -*- encoding: 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 |gem|
6
+ gem.name = "fizx-aws-keychain-util"
7
+ gem.version = '0.0.9'
8
+ gem.authors = ["Zach Wily"]
9
+ gem.email = ["zach@zwily.com"]
10
+ gem.description = %q{Helps manage a keychain of AWS credentials on OS X.}
11
+ gem.summary = %q{Helps manage a keychain of AWS credentials on OS X.}
12
+ gem.homepage = ""
13
+
14
+ gem.files = `git ls-files`.split($/)
15
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
16
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
17
+ gem.require_paths = ["lib"]
18
+
19
+ gem.add_dependency('ruby-keychain')
20
+ gem.add_dependency('highline')
21
+ gem.add_dependency('aws-sdk')
22
+ end
@@ -0,0 +1,25 @@
1
+ require 'aws-sdk'
2
+ require 'keychain'
3
+ require 'aws-keychain-util'
4
+
5
+ module AwsKeychainUtil
6
+ class CredentialProvider
7
+ include AWS::Core::CredentialProviders::Provider
8
+
9
+ attr_reader :item, :keychain
10
+
11
+ def initialize(item = 'AWS', keychain = nil)
12
+ @item, @keychain = item, keychain
13
+ end
14
+
15
+ def get_credentials
16
+ keychain = @keychain ? Keychain.open(@keychain) : AwsKeychainUtil.load_keychain
17
+ item = keychain.generic_passwords.where(:label => @item).first
18
+ return {} unless item
19
+ {
20
+ access_key_id: item.attributes[:account],
21
+ secret_access_key: item.password
22
+ }
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,13 @@
1
+ module AwsKeychainUtil
2
+ PREFS_FILE = File.expand_path "~/.aws-keychain-util"
3
+
4
+ def self.load_keychain
5
+ keychain = if File.exist? PREFS_FILE
6
+ prefs = JSON.parse(File.read(PREFS_FILE))
7
+ Keychain.open(prefs['aws_keychain_name'])
8
+ else
9
+ Keychain.default
10
+ end
11
+ keychain
12
+ end
13
+ end
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fizx-aws-keychain-util
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.9
5
+ platform: ruby
6
+ authors:
7
+ - Zach Wily
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-07-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ruby-keychain
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: highline
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: aws-sdk
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: Helps manage a keychain of AWS credentials on OS X.
56
+ email:
57
+ - zach@zwily.com
58
+ executables:
59
+ - aws-creds
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - ".gitignore"
64
+ - Gemfile
65
+ - LICENSE.txt
66
+ - README.md
67
+ - Rakefile
68
+ - bin/aws-creds
69
+ - fizx-aws-keychain-util.gemspec
70
+ - lib/aws-keychain-util.rb
71
+ - lib/aws-keychain-util/credential_provider.rb
72
+ homepage: ''
73
+ licenses: []
74
+ metadata: {}
75
+ post_install_message:
76
+ rdoc_options: []
77
+ require_paths:
78
+ - lib
79
+ required_ruby_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ required_rubygems_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ requirements: []
90
+ rubyforge_project:
91
+ rubygems_version: 2.2.2
92
+ signing_key:
93
+ specification_version: 4
94
+ summary: Helps manage a keychain of AWS credentials on OS X.
95
+ test_files: []
96
+ has_rdoc: