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:
parent
42aaedd9b3
commit
12600a2269
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user