1
0
mirror of https://github.com/OpenNebula/one.git synced 2025-03-25 02:50:08 +03:00

F OpenNebula/one#6274: Support OpenSSH formatted ssh priv keys (#2726)

This commit is contained in:
Daniel Clavijo Coca 2023-09-18 03:07:09 -06:00 committed by GitHub
parent 42aaedd9b3
commit 12600a2269
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -14,24 +14,23 @@
# limitations under the License. #
#--------------------------------------------------------------------------- #
require 'pp'
require 'openssl'
require 'base64'
require 'fileutils'
module OpenNebula; end
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={})
def initialize(options = {})
@private_key = nil
@public_key = nil
@ -39,17 +38,22 @@ class OpenNebula::SshAuth
if options[:private_key]
begin
@private_key = File.read(options[:private_key])
rescue Exception => e
raise "Cannot read #{options[:private_key]}"
rescue StandardError => e
raise "Cannot read #{options[:private_key]}\n #{e}"
end
@private_key_rsa = OpenSSL::PKey::RSA.new(@private_key)
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
# Initialize the public key
if options[:public_key]
@public_key = options[:public_key]
elsif @private_key != nil
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
@ -58,16 +62,16 @@ class OpenNebula::SshAuth
end
if @private_key.nil? && @public_key.nil?
raise "You have to define at least one of the keys"
raise 'You have to define at least one of the keys'
end
@public_key_rsa = OpenSSL::PKey::RSA.new(Base64::decode64(@public_key))
@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)
def login_token(user, expire = 3600)
expire ||= 3600
return encrypt("#{user}:#{Time.now.to_i + expire.to_i}")
@ -83,35 +87,50 @@ class OpenNebula::SshAuth
def authenticate(user, token)
begin
token_plain = decrypt(token)
_user, time = token_plain.split(':')
t_user, time = token_plain.split(':')
if user == _user
if Time.now.to_i >= time.to_i
return "ssh proxy expired, login again to renew it"
else
return true
end
else
return "invalid credentials"
end
rescue
return "error"
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'
end
end
private
def openssh_to_pem(private_key)
temp_file = Tempfile.new('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}"
_out, err, status = Open3.capture3(command)
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
###########################################################################
# 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
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))
@public_key_rsa.public_decrypt(Base64.decode64(data))
end
end