awsudo 1.0.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 +7 -0
- data/CHANGELOG.md +0 -0
- data/CONTRIBUTING.md +20 -0
- data/LICENSE +25 -0
- data/README.md +79 -0
- data/bin/aws-agent +62 -0
- data/bin/awsudo +34 -0
- data/lib/awsudo.rb +101 -0
- metadata +72 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8e4b45a3d628800167e5024b822807b35157daa3
|
4
|
+
data.tar.gz: 6d4ba8e55a5795fa72b89f1c4e4d9aa4212a3da5
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 18fe20ae36c9fb52041fa179880bba1131ba220f87d50955f4befc8d1a6bc3d03b7bda06e7f353613e2e39c2a47326196f7d0bf8f82eead9eaa94d24e3d15271
|
7
|
+
data.tar.gz: e1d3e25b4cd51d0494a99f0fd772f499704deefb3f25340a89fa8e28e2a17823450e823a267b78b385b349696a356d0fe46dfecc385dd1ee119126c55db82686
|
data/CHANGELOG.md
ADDED
File without changes
|
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
How to contribute
|
2
|
+
=================
|
3
|
+
Thanks for your interest!
|
4
|
+
|
5
|
+
Contributing is easy. After signing the [Contributor License Agreement (CLA)](https://ea.tap.thinksmart.com/prod/Portal/ShowWorkFlow/AnonymousEmbed/26adfdf8-b74e-4212-bb4a-3e756b722c32) just follow the steps described below.
|
6
|
+
|
7
|
+
Getting Started
|
8
|
+
---------------
|
9
|
+
* Make sure you have a [GitHub account](https://github.com/signup/free)
|
10
|
+
* Fork the repository on GitHub
|
11
|
+
|
12
|
+
Making changes
|
13
|
+
---------------
|
14
|
+
* Create a topic branch from where you want to base your work.
|
15
|
+
* Make commits of logical units, adding messages for describing what was done.
|
16
|
+
|
17
|
+
Submitting changes
|
18
|
+
------------------
|
19
|
+
* Push your changes to a topic branch in your fork of the repository.
|
20
|
+
* Submit a pull request to the repository.
|
data/LICENSE
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
Copyright (C) 2015 Electronic Arts Inc. All rights reserved.
|
2
|
+
|
3
|
+
Redistribution and use in source and binary forms, with or without
|
4
|
+
modification, are permitted provided that the following conditions
|
5
|
+
are met:
|
6
|
+
|
7
|
+
1. Redistributions of source code must retain the above copyright
|
8
|
+
notice, this list of conditions and the following disclaimer.
|
9
|
+
2. Redistributions in binary form must reproduce the above copyright
|
10
|
+
notice, this list of conditions and the following disclaimer in the
|
11
|
+
documentation and/or other materials provided with the distribution.
|
12
|
+
3. Neither the name of Electronic Arts, Inc. ("EA") nor the names of
|
13
|
+
its contributors may be used to endorse or promote products derived
|
14
|
+
from this software without specific prior written permission.
|
15
|
+
|
16
|
+
THIS SOFTWARE IS PROVIDED BY ELECTRONIC ARTS AND ITS CONTRIBUTORS "AS IS" AND ANY
|
17
|
+
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
18
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
19
|
+
DISCLAIMED. IN NO EVENT SHALL ELECTRONIC ARTS OR ITS CONTRIBUTORS BE LIABLE FOR ANY
|
20
|
+
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
21
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
22
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
23
|
+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
24
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
25
|
+
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
awsudo + aws-agent
|
2
|
+
==================
|
3
|
+
|
4
|
+
Overview
|
5
|
+
------------
|
6
|
+
|
7
|
+
**awsudo** enables users to execute commands that make API calls to AWS under
|
8
|
+
the security context of an IAM role. The IAM role is assumed only upon
|
9
|
+
successful authentication against a SAML compliant federation service.
|
10
|
+
|
11
|
+
**aws-agent** enables users to authenticate against a SAML compliant federation
|
12
|
+
service once, after which aws-agent provides temporary credentials to awsudo
|
13
|
+
to use.
|
14
|
+
|
15
|
+
Synopsis
|
16
|
+
------------
|
17
|
+
|
18
|
+
awsudo {role-name | role-arn} command
|
19
|
+
|
20
|
+
aws-agent
|
21
|
+
|
22
|
+
Requirements
|
23
|
+
------------
|
24
|
+
|
25
|
+
* UNIX, UNIX-like or GNU/Linux operating system
|
26
|
+
* SAML compliant federation service
|
27
|
+
* ruby 1.9 or above
|
28
|
+
* ruby gems: aws-sdk
|
29
|
+
|
30
|
+
Install
|
31
|
+
------------
|
32
|
+
|
33
|
+
sudo gem install awsudo
|
34
|
+
|
35
|
+
Configuration
|
36
|
+
------------
|
37
|
+
|
38
|
+
awsudo and aws-agent expect a configuration file named .awsudo in your home directory
|
39
|
+
containing the values for your identity provider login url and the SAML provider name
|
40
|
+
configured in AWS. This is an example, your setup may vary:
|
41
|
+
|
42
|
+
IDP_LOGIN_URL = https://sts.example.com/adfs/ls/IdpInitiatedSignOn.aspx?loginToRp=urn:amazon:webservices
|
43
|
+
SAML_PROVIDER_NAME = ADFS
|
44
|
+
|
45
|
+
In addition to .awsudo, you can create .aws-roles in your home directory to map
|
46
|
+
IAM roles ARNs to more easy to remember alias names, one per line, separated by spaces. Example:
|
47
|
+
|
48
|
+
myaccount-admin arn:aws:iam::123456789012:role/myaccount-admin
|
49
|
+
|
50
|
+
Examples
|
51
|
+
------------
|
52
|
+
|
53
|
+
### awsudo
|
54
|
+
|
55
|
+
$ awsudo arn:aws:iam::123456789012:role/myaccount-admin aws ec2 describe-tags --region us-west-2
|
56
|
+
|
57
|
+
$ awsudo myaccount-admin aws ec2 describe-instances --region us-east-1
|
58
|
+
|
59
|
+
awsudo will ask your federated credentials every time. To avoid this use aws-agent as follows:
|
60
|
+
|
61
|
+
### aws-agent
|
62
|
+
|
63
|
+
$ aws-agent
|
64
|
+
Login: username
|
65
|
+
Password:
|
66
|
+
AWS_AUTH_SOCK=/var/folders/xz/lx178g0d0rb36x95446zwgd80000gp/T/aws-20150623-20990-58v1c4/agent; export AWS_AUTH_SOCK;
|
67
|
+
|
68
|
+
then execute the commands printed by aws-agent. awsudo will now ask for temporary credentials to aws-agent.
|
69
|
+
|
70
|
+
Author
|
71
|
+
-------
|
72
|
+
|
73
|
+
[Gerardo Santana Gomez Garrido](https://github.com/santana)
|
74
|
+
|
75
|
+
Contributors
|
76
|
+
-------------
|
77
|
+
* [Matthew Wygant](https://github.com/mkwygant)
|
78
|
+
* [Ivan Zenteno](https://github.com/k001)
|
79
|
+
* [David Hannon](https://github.com/dhannon)
|
data/bin/aws-agent
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Copyright (C) 2015 Electronic Arts Inc. All rights reserved.
|
3
|
+
|
4
|
+
require 'awsudo'
|
5
|
+
require 'logger'
|
6
|
+
require 'socket'
|
7
|
+
require 'tmpdir'
|
8
|
+
|
9
|
+
def usage
|
10
|
+
warn <<EOS
|
11
|
+
Usage:
|
12
|
+
|
13
|
+
#{File.basename $0}
|
14
|
+
EOS
|
15
|
+
exit 1
|
16
|
+
end
|
17
|
+
|
18
|
+
config = Hash[*File.read(File.join(ENV['HOME'], '.awsudo')).
|
19
|
+
scan(/^(\w+)\s*=\s*(.*)$/).flatten]
|
20
|
+
AWSUDO.config(config['IDP_LOGIN_URL'], config['SAML_PROVIDER_NAME'])
|
21
|
+
|
22
|
+
LOGFILE = File.join(ENV['HOME'], ".aws-agent.log")
|
23
|
+
logger = Logger.new(LOGFILE, "weekly")
|
24
|
+
logger.progname = "aws-agent"
|
25
|
+
logger.level = Logger::WARN
|
26
|
+
|
27
|
+
username, password = AWSUDO.get_federated_credentials
|
28
|
+
|
29
|
+
socket_dir = Dir.mktmpdir("aws-")
|
30
|
+
socket_name = File.join(socket_dir, "agent")
|
31
|
+
puts "AWS_AUTH_SOCK=#{socket_name}; export AWS_AUTH_SOCK;"
|
32
|
+
Process.daemon
|
33
|
+
$0 = 'aws-agent'
|
34
|
+
Process.setrlimit(Process::RLIMIT_CORE, 0, 0)
|
35
|
+
UNIXServer.open(socket_name) do |socket|
|
36
|
+
loop do
|
37
|
+
Thread.new(socket.accept) do |client|
|
38
|
+
logger.debug "thread started"
|
39
|
+
logger.debug "connection accepted: #{socket.inspect}"
|
40
|
+
begin
|
41
|
+
role = client.gets.strip
|
42
|
+
logger.debug "role received: #{role}"
|
43
|
+
role_arn = AWSUDO.resolve_role(role)
|
44
|
+
logger.debug "role ARN resolved: #{role_arn}"
|
45
|
+
saml_assertion = AWSUDO.get_saml_assertion(username, password)
|
46
|
+
credentials = AWSUDO.assume_role_with_saml(saml_assertion, role_arn)
|
47
|
+
client.puts credentials.to_h.to_json
|
48
|
+
rescue => e
|
49
|
+
logger.error e
|
50
|
+
error = {:error => e}.to_json
|
51
|
+
client.print error
|
52
|
+
ensure
|
53
|
+
logger.debug "Closing connection"
|
54
|
+
client.close
|
55
|
+
logger.debug "Connection closed"
|
56
|
+
end
|
57
|
+
logger.debug "Thread ending"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
FileUtils.rmdir socket_dir
|
data/bin/awsudo
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Copyright (C) 2015 Electronic Arts Inc. All rights reserved.
|
3
|
+
|
4
|
+
require 'awsudo'
|
5
|
+
|
6
|
+
def usage
|
7
|
+
warn <<-EOS
|
8
|
+
Usage:
|
9
|
+
|
10
|
+
#{File.basename $0} {role-name | role-arn} command
|
11
|
+
EOS
|
12
|
+
exit 1
|
13
|
+
end
|
14
|
+
|
15
|
+
usage if ARGV.size < 2
|
16
|
+
|
17
|
+
config = Hash[*File.read(File.join(ENV['HOME'], '.awsudo')).
|
18
|
+
scan(/^(\w+)\s*=\s*(.*)$/).flatten]
|
19
|
+
AWSUDO.config(config['IDP_LOGIN_URL'], config['SAML_PROVIDER_NAME'])
|
20
|
+
|
21
|
+
role = ARGV.shift
|
22
|
+
credentials =
|
23
|
+
begin
|
24
|
+
AWSUDO.assume_role(role)
|
25
|
+
rescue => e
|
26
|
+
warn e
|
27
|
+
exit 2
|
28
|
+
end
|
29
|
+
|
30
|
+
ENV['AWS_ACCESS_KEY_ID'] = credentials['access_key_id'] || credentials[:access_key_id]
|
31
|
+
ENV['AWS_SECRET_ACCESS_KEY'] = credentials['secret_access_key'] || credentials[:secret_access_key]
|
32
|
+
ENV['AWS_SESSION_TOKEN'] = credentials['session_token'] || credentials[:session_token]
|
33
|
+
|
34
|
+
exec *ARGV if ARGV.size > 0
|
data/lib/awsudo.rb
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
# Copyright (C) 2015 Electronic Arts Inc. All rights reserved.
|
2
|
+
|
3
|
+
require 'aws-sdk'
|
4
|
+
require 'io/console'
|
5
|
+
require 'json'
|
6
|
+
require 'net/http'
|
7
|
+
require 'net/https'
|
8
|
+
require 'rexml/document'
|
9
|
+
require 'socket'
|
10
|
+
require 'uri'
|
11
|
+
|
12
|
+
module AWSUDO
|
13
|
+
AWS_ROLES = File.join(ENV['HOME'], '.aws-roles')
|
14
|
+
|
15
|
+
class << self
|
16
|
+
attr_reader :idp_login_url, :saml_provider_name
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.config(idp_login_url, saml_provider_name)
|
20
|
+
@idp_login_url = idp_login_url
|
21
|
+
@saml_provider_name = saml_provider_name
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.get_federated_credentials
|
25
|
+
fd = IO.sysopen("/dev/tty", "w")
|
26
|
+
console = IO.new(fd,"w")
|
27
|
+
console.print "Login: "
|
28
|
+
username = STDIN.gets.chomp
|
29
|
+
console.print "Password: "
|
30
|
+
password = STDIN.noecho(&:gets).chomp
|
31
|
+
console.print "\n"
|
32
|
+
[username, password]
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.assume_role_using_agent(role)
|
36
|
+
socket_name = ENV['AWS_AUTH_SOCK']
|
37
|
+
credentials = UNIXSocket.open(socket_name) do |client|
|
38
|
+
client.puts role
|
39
|
+
response = client.gets
|
40
|
+
raise "Connection closed by peer" if response.nil?
|
41
|
+
JSON.parse(response.strip)
|
42
|
+
end
|
43
|
+
|
44
|
+
raise credentials['error'] if credentials['error']
|
45
|
+
credentials
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.assume_role_using_password(role)
|
49
|
+
username, password = get_federated_credentials
|
50
|
+
saml_assertion = get_saml_assertion(username, password)
|
51
|
+
role_arn = resolve_role(role)
|
52
|
+
assume_role_with_saml(saml_assertion, role_arn).to_h
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.assume_role(role)
|
56
|
+
assume_role_using_agent(role) rescue assume_role_using_password(role)
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.get_saml_assertion(username, password)
|
60
|
+
uri = URI.parse(idp_login_url)
|
61
|
+
|
62
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
63
|
+
http.use_ssl = true
|
64
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
65
|
+
|
66
|
+
req = Net::HTTP::Post.new(uri.request_uri)
|
67
|
+
req.set_form_data({'username' => username, 'password' => password})
|
68
|
+
res = http.request(req)
|
69
|
+
|
70
|
+
raise "Authentication failed" if res['Location'].nil?
|
71
|
+
uri = URI.parse(res['Location'])
|
72
|
+
req = Net::HTTP::Get.new(uri.request_uri)
|
73
|
+
req['Cookie'] = res['Set-Cookie']
|
74
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
75
|
+
http.use_ssl = true
|
76
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
77
|
+
res = http.request(req)
|
78
|
+
|
79
|
+
doc = REXML::Document.new(res.body)
|
80
|
+
REXML::XPath.first(doc, '/html/body/form/input[@name = "SAMLResponse"]/@value').to_s
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.assume_role_with_saml(saml_assertion, role_arn)
|
84
|
+
principal_arn = "#{role_arn[/^arn:aws:iam::\d+:/]}saml-provider/#{saml_provider_name}"
|
85
|
+
sts = Aws::STS::Client.new(credentials: Aws::Credentials.new('a', 'b', 'c'), region: 'us-east-1')
|
86
|
+
sts.assume_role_with_saml(
|
87
|
+
role_arn: role_arn,
|
88
|
+
principal_arn: principal_arn,
|
89
|
+
saml_assertion: saml_assertion).credentials
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.resolve_role(role, roles_filename = AWS_ROLES)
|
93
|
+
return role if role =~ /^arn:aws:iam::\d+:role\/\S+$/
|
94
|
+
raise "`#{role}' is not a valid role" if role =~ /\s/
|
95
|
+
line = File.readlines(roles_filename).find {|line| line =~ /^#{role}\s+arn:aws:iam::\d+:role\/\S+\s*$/ }
|
96
|
+
raise "`#{role}' is not a valid role" if line.nil?
|
97
|
+
role_arn = line.split(/\s+/)[1]
|
98
|
+
raise "`#{role}' is not a valid role" if role_arn.nil?
|
99
|
+
role_arn
|
100
|
+
end
|
101
|
+
end
|
metadata
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: awsudo
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Gerardo Santana Gomez Garrido
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-02-12 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: aws-sdk
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2'
|
27
|
+
description: |
|
28
|
+
awsudo enables users to execute commands that make API calls to AWS under the
|
29
|
+
security context of an IAM role. The IAM role is assumed only upon successful
|
30
|
+
authentication against a SAML compliant federation service.
|
31
|
+
|
32
|
+
aws-agent enables users to authenticate against a SAML compliant federation
|
33
|
+
service once, after which aws-agent provides temporary credentials to awsudo to
|
34
|
+
use.
|
35
|
+
email: gsantana@ea.com
|
36
|
+
executables:
|
37
|
+
- awsudo
|
38
|
+
- aws-agent
|
39
|
+
extensions: []
|
40
|
+
extra_rdoc_files: []
|
41
|
+
files:
|
42
|
+
- bin/aws-agent
|
43
|
+
- bin/awsudo
|
44
|
+
- lib/awsudo.rb
|
45
|
+
- LICENSE
|
46
|
+
- CHANGELOG.md
|
47
|
+
- CONTRIBUTING.md
|
48
|
+
- README.md
|
49
|
+
homepage: https://github.com/electronicarts/awsudo
|
50
|
+
licenses: []
|
51
|
+
metadata: {}
|
52
|
+
post_install_message:
|
53
|
+
rdoc_options: []
|
54
|
+
require_paths:
|
55
|
+
- lib
|
56
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - '>='
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
|
+
requirements:
|
63
|
+
- - '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
requirements: []
|
67
|
+
rubyforge_project:
|
68
|
+
rubygems_version: 2.0.14
|
69
|
+
signing_key:
|
70
|
+
specification_version: 4
|
71
|
+
summary: executes a command with the permissions given by an AWS IAM role
|
72
|
+
test_files: []
|