mirror of
https://github.com/fog/fog.git
synced 2022-11-09 13:51:43 -05:00
Merge pull request #1854 from kevinykchan/ssh-agent
[joyent|compute] support for http-signature-auth using ssh-agent -- Closes #1854
This commit is contained in:
commit
558db911f1
5 changed files with 46 additions and 33 deletions
|
@ -1,6 +1,7 @@
|
|||
require 'fog/joyent'
|
||||
require 'fog/joyent/errors'
|
||||
require 'fog/compute'
|
||||
require 'net/ssh'
|
||||
|
||||
module Fog
|
||||
module Compute
|
||||
|
@ -9,8 +10,10 @@ module Fog
|
|||
|
||||
recognizes :joyent_password
|
||||
recognizes :joyent_url
|
||||
|
||||
recognizes :joyent_keyname
|
||||
recognizes :joyent_keyfile
|
||||
recognizes :joyent_keyphrase
|
||||
recognizes :joyent_version
|
||||
|
||||
model_path 'fog/joyent/models/compute'
|
||||
|
@ -100,12 +103,12 @@ module Fog
|
|||
|
||||
class Real
|
||||
def initialize(options = {})
|
||||
|
||||
@connection_options = options[:connection_options] || {}
|
||||
@persistent = options[:persistent] || false
|
||||
|
||||
@joyent_url = options[:joyent_url] || 'https://us-sw-1.api.joyentcloud.com'
|
||||
@joyent_version = options[:joyent_version] || '~6.5'
|
||||
|
||||
@joyent_username = options[:joyent_username]
|
||||
|
||||
unless @joyent_username
|
||||
|
@ -115,16 +118,15 @@ module Fog
|
|||
if options[:joyent_keyname] && options[:joyent_keyfile]
|
||||
if File.exists?(options[:joyent_keyfile])
|
||||
@joyent_keyname = options[:joyent_keyname]
|
||||
@joyent_key = File.read(options[:joyent_keyfile])
|
||||
@joyent_keyfile = options[:joyent_keyfile]
|
||||
@joyent_keyphrase = options[:joyent_keyphrase]
|
||||
|
||||
if @joyent_key.lines.first.include?('-----BEGIN DSA PRIVATE KEY-----')
|
||||
@key = OpenSSL::PKey::DSA.new(@joyent_key)
|
||||
elsif @joyent_key.lines.first.include?('-----BEGIN RSA PRIVATE KEY-----')
|
||||
@key = OpenSSL::PKey::RSA.new(@joyent_key)
|
||||
else
|
||||
raise ArgumentError, "options[joyent_keyfile] provided must be an RSA or DSA private key"
|
||||
end
|
||||
@key_manager = Net::SSH::Authentication::KeyManager.new(nil, {
|
||||
:keys_only => true,
|
||||
:passphrase => @joyent_keyphrase
|
||||
})
|
||||
|
||||
@key_manager.add(@joyent_keyfile)
|
||||
|
||||
@header_method = method(:header_for_signature_auth)
|
||||
else
|
||||
|
@ -133,7 +135,6 @@ module Fog
|
|||
|
||||
elsif options[:joyent_password]
|
||||
@joyent_password = options[:joyent_password]
|
||||
|
||||
@header_method = method(:header_for_basic_auth)
|
||||
else
|
||||
raise ArgumentError, "Must provide either a joyent_password or joyent_keyname and joyent_keyfile pair"
|
||||
|
@ -146,25 +147,23 @@ module Fog
|
|||
)
|
||||
end
|
||||
|
||||
def request(request = {})
|
||||
request[:headers] = {
|
||||
def request(opts = {})
|
||||
opts[:headers] = {
|
||||
"X-Api-Version" => @joyent_version,
|
||||
"Content-Type" => "application/json",
|
||||
"Accept" => "application/json"
|
||||
}.merge(request[:headers] || {}).merge(@header_method.call)
|
||||
}.merge(opts[:headers] || {}).merge(@header_method.call)
|
||||
|
||||
if request[:body]
|
||||
request[:body] = Fog::JSON.encode(request[:body])
|
||||
if opts[:body]
|
||||
opts[:body] = Fog::JSON.encode(opts[:body])
|
||||
end
|
||||
|
||||
response = @connection.request(request)
|
||||
|
||||
response = @connection.request(opts)
|
||||
if response.headers["Content-Type"] == "application/json"
|
||||
response.body = json_decode(response.body)
|
||||
end
|
||||
|
||||
raise_if_error!(request, response)
|
||||
|
||||
response
|
||||
rescue Excon::Errors::Error => e
|
||||
raise_if_error!(e.request, e.response)
|
||||
|
@ -185,22 +184,34 @@ module Fog
|
|||
|
||||
def header_for_signature_auth
|
||||
date = Time.now.utc.httpdate
|
||||
begin
|
||||
signature = Base64.encode64(@key.sign("sha256", date)).delete("\r\n")
|
||||
rescue OpenSSL::PKey::PKeyError => e
|
||||
if e.message == 'wrong public key type'
|
||||
puts 'ERROR: Your version of ruby/openssl does not suport DSA key signing'
|
||||
puts 'see: http://bugs.ruby-lang.org/issues/4734'
|
||||
puts 'workaround: Please use an RSA key instead'
|
||||
end
|
||||
raise
|
||||
|
||||
# Force KeyManager to load the key(s)
|
||||
@key_manager.each_identity {}
|
||||
|
||||
key = @key_manager.known_identities.keys.first
|
||||
|
||||
sig = if key.kind_of? OpenSSL::PKey::RSA
|
||||
@key_manager.sign(key, date)[15..-1]
|
||||
else
|
||||
key = OpenSSL::PKey::DSA.new(File.read(@joyent_keyfile), @joyent_keyphrase)
|
||||
key.sign('sha1', date)
|
||||
end
|
||||
|
||||
key_id = "/#{@joyent_username}/keys/#{@joyent_keyname}"
|
||||
key_type = key.class.to_s.split('::').last.downcase.to_sym
|
||||
|
||||
unless [:rsa, :dsa].include? key_type
|
||||
raise Joyent::Errors::Unauthorized.new('Invalid key type -- only rsa or dsa key is supported')
|
||||
end
|
||||
|
||||
signature = Base64.encode64(sig).delete("\r\n")
|
||||
|
||||
{
|
||||
"Date" => date,
|
||||
"Authorization" => "Signature keyId=\"#{key_id}\",algorithm=\"rsa-sha256\" #{signature}"
|
||||
"Authorization" => "Signature keyId=\"#{key_id}\",algorithm=\"#{key_type}-sha1\" #{signature}"
|
||||
}
|
||||
rescue Net::SSH::Authentication::KeyManagerError => e
|
||||
raise Joyent::Errors::Unauthorized.new('SSH Signing Error: :#{e.message}', e)
|
||||
end
|
||||
|
||||
def decode_time_attrs(obj)
|
||||
|
|
|
@ -20,9 +20,9 @@ module Fog
|
|||
# https://us-west-1.api.joyentcloud.com/docs#cloudapi-http-responses
|
||||
#
|
||||
# HTTP Status Codes
|
||||
#
|
||||
#
|
||||
# Your client should check for each of the following status codes from any API request:
|
||||
#
|
||||
#
|
||||
# Response Code Description
|
||||
|
||||
# 400 Bad Request Invalid HTTP Request
|
||||
|
|
|
@ -26,7 +26,9 @@ module Fog
|
|||
|
||||
def get(machine_id)
|
||||
data = service.get_machine(machine_id).body
|
||||
new(data)
|
||||
server = new(data)
|
||||
server.tags = server.list_tags if server.tags.nil?
|
||||
server
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -6,7 +6,7 @@ module Fog
|
|||
request(
|
||||
:path => "/my/machines/#{machine_id}",
|
||||
:method => "DELETE",
|
||||
:expects => [200, 204]
|
||||
:expects => [200, 204, 410]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -20,7 +20,7 @@ module Fog
|
|||
request(
|
||||
:method => "GET",
|
||||
:path => "/my/machines/#{uuid}",
|
||||
:expects => 200
|
||||
:expects => [200, 410]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue