mirror of
https://github.com/OpenNebula/one.git
synced 2025-03-22 18:50:08 +03:00
feature #795: Adapt EC2 to the new Cloud Auth system
This commit is contained in:
parent
f97ef6be7b
commit
afdf9d23c5
@ -24,5 +24,7 @@ PORT=4567
|
||||
# SSL proxy that serves the API (set if is being used)
|
||||
#SSL_SERVER=fqdm.of.the.server
|
||||
|
||||
AUTH=ec2
|
||||
|
||||
# VM types allowed and its template file (inside templates directory)
|
||||
VM_TYPE=[NAME=m1.small, TEMPLATE=m1.small.erb]
|
||||
|
@ -15,7 +15,6 @@
|
||||
#--------------------------------------------------------------------------- #
|
||||
|
||||
require 'rubygems'
|
||||
require 'sinatra'
|
||||
require 'erb'
|
||||
require 'time'
|
||||
require 'AWS'
|
||||
@ -62,118 +61,26 @@ class EC2QueryServer < CloudServer
|
||||
|
||||
###########################################################################
|
||||
|
||||
def initialize(config_file,template,views)
|
||||
super(config_file)
|
||||
@config.add_configuration_value("TEMPLATE_LOCATION",template)
|
||||
@config.add_configuration_value("VIEWS",views)
|
||||
|
||||
if @config[:ssl_server]
|
||||
@server_host=@config[:ssl_server]
|
||||
else
|
||||
@server_host=@config[:server]
|
||||
end
|
||||
|
||||
@server_port=@config[:port]
|
||||
|
||||
print_configuration
|
||||
def initialize(config)
|
||||
super(config)
|
||||
end
|
||||
|
||||
###########################################################################
|
||||
# Authentication functions
|
||||
###########################################################################
|
||||
|
||||
# EC2 protocol authentication function
|
||||
# params:: of the request
|
||||
# [return] true if authenticated
|
||||
def authenticate(params,env)
|
||||
|
||||
failed = 'Authentication failed. '
|
||||
|
||||
# For https, the web service should be set to include the user cert in the environment.
|
||||
cert_line = env['HTTP_SSL_CLIENT_CERT']
|
||||
cert_line = nil if cert_line == '(null)' # For Apache mod_ssl
|
||||
|
||||
if cert_line == nil
|
||||
# Use the secret key for authentication.
|
||||
|
||||
password = get_user_password(params['AWSAccessKeyId'])
|
||||
return nil if !password
|
||||
def authenticate(env, params)
|
||||
econe_host = @config[:ssl_server]
|
||||
econe_host ||= @config[:server]
|
||||
|
||||
signature = case params['SignatureVersion']
|
||||
when "1" then signature_version_1(params.clone, password)
|
||||
when "2" then signature_version_2(params,
|
||||
password,
|
||||
env,
|
||||
true,
|
||||
false)
|
||||
end
|
||||
econe_port = @config[:port]
|
||||
|
||||
if params['Signature']==signature
|
||||
return one_client_user(params['AWSAccessKeyId'], password)
|
||||
else
|
||||
if params['SignatureVersion']=="2"
|
||||
signature = signature_version_2(params,
|
||||
password,
|
||||
env,
|
||||
false,
|
||||
false)
|
||||
if params['Signature']==signature
|
||||
return one_client_user(params['AWSAccessKeyId'], password)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
else
|
||||
# Use the https credentials for authentication
|
||||
require 'server_auth'
|
||||
while cert_line
|
||||
begin
|
||||
cert_array=cert_line.scan(/([^\s]*)\s/)
|
||||
cert_array = cert_array[2..-3]
|
||||
cert_array.unshift('-----BEGIN CERTIFICATE-----')
|
||||
cert_array.push('-----END CERTIFICATE-----')
|
||||
cert_pem = cert_array.join("\n")
|
||||
cert = OpenSSL::X509::Certificate.new(cert_pem)
|
||||
rescue
|
||||
raise failed + "Could not create X509 certificate from " + cert_line
|
||||
end
|
||||
# Password should be DN with whitespace removed.
|
||||
subjectname = cert.subject.to_s.delete("\s")
|
||||
begin
|
||||
username = get_username(subjectname)
|
||||
rescue
|
||||
username = nil
|
||||
end
|
||||
break if username
|
||||
chain_dn = (!chain_dn ? "" : chain_dn) + "\n" + subjectname
|
||||
chain_index = !chain_index ? 0 : chain_index + 1
|
||||
cert_line = env["HTTP_SSL_CLIENT_CERT_CHAIN_#{chain_index}"]
|
||||
cert_line = nil if cert_line == '(null)' # For Apache mod_ssl
|
||||
end
|
||||
|
||||
if !cert_line
|
||||
raise failed + "Username not found in certificate chain " + chain_dn
|
||||
end
|
||||
|
||||
auth = ServerAuth.new
|
||||
|
||||
login = auth.login_token(username, subjectname, 300)
|
||||
|
||||
STDERR.puts login
|
||||
|
||||
return one_client_user("dummy", login)
|
||||
|
||||
end
|
||||
params.merge!({:econe_host => econe_host, :econe_port => econe_port})
|
||||
super(env, params)
|
||||
end
|
||||
|
||||
|
||||
###########################################################################
|
||||
# Repository Interface
|
||||
###########################################################################
|
||||
|
||||
def upload_image(params, one_client)
|
||||
image = ImageEC2.new(Image.build_xml, one_client, params['file'])
|
||||
def upload_image(params)
|
||||
image = ImageEC2.new(Image.build_xml, self.client, params['file'])
|
||||
|
||||
template = image.to_one_template
|
||||
if OpenNebula.is_error?(template)
|
||||
@ -191,11 +98,11 @@ class EC2QueryServer < CloudServer
|
||||
return response.result(binding), 200
|
||||
end
|
||||
|
||||
def register_image(params, one_client)
|
||||
def register_image(params)
|
||||
# Get the Image ID
|
||||
tmp, img=params['ImageLocation'].split('-')
|
||||
|
||||
image = Image.new(Image.build_xml(img.to_i), one_client)
|
||||
image = Image.new(Image.build_xml(img.to_i), self.client)
|
||||
|
||||
# Enable the new Image
|
||||
rc = image.info
|
||||
@ -211,9 +118,9 @@ class EC2QueryServer < CloudServer
|
||||
return response.result(binding), 200
|
||||
end
|
||||
|
||||
def describe_images(params, one_client)
|
||||
def describe_images(params)
|
||||
user_flag = OpenNebula::Pool::INFO_GROUP
|
||||
impool = ImagePool.new(one_client, user_flag)
|
||||
impool = ImagePool.new(self.client, user_flag)
|
||||
impool.info
|
||||
|
||||
erb_version = params['Version']
|
||||
@ -226,11 +133,11 @@ class EC2QueryServer < CloudServer
|
||||
# Instance Interface
|
||||
###########################################################################
|
||||
|
||||
def run_instances(params, one_client)
|
||||
def run_instances(params)
|
||||
# Get the instance type and path
|
||||
if params['InstanceType'] != nil
|
||||
instance_type_name = params['InstanceType']
|
||||
instance_type = @instance_types[instance_type_name]
|
||||
instance_type = @config[:instance_types][instance_type_name]
|
||||
|
||||
if instance_type != nil
|
||||
path = @config[:template_location] + "/#{instance_type['TEMPLATE']}"
|
||||
@ -252,7 +159,7 @@ class EC2QueryServer < CloudServer
|
||||
template_text = template.result(binding)
|
||||
|
||||
# Start the VM.
|
||||
vm = VirtualMachine.new(VirtualMachine.build_xml, one_client)
|
||||
vm = VirtualMachine.new(VirtualMachine.build_xml, self.client)
|
||||
|
||||
rc = vm.allocate(template_text)
|
||||
if OpenNebula::is_error?(rc)
|
||||
@ -270,26 +177,26 @@ class EC2QueryServer < CloudServer
|
||||
return response.result(binding), 200
|
||||
end
|
||||
|
||||
def describe_instances(params, one_client)
|
||||
def describe_instances(params)
|
||||
user_flag = OpenNebula::Pool::INFO_MINE
|
||||
vmpool = VirtualMachinePool.new(one_client, user_flag)
|
||||
vmpool = VirtualMachinePool.new(self.client, user_flag)
|
||||
vmpool.info
|
||||
|
||||
erb_version = params['Version']
|
||||
erb_user_name = params['AWSAccessKeyId']
|
||||
|
||||
|
||||
response = ERB.new(File.read(@config[:views]+"/describe_instances.erb"))
|
||||
return response.result(binding), 200
|
||||
end
|
||||
|
||||
def terminate_instances(params, one_client)
|
||||
def terminate_instances(params)
|
||||
# Get the VM ID
|
||||
vmid=params['InstanceId.1']
|
||||
vmid=params['InstanceId.01'] if !vmid
|
||||
|
||||
tmp, vmid=vmid.split('-') if vmid[0]==?i
|
||||
|
||||
vm = VirtualMachine.new(VirtualMachine.build_xml(vmid),one_client)
|
||||
vm = VirtualMachine.new(VirtualMachine.build_xml(vmid),self.client)
|
||||
rc = vm.info
|
||||
|
||||
return OpenNebula::Error.new('Unsupported'),400 if OpenNebula::is_error?(rc)
|
||||
@ -308,55 +215,6 @@ class EC2QueryServer < CloudServer
|
||||
return response.result(binding), 200
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Calculates signature version 1
|
||||
def signature_version_1(params, secret_key, digest='sha1')
|
||||
params.delete('Signature')
|
||||
req_desc = params.sort {|x,y| x[0].downcase <=> y[0].downcase}.to_s
|
||||
|
||||
digest_generator = OpenSSL::Digest::Digest.new(digest)
|
||||
digest = OpenSSL::HMAC.digest(digest_generator,
|
||||
secret_key,
|
||||
req_desc)
|
||||
b64sig = Base64.b64encode(digest)
|
||||
return b64sig.strip
|
||||
end
|
||||
|
||||
# Calculates signature version 2
|
||||
def signature_version_2(params, secret_key, env, includeport=true, urlencode=true)
|
||||
signature_params = params.reject { |key,value|
|
||||
key=='Signature' or key=='file' }
|
||||
|
||||
if includeport
|
||||
server_str = @server_host + ':' + @server_port
|
||||
else
|
||||
server_str = @server_host
|
||||
end
|
||||
|
||||
canonical_str = AWS.canonical_string(signature_params,
|
||||
server_str,
|
||||
env['REQUEST_METHOD'])
|
||||
|
||||
# Use the correct signature strength
|
||||
sha_strength = case params['SignatureMethod']
|
||||
when "HmacSHA1" then 'sha1'
|
||||
when "HmacSHA256" then 'sha256'
|
||||
else 'sha1'
|
||||
end
|
||||
|
||||
digest = OpenSSL::Digest::Digest.new(sha_strength)
|
||||
b64hmac =
|
||||
Base64.encode64(
|
||||
OpenSSL::HMAC.digest(digest, secret_key, canonical_str)).gsub("\n","")
|
||||
|
||||
if urlencode
|
||||
return CGI::escape(b64hmac)
|
||||
else
|
||||
return b64hmac
|
||||
end
|
||||
end
|
||||
|
||||
###########################################################################
|
||||
# Helper functions
|
||||
###########################################################################
|
||||
|
@ -42,19 +42,28 @@ require 'rubygems'
|
||||
require 'sinatra'
|
||||
|
||||
require 'EC2QueryServer'
|
||||
require 'Configuration'
|
||||
|
||||
include OpenNebula
|
||||
|
||||
begin
|
||||
$econe_server = EC2QueryServer.new(CONFIGURATION_FILE,
|
||||
TEMPLATE_LOCATION, VIEWS_LOCATION)
|
||||
config = Configuration.new(CONFIGURATION_FILE)
|
||||
config.add_configuration_value("TEMPLATE_LOCATION", TEMPLATE_LOCATION)
|
||||
config.add_configuration_value("VIEWS", VIEWS_LOCATION)
|
||||
|
||||
instance_types = CloudServer.get_instance_types(config)
|
||||
config.add_configuration_value("INSTANCE_TYPES", instance_types)
|
||||
|
||||
CloudServer.print_configuration(config)
|
||||
|
||||
set :config, config
|
||||
rescue Exception => e
|
||||
puts "Error starting server: #{e}"
|
||||
exit(-1)
|
||||
end
|
||||
|
||||
if CloudServer.is_port_open?($econe_server.config[:server],
|
||||
$econe_server.config[:port])
|
||||
if CloudServer.is_port_open?(settings.config[:server],
|
||||
settings.config[:port])
|
||||
puts "Port busy, please shutdown the service or move econe server port."
|
||||
exit
|
||||
end
|
||||
@ -62,23 +71,18 @@ end
|
||||
##############################################################################
|
||||
# Sinatra Configuration
|
||||
##############################################################################
|
||||
set :host, $econe_server.config[:server]
|
||||
set :port, $econe_server.config[:port]
|
||||
set :host, settings.config[:server]
|
||||
set :port, settings.config[:port]
|
||||
|
||||
##############################################################################
|
||||
# Actions
|
||||
##############################################################################
|
||||
|
||||
before do
|
||||
#STDERR.puts Time.now.strftime("%d/%b/%Y %H:%M:%S")
|
||||
begin
|
||||
@client = $econe_server.authenticate(params,env)
|
||||
rescue => e
|
||||
#STDERR.puts Time.now.strftime("%d/%b/%Y %H:%M:%S") + ' Exception in authentication. ' + e.to_s
|
||||
@client = nil
|
||||
end
|
||||
|
||||
if @client.nil?
|
||||
@econe_server = EC2QueryServer.new(settings.config)
|
||||
result = @econe_server.authenticate(request.env, params)
|
||||
if result
|
||||
# Add a log message
|
||||
error 400, error_xml("AuthFailure", 0)
|
||||
end
|
||||
end
|
||||
@ -86,7 +90,7 @@ end
|
||||
helpers do
|
||||
def error_xml(code,id)
|
||||
message = ''
|
||||
|
||||
|
||||
case code
|
||||
when 'AuthFailure'
|
||||
message = 'User not authorized'
|
||||
@ -94,45 +98,44 @@ helpers do
|
||||
message = 'Specified AMI ID does not exist'
|
||||
when 'Unsupported'
|
||||
message = 'The instance type or feature is not supported in your requested Availability Zone.'
|
||||
else
|
||||
else
|
||||
message = code
|
||||
end
|
||||
|
||||
|
||||
xml = "<Response><Errors><Error><Code>"+
|
||||
code +
|
||||
"</Code><Message>" +
|
||||
message +
|
||||
"</Message></Error></Errors><RequestID>" +
|
||||
id.to_s +
|
||||
code +
|
||||
"</Code><Message>" +
|
||||
message +
|
||||
"</Message></Error></Errors><RequestID>" +
|
||||
id.to_s +
|
||||
"</RequestID></Response>"
|
||||
|
||||
return xml
|
||||
end
|
||||
|
||||
return xml
|
||||
end
|
||||
end
|
||||
|
||||
post '/' do
|
||||
do_http_request(params, @client)
|
||||
do_http_request(params)
|
||||
end
|
||||
|
||||
get '/' do
|
||||
do_http_request(params, @client)
|
||||
do_http_request(params)
|
||||
end
|
||||
|
||||
def do_http_request(params, client)
|
||||
|
||||
def do_http_request(params)
|
||||
case params['Action']
|
||||
when 'UploadImage'
|
||||
result,rc = $econe_server.upload_image(params, client)
|
||||
result,rc = @econe_server.upload_image(params)
|
||||
when 'RegisterImage'
|
||||
result,rc = $econe_server.register_image(params, client)
|
||||
result,rc = @econe_server.register_image(params)
|
||||
when 'DescribeImages'
|
||||
result,rc = $econe_server.describe_images(params, client)
|
||||
result,rc = @econe_server.describe_images(params)
|
||||
when 'RunInstances'
|
||||
result,rc = $econe_server.run_instances(params, client)
|
||||
result,rc = @econe_server.run_instances(params)
|
||||
when 'DescribeInstances'
|
||||
result,rc = $econe_server.describe_instances(params, client)
|
||||
result,rc = @econe_server.describe_instances(params)
|
||||
when 'TerminateInstances'
|
||||
result,rc = $econe_server.terminate_instances(params, client)
|
||||
result,rc = @econe_server.terminate_instances(params)
|
||||
end
|
||||
|
||||
if OpenNebula::is_error?(result)
|
||||
@ -141,5 +144,5 @@ def do_http_request(params, client)
|
||||
|
||||
headers['Content-Type'] = 'application/xml'
|
||||
|
||||
result
|
||||
result
|
||||
end
|
||||
|
Loading…
x
Reference in New Issue
Block a user