From aee86437c3445c878ba6091e68cd9d657379f125 Mon Sep 17 00:00:00 2001 From: Ted Date: Mon, 12 Sep 2011 11:06:51 -0500 Subject: [PATCH 01/23] Added support for X509 authorization in econe server. --- src/cloud/common/CloudServer.rb | 39 +++++++++++- src/cloud/ec2/lib/EC2QueryServer.rb | 96 ++++++++++++++++++++++------- src/cloud/ec2/lib/econe-server.rb | 8 ++- 3 files changed, 119 insertions(+), 24 deletions(-) diff --git a/src/cloud/common/CloudServer.rb b/src/cloud/common/CloudServer.rb index e05757ee07..070ae9d59d 100755 --- a/src/cloud/common/CloudServer.rb +++ b/src/cloud/common/CloudServer.rb @@ -88,7 +88,12 @@ class CloudServer # [return] an OpenNebula client session def one_client_user(name, password) client = Client.new("dummy:dummy") - client.one_auth = "#{name}:#{password}" + if name=="dummy" + #STDERR.puts "#{password}" + client.one_auth = "#{password}" + else + client.one_auth = "#{name}:#{password}" + end return client end @@ -101,7 +106,37 @@ class CloudServer return @user_pool["USER[NAME=\"#{name}\"]/PASSWORD"] end - + # Gets the username associated with a password + # password:: _String_ the password + # [return] _Hash_ with the username + def get_username(password) + @user_pool.info + #STDERR.puts 'the password is ' + password + #STDERR.puts @user_pool["User[PASSWORD=\"#{password}\"]"] + username = @user_pool["User[PASSWORD=\"#{password}\"]/NAME"] + return username if (username != nil) + + # Check if the DN is part of a |-separted multi-DN password + user_elts = Array.new + @user_pool.each {|e| user_elts << e['PASSWORD']} + multiple_users = user_elts.select {|e| e=~ /\|/ } + matched = nil + multiple_users.each do |e| + e.to_s.split('|').each do |w| + if (w == password) + matched=e + break + end + end + break if matched + end + if matched + password = matched.to_s + end + puts("The password is " + password) + return @user_pool["USER[PASSWORD=\"#{password}\"]/NAME"] + end + # Finds out if a port is available on ip # ip:: _String_ IP address where the port to check is # port:: _String_ port to find out whether is open diff --git a/src/cloud/ec2/lib/EC2QueryServer.rb b/src/cloud/ec2/lib/EC2QueryServer.rb index 1d8c786729..1329f8b8eb 100644 --- a/src/cloud/ec2/lib/EC2QueryServer.rb +++ b/src/cloud/ec2/lib/EC2QueryServer.rb @@ -86,34 +86,88 @@ class EC2QueryServer < CloudServer # params:: of the request # [return] true if authenticated def authenticate(params,env) - password = get_user_password(params['AWSAccessKeyId']) - return nil if !password + + 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 - 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 - - if params['Signature']==signature - return one_client_user(params['AWSAccessKeyId'], password) - else - if params['SignatureVersion']=="2" - signature = signature_version_2(params, + signature = case params['SignatureVersion'] + when "1" then signature_version_1(params.clone, password) + when "2" then signature_version_2(params, password, env, - false, + true, false) - if params['Signature']==signature - return one_client_user(params['AWSAccessKeyId'], password) + end + + 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 - end - return nil + 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_pem = cert_line.scan(/(-+BEGIN CERTIFICATE-+\n[^-]*\n-+END CERTIFICATE-+)/) + 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") + #STDERR.puts 'the cert is' + cert.subject.to_s + begin + username = get_username(subjectname) + rescue + username = nil + end + #STDERR.puts "the username is " + username + 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 end diff --git a/src/cloud/ec2/lib/econe-server.rb b/src/cloud/ec2/lib/econe-server.rb index 4255af408d..c9ac018571 100644 --- a/src/cloud/ec2/lib/econe-server.rb +++ b/src/cloud/ec2/lib/econe-server.rb @@ -70,7 +70,13 @@ set :port, $econe_server.config[:port] ############################################################################## before do - @client = $econe_server.authenticate(params,env) + #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? error 400, error_xml("AuthFailure", 0) From 20611d2e4a80ea3df50d1d8237d975a2cb8665c9 Mon Sep 17 00:00:00 2001 From: Ted Date: Mon, 12 Sep 2011 11:20:20 -0500 Subject: [PATCH 02/23] Removed comment line. --- src/cloud/ec2/lib/EC2QueryServer.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/src/cloud/ec2/lib/EC2QueryServer.rb b/src/cloud/ec2/lib/EC2QueryServer.rb index 1329f8b8eb..3c573e32d2 100644 --- a/src/cloud/ec2/lib/EC2QueryServer.rb +++ b/src/cloud/ec2/lib/EC2QueryServer.rb @@ -134,7 +134,6 @@ class EC2QueryServer < CloudServer cert_array.unshift('-----BEGIN CERTIFICATE-----') cert_array.push('-----END CERTIFICATE-----') cert_pem = cert_array.join("\n") - #cert_pem = cert_line.scan(/(-+BEGIN CERTIFICATE-+\n[^-]*\n-+END CERTIFICATE-+)/) cert = OpenSSL::X509::Certificate.new(cert_pem) rescue raise failed + "Could not create X509 certificate from " + cert_line From a013c562c93c77e96883078f8e8981e592310055 Mon Sep 17 00:00:00 2001 From: Ted Date: Mon, 12 Sep 2011 11:21:32 -0500 Subject: [PATCH 03/23] Removed comment lines. --- src/cloud/ec2/lib/EC2QueryServer.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/cloud/ec2/lib/EC2QueryServer.rb b/src/cloud/ec2/lib/EC2QueryServer.rb index 3c573e32d2..b4845a5805 100644 --- a/src/cloud/ec2/lib/EC2QueryServer.rb +++ b/src/cloud/ec2/lib/EC2QueryServer.rb @@ -139,14 +139,12 @@ class EC2QueryServer < CloudServer 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") - #STDERR.puts 'the cert is' + cert.subject.to_s + subjectname = cert.subject.to_s.delete("\s") begin username = get_username(subjectname) rescue username = nil end - #STDERR.puts "the username is " + username break if username chain_dn = (!chain_dn ? "" : chain_dn) + "\n" + subjectname chain_index = !chain_index ? 0 : chain_index + 1 From f97ef6be7bd222795626b4e7a781ddbf385a2a5f Mon Sep 17 00:00:00 2001 From: Daniel Molina Date: Tue, 20 Sep 2011 18:24:51 +0200 Subject: [PATCH 04/23] feature #795: Add new Cloud Auth system --- src/cloud/common/CloudAuth.rb | 37 ++++++ src/cloud/common/CloudAuth/BasicCloudAuth.rb | 20 +++ src/cloud/common/CloudAuth/EC2CloudAuth.rb | 76 +++++++++++ src/cloud/common/CloudAuth/X509CloudAuth.rb | 87 +++++++++++++ src/cloud/common/CloudServer.rb | 126 +++++-------------- 5 files changed, 253 insertions(+), 93 deletions(-) create mode 100644 src/cloud/common/CloudAuth.rb create mode 100644 src/cloud/common/CloudAuth/BasicCloudAuth.rb create mode 100644 src/cloud/common/CloudAuth/EC2CloudAuth.rb create mode 100644 src/cloud/common/CloudAuth/X509CloudAuth.rb diff --git a/src/cloud/common/CloudAuth.rb b/src/cloud/common/CloudAuth.rb new file mode 100644 index 0000000000..0dae4b76d9 --- /dev/null +++ b/src/cloud/common/CloudAuth.rb @@ -0,0 +1,37 @@ +class CloudAuth + AUTH_MODULES = { + "basic" => 'BasicCloudAuth', + "ec2" => 'EC2CloudAuth', + "x509" => 'X509CloudAuth' + } + + attr_reader :client, :token + + def initialize(conf) + @xmlrpc = conf[:one_xmlrpc] + + if AUTH_MODULES.include?(conf[:auth]) + require 'CloudAuth/' + AUTH_MODULES[conf[:auth]] + extend Kernel.const_get(AUTH_MODULES[conf[:auth]]) + else + raise "Auth module not specified" + end + end + + protected + + def get_password(username) + @oneadmin_client ||= OpenNebula::Client.new(nil, @xmlrpc) + + if @user_pool.nil? + @user_pool ||= OpenNebula::UserPool.new(@oneadmin_client) + + rc = @user_pool.info + if OpenNebula.is_error?(rc) + raise rc.message + end + end + + return @user_pool["USER[NAME=\"#{username}\"]/PASSWORD"] + end +end \ No newline at end of file diff --git a/src/cloud/common/CloudAuth/BasicCloudAuth.rb b/src/cloud/common/CloudAuth/BasicCloudAuth.rb new file mode 100644 index 0000000000..c4d53e8c03 --- /dev/null +++ b/src/cloud/common/CloudAuth/BasicCloudAuth.rb @@ -0,0 +1,20 @@ +module BasicCloudAuth + def auth(env, params={}) + auth = Rack::Auth::Basic::Request.new(env) + + if auth.provided? && auth.basic? + username, password = auth.credentials + + one_pass = get_password(username) + if one_pass && one_pass == password + @token = "#{username}:#{password}" + @client = Client.new(@token, @xmlrpc, false) + return nil + else + return "Authentication failure" + end + else + return "Basic auth not provided" + end + end +end \ No newline at end of file diff --git a/src/cloud/common/CloudAuth/EC2CloudAuth.rb b/src/cloud/common/CloudAuth/EC2CloudAuth.rb new file mode 100644 index 0000000000..2842d8c940 --- /dev/null +++ b/src/cloud/common/CloudAuth/EC2CloudAuth.rb @@ -0,0 +1,76 @@ +module EC2CloudAuth + def auth(env, params={}) + username = params['AWSAccessKeyId'] + one_pass = get_password(username) + return "Invalid credentials" unless one_pass + + signature = case params['SignatureVersion'] + when "1" then signature_v1(params.clone,one_pass) + when "2" then signature_v2(params.clone,one_pass,env,true,false) + end + + if params['Signature'] != signature + if params['SignatureVersion']=="2" + signature = signature_v2(params.clone,one_pass,env,false,false) + if params['Signature'] != signature + return "Invalid Credentials" + end + else + return "Invalid Credentials" + end + end + + @token = "#{username}:#{one_pass}" + @client = Client.new(@token, @xmlrpc, false) + return nil + end + + private + + # Calculates signature version 1 + def signature_v1(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_v2(params, secret_key, env, include_port=true, urlencode=true) + params.delete('Signature') + params.delete('file') + + server_host = params.delete(:econe_host) + server_port = params.delete(:econe_port) + if include_port + server_str = "#{server_host}:#{server_port}" + else + server_str = server_host + end + + canonical_str = AWS.canonical_string( + 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) + hmac = OpenSSL::HMAC.digest(digest, secret_key, canonical_str) + b64hmac = Base64.encode64(hmac).gsub("\n","") + + if urlencode + return CGI::escape(b64hmac) + else + return b64hmac + end + end +end \ No newline at end of file diff --git a/src/cloud/common/CloudAuth/X509CloudAuth.rb b/src/cloud/common/CloudAuth/X509CloudAuth.rb new file mode 100644 index 0000000000..6488b7999e --- /dev/null +++ b/src/cloud/common/CloudAuth/X509CloudAuth.rb @@ -0,0 +1,87 @@ +module X509CloudAuth + # TBD Adapt to the new CloudAuth system + + # Gets the username associated with a password + # password:: _String_ the password + # [return] _Hash_ with the username + def get_username(password) + @user_pool.info + #STDERR.puts 'the password is ' + password + #STDERR.puts @user_pool["User[PASSWORD=\"#{password}\"]"] + username = @user_pool["User[PASSWORD=\"#{password}\"]/NAME"] + return username if (username != nil) + + # Check if the DN is part of a |-separted multi-DN password + user_elts = Array.new + @user_pool.each {|e| user_elts << e['PASSWORD']} + multiple_users = user_elts.select {|e| e=~ /\|/ } + matched = nil + multiple_users.each do |e| + e.to_s.split('|').each do |w| + if (w == password) + matched=e + break + end + end + break if matched + end + if matched + password = matched.to_s + end + puts("The password is " + password) + return @user_pool["USER[PASSWORD=\"#{password}\"]/NAME"] + end + + def auth(env, params={}) + 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 + + # 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 + msg = "" + msg << failed + msg << "Username not found in certificate chain " + msg << chain_dn + raise msg + end + + auth = ServerAuth.new + + login = auth.login_token(username, subjectname, 300) + + STDERR.puts login + + return one_client_user("dummy", login) + end +end \ No newline at end of file diff --git a/src/cloud/common/CloudServer.rb b/src/cloud/common/CloudServer.rb index 070ae9d59d..b5abc10ca2 100755 --- a/src/cloud/common/CloudServer.rb +++ b/src/cloud/common/CloudServer.rb @@ -14,9 +14,8 @@ # limitations under the License. # #--------------------------------------------------------------------------- # -require 'Configuration' require 'OpenNebula' -require 'pp' +require 'CloudAuth' ############################################################################## # This class represents a generic Cloud Server using the OpenNebula Cloud @@ -28,115 +27,39 @@ class CloudServer # Public attributes ########################################################################## attr_reader :config - attr_reader :one_client # Initializes the Cloud server based on a config file # config_file:: _String_ for the server. MUST include the following # variables: - # USER - # PASSWORD + # AUTH # VM_TYPE - # IMAGE_DIR - # DATABASE - def initialize(config_file) - + # XMLRPC + def initialize(config) # --- Load the Cloud Server configuration file --- + @config = config + @cloud_auth = CloudAuth.new(@config) + end - @config = Configuration.new(config_file) + def authenticate(env, params={}) + @cloud_auth.auth(env, params) + end - if @config[:vm_type] == nil - raise "No VM_TYPE defined." - end - - @instance_types = Hash.new - - if @config[:vm_type].kind_of?(Array) - @config[:vm_type].each {|type| - @instance_types[type['NAME']]=type - } - else - @instance_types[@config[:vm_type]['NAME']]=@config[:vm_type] - end - - # --- Start an OpenNebula Session --- - - @one_client = Client.new(nil,@config[:one_xmlrpc]) - @user_pool = UserPool.new(@one_client) + def client + @cloud_auth.client end # # Prints the configuration of the server # - def print_configuration + def self.print_configuration(config) puts "--------------------------------------" puts " Server configuration " puts "--------------------------------------" - pp @config + pp config - puts "--------------------------------------" - puts " Registered Instance Types " - puts "--------------------------------------" - pp @instance_types + STDOUT.flush end - ########################################################################### - # USER and OpenNebula Session Methods - ########################################################################### - - # Generates an OpenNebula Session for the given user - # user:: _Hash_ the user information - # [return] an OpenNebula client session - def one_client_user(name, password) - client = Client.new("dummy:dummy") - if name=="dummy" - #STDERR.puts "#{password}" - client.one_auth = "#{password}" - else - client.one_auth = "#{name}:#{password}" - end - - return client - end - - # Gets the data associated with a user - # name:: _String_ the name of the user - # [return] _Hash_ with the user data - def get_user_password(name) - @user_pool.info - return @user_pool["USER[NAME=\"#{name}\"]/PASSWORD"] - end - - # Gets the username associated with a password - # password:: _String_ the password - # [return] _Hash_ with the username - def get_username(password) - @user_pool.info - #STDERR.puts 'the password is ' + password - #STDERR.puts @user_pool["User[PASSWORD=\"#{password}\"]"] - username = @user_pool["User[PASSWORD=\"#{password}\"]/NAME"] - return username if (username != nil) - - # Check if the DN is part of a |-separted multi-DN password - user_elts = Array.new - @user_pool.each {|e| user_elts << e['PASSWORD']} - multiple_users = user_elts.select {|e| e=~ /\|/ } - matched = nil - multiple_users.each do |e| - e.to_s.split('|').each do |w| - if (w == password) - matched=e - break - end - end - break if matched - end - if matched - password = matched.to_s - end - puts("The password is " + password) - return @user_pool["USER[PASSWORD=\"#{password}\"]/NAME"] - end - # Finds out if a port is available on ip # ip:: _String_ IP address where the port to check is # port:: _String_ port to find out whether is open @@ -157,5 +80,22 @@ class CloudServer return false end -end + def self.get_instance_types(config) + if config[:vm_type] == nil + raise "No VM_TYPE defined." + end + + instance_types = Hash.new + + if config[:vm_type].kind_of?(Array) + config[:vm_type].each {|type| + instance_types[type['NAME']]=type + } + else + instance_types[config[:vm_type]['NAME']]=config[:vm_type] + end + + instance_types + end +end From afdf9d23c529de3e06c1a123fda560f6297d7a6c Mon Sep 17 00:00:00 2001 From: Daniel Molina Date: Tue, 20 Sep 2011 18:25:23 +0200 Subject: [PATCH 05/23] feature #795: Adapt EC2 to the new Cloud Auth system --- src/cloud/ec2/etc/econe.conf | 2 + src/cloud/ec2/lib/EC2QueryServer.rb | 186 ++++------------------------ src/cloud/ec2/lib/econe-server.rb | 77 ++++++------ 3 files changed, 64 insertions(+), 201 deletions(-) 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 From 196c021cafe0701e67cd05ad0787876df6fb8911 Mon Sep 17 00:00:00 2001 From: Daniel Molina Date: Tue, 20 Sep 2011 18:25:57 +0200 Subject: [PATCH 06/23] feature #795: Adapt OCCI to the new Cloud Auth system --- src/cloud/occi/etc/occi-server.conf | 2 + src/cloud/occi/lib/OCCIServer.rb | 140 +++++----------------------- src/cloud/occi/lib/occi-server.rb | 60 +++++++----- 3 files changed, 62 insertions(+), 140 deletions(-) diff --git a/src/cloud/occi/etc/occi-server.conf b/src/cloud/occi/etc/occi-server.conf index 2eddedc706..a92f46ce2a 100644 --- a/src/cloud/occi/etc/occi-server.conf +++ b/src/cloud/occi/etc/occi-server.conf @@ -24,6 +24,8 @@ PORT=4567 # SSL proxy that serves the API (set if is being used) #SSL_SERVER=https://localhost:443 +AUTH=basic + # Configuration for OpenNebula's Virtual Networks BRIDGE= diff --git a/src/cloud/occi/lib/OCCIServer.rb b/src/cloud/occi/lib/OCCIServer.rb index f86f90dda5..dc2f54caa0 100755 --- a/src/cloud/occi/lib/OCCIServer.rb +++ b/src/cloud/occi/lib/OCCIServer.rb @@ -15,12 +15,9 @@ #--------------------------------------------------------------------------- # # Common cloud libs -require 'rubygems' -require 'sinatra' require 'CloudServer' # OCA -require 'OpenNebula' include OpenNebula # OCCI libs @@ -43,29 +40,13 @@ class OCCIServer < CloudServer # Server initializer # config_file:: _String_ path of the config file # template:: _String_ path to the location of the templates - def initialize(config_file,template) - super(config_file) + def initialize(config) + super(config) - @config.add_configuration_value("TEMPLATE_LOCATION",template) - - if @config[:ssl_server] - @base_url=@config[:ssl_server] + if config[:ssl_server] + @base_url=config[:ssl_server] else - @base_url="http://#{@config[:server]}:#{@config[:port]}" - end - - print_configuration - end - - # Retrieve a client with the user credentials - # requestenv:: _Hash_ Hash containing the environment of the request - # [return] _Client_ client with the user credentials - def get_client(requestenv) - auth = Rack::Auth::Basic::Request.new(requestenv) - if auth - return one_client_user(auth.credentials[0], auth.credentials[1]) - else - return nil + @base_url="http://#{config[:server]}:#{config[:port]}" end end @@ -92,13 +73,8 @@ class OCCIServer < CloudServer # --- Get User's VMs --- user_flag = -1 - one_client = get_client(request.env) - if !one_client - return "No authorization data present", 401 - end - vmpool = VirtualMachinePoolOCCI.new( - one_client, + self.client, user_flag) # --- Prepare XML Response --- @@ -124,13 +100,8 @@ class OCCIServer < CloudServer # --- Get User's VNETs --- user_flag = -1 - one_client = get_client(request.env) - if !one_client - return "No authorization data present", 401 - end - network_pool = VirtualNetworkPoolOCCI.new( - one_client, + self.client, user_flag) # --- Prepare XML Response --- @@ -155,13 +126,8 @@ class OCCIServer < CloudServer # --- Get User's Images --- user_flag = -1 - one_client = get_client(request.env) - if !one_client - return "No authorization data present", 401 - end - image_pool = ImagePoolOCCI.new( - one_client, + self.client, user_flag) # --- Prepare XML Response --- @@ -193,16 +159,11 @@ class OCCIServer < CloudServer # [return] _String_,_Integer_ COMPUTE Representation or error, status code def post_compute(request) # --- Create the new Instance --- - one_client = get_client(request.env) - if !one_client - return "No authorization data present", 401 - end - vm = VirtualMachineOCCI.new( VirtualMachine.build_xml, - one_client, + self.client, request.body.read, - @instance_types, + @config[:instance_types], @config[:template_location]) # --- Generate the template and Allocate the new Instance --- @@ -223,14 +184,9 @@ class OCCIServer < CloudServer # status code def get_compute(request, params) # --- Get the VM --- - one_client = get_client(request.env) - if !one_client - return "No authorization data present", 401 - end - vm = VirtualMachineOCCI.new( VirtualMachine.build_xml(params[:id]), - one_client) + self.client) # --- Prepare XML Response --- rc = vm.info @@ -253,14 +209,9 @@ class OCCIServer < CloudServer # status code def delete_compute(request, params) # --- Get the VM --- - one_client = get_client(request.env) - if !one_client - return "No authorization data present", 401 - end - vm = VirtualMachineOCCI.new( VirtualMachine.build_xml(params[:id]), - one_client) + self.client) rc = vm.info return rc, 404 if OpenNebula::is_error?(rc) @@ -278,14 +229,9 @@ class OCCIServer < CloudServer # status code def put_compute(request, params) # --- Get the VM --- - one_client = get_client(request.env) - if !one_client - return "No authorization data present", 401 - end - vm = VirtualMachineOCCI.new( VirtualMachine.build_xml(params[:id]), - one_client) + self.client) rc = vm.info return rc, 400 if OpenNebula.is_error?(rc) @@ -362,14 +308,9 @@ class OCCIServer < CloudServer # [return] _String_,_Integer_ Network Representation or error, status code def post_network(request) # --- Create the new Instance --- - one_client = get_client(request.env) - if !one_client - return "No authorization data present", 401 - end - network = VirtualNetworkOCCI.new( VirtualNetwork.build_xml, - one_client, + self.client, request.body, @config[:bridge]) @@ -390,15 +331,9 @@ class OCCIServer < CloudServer # [return] _String_,_Integer_ NETWORK occi representation or error, # status code def get_network(request, params) - # --- Get the VNET --- - one_client = get_client(request.env) - if !one_client - return "No authorization data present", 401 - end - network = VirtualNetworkOCCI.new( VirtualNetwork.build_xml(params[:id]), - one_client) + self.client) # --- Prepare XML Response --- rc = network.info @@ -419,15 +354,9 @@ class OCCIServer < CloudServer # [return] _String_,_Integer_ Delete confirmation msg or error, # status code def delete_network(request, params) - # --- Get the VNET --- - one_client = get_client(request.env) - if !one_client - return "No authorization data present", 401 - end - network = VirtualNetworkOCCI.new( VirtualNetwork.build_xml(params[:id]), - one_client) + self.client) rc = network.info return rc, 404 if OpenNebula::is_error?(rc) @@ -446,15 +375,10 @@ class OCCIServer < CloudServer def put_network(request, params) xmldoc = XMLElement.build_xml(request.body, 'NETWORK') vnet_info = XMLElement.new(xmldoc) if xmldoc != nil - - one_client = get_client(request.env) - if !one_client - return "No authorization data present", 401 - end vnet = VirtualNetworkOCCI.new( VirtualNetwork.build_xml(params[:id]), - one_client) + self.client) rc = vnet.info return rc, 400 if OpenNebula.is_error?(rc) @@ -487,11 +411,6 @@ class OCCIServer < CloudServer error = OpenNebula::Error.new(error_msg) return error, 400 end - - one_client = get_client(request.env) - if !one_client - return "No authorization data present", 401 - end # --- Create and Add the new Image --- occixml = request.params['occixml'] @@ -499,7 +418,7 @@ class OCCIServer < CloudServer image = ImageOCCI.new( Image.build_xml, - one_client, + self.client, occixml, request.params['file']) @@ -521,14 +440,9 @@ class OCCIServer < CloudServer # status code def get_storage(request, params) # --- Get the Image --- - one_client = get_client(request.env) - if !one_client - return "No authorization data present", 401 - end - image = ImageOCCI.new( Image.build_xml(params[:id]), - one_client) + self.client) rc = image.info @@ -550,20 +464,15 @@ class OCCIServer < CloudServer # status code def delete_storage(request, params) # --- Get the Image --- - one_client = get_client(request.env) - if !one_client - return "No authorization data present", 401 - end - image = ImageOCCI.new( Image.build_xml(params[:id]), - one_client) + self.client) rc = image.info return rc, 404 if OpenNebula::is_error?(rc) # --- Delete the Image --- - rc = @img_repo.delete(image) + rc = image.delete return rc, 500 if OpenNebula::is_error?(rc) return "", 204 @@ -576,15 +485,10 @@ class OCCIServer < CloudServer def put_storage(request, params) xmldoc = XMLElement.build_xml(request.body, 'STORAGE') image_info = XMLElement.new(xmldoc) if xmldoc != nil - - one_client = get_client(request.env) - if !one_client - return "No authorization data present", 401 - end image = ImageOCCI.new( Image.build_xml(params[:id]), - one_client) + self.client) rc = image.info return rc, 400 if OpenNebula.is_error?(rc) diff --git a/src/cloud/occi/lib/occi-server.rb b/src/cloud/occi/lib/occi-server.rb index 136bfd482c..81950cc5ae 100755 --- a/src/cloud/occi/lib/occi-server.rb +++ b/src/cloud/occi/lib/occi-server.rb @@ -43,21 +43,29 @@ $: << RUBY_LIB_LOCATION+"/cloud" # For the Repository Manager ################################################ require 'rubygems' require 'sinatra' -require 'OCCIServer' -require 'OpenNebula' +require 'OCCIServer' +require 'Configuration' include OpenNebula begin - $occi_server = OCCIServer.new(CONFIGURATION_FILE, TEMPLATE_LOCATION) + config = Configuration.new(CONFIGURATION_FILE) + config.add_configuration_value("TEMPLATE_LOCATION", TEMPLATE_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?($occi_server.config[:server], - $occi_server.config[:port]) +if CloudServer.is_port_open?(settings.config[:server], + settings.config[:port]) puts "Port busy, please shutdown the service or move occi server port." exit end @@ -65,13 +73,21 @@ end ############################################################################## # Sinatra Configuration ############################################################################## -set :host, $occi_server.config[:server] -set :port, $occi_server.config[:port] +set :host, settings.config[:server] +set :port, settings.config[:port] ############################################################################## # Helpers ############################################################################## +before do + @occi_server = OCCIServer.new(settings.config) + result = @occi_server.authenticate(request.env) + if result + error 401, result + end +end + # Response treatment helpers do def treat_response(result,rc) @@ -93,32 +109,32 @@ end ################################################### post '/compute' do - result,rc = $occi_server.post_compute(request) + result,rc = @occi_server.post_compute(request) treat_response(result,rc) end get '/compute' do - result,rc = $occi_server.get_computes(request) + result,rc = @occi_server.get_computes(request) treat_response(result,rc) end post '/network' do - result,rc = $occi_server.post_network(request) + result,rc = @occi_server.post_network(request) treat_response(result,rc) end get '/network' do - result,rc = $occi_server.get_networks(request) + result,rc = @occi_server.get_networks(request) treat_response(result,rc) end post '/storage' do - result,rc = $occi_server.post_storage(request) + result,rc = @occi_server.post_storage(request) treat_response(result,rc) end get '/storage' do - result,rc = $occi_server.get_storages(request) + result,rc = @occi_server.get_storages(request) treat_response(result,rc) end @@ -127,46 +143,46 @@ end ################################################### get '/compute/:id' do - result,rc = $occi_server.get_compute(request, params) + result,rc = @occi_server.get_compute(request, params) treat_response(result,rc) end delete '/compute/:id' do - result,rc = $occi_server.delete_compute(request, params) + result,rc = @occi_server.delete_compute(request, params) treat_response(result,rc) end put '/compute/:id' do - result,rc = $occi_server.put_compute(request, params) + result,rc = @occi_server.put_compute(request, params) treat_response(result,rc) end get '/network/:id' do - result,rc = $occi_server.get_network(request, params) + result,rc = @occi_server.get_network(request, params) treat_response(result,rc) end delete '/network/:id' do - result,rc = $occi_server.delete_network(request, params) + result,rc = @occi_server.delete_network(request, params) treat_response(result,rc) end put '/network/:id' do - result,rc = $occi_server.put_network(request, params) + result,rc = @occi_server.put_network(request, params) treat_response(result,rc) end get '/storage/:id' do - result,rc = $occi_server.get_storage(request, params) + result,rc = @occi_server.get_storage(request, params) treat_response(result,rc) end delete '/storage/:id' do - result,rc = $occi_server.delete_storage(request, params) + result,rc = @occi_server.delete_storage(request, params) treat_response(result,rc) end put '/storage/:id' do - result,rc = $occi_server.put_storage(request, params) + result,rc = @occi_server.put_storage(request, params) treat_response(result,rc) end From 96db2d58443e2fc2dbed143539363db4955a7a81 Mon Sep 17 00:00:00 2001 From: Daniel Molina Date: Tue, 20 Sep 2011 18:27:36 +0200 Subject: [PATCH 07/23] feature #795: Adapt Sunstone to the new Cloud Auth system --- src/sunstone/etc/sunstone-server.conf | 5 +++ src/sunstone/models/SunstoneServer.rb | 33 +--------------- src/sunstone/sunstone-server.rb | 54 +++++++++++++++------------ 3 files changed, 37 insertions(+), 55 deletions(-) diff --git a/src/sunstone/etc/sunstone-server.conf b/src/sunstone/etc/sunstone-server.conf index 37ff230bdc..87e53d4047 100644 --- a/src/sunstone/etc/sunstone-server.conf +++ b/src/sunstone/etc/sunstone-server.conf @@ -1,7 +1,12 @@ +# OpenNebula sever contact information +ONE_XMLRPC=http://localhost:2633/RPC2 + # Server Configuration HOST=127.0.0.1 PORT=9869 +AUTH=basic + # VNC Configuration VNC_PROXY_BASE_PORT=29876 NOVNC_PATH= diff --git a/src/sunstone/models/SunstoneServer.rb b/src/sunstone/models/SunstoneServer.rb index fc09d4632f..02283816a4 100644 --- a/src/sunstone/models/SunstoneServer.rb +++ b/src/sunstone/models/SunstoneServer.rb @@ -23,37 +23,8 @@ class SunstoneServer # FLAG that will filter the elements retrieved from the Pools POOL_FILTER = Pool::INFO_GROUP - def initialize(username, password) - # TBD one_client_user(name) from CloudServer - @client = Client.new("dummy:dummy") - @client.one_auth = "#{username}:#{password}" - end - - ############################################################################ - # - ############################################################################ - def self.authorize(user="", sha1_pass="") - if user.empty? || sha1_pass.empty? - return [401, false] - end - - # TBD get_user_password(name) from CloudServer - user_pool = UserPool.new(Client.new) - rc = user_pool.info - if OpenNebula.is_error?(rc) - return [500, false] - end - - user_pass = user_pool["USER[NAME=\"#{user}\"]/PASSWORD"] - user_id = user_pool["USER[NAME=\"#{user}\"]/ID"] - user_gid = user_pool["USER[NAME=\"#{user}\"]/GID"] - user_gname = user_pool["USER[NAME=\"#{user}\"]/GNAME"] - - if user_pass == sha1_pass - return [204, [user_id, user_gid, user_gname]] - else - return [401, nil] - end + def initialize(token, xmlrpc) + @client = Client.new(token, xmlrpc, false) end ############################################################################ diff --git a/src/sunstone/sunstone-server.rb b/src/sunstone/sunstone-server.rb index d348cf41f1..c4935e8b61 100755 --- a/src/sunstone/sunstone-server.rb +++ b/src/sunstone/sunstone-server.rb @@ -36,6 +36,7 @@ end SUNSTONE_ROOT_DIR = File.dirname(__FILE__) $: << RUBY_LIB_LOCATION +$: << RUBY_LIB_LOCATION+'/cloud' $: << SUNSTONE_ROOT_DIR+'/models' ############################################################################## @@ -45,7 +46,7 @@ require 'rubygems' require 'sinatra' require 'erb' -require 'cloud/Configuration' +require 'Configuration' require 'SunstoneServer' require 'SunstonePlugins' @@ -67,32 +68,35 @@ helpers do end def build_session - auth = Rack::Auth::Basic::Request.new(request.env) - if auth.provided? && auth.basic? && auth.credentials - user = auth.credentials[0] - sha1_pass = Digest::SHA1.hexdigest(auth.credentials[1]) + cloud_auth = CloudAuth.new(settings.config) - rc = SunstoneServer.authorize(user, sha1_pass) - if rc[1] - session[:user] = user - session[:user_id] = rc[1][0] - session[:user_gid] = rc[1][1] - session[:user_gname] = rc[1][2] - session[:password] = sha1_pass - session[:ip] = request.ip - session[:remember] = params[:remember] + result = cloud_auth.auth(request.env, params) + if result + return [401, ""] + else + user_id = OpenNebula::User::SELF + user = OpenNebula::User.new_with_id(user_id, cloud_auth.client) - if params[:remember] - env['rack.session.options'][:expire_after] = 30*60*60*24 - end - - return [204, ""] - else - return [rc.first, ""] + rc = user.info + if OpenNebula.is_error?(rc) + # Add a log message + return [500, ""] end - end + + session[:user] = user['NAME'] + session[:user_id] = user['ID'] + session[:user_gid] = user['GID'] + session[:user_gname] = user['GNAME'] + session[:token] = cloud_auth.token + session[:ip] = request.ip + session[:remember] = params[:remember] - return [401, ""] + if params[:remember] + env['rack.session.options'][:expire_after] = 30*60*60*24 + end + + return [204, ""] + end end def destroy_session @@ -105,7 +109,9 @@ before do unless request.path=='/login' || request.path=='/' halt 401 unless authorized? - @SunstoneServer = SunstoneServer.new(session[:user], session[:password]) + @SunstoneServer = SunstoneServer.new( + session[:token], + settings.config[:one_xmlrpc]) end end From 54f769170dd2fb4afdd34090f0a2d5fec5b19e3c Mon Sep 17 00:00:00 2001 From: Daniel Molina Date: Wed, 21 Sep 2011 18:59:13 +0200 Subject: [PATCH 08/23] feature #795: Add new Cloud Auth files to install.sh --- install.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/install.sh b/install.sh index 8a28e0d9cd..a32946f643 100755 --- a/install.sh +++ b/install.sh @@ -202,6 +202,7 @@ LIB_DIRS="$LIB_LOCATION/ruby \ $LIB_LOCATION/ruby/cloud/econe \ $LIB_LOCATION/ruby/cloud/econe/views \ $LIB_LOCATION/ruby/cloud/occi \ + $LIB_LOCATION/ruby/cloud/CloudAuth \ $LIB_LOCATION/ruby/onedb \ $LIB_LOCATION/tm_commands \ $LIB_LOCATION/tm_commands/nfs \ @@ -349,6 +350,7 @@ INSTALL_FILES=( HOOK_FT_FILES:$VAR_LOCATION/remotes/hooks/ft HOOK_NETWORK_FILES:$VAR_LOCATION/remotes/hooks/vnm COMMON_CLOUD_LIB_FILES:$LIB_LOCATION/ruby/cloud + CLOUD_AUTH_LIB_FILES:$LIB_LOCATION/ruby/cloud/CloudAuth ECO_LIB_FILES:$LIB_LOCATION/ruby/cloud/econe ECO_LIB_VIEW_FILES:$LIB_LOCATION/ruby/cloud/econe/views ECO_BIN_FILES:$BIN_LOCATION @@ -796,10 +798,15 @@ RUBY_OPENNEBULA_LIB_FILES="src/oca/ruby/OpenNebula/Host.rb \ COMMON_CLOUD_LIB_FILES="src/cloud/common/CloudServer.rb \ src/cloud/common/CloudClient.rb \ + src/cloud/common/CloudAuth.rb src/cloud/common/Configuration.rb" COMMON_CLOUD_CLIENT_LIB_FILES="src/cloud/common/CloudClient.rb" +CLOUD_AUTH_LIB_FILES="src/cloud/common/CloudAuth/BasicCloudAuth.rb \ + src/cloud/common/CloudAuth/EC2CloudAuth.rb \ + src/cloud/common/CloudAuth/X509CloudAuth.rb" + #------------------------------------------------------------------------------- # EC2 Query for OpenNebula #------------------------------------------------------------------------------- From 9419406f6e7cfd867e968392bba2b63b74d507c9 Mon Sep 17 00:00:00 2001 From: Daniel Molina Date: Wed, 21 Sep 2011 19:00:49 +0200 Subject: [PATCH 09/23] feature #795: Add conf variable for Cloud Auth --- src/cloud/common/CloudAuth.rb | 10 +++++----- src/cloud/common/CloudServer.rb | 18 ------------------ 2 files changed, 5 insertions(+), 23 deletions(-) diff --git a/src/cloud/common/CloudAuth.rb b/src/cloud/common/CloudAuth.rb index 0dae4b76d9..292c5718e4 100644 --- a/src/cloud/common/CloudAuth.rb +++ b/src/cloud/common/CloudAuth.rb @@ -8,11 +8,11 @@ class CloudAuth attr_reader :client, :token def initialize(conf) - @xmlrpc = conf[:one_xmlrpc] + @conf = conf - if AUTH_MODULES.include?(conf[:auth]) - require 'CloudAuth/' + AUTH_MODULES[conf[:auth]] - extend Kernel.const_get(AUTH_MODULES[conf[:auth]]) + if AUTH_MODULES.include?(@conf[:auth]) + require 'CloudAuth/' + AUTH_MODULES[@conf[:auth]] + extend Kernel.const_get(AUTH_MODULES[@conf[:auth]]) else raise "Auth module not specified" end @@ -21,7 +21,7 @@ class CloudAuth protected def get_password(username) - @oneadmin_client ||= OpenNebula::Client.new(nil, @xmlrpc) + @oneadmin_client ||= OpenNebula::Client.new(nil, @conf[:one_xmlrpc]) if @user_pool.nil? @user_pool ||= OpenNebula::UserPool.new(@oneadmin_client) diff --git a/src/cloud/common/CloudServer.rb b/src/cloud/common/CloudServer.rb index b5abc10ca2..91c5335701 100755 --- a/src/cloud/common/CloudServer.rb +++ b/src/cloud/common/CloudServer.rb @@ -80,22 +80,4 @@ class CloudServer return false end - - def self.get_instance_types(config) - if config[:vm_type] == nil - raise "No VM_TYPE defined." - end - - instance_types = Hash.new - - if config[:vm_type].kind_of?(Array) - config[:vm_type].each {|type| - instance_types[type['NAME']]=type - } - else - instance_types[config[:vm_type]['NAME']]=config[:vm_type] - end - - instance_types - end end From 2be5ec56942c1d9b9a23f68b8d7e8fd433670a00 Mon Sep 17 00:00:00 2001 From: Daniel Molina Date: Wed, 21 Sep 2011 19:01:31 +0200 Subject: [PATCH 10/23] feature #795: Use yaml conf file in EC2 --- src/cloud/ec2/lib/EC2QueryServer.rb | 4 ++-- src/cloud/ec2/lib/econe-server.rb | 34 ++++++++++++++--------------- src/cloud/occi/etc/occi-server.conf | 32 ++++++++++++++++----------- 3 files changed, 38 insertions(+), 32 deletions(-) diff --git a/src/cloud/ec2/lib/EC2QueryServer.rb b/src/cloud/ec2/lib/EC2QueryServer.rb index 5240d73be8..0783f3da34 100644 --- a/src/cloud/ec2/lib/EC2QueryServer.rb +++ b/src/cloud/ec2/lib/EC2QueryServer.rb @@ -137,10 +137,10 @@ class EC2QueryServer < CloudServer # Get the instance type and path if params['InstanceType'] != nil instance_type_name = params['InstanceType'] - instance_type = @config[:instance_types][instance_type_name] + instance_type = @config[:instance_types][instance_type_name.to_sym] if instance_type != nil - path = @config[:template_location] + "/#{instance_type['TEMPLATE']}" + path = @config[:template_location] + "/#{instance_type[:template]}" end end diff --git a/src/cloud/ec2/lib/econe-server.rb b/src/cloud/ec2/lib/econe-server.rb index def4803bb5..a3f9da84e7 100644 --- a/src/cloud/ec2/lib/econe-server.rb +++ b/src/cloud/ec2/lib/econe-server.rb @@ -46,34 +46,34 @@ require 'Configuration' include OpenNebula +############################################################################## +# Parse Configuration file +############################################################################## begin - 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 + conf = YAML.load_file(CONFIGURATION_FILE) rescue Exception => e - puts "Error starting server: #{e}" - exit(-1) + puts "Error parsing config file #{CONFIGURATION_FILE}: #{e.message}" + exit 1 end -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 +conf[:template_location] = TEMPLATE_LOCATION +conf[:views] = VIEWS_LOCATION + +CloudServer.print_configuration(conf) ############################################################################## # Sinatra Configuration ############################################################################## +set :config, conf set :host, settings.config[:server] set :port, settings.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 1 +end + ############################################################################## # Actions ############################################################################## diff --git a/src/cloud/occi/etc/occi-server.conf b/src/cloud/occi/etc/occi-server.conf index a92f46ce2a..ec74643b19 100644 --- a/src/cloud/occi/etc/occi-server.conf +++ b/src/cloud/occi/etc/occi-server.conf @@ -14,23 +14,29 @@ # limitations under the License. # #--------------------------------------------------------------------------- # -# OpenNebula server contact information -ONE_XMLRPC=http://localhost:2633/RPC2 +# OpenNebula sever contact information +:one_xmlrpc: http://localhost:2633/RPC2 -# Host and port where the occi server will run -SERVER= -PORT=4567 +# Host and port where econe server will run +:server: localhost +:port: 4567 # SSL proxy that serves the API (set if is being used) -#SSL_SERVER=https://localhost:443 - -AUTH=basic +#:ssl_server: fqdm.of.the.server # Configuration for OpenNebula's Virtual Networks -BRIDGE= +#:bridge: +:bridge: vbr1 + +:auth: basic # VM types allowed and its template file (inside templates directory) -VM_TYPE=[NAME=custom, TEMPLATE=custom.erb] -VM_TYPE=[NAME=small, TEMPLATE=small.erb] -VM_TYPE=[NAME=medium, TEMPLATE=medium.erb] -VM_TYPE=[NAME=large, TEMPLATE=large.erb] +:instance_types: + :custom: + :template: custom.erb + :small: + :template: small.erb + :medium: + :template: medium.erb + :large: + :template: large.erb From 3276069da2cbfeed8f88cedec5490e476007f513 Mon Sep 17 00:00:00 2001 From: Daniel Molina Date: Wed, 21 Sep 2011 19:02:30 +0200 Subject: [PATCH 11/23] feature #795: Use yaml conf file in OCCI --- src/cloud/occi/lib/VirtualMachineOCCI.rb | 4 ++-- src/cloud/occi/lib/occi-server.rb | 30 ++++++++++++------------ 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/cloud/occi/lib/VirtualMachineOCCI.rb b/src/cloud/occi/lib/VirtualMachineOCCI.rb index 6d62bfe724..7ed361cc00 100755 --- a/src/cloud/occi/lib/VirtualMachineOCCI.rb +++ b/src/cloud/occi/lib/VirtualMachineOCCI.rb @@ -77,8 +77,8 @@ class VirtualMachineOCCI < VirtualMachine if @vm_info != nil itype = @vm_info['INSTANCE_TYPE'] - if itype != nil and types[itype] != nil - @template = base + "/#{types[itype]['TEMPLATE']}" + if itype != nil and types[itype.to_sym] != nil + @template = base + "/#{types[itype.to_sym][:template]}" end end diff --git a/src/cloud/occi/lib/occi-server.rb b/src/cloud/occi/lib/occi-server.rb index 81950cc5ae..012dcb5912 100755 --- a/src/cloud/occi/lib/occi-server.rb +++ b/src/cloud/occi/lib/occi-server.rb @@ -45,34 +45,34 @@ require 'rubygems' require 'sinatra' require 'OCCIServer' -require 'Configuration' include OpenNebula +############################################################################## +# Parse Configuration file +############################################################################## begin - config = Configuration.new(CONFIGURATION_FILE) - config.add_configuration_value("TEMPLATE_LOCATION", TEMPLATE_LOCATION) - - instance_types = CloudServer.get_instance_types(config) - config.add_configuration_value("INSTANCE_TYPES", instance_types) - - CloudServer.print_configuration(config) - - set :config, config + conf = YAML.load_file(CONFIGURATION_FILE) rescue Exception => e - puts "Error starting server: #{e}" - exit(-1) + puts "Error parsing config file #{CONFIGURATION_FILE}: #{e.message}" + exit 1 end +conf[:template_location] = TEMPLATE_LOCATION + +CloudServer.print_configuration(conf) + +############################################################################## +# Sinatra Configuration +############################################################################## +set :config, conf + if CloudServer.is_port_open?(settings.config[:server], settings.config[:port]) puts "Port busy, please shutdown the service or move occi server port." exit end -############################################################################## -# Sinatra Configuration -############################################################################## set :host, settings.config[:server] set :port, settings.config[:port] From 1ae44ee2bc1550b00da8662437830c8628d71327 Mon Sep 17 00:00:00 2001 From: Daniel Molina Date: Wed, 21 Sep 2011 19:08:41 +0200 Subject: [PATCH 12/23] feature #795: Change sunstone init script --- src/sunstone/bin/sunstone-server | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/src/sunstone/bin/sunstone-server b/src/sunstone/bin/sunstone-server index 74ad261176..8afe86013f 100755 --- a/src/sunstone/bin/sunstone-server +++ b/src/sunstone/bin/sunstone-server @@ -19,13 +19,13 @@ if [ -z "$ONE_LOCATION" ]; then SUNSTONE_PID=/var/run/one/sunstone.pid - SUNSTONE_SERVER=/usr/lib/one/sunstone/config.ru + SUNSTONE_SERVER=/usr/lib/one/sunstone/sunstone-server.rb SUNSTONE_LOCK_FILE=/var/lock/one/.sunstone.lock SUNSTONE_LOG=/var/log/one/sunstone.log SUNSTONE_CONF=/etc/one/sunstone-server.conf else SUNSTONE_PID=$ONE_LOCATION/var/sunstone.pid - SUNSTONE_SERVER=$ONE_LOCATION/lib/sunstone/config.ru + SUNSTONE_SERVER=$ONE_LOCATION/lib/sunstone/sunstone-server.rb SUNSTONE_LOCK_FILE=$ONE_LOCATION/var/.sunstone.lock SUNSTONE_LOG=$ONE_LOCATION/var/sunstone.log SUNSTONE_CONF=$ONE_LOCATION/etc/sunstone-server.conf @@ -56,23 +56,16 @@ start() echo "Can not find $SUNSTONE_SERVER." exit 1 fi - - source $SUNSTONE_CONF - - lsof -i:$PORT &> /dev/null - if [ $? -eq 0 ]; then - echo "The port $PORT is being used. Please specify a different one." - exit 1 - fi # Start the sunstone daemon touch $SUNSTONE_LOCK_FILE - rackup $SUNSTONE_SERVER -s thin -p $PORT -o $HOST \ - -P $SUNSTONE_PID &> $SUNSTONE_LOG & + ruby $SUNSTONE_SERVER > $SUNSTONE_LOG 2>&1 & LASTPID=$! if [ $? -ne 0 ]; then echo "Error executing $SUNSTONE_SERVER, please check the log $SUNSTONE_LOG" exit 1 + else + echo $LASTPID > $SUNSTONE_PID fi sleep 1 @@ -83,7 +76,7 @@ start() exit 1 fi - echo "sunstone-server listening on $HOST:$PORT" + echo "sunstone-server started" } # From 9e3c3cb8982b09433154c0b00a3b437b3e9879d6 Mon Sep 17 00:00:00 2001 From: Daniel Molina Date: Wed, 21 Sep 2011 19:09:23 +0200 Subject: [PATCH 13/23] feature #795: Add hash_passwords option --- src/cloud/common/CloudAuth/BasicCloudAuth.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/cloud/common/CloudAuth/BasicCloudAuth.rb b/src/cloud/common/CloudAuth/BasicCloudAuth.rb index c4d53e8c03..63a60369d1 100644 --- a/src/cloud/common/CloudAuth/BasicCloudAuth.rb +++ b/src/cloud/common/CloudAuth/BasicCloudAuth.rb @@ -5,6 +5,10 @@ module BasicCloudAuth if auth.provided? && auth.basic? username, password = auth.credentials + if @conf[:hash_passwords] + password = Digest::SHA1.hexdigest(password) + end + one_pass = get_password(username) if one_pass && one_pass == password @token = "#{username}:#{password}" From b52e99c0dd408a18c6035b5bd1248ff744fad14c Mon Sep 17 00:00:00 2001 From: Daniel Molina Date: Wed, 21 Sep 2011 19:10:17 +0200 Subject: [PATCH 14/23] feature #795: Add econe.conf yaml --- src/cloud/ec2/etc/econe.conf | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/cloud/ec2/etc/econe.conf b/src/cloud/ec2/etc/econe.conf index 5b77f70931..6d71e86e56 100644 --- a/src/cloud/ec2/etc/econe.conf +++ b/src/cloud/ec2/etc/econe.conf @@ -15,16 +15,18 @@ #--------------------------------------------------------------------------- # # OpenNebula sever contact information -ONE_XMLRPC=http://localhost:2633/RPC2 +:one_xmlrpc: http://localhost:2633/RPC2 # Host and port where econe server will run -SERVER= -PORT=4567 +:server: localhost +:port: 4567 # SSL proxy that serves the API (set if is being used) -#SSL_SERVER=fqdm.of.the.server +#:ssl_server: fqdm.of.the.server -AUTH=ec2 +:auth: ec2 # VM types allowed and its template file (inside templates directory) -VM_TYPE=[NAME=m1.small, TEMPLATE=m1.small.erb] +:instance_types: + :m1.small: + :template: m1.small.erb From 4623bdc264c394c185eb1126fb1d2ead43e9a43b Mon Sep 17 00:00:00 2001 From: Daniel Molina Date: Wed, 21 Sep 2011 19:10:49 +0200 Subject: [PATCH 15/23] feature #795: Use yaml conf file in Sunstone --- src/sunstone/etc/sunstone-server.conf | 12 ++++++------ src/sunstone/sunstone-server.rb | 14 +++++++++++--- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/sunstone/etc/sunstone-server.conf b/src/sunstone/etc/sunstone-server.conf index 87e53d4047..b15fb6941d 100644 --- a/src/sunstone/etc/sunstone-server.conf +++ b/src/sunstone/etc/sunstone-server.conf @@ -1,12 +1,12 @@ # OpenNebula sever contact information -ONE_XMLRPC=http://localhost:2633/RPC2 +:one_xmlrpc: http://localhost:2633/RPC2 # Server Configuration -HOST=127.0.0.1 -PORT=9869 +:host: 127.0.0.1 +:port: 9869 -AUTH=basic +:auth: basic # VNC Configuration -VNC_PROXY_BASE_PORT=29876 -NOVNC_PATH= +:vnc_proxy_base_port: 29876 +:novnc_path: diff --git a/src/sunstone/sunstone-server.rb b/src/sunstone/sunstone-server.rb index c4935e8b61..99ddb7f862 100755 --- a/src/sunstone/sunstone-server.rb +++ b/src/sunstone/sunstone-server.rb @@ -46,16 +46,24 @@ require 'rubygems' require 'sinatra' require 'erb' -require 'Configuration' +require 'CloudAuth' require 'SunstoneServer' require 'SunstonePlugins' -set :config, Configuration.new(CONFIGURATION_FILE) +begin + conf = YAML.load_file(CONFIGURATION_FILE) +rescue Exception => e + puts "Error parsing config file #{CONFIGURATION_FILE}: #{e.message}" + exit 1 +end + +conf[:hash_passwords] = true ############################################################################## # Sinatra Configuration ############################################################################## use Rack::Session::Pool, :key => 'sunstone' +set :config, conf set :host, settings.config[:host] set :port, settings.config[:port] @@ -82,7 +90,7 @@ helpers do # Add a log message return [500, ""] end - + session[:user] = user['NAME'] session[:user_id] = user['ID'] session[:user_gid] = user['GID'] From 7e6c4eb33341558db2cd1f76a16fb499b3155eee Mon Sep 17 00:00:00 2001 From: Daniel Molina Date: Thu, 22 Sep 2011 13:16:59 +0200 Subject: [PATCH 16/23] feature #795: Check server error when authenticating --- src/cloud/ec2/lib/econe-server.rb | 8 +++++++- src/cloud/occi/lib/occi-server.rb | 7 ++++++- src/sunstone/sunstone-server.rb | 7 ++++++- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/cloud/ec2/lib/econe-server.rb b/src/cloud/ec2/lib/econe-server.rb index a3f9da84e7..2bc6f047c1 100644 --- a/src/cloud/ec2/lib/econe-server.rb +++ b/src/cloud/ec2/lib/econe-server.rb @@ -80,7 +80,13 @@ end before do @econe_server = EC2QueryServer.new(settings.config) - result = @econe_server.authenticate(request.env, params) + + begin + result = @econe_server.authenticate(request.env, params) + rescue Exception => e + error 500, e.message + end + if result # Add a log message error 400, error_xml("AuthFailure", 0) diff --git a/src/cloud/occi/lib/occi-server.rb b/src/cloud/occi/lib/occi-server.rb index 012dcb5912..161f0afc4c 100755 --- a/src/cloud/occi/lib/occi-server.rb +++ b/src/cloud/occi/lib/occi-server.rb @@ -82,7 +82,12 @@ set :port, settings.config[:port] before do @occi_server = OCCIServer.new(settings.config) - result = @occi_server.authenticate(request.env) + begin + result = @occi_server.authenticate(request.env) + rescue Exception => e + error 500, e.message + end + if result error 401, result end diff --git a/src/sunstone/sunstone-server.rb b/src/sunstone/sunstone-server.rb index 99ddb7f862..49e99f831b 100755 --- a/src/sunstone/sunstone-server.rb +++ b/src/sunstone/sunstone-server.rb @@ -78,7 +78,12 @@ helpers do def build_session cloud_auth = CloudAuth.new(settings.config) - result = cloud_auth.auth(request.env, params) + begin + result = cloud_auth.auth(request.env, params) + rescue Exception => e + error 500, e.message + end + if result return [401, ""] else From df4774562bc002b76c85fe6b939017e89008cd78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn?= Date: Thu, 22 Sep 2011 15:29:05 +0200 Subject: [PATCH 17/23] Feature #795: Add comments to occi-server.conf, leave the bridge unset by default --- src/cloud/occi/etc/occi-server.conf | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/cloud/occi/etc/occi-server.conf b/src/cloud/occi/etc/occi-server.conf index ec74643b19..2bf8516b9f 100644 --- a/src/cloud/occi/etc/occi-server.conf +++ b/src/cloud/occi/etc/occi-server.conf @@ -17,7 +17,7 @@ # OpenNebula sever contact information :one_xmlrpc: http://localhost:2633/RPC2 -# Host and port where econe server will run +# Host and port where OCCI server will run :server: localhost :port: 4567 @@ -25,9 +25,11 @@ #:ssl_server: fqdm.of.the.server # Configuration for OpenNebula's Virtual Networks -#:bridge: -:bridge: vbr1 +#:bridge: NAME_OF_DEFAULT_BRIDGE +# Authentication protocol for the OCCI server: +# basic, for OpenNebula's user-password scheme +# x509, for x509 certificates based authentication :auth: basic # VM types allowed and its template file (inside templates directory) From 91a7f0015bf69fe0a0895f1d04208eaaf22d18f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn?= Date: Thu, 22 Sep 2011 08:57:59 -0700 Subject: [PATCH 18/23] Feature #795: Add comments to econe.conf --- src/cloud/ec2/etc/econe.conf | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/cloud/ec2/etc/econe.conf b/src/cloud/ec2/etc/econe.conf index 6d71e86e56..151279862d 100644 --- a/src/cloud/ec2/etc/econe.conf +++ b/src/cloud/ec2/etc/econe.conf @@ -24,6 +24,9 @@ # SSL proxy that serves the API (set if is being used) #:ssl_server: fqdm.of.the.server +# Authentication protocol for the econe server: +# ec2, default Acess key and Secret key scheme +# x509, for x509 certificates based authentication :auth: ec2 # VM types allowed and its template file (inside templates directory) From 41cc2bbb161a7ac06ba6069e030857146326c189 Mon Sep 17 00:00:00 2001 From: Daniel Molina Date: Fri, 23 Sep 2011 01:14:53 +0200 Subject: [PATCH 19/23] feature #795: Fix bugs in X509CloudAuth --- src/cloud/common/CloudAuth/BasicCloudAuth.rb | 4 +-- src/cloud/common/CloudAuth/EC2CloudAuth.rb | 4 +-- src/cloud/common/CloudAuth/X509CloudAuth.rb | 33 +++++++++++--------- 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/cloud/common/CloudAuth/BasicCloudAuth.rb b/src/cloud/common/CloudAuth/BasicCloudAuth.rb index 63a60369d1..0bea9ecda1 100644 --- a/src/cloud/common/CloudAuth/BasicCloudAuth.rb +++ b/src/cloud/common/CloudAuth/BasicCloudAuth.rb @@ -12,7 +12,7 @@ module BasicCloudAuth one_pass = get_password(username) if one_pass && one_pass == password @token = "#{username}:#{password}" - @client = Client.new(@token, @xmlrpc, false) + @client = Client.new(@token, @conf[:one_xmlrpc], false) return nil else return "Authentication failure" @@ -21,4 +21,4 @@ module BasicCloudAuth return "Basic auth not provided" end end -end \ No newline at end of file +end diff --git a/src/cloud/common/CloudAuth/EC2CloudAuth.rb b/src/cloud/common/CloudAuth/EC2CloudAuth.rb index 2842d8c940..c47514ea16 100644 --- a/src/cloud/common/CloudAuth/EC2CloudAuth.rb +++ b/src/cloud/common/CloudAuth/EC2CloudAuth.rb @@ -21,7 +21,7 @@ module EC2CloudAuth end @token = "#{username}:#{one_pass}" - @client = Client.new(@token, @xmlrpc, false) + @client = Client.new(@token, @conf[:one_xmlrpc], false) return nil end @@ -73,4 +73,4 @@ module EC2CloudAuth return b64hmac end end -end \ No newline at end of file +end diff --git a/src/cloud/common/CloudAuth/X509CloudAuth.rb b/src/cloud/common/CloudAuth/X509CloudAuth.rb index 6488b7999e..835d65a565 100644 --- a/src/cloud/common/CloudAuth/X509CloudAuth.rb +++ b/src/cloud/common/CloudAuth/X509CloudAuth.rb @@ -1,14 +1,20 @@ module X509CloudAuth - # TBD Adapt to the new CloudAuth system - # Gets the username associated with a password # password:: _String_ the password # [return] _Hash_ with the username def get_username(password) - @user_pool.info - #STDERR.puts 'the password is ' + password - #STDERR.puts @user_pool["User[PASSWORD=\"#{password}\"]"] - username = @user_pool["User[PASSWORD=\"#{password}\"]/NAME"] + @oneadmin_client ||= OpenNebula::Client.new(nil, @conf[:one_xmlrpc]) + + if @user_pool.nil? + @user_pool ||= OpenNebula::UserPool.new(@oneadmin_client) + + rc = @user_pool.info + if OpenNebula.is_error?(rc) + raise rc.message + end + end + + username = @user_pool["USER[PASSWORD=\"#{password}\"]/NAME"] return username if (username != nil) # Check if the DN is part of a |-separted multi-DN password @@ -28,7 +34,7 @@ module X509CloudAuth if matched password = matched.to_s end - puts("The password is " + password) + return @user_pool["USER[PASSWORD=\"#{password}\"]/NAME"] end @@ -43,7 +49,7 @@ module X509CloudAuth while cert_line begin cert_array=cert_line.scan(/([^\s]*)\s/) - cert_array = cert_array[2..-3] + cert_array = cert_array[2..-2] cert_array.unshift('-----BEGIN CERTIFICATE-----') cert_array.push('-----END CERTIFICATE-----') cert_pem = cert_array.join("\n") @@ -72,16 +78,15 @@ module X509CloudAuth msg = "" msg << failed msg << "Username not found in certificate chain " - msg << chain_dn + msg << chain_dn if chain_dn raise msg end auth = ServerAuth.new - login = auth.login_token(username, subjectname, 300) + @token = auth.login_token(username, subjectname, 300) + @client = Client.new(@token, @conf[:one_xmlrpc], false) - STDERR.puts login - - return one_client_user("dummy", login) + return nil end -end \ No newline at end of file +end From b9c86afd38e95fa6b21eef77164c1069f31dc076 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Fri, 23 Sep 2011 11:20:19 +0200 Subject: [PATCH 20/23] Feature #795: Added special x509 sunstone login screen. Does not show username field and password field. (cherry picked from commit 015df5c95648680d225fc7f5e6683fdade48c7c5) --- src/sunstone/public/images/panel_short.png | Bin 0 -> 1825 bytes src/sunstone/sunstone-server.rb | 15 ++++--- src/sunstone/templates/login_x509.html | 47 +++++++++++++++++++++ 3 files changed, 56 insertions(+), 6 deletions(-) create mode 100644 src/sunstone/public/images/panel_short.png create mode 100644 src/sunstone/templates/login_x509.html diff --git a/src/sunstone/public/images/panel_short.png b/src/sunstone/public/images/panel_short.png new file mode 100644 index 0000000000000000000000000000000000000000..2eb300fe1fbc9ce1782fd1be8375fe607eb30578 GIT binary patch literal 1825 zcmb`IZB)`(8o>W#`IhFAr==`)hOu?B#%#^dP_g9)qd~+q$z2O6-y#r6l#~p74YZM1n)y8QW#-;<@9*B{-20s0hkKq| z{3QY7?&{?V0DwC-1|$Liglb=NogMA-1e9p819Fy#IS2^)w;AjkNAh6|2-rUMTaw0d zd&GqnlW-OQ)_Z;&5TN?zW_!@-92SpuQoF!h{uDwv^8IW3#X2l_AU<1V)SxoT4c=Yz z+EdbEPv--a5F&TYH)6eC-@V>u*;s6w|S*Y|IH-lWy1^JON zucp)D%2>Hd;;`$;!!z%=(YG_H`T48&xm<28=$4vrEj8gUDnRpXwO@i*Lq*(>S2Q#< z=nVz~lF#R#f(mO}$hpt^c=t44jQ%e&PW33~t_Pk#V6y0RGrzdFc=zDm2;EW09KF50 zz1qVk{MhyC>K~aiBh4%{7!0qS;%`DulpB>H`*JkvWH_eEr|(fgDwS&DR8>`JOG`?u zKMaEjl&|XEeE*Zqo6q+t}>?BmF*45R0CJdhc zJbGkU(oNbSL!RF2HPw-~_7=*~$;m_U^yz94U9fvZr!Vr^*|MH7Z%qe>uK1!pG&QfC z<{fDy;3uC43J}*kh9fZB(W0G28@&IjcAUO_8&k^MxEob-A~s5wF1ZXzDoII6zFz8PjmPK>5TX#-|weg&F?9u`AD+ zk(LL%-;FK1Gc=yTD7Ea*O-M@mX=LA;CB_3w}-xF+a0-FKGl20`zQV5$B$8PT_zaLVW;hww7(wXD!cRHy?YD!IXNq* z)Go9+lSXWrDk&+UK!sdhJ7Pj#*f+h&q>CBrUoT~@EaN?EH|Lp8m!I zD)fRX79G#@3K$jf$%|eU$}KniJbWWZ!*doUCBc;b)o^UtkagoB$qXgp!fLd?l~~Uw znH=six;WX4BvRbV*^MSDG>%@=2T>+>aH1GG_YV_F7O+(>Z3`4E^?&>}uxJ7(IQaK( F{|`jgVDA6` literal 0 HcmV?d00001 diff --git a/src/sunstone/sunstone-server.rb b/src/sunstone/sunstone-server.rb index 49e99f831b..e2bc06ab93 100755 --- a/src/sunstone/sunstone-server.rb +++ b/src/sunstone/sunstone-server.rb @@ -52,13 +52,12 @@ require 'SunstonePlugins' begin conf = YAML.load_file(CONFIGURATION_FILE) + conf[:hash_passwords] = true rescue Exception => e puts "Error parsing config file #{CONFIGURATION_FILE}: #{e.message}" exit 1 end -conf[:hash_passwords] = true - ############################################################################## # Sinatra Configuration ############################################################################## @@ -144,9 +143,10 @@ end # HTML Requests ############################################################################## get '/' do - return File.read(File.dirname(__FILE__)+ - '/templates/login.html') unless authorized? - + if !authorized? + templ = settings.config[:auth]=="basic"? "login.html" : "login_x509.html" + return File.read(File.dirname(__FILE__)+'/templates/'+templ) + end time = Time.now + 60 response.set_cookie("one-user", :value=>"#{session[:user]}", @@ -165,7 +165,10 @@ get '/' do end get '/login' do - File.read(SUNSTONE_ROOT_DIR+'/templates/login.html') + if !authorized? + templ = settings.confing[:auth]=="basic"? "login.html" : "login_x509.html" + return File.read(File.dirname(__FILE__)+'/templates/'+templ) + end end ############################################################################## diff --git a/src/sunstone/templates/login_x509.html b/src/sunstone/templates/login_x509.html new file mode 100644 index 0000000000..790e52028c --- /dev/null +++ b/src/sunstone/templates/login_x509.html @@ -0,0 +1,47 @@ + + + + OpenNebula Sunstone Login + + + + + + + + + + + + + + + +
+
+
+ +
+ Invalid username or password +
+
+ OpenNebula is not running +
+ +
+
+
+ + + + +
+
+
+
+ + From 0e002c67fbf2b3f86051c4a5594300fc225ef855 Mon Sep 17 00:00:00 2001 From: Daniel Molina Date: Fri, 23 Sep 2011 15:20:19 +0200 Subject: [PATCH 21/23] feature #795: Update install.sh sunstone x509 login --- install.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/install.sh b/install.sh index a32946f643..31643fff96 100755 --- a/install.sh +++ b/install.sh @@ -940,7 +940,8 @@ SUNSTONE_MODELS_JSON_FILES="src/sunstone/models/OpenNebulaJSON/HostJSON.rb \ src/sunstone/models/OpenNebulaJSON/AclJSON.rb \ src/sunstone/models/OpenNebulaJSON/VirtualNetworkJSON.rb" -SUNSTONE_TEMPLATE_FILES="src/sunstone/templates/login.html" +SUNSTONE_TEMPLATE_FILES="src/sunstone/templates/login.html \ + src/sunstone/templates/login_x509.html" SUNSTONE_VIEWS_FILES="src/sunstone/views/index.erb" @@ -1021,6 +1022,7 @@ SUNSTONE_PUBLIC_IMAGES_FILES="src/sunstone/public/images/ajax-loader.gif \ src/sunstone/public/images/opennebula-sunstone-big.png \ src/sunstone/public/images/opennebula-sunstone-small.png \ src/sunstone/public/images/panel.png \ + src/sunstone/public/images/panel_short.png \ src/sunstone/public/images/pbar.gif \ src/sunstone/public/images/Refresh-icon.png \ src/sunstone/public/images/vnc_off.png \ From 9ae26c552632dcc49f1e8b46bf01ec9d0c807d3a Mon Sep 17 00:00:00 2001 From: Daniel Molina Date: Fri, 23 Sep 2011 15:20:57 +0200 Subject: [PATCH 22/23] feature #795: Fix format in econe authentication error --- src/cloud/ec2/lib/econe-server.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cloud/ec2/lib/econe-server.rb b/src/cloud/ec2/lib/econe-server.rb index 2bc6f047c1..fc3e8413f1 100644 --- a/src/cloud/ec2/lib/econe-server.rb +++ b/src/cloud/ec2/lib/econe-server.rb @@ -84,7 +84,8 @@ before do begin result = @econe_server.authenticate(request.env, params) rescue Exception => e - error 500, e.message + # Add a log message + error 500, error_xml("AuthFailure", 0) end if result From 1257296e84a1eb04a4e65dc0b1acf23e16a0af56 Mon Sep 17 00:00:00 2001 From: Daniel Molina Date: Fri, 23 Sep 2011 16:19:50 +0200 Subject: [PATCH 23/23] feature #795: Add LICENSE headers --- src/cloud/common/CloudAuth.rb | 16 ++++++++++++++++ src/cloud/common/CloudAuth/BasicCloudAuth.rb | 16 ++++++++++++++++ src/cloud/common/CloudAuth/EC2CloudAuth.rb | 16 ++++++++++++++++ src/cloud/common/CloudAuth/X509CloudAuth.rb | 16 ++++++++++++++++ 4 files changed, 64 insertions(+) diff --git a/src/cloud/common/CloudAuth.rb b/src/cloud/common/CloudAuth.rb index 292c5718e4..859e4bea39 100644 --- a/src/cloud/common/CloudAuth.rb +++ b/src/cloud/common/CloudAuth.rb @@ -1,3 +1,19 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2011, OpenNebula Project Leads (OpenNebula.org) # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + class CloudAuth AUTH_MODULES = { "basic" => 'BasicCloudAuth', diff --git a/src/cloud/common/CloudAuth/BasicCloudAuth.rb b/src/cloud/common/CloudAuth/BasicCloudAuth.rb index 0bea9ecda1..ea6e703f0d 100644 --- a/src/cloud/common/CloudAuth/BasicCloudAuth.rb +++ b/src/cloud/common/CloudAuth/BasicCloudAuth.rb @@ -1,3 +1,19 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2011, OpenNebula Project Leads (OpenNebula.org) # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + module BasicCloudAuth def auth(env, params={}) auth = Rack::Auth::Basic::Request.new(env) diff --git a/src/cloud/common/CloudAuth/EC2CloudAuth.rb b/src/cloud/common/CloudAuth/EC2CloudAuth.rb index c47514ea16..fe8321f14c 100644 --- a/src/cloud/common/CloudAuth/EC2CloudAuth.rb +++ b/src/cloud/common/CloudAuth/EC2CloudAuth.rb @@ -1,3 +1,19 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2011, OpenNebula Project Leads (OpenNebula.org) # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + module EC2CloudAuth def auth(env, params={}) username = params['AWSAccessKeyId'] diff --git a/src/cloud/common/CloudAuth/X509CloudAuth.rb b/src/cloud/common/CloudAuth/X509CloudAuth.rb index 835d65a565..4348ee7e9b 100644 --- a/src/cloud/common/CloudAuth/X509CloudAuth.rb +++ b/src/cloud/common/CloudAuth/X509CloudAuth.rb @@ -1,3 +1,19 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2011, OpenNebula Project Leads (OpenNebula.org) # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + module X509CloudAuth # Gets the username associated with a password # password:: _String_ the password