awsudo 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|