diff --git a/src/authm_mad/remotes/ssh/ssh_auth.rb b/src/authm_mad/remotes/ssh/ssh_auth.rb index f545c840071..0a678d017fa 100644 --- a/src/authm_mad/remotes/ssh/ssh_auth.rb +++ b/src/authm_mad/remotes/ssh/ssh_auth.rb @@ -20,117 +20,121 @@ require 'open3' require 'tempfile' -# SSH key authentication class. It can be used as a driver for auth_mad -# as auth method is defined. It also holds some helper methods to be used -# by oneauth command -class OpenNebula::SshAuth - - # Initialize SshAuth object - # - # @param [Hash] default options for path - # @option options [String] :public_key public key for the user - # @option options [String] :private_key key private key for the user. - def initialize(options = {}) - @private_key = nil - @public_key = nil - - # Initialize the private key - if options[:private_key] - begin - @private_key = File.read(options[:private_key]) - rescue StandardError => e - raise "Cannot read #{options[:private_key]}\n #{e}" +module OpenNebula + + # SSH key authentication class. It can be used as a driver for auth_mad + # as auth method is defined. It also holds some helper methods to be used + # by oneauth command + class SshAuth + + # Initialize SshAuth object + # + # @param [Hash] default options for path + # @option options [String] :public_key public key for the user + # @option options [String] :private_key key private key for the user. + def initialize(options = {}) + @private_key = nil + @public_key = nil + + # Initialize the private key + if options[:private_key] + begin + @private_key = File.read(options[:private_key]) + rescue StandardError => e + raise "Cannot read #{options[:private_key]}\n #{e}" + end + + begin + @private_key_rsa = OpenSSL::PKey::RSA.new(@private_key) + rescue OpenSSL::PKey::RSAError + private_key_pem = openssh_to_pem(@private_key) + @private_key_rsa = OpenSSL::PKey::RSA.new(private_key_pem) + end end - begin - @private_key_rsa = OpenSSL::PKey::RSA.new(@private_key) - rescue OpenSSL::PKey::RSAError - private_key_pem = openssh_to_pem(@private_key) - @private_key_rsa = OpenSSL::PKey::RSA.new(private_key_pem) + # Initialize the public key + if options[:public_key] + @public_key = options[:public_key] + elsif !@private_key.nil? + # Init ssh keys using private key. public key is extracted in a + # format compatible with openssl. The public key does not contain + # "---- BEGIN/END PUBLIC KEY ----" and is in a single line + @public_key = @private_key_rsa.public_key.to_pem.split("\n") + @public_key = @public_key.reject {|l| l.match(/PUBLIC KEY/) }.join('') end - end - # Initialize the public key - if options[:public_key] - @public_key = options[:public_key] - elsif !@private_key.nil? - # Init ssh keys using private key. public key is extracted in a - # format compatible with openssl. The public key does not contain - # "---- BEGIN/END PUBLIC KEY ----" and is in a single line - @public_key = @private_key_rsa.public_key.to_pem.split("\n") - @public_key = @public_key.reject {|l| l.match(/PUBLIC KEY/) }.join('') - end + if @private_key.nil? && @public_key.nil? + raise 'You have to define at least one of the keys' + end - if @private_key.nil? && @public_key.nil? - raise 'You have to define at least one of the keys' + @public_key_rsa = OpenSSL::PKey::RSA.new(Base64.decode64(@public_key)) end - @public_key_rsa = OpenSSL::PKey::RSA.new(Base64.decode64(@public_key)) - end - - # Creates a login token for ssh authentication. - # By default it is valid for 1 hour but it can be changed to any number - # of seconds with expire parameter (in seconds) - def login_token(user, expire = 3600) - expire ||= 3600 + # Creates a login token for ssh authentication. + # By default it is valid for 1 hour but it can be changed to any number + # of seconds with expire parameter (in seconds) + def login_token(user, expire = 3600) + expire ||= 3600 - return encrypt("#{user}:#{Time.now.to_i + expire.to_i}") - end + return encrypt("#{user}:#{Time.now.to_i + expire.to_i}") + end - # Returns a valid password string to create a user using this auth driver. - # In this case the ssh public key. - def password - @public_key - end + # Returns a valid password string to create a user using this auth driver. + # In this case the ssh public key. + def password + @public_key + end - # Checks the proxy created with the login method - def authenticate(user, token) - begin - token_plain = decrypt(token) - t_user, time = token_plain.split(':') + # Checks the proxy created with the login method + def authenticate(user, token) + begin + token_plain = decrypt(token) + t_user, time = token_plain.split(':') - return 'invalid credentials' unless user == t_user - return 'ssh proxy expired, login again to renew it' if Time.now.to_i >= time.to_i + return 'invalid credentials' unless user == t_user + return 'ssh proxy expired, login again to renew it' if Time.now.to_i >= time.to_i - return true - rescue StandardError - return 'error' + return true + rescue StandardError + return 'error' + end end - end - private + private - def openssh_to_pem(private_key) - temp_file = Tempfile.new('private_key') + def openssh_to_pem(private_key) + temp_file = Tempfile.new('private_key') - File.write(temp_file.path, private_key) + File.write(temp_file.path, private_key) - # Use ssh-keygen to convert the key - command = "ssh-keygen -p -N '' -m PEM -f #{temp_file.path}" + # Use ssh-keygen to convert the key + command = "ssh-keygen -p -N '' -m PEM -f #{temp_file.path}" - _out, err, status = Open3.capture3(command) + _out, err, status = Open3.capture3(command) - raise "Failed to convert key: #{err}" unless status.success? + raise "Failed to convert key: #{err}" unless status.success? - pem_key = File.read(temp_file.path) - return pem_key - ensure - temp_file.close - temp_file.unlink if temp_file - end + pem_key = File.read(temp_file.path) + return pem_key + ensure + temp_file.close + temp_file.unlink if temp_file + end - ########################################################################### - # Methods to handle ssh keys - ########################################################################### - # Encrypts data with the private key of the user and returns - # base 64 encoded output in a single line - def encrypt(data) - Base64.encode64(@private_key_rsa.private_encrypt(data)).gsub!("\n", '').strip - end + ########################################################################### + # Methods to handle ssh keys + ########################################################################### + # Encrypts data with the private key of the user and returns + # base 64 encoded output in a single line + def encrypt(data) + Base64.encode64(@private_key_rsa.private_encrypt(data)).gsub!("\n", '').strip + end + + # Decrypts base 64 encoded data with pub_key (public key) + def decrypt(data) + @public_key_rsa.public_decrypt(Base64.decode64(data)) + end - # Decrypts base 64 encoded data with pub_key (public key) - def decrypt(data) - @public_key_rsa.public_decrypt(Base64.decode64(data)) end end