diff --git a/src/cloud/ec2/etc/econe.conf b/src/cloud/ec2/etc/econe.conf index ca77a3ee8f..5b77f70931 100644 --- a/src/cloud/ec2/etc/econe.conf +++ b/src/cloud/ec2/etc/econe.conf @@ -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] diff --git a/src/cloud/ec2/lib/EC2QueryServer.rb b/src/cloud/ec2/lib/EC2QueryServer.rb index b4845a5805..5240d73be8 100644 --- a/src/cloud/ec2/lib/EC2QueryServer.rb +++ b/src/cloud/ec2/lib/EC2QueryServer.rb @@ -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 ########################################################################### diff --git a/src/cloud/ec2/lib/econe-server.rb b/src/cloud/ec2/lib/econe-server.rb index c9ac018571..def4803bb5 100644 --- a/src/cloud/ec2/lib/econe-server.rb +++ b/src/cloud/ec2/lib/econe-server.rb @@ -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 = ""+ - code + - "" + - message + - "" + - id.to_s + + code + + "" + + message + + "" + + id.to_s + "" - - 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