1
0
mirror of https://github.com/OpenNebula/one.git synced 2025-08-24 17:49:28 +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

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
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(':')
return 'invalid credentials' unless user == t_user
return 'ssh proxy expired, login again to renew it' if Time.now.to_i >= time.to_i
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"
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