1
0
mirror of https://github.com/OpenNebula/one.git synced 2024-12-23 17:33:56 +03:00

feature #754: Basic X509 authorization

This commit is contained in:
Ruben S. Montero 2011-08-19 03:13:50 +02:00
parent 3c369e67f2
commit 7347a36990
3 changed files with 184 additions and 1 deletions

View File

@ -226,6 +226,7 @@ VAR_DIRS="$VAR_LOCATION/remotes \
$VAR_LOCATION/remotes/auth \
$VAR_LOCATION/remotes/auth/plain \
$VAR_LOCATION/remotes/auth/ssh \
$VAR_LOCATION/remotes/auth/x509 \
$VAR_LOCATION/remotes/auth/dummy"
SUNSTONE_DIRS="$SUNSTONE_LOCATION/models \
@ -319,6 +320,7 @@ INSTALL_FILES=(
IM_PROBES_XEN_FILES:$VAR_LOCATION/remotes/im/xen.d
IM_PROBES_GANGLIA_FILES:$VAR_LOCATION/remotes/im/ganglia.d
AUTH_SSH_FILES:$VAR_LOCATION/remotes/auth/ssh
AUTH_X509_FILES:$VAR_LOCATION/remotes/auth/x509
AUTH_DUMMY_FILES:$VAR_LOCATION/remotes/auth/dummy
AUTH_PLAIN_FILES:$VAR_LOCATION/remotes/auth/plain
VMM_EXEC_KVM_SCRIPTS:$VAR_LOCATION/remotes/vmm/kvm
@ -485,7 +487,8 @@ RUBY_LIB_FILES="src/mad/ruby/ActionManager.rb \
src/mad/ruby/Ganglia.rb \
src/oca/ruby/OpenNebula.rb \
src/tm_mad/TMScript.rb \
src/authm_mad/remotes/ssh/ssh_auth.rb"
src/authm_mad/remotes/ssh/ssh_auth.rb \
src/authm_mad/remotes/x509/x509_auth.rb"
#-----------------------------------------------------------------------------
# MAD Script library files, to be installed under $LIB_LOCATION/<script lang>
@ -580,6 +583,8 @@ IM_PROBES_GANGLIA_FILES="src/im_mad/remotes/ganglia.d/ganglia_probe"
# Auth Manager drivers to be installed under $REMOTES_LOCATION/auth
#-------------------------------------------------------------------------------
AUTH_X509_FILES="src/authm_mad/remotes/x509/authenticate"
AUTH_SSH_FILES="src/authm_mad/remotes/ssh/authenticate"
AUTH_DUMMY_FILES="src/authm_mad/remotes/dummy/authenticate"

View File

@ -0,0 +1,54 @@
#!/usr/bin/env ruby
# -------------------------------------------------------------------------- #
# 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. #
#--------------------------------------------------------------------------- #
ONE_LOCATION=ENV["ONE_LOCATION"]
if !ONE_LOCATION
RUBY_LIB_LOCATION="/usr/lib/one/ruby"
ETC_LOCATION="/etc/one/"
else
RUBY_LIB_LOCATION=ONE_LOCATION+"/lib/ruby"
ETC_LOCATION=ONE_LOCATION+"/etc/"
end
$: << RUBY_LIB_LOCATION
require 'x509_auth'
require 'scripts_common'
user = ARGV[0] # username as registered in OpenNebula
pass = ARGV[1] # DN registered for this user
secret = ARGV[2] # Base64 string in the form proxy:usercert, usercert is pem
#OpenNebula.log_debug("Authenticating #{user}, with password #{pass} (#{secret})")
#TODO Check errors in these operations
dsecret = Base64::decode64(secret)
proxy, cert = dsecret.split(':')
x509_auth = X509Auth.new(:cert=>cert)
rc = x509_auth.authenticate(user,pass,proxy)
if rc == true
exit 0
else
OpenNebula.error_message rc
exit -1
end

View File

@ -0,0 +1,124 @@
# -------------------------------------------------------------------------- #
# 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. #
#--------------------------------------------------------------------------- #
require 'openssl'
require 'base64'
require 'fileutils'
# X509 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 X509Auth
# Initialize x509Auth object
#
# @param [Hash] default options for path
# @option options [String] :cert public cert for the user
# @option options [String] :key private key for the user
def initialize(options={})
@options={
:cert => nil,
:key => nil
}.merge!(options)
@cert = OpenSSL::X509::Certificate.new(@options[:cert])
@dn = @cert.subject.to_s
if @options[:key]
@key = OpenSSL::PKey::RSA.new(@options[:key])
end
end
###########################################################################
# Client side
###########################################################################
# Creates the login file for x509 authentication at ~/.one/one_x509.
# 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(user, expire=3600)
# Init proxy file path and creates ~/.one directory if needed
# Set instance variables
proxy_dir=ENV['HOME']+'/.one'
begin
FileUtils.mkdir_p(proxy_dir)
rescue Errno::EEXIST
end
one_proxy_path = proxy_dir + '/one_x509'
#Create the x509 proxy
time = Time.now.to_i+expire
text_to_sign = "#{@dn}:#{time}"
signed_text = encrypt(text_to_sign)
token = "#{signed_text}:#{@cert.to_pem}"
token64 = Base64::encode64(token).strip.delete!("\n")
proxy="#{user}:x509:#{token64}"
file = File.open(one_proxy_path, "w")
file.write(proxy)
file.close
# Help string
puts "export ONE_AUTH=#{ENV['HOME']}/.one/one_x509"
token64
end
###########################################################################
# Server side
###########################################################################
# auth method for auth_mad
def authenticate(user, pass, token)
begin
plain = decrypt(token)
subject, time_expire = plain.split(':')
if ((subject != @dn) || (subject != pass))
return "Certificate subject missmatch"
elsif Time.now.to_i >= time_expire.to_i
return "x509 proxy expired, login again to renew it"
end
return true
rescue
return "Can not decrypt security token"
end
end
private
###########################################################################
# 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)
return nil if !@key
Base64::encode64(@key.private_encrypt(data)).delete!("\n").strip
end
# Decrypts base 64 encoded data with pub_key (public key)
def decrypt(data)
@cert.public_key.public_decrypt(Base64::decode64(data))
end
end