mirror of
https://github.com/OpenNebula/one.git
synced 2025-03-16 22:50:10 +03:00
Merge branch 'master' of git.opennebula.org:one
This commit is contained in:
commit
6683dd64e8
@ -387,17 +387,26 @@ public:
|
||||
|
||||
/**
|
||||
* Gets the authorization requests in a single string
|
||||
* @return a space separated list of auth requests.
|
||||
* @return a space separated list of auth requests, or an empty string if
|
||||
* no auth requests were added
|
||||
*/
|
||||
string get_auths()
|
||||
{
|
||||
ostringstream oss;
|
||||
unsigned int i;
|
||||
|
||||
for (unsigned int i=0; i<auths.size(); i++)
|
||||
if ( auths.empty() )
|
||||
{
|
||||
return string();
|
||||
}
|
||||
|
||||
for (i=0; i<auths.size()-1; i++)
|
||||
{
|
||||
oss << auths[i] << " ";
|
||||
}
|
||||
|
||||
oss << auths[i];
|
||||
|
||||
return oss.str();
|
||||
};
|
||||
|
||||
|
@ -71,9 +71,11 @@ private:
|
||||
* "AUTHORIZE OPERATION_ID USER_ID REQUEST1 REQUEST2..."
|
||||
* @param oid an id to identify the request.
|
||||
* @param uid the user id.
|
||||
* @param requests space separated list of requests in the form OP:OBJ:ID
|
||||
* @param requests space separated list of requests in the form OP:OB:ID
|
||||
* @param acl is the authorization result using the ACL engine for
|
||||
* this request
|
||||
*/
|
||||
void authorize(int oid, int uid, const string& requests) const;
|
||||
void authorize(int oid, int uid, const string& requests, bool acl) const;
|
||||
|
||||
/**
|
||||
* Sends an authorization request to the MAD:
|
||||
|
45
install.sh
45
install.sh
@ -222,7 +222,13 @@ VAR_DIRS="$VAR_LOCATION/remotes \
|
||||
$VAR_LOCATION/remotes/hooks \
|
||||
$VAR_LOCATION/remotes/hooks/vnm \
|
||||
$VAR_LOCATION/remotes/image \
|
||||
$VAR_LOCATION/remotes/image/fs"
|
||||
$VAR_LOCATION/remotes/image/fs \
|
||||
$VAR_LOCATION/remotes/auth \
|
||||
$VAR_LOCATION/remotes/auth/plain \
|
||||
$VAR_LOCATION/remotes/auth/ssh \
|
||||
$VAR_LOCATION/remotes/auth/x509 \
|
||||
$VAR_LOCATION/remotes/auth/server \
|
||||
$VAR_LOCATION/remotes/auth/dummy"
|
||||
|
||||
SUNSTONE_DIRS="$SUNSTONE_LOCATION/models \
|
||||
$SUNSTONE_LOCATION/models/OpenNebulaJSON \
|
||||
@ -314,6 +320,11 @@ INSTALL_FILES=(
|
||||
IM_PROBES_KVM_FILES:$VAR_LOCATION/remotes/im/kvm.d
|
||||
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_SERVER_FILES:$VAR_LOCATION/remotes/auth/server
|
||||
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
|
||||
VMM_EXEC_XEN_SCRIPTS:$VAR_LOCATION/remotes/vmm/xen
|
||||
VMM_EXEC_XEN_KVM_POLL:$VAR_LOCATION/remotes/vmm/kvm/poll
|
||||
@ -455,8 +466,7 @@ BIN_FILES="src/nebula/oned \
|
||||
src/cli/onetemplate \
|
||||
src/cli/oneacl \
|
||||
src/onedb/onedb \
|
||||
share/scripts/one \
|
||||
src/authm_mad/oneauth"
|
||||
share/scripts/one"
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# C/C++ OpenNebula API Library & Development files
|
||||
@ -478,11 +488,9 @@ 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/one_usage.rb \
|
||||
src/authm_mad/quota.rb \
|
||||
src/authm_mad/simple_auth.rb \
|
||||
src/authm_mad/simple_permissions.rb \
|
||||
src/authm_mad/ssh_auth.rb"
|
||||
src/authm_mad/remotes/ssh/ssh_auth.rb \
|
||||
src/authm_mad/remotes/server/server_auth.rb \
|
||||
src/authm_mad/remotes/x509/x509_auth.rb"
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# MAD Script library files, to be installed under $LIB_LOCATION/<script lang>
|
||||
@ -556,7 +564,7 @@ VMM_EXEC_XEN_KVM_POLL="src/vmm_mad/remotes/poll_xen_kvm.rb"
|
||||
VMM_EXEC_GANGLIA_POLL="src/vmm_mad/remotes/poll_ganglia.rb"
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Information Manager Probes, to be installed under $LIB_LOCATION/remotes
|
||||
# Information Manager Probes, to be installed under $REMOTES_LOCATION/im
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
IM_PROBES_FILES="src/im_mad/remotes/run_probes"
|
||||
@ -573,6 +581,19 @@ IM_PROBES_KVM_FILES="src/im_mad/remotes/kvm.d/kvm.rb \
|
||||
|
||||
IM_PROBES_GANGLIA_FILES="src/im_mad/remotes/ganglia.d/ganglia_probe"
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Auth Manager drivers to be installed under $REMOTES_LOCATION/auth
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
AUTH_SERVER_FILES="src/authm_mad/remotes/server/authenticate"
|
||||
|
||||
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"
|
||||
|
||||
AUTH_PLAIN_FILES="src/authm_mad/remotes/plain/authenticate"
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Transfer Manager commands, to be installed under $LIB_LOCATION/tm_commands
|
||||
@ -682,11 +703,11 @@ TM_LVM_ETC_FILES="src/tm_mad/lvm/tm_lvm.conf \
|
||||
HM_ETC_FILES="src/hm_mad/hmrc"
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Hook Manager driver config. files, to be installed under $ETC_LOCATION/hm
|
||||
# Auth Manager drivers config. files, to be installed under $ETC_LOCATION/auth
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
AUTH_ETC_FILES="src/authm_mad/auth_mad \
|
||||
src/authm_mad/auth.conf"
|
||||
AUTH_ETC_FILES="src/authm_mad/remotes/server/server_auth.conf \
|
||||
src/authm_mad/remotes/x509/x509_auth.conf"
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Sample files, to be installed under $SHARE_LOCATION/examples
|
||||
|
@ -96,7 +96,10 @@ void AuthRequest::add_auth(Object ob,
|
||||
// User can show and MANAGE (change passwd) their own information
|
||||
( uid == ob_id_int && ob == USER &&
|
||||
( op == INFO || op == MANAGE )
|
||||
)
|
||||
) ||
|
||||
|
||||
// Users can show their group information
|
||||
( ob == GROUP && gid == ob_id_int && op == INFO )
|
||||
)
|
||||
{
|
||||
auth = true;
|
||||
@ -285,7 +288,8 @@ void AuthManager::authorize_action(AuthRequest * ar)
|
||||
|
||||
if (authm_md == 0)
|
||||
{
|
||||
goto error_driver;
|
||||
ar->message = "Could not find Authorization driver";
|
||||
goto error;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
@ -300,15 +304,23 @@ void AuthManager::authorize_action(AuthRequest * ar)
|
||||
|
||||
auths = ar->get_auths();
|
||||
|
||||
authm_md->authorize(ar->id, ar->uid, auths);
|
||||
if ( auths.empty() )
|
||||
{
|
||||
ar->message = "Empty authorization string";
|
||||
goto error;
|
||||
}
|
||||
|
||||
authm_md->authorize(ar->id, ar->uid, auths, ar->self_authorize);
|
||||
|
||||
return;
|
||||
|
||||
error_driver:
|
||||
ar->result = false;
|
||||
ar->message = "Could not find Authorization driver";
|
||||
error:
|
||||
ar->result = false;
|
||||
ar->notify();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
|
@ -25,20 +25,23 @@
|
||||
/* Driver ASCII Protocol Implementation */
|
||||
/* ************************************************************************** */
|
||||
|
||||
void AuthManagerDriver::authorize(int oid, int uid, const string& reqs) const
|
||||
void AuthManagerDriver::authorize(int oid,
|
||||
int uid,
|
||||
const string& reqs,
|
||||
bool acl) const
|
||||
{
|
||||
ostringstream os;
|
||||
|
||||
os << "AUTHORIZE " << oid << " " << uid << " " << reqs << endl;
|
||||
os << "AUTHORIZE " << oid << " " << uid << " " << reqs << " " << acl <<endl;
|
||||
|
||||
write(os);
|
||||
}
|
||||
|
||||
void AuthManagerDriver::authenticate(int oid,
|
||||
int uid,
|
||||
const string& username,
|
||||
const string& password,
|
||||
const string& session) const
|
||||
int uid,
|
||||
const string& username,
|
||||
const string& password,
|
||||
const string& session) const
|
||||
{
|
||||
ostringstream os;
|
||||
|
||||
|
@ -182,7 +182,7 @@ public:
|
||||
string astr = "VM:VGhpcyBpcyBhIHRlbXBsYXRlCg==:CREATE:-1:0:0 "
|
||||
"IMAGE:2:USE:3:0:0 "
|
||||
"NET:4:DELETE:5:1:0 "
|
||||
"HOST:6:MANAGE:7:1:0";
|
||||
"HOST:6:MANAGE:7:1:0 0";
|
||||
|
||||
ar.add_auth(AuthRequest::VM,
|
||||
"This is a template\n",
|
||||
@ -214,7 +214,6 @@ public:
|
||||
|
||||
am->trigger(AuthManager::AUTHORIZE,&ar);
|
||||
ar.wait();
|
||||
|
||||
/*
|
||||
if ( ar.result != false )
|
||||
{
|
||||
@ -229,6 +228,56 @@ public:
|
||||
//*/
|
||||
CPPUNIT_ASSERT(ar.result==false);
|
||||
CPPUNIT_ASSERT(ar.message==astr);
|
||||
|
||||
AuthRequest ar1(2, 2);
|
||||
|
||||
string astr1= "VM:VGhpcyBpcyBhIHRlbXBsYXRlCg==:CREATE:-1:0:0 0";
|
||||
|
||||
ar1.add_auth(AuthRequest::VM,
|
||||
"This is a template\n",
|
||||
0,
|
||||
AuthRequest::CREATE,
|
||||
-1,
|
||||
false);
|
||||
|
||||
am->trigger(AuthManager::AUTHORIZE,&ar1);
|
||||
ar1.wait();
|
||||
/*
|
||||
if ( ar1.result != false )
|
||||
{
|
||||
cout << endl << "ar.result: " << ar1.result << endl;
|
||||
}
|
||||
|
||||
if ( ar1.message != astr1 )
|
||||
{
|
||||
cout << endl << "ar.message: " << ar1.message;
|
||||
cout << endl << "expected: " << astr1 << endl;
|
||||
}
|
||||
//*/
|
||||
CPPUNIT_ASSERT(ar1.result==false);
|
||||
CPPUNIT_ASSERT(ar1.message==astr1);
|
||||
|
||||
AuthRequest ar2(2, 2);
|
||||
|
||||
string astr2= "Empty authorization string";
|
||||
|
||||
|
||||
am->trigger(AuthManager::AUTHORIZE,&ar2);
|
||||
ar2.wait();
|
||||
/*
|
||||
if ( ar1.result != false )
|
||||
{
|
||||
cout << endl << "ar.result: " << ar1.result << endl;
|
||||
}
|
||||
|
||||
if ( ar1.message != astr1 )
|
||||
{
|
||||
cout << endl << "ar.message: " << ar1.message;
|
||||
cout << endl << "expected: " << astr1 << endl;
|
||||
}
|
||||
//*/
|
||||
CPPUNIT_ASSERT(ar2.result==false);
|
||||
CPPUNIT_ASSERT(ar2.message==astr2);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,8 +0,0 @@
|
||||
:database: sqlite://auth.db
|
||||
:authentication: simple
|
||||
:quota:
|
||||
:enabled: false
|
||||
:defaults:
|
||||
:cpu: 10.0
|
||||
:memory: 1048576
|
||||
:num_vms: 10
|
@ -1 +0,0 @@
|
||||
|
@ -28,87 +28,130 @@ end
|
||||
|
||||
$: << RUBY_LIB_LOCATION
|
||||
|
||||
require 'pp'
|
||||
|
||||
require 'rubygems'
|
||||
require 'scripts_common'
|
||||
require 'OpenNebulaDriver'
|
||||
require 'simple_auth'
|
||||
require 'simple_permissions'
|
||||
require 'yaml'
|
||||
require 'sequel'
|
||||
require 'ssh_auth'
|
||||
require 'getoptlong'
|
||||
|
||||
class AuthorizationManager < OpenNebulaDriver
|
||||
def initialize
|
||||
# This is a generic AuthZ/AuthN driver able to manage multiple authentication
|
||||
# protocols (simultaneosly). It also supports the definition of custom
|
||||
# authorization methods
|
||||
class AuthDriver < OpenNebulaDriver
|
||||
|
||||
# Auth Driver Protocol constants
|
||||
ACTION = {
|
||||
:authN => "AUTHENTICATE",
|
||||
:authZ => "AUTHORIZE"
|
||||
}
|
||||
|
||||
|
||||
# Initialize an AuthDriver
|
||||
#
|
||||
# @param [String] the authorization method to be used, nil to use the
|
||||
# built-in ACL engine
|
||||
def initialize(authZ, nthreads)
|
||||
super(
|
||||
:concurrency => 15,
|
||||
:threaded => true
|
||||
"auth",
|
||||
:concurrency => nthreads,
|
||||
:threaded => nthreads > 0,
|
||||
:local_actions => {ACTION[:authN] => nil, ACTION[:authZ] => nil}
|
||||
)
|
||||
|
||||
register_action(ACTION[:authN].to_sym, method("authN"))
|
||||
register_action(ACTION[:authZ].to_sym, method("authZ"))
|
||||
|
||||
if authZ != nil
|
||||
@authZ_cmd = File.join(@local_scripts_path, authZ)
|
||||
@authZ_cmd = File.join(@authZ_cmd, ACTION[:authZ].downcase)
|
||||
else
|
||||
@authZ_cmd = nil
|
||||
end
|
||||
end
|
||||
|
||||
# Authenticate a user based in a string of the form user:secret when using the
|
||||
# driver secret is protocol:token
|
||||
# @param [String] the id for this request, used by OpenNebula core
|
||||
# to identify the request
|
||||
# @param [String] id of the user, "-1" if not in defined in OpenNebula
|
||||
# @param [Strgin] user filed of the auth string
|
||||
# @param [String] password of the user registered in OpenNebula "-" if none
|
||||
# @param [String] secret filed of the auth string
|
||||
def authN(request_id, user_id, user, password, secret)
|
||||
#OpenNebula.log_debug("authN: #{request_id} #{user_id} #{password} #{secret}")
|
||||
|
||||
secret_attr = secret.split(':')
|
||||
|
||||
if secret_attr.length == 1
|
||||
protocol = "plain"
|
||||
else
|
||||
protocol = secret_attr[0]
|
||||
secret_attr.shift
|
||||
end
|
||||
|
||||
#build path for the auth action
|
||||
#/var/lib/one/remotes/auth/<protocol>/authenticate
|
||||
authN_path = File.join(@local_scripts_path, protocol)
|
||||
|
||||
config_data=File.read(ETC_LOCATION+'/auth/auth.conf')
|
||||
STDERR.puts(config_data)
|
||||
@config=YAML::load(config_data)
|
||||
command = File.join(authN_path,ACTION[:authN].downcase)
|
||||
command << ' ' << user << ' ' << password << ' ' << secret_attr.join(' ')
|
||||
|
||||
local_action(command, request_id, ACTION[:authN])
|
||||
end
|
||||
|
||||
# Authenticate a user based in a string of the form user:secret when using the
|
||||
# driver secret is protocol:token
|
||||
# @param [String] the id for this request, used by OpenNebula core
|
||||
# to identify the request
|
||||
# @param [String] id of the user, "-1" if not in defined in OpenNebula
|
||||
# @param [Array] of auth strings, last element is the ACL evaluation of
|
||||
# the overall request (0 = denied, 1 = granted). Each request is in
|
||||
# the form:
|
||||
# OBJECT:<TEMPLATE_64|OBJECT_ID>:OPERATION:OWNER:PUBLIC:ACL_EVAL
|
||||
def authZ(request_id, user_id, *requests)
|
||||
|
||||
STDERR.puts @config.inspect
|
||||
requests.flatten!
|
||||
|
||||
#OpenNebula.log_debug("authZ: #{request_id} #{user_id} #{requests}")
|
||||
|
||||
database_url=@config[:database]
|
||||
@db=Sequel.connect(database_url)
|
||||
|
||||
# Get authentication driver
|
||||
begin
|
||||
driver_prefix=@config[:authentication].capitalize
|
||||
driver_name="#{driver_prefix}Auth"
|
||||
driver=Kernel.const_get(driver_name.to_sym)
|
||||
@authenticate=driver.new
|
||||
if @authZ_cmd == nil
|
||||
if requests[-1] == "1"
|
||||
result = RESULT[:success]
|
||||
else
|
||||
result = RESULT[:failure]
|
||||
end
|
||||
|
||||
send_message(ACTION[:authZ],result,request_id,"-")
|
||||
else
|
||||
command = @authZ_cmd.clone
|
||||
command << ' ' << user_id << ' ' << requests.join(' ')
|
||||
|
||||
STDERR.puts "Using '#{driver_prefix}' driver for authentication"
|
||||
rescue
|
||||
STDERR.puts "Driver '#{driver_prefix}' not found, "<<
|
||||
"using SimpleAuth instead"
|
||||
@authenticate=SimpleAuth.new
|
||||
end
|
||||
|
||||
@permissions=SimplePermissions.new(@db, OpenNebula::Client.new,
|
||||
@config)
|
||||
|
||||
register_action(:AUTHENTICATE, method('action_authenticate'))
|
||||
register_action(:AUTHORIZE, method('action_authorize'))
|
||||
end
|
||||
|
||||
def action_authenticate(request_id, user_id, user, password, token)
|
||||
auth=@authenticate.auth(user_id, user, password, token)
|
||||
if auth==true
|
||||
send_message('AUTHENTICATE', RESULT[:success],
|
||||
request_id, 'Successfully authenticated')
|
||||
else
|
||||
send_message('AUTHENTICATE', RESULT[:failure],
|
||||
request_id, auth)
|
||||
end
|
||||
end
|
||||
|
||||
def action_authorize(request_id, user_id, *tokens)
|
||||
begin
|
||||
auth=@permissions.auth(user_id, tokens.flatten)
|
||||
rescue Exception => e
|
||||
auth="Error: #{e}"
|
||||
end
|
||||
|
||||
if auth==true
|
||||
send_message('AUTHORIZE', RESULT[:success],
|
||||
request_id, 'success')
|
||||
else
|
||||
send_message('AUTHORIZE', RESULT[:failure],
|
||||
request_id, auth)
|
||||
local_action(command, request_id, ACTION[:authZ])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Auth Driver Main program
|
||||
opts = GetoptLong.new(
|
||||
[ '--threads', '-t', GetoptLong::REQUIRED_ARGUMENT ],
|
||||
[ '--authz', '-z', GetoptLong::REQUIRED_ARGUMENT ]
|
||||
)
|
||||
|
||||
threads = 15
|
||||
authz = nil
|
||||
|
||||
begin
|
||||
am=AuthorizationManager.new
|
||||
opts.each do |opt, arg|
|
||||
case opt
|
||||
when '--threads'
|
||||
threads = arg.to_i
|
||||
when '--authz'
|
||||
authz = arg
|
||||
end
|
||||
end
|
||||
rescue Exception => e
|
||||
puts "Error: #{e}"
|
||||
exit(-1)
|
||||
end
|
||||
|
||||
am.start_driver
|
||||
auth_driver = AuthDriver.new(authz, threads)
|
||||
|
||||
auth_driver.start_driver
|
||||
|
@ -1,90 +0,0 @@
|
||||
# -------------------------------------------------------------------------- #
|
||||
# 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 'OpenNebula'
|
||||
|
||||
# This class holds usage information for a virtual machine or
|
||||
# total usage for a user. Variables inside are cpu and memory
|
||||
# consumption
|
||||
class VmUsage
|
||||
attr_accessor :cpu, :memory, :num_vms
|
||||
def initialize(cpu, memory, num_vms=0)
|
||||
@cpu=cpu
|
||||
@memory=memory
|
||||
@num_vms=num_vms
|
||||
end
|
||||
end
|
||||
|
||||
# This class retrieves and caches vms and its consuption grouped
|
||||
# by users. 'update_user' method should be called to fill data for
|
||||
# a user before any calculation is made
|
||||
class OneUsage
|
||||
# 'client' is an OpenNebula::Client object used to connect
|
||||
# to OpenNebula daemon. Ideally it should connect as user 0
|
||||
def initialize(client)
|
||||
@client=client
|
||||
@users=Hash.new
|
||||
end
|
||||
|
||||
# Gets information about VMs defined for a user. It caches new
|
||||
# VMs and takes out from the cache deleted VMs
|
||||
def update_user(user)
|
||||
@users[user]=Hash.new if !@users[user]
|
||||
|
||||
vmpool=OpenNebula::VirtualMachinePool.new(@client, user)
|
||||
vmpool.info
|
||||
|
||||
one_ids=vmpool.map {|vm| vm.id }
|
||||
vms=@users[user]
|
||||
user_ids=vms.keys
|
||||
|
||||
deleted_vms=user_ids-one_ids
|
||||
added_vms=one_ids-user_ids
|
||||
|
||||
deleted_vms.each {|vmid| vms.delete(vmid) }
|
||||
|
||||
added_vms.each do |vmid|
|
||||
vm=OpenNebula::VirtualMachine.new(
|
||||
OpenNebula::VirtualMachine.build_xml(vmid), @client)
|
||||
vm.info
|
||||
|
||||
usage=VmUsage.new(vm['TEMPLATE/CPU'].to_f,
|
||||
vm['TEMPLATE/MEMORY'].to_i)
|
||||
vms[vmid.to_i]=usage
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the cache of defined VMs for a user. It is a hash with
|
||||
# VM id as key and VmUsage as value
|
||||
def vms(user)
|
||||
vms=@users[user]
|
||||
@users[user]=vms=Hash.new if !vms
|
||||
vms
|
||||
end
|
||||
|
||||
# Returns total consumption by a user into a VmUsage object
|
||||
def total(user)
|
||||
usage=VmUsage.new(0.0, 0, 0)
|
||||
|
||||
@users[user].each do |id, vm|
|
||||
usage.cpu+=vm.cpu
|
||||
usage.memory+=vm.memory
|
||||
usage.num_vms+=1
|
||||
end if @users[user]
|
||||
|
||||
usage
|
||||
end
|
||||
end
|
@ -1,112 +0,0 @@
|
||||
#!/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/"
|
||||
VAR_LOCATION="/var/lib/one"
|
||||
else
|
||||
RUBY_LIB_LOCATION=ONE_LOCATION+"/lib/ruby"
|
||||
ETC_LOCATION=ONE_LOCATION+"/etc/"
|
||||
VAR_LOCATION="#{ONE_LOCATION}/var"
|
||||
end
|
||||
|
||||
$: << RUBY_LIB_LOCATION
|
||||
$: << RUBY_LIB_LOCATION+'/cli'
|
||||
|
||||
require 'OpenNebula'
|
||||
|
||||
require 'rubygems'
|
||||
require 'sequel'
|
||||
require 'quota'
|
||||
require 'ssh_auth'
|
||||
require 'yaml'
|
||||
|
||||
require 'command_parser'
|
||||
require 'one_helper'
|
||||
|
||||
cmd=CommandParser::CmdParser.new(ARGV) do
|
||||
usage "oneauth COMMAND [args..]"
|
||||
|
||||
description "This command contains a set of utilities to " <<
|
||||
"manage authorization module."
|
||||
|
||||
set :option, CommandParser::OPTIONS
|
||||
|
||||
set :format, :userid, OpenNebulaHelper.name_to_id_desc("USER") do |arg|
|
||||
OpenNebulaHelper.name_to_id(arg, "USER")
|
||||
end
|
||||
|
||||
# Helpers
|
||||
def get_database
|
||||
config_data=File.read(ETC_LOCATION+'/auth/auth.conf')
|
||||
config=YAML::load(config_data)
|
||||
|
||||
database_url=config[:database]
|
||||
db=Sequel.connect(database_url)
|
||||
end
|
||||
|
||||
def add_quota(uid, cpu, memory, num_vms=nil)
|
||||
db=get_database
|
||||
quota=Quota.new(db, OpenNebula::Client.new)
|
||||
quota.set(uid.to_i, cpu.to_f, memory.to_i, num_vms)
|
||||
end
|
||||
|
||||
# Commands
|
||||
quotaset_desc = <<-EOT.unindent
|
||||
Sets CPU, MEMORY and NUM_VMs quota for a given user
|
||||
EOT
|
||||
|
||||
command 'quota-set', quotaset_desc , :userid, :cpu, :memory, :num_vms do
|
||||
Dir.chdir VAR_LOCATION
|
||||
begin
|
||||
add_quota(*args[1..4])
|
||||
rescue Exception => e
|
||||
exit_with_code -1, "Error starting server: #{e}"
|
||||
end
|
||||
exit_with_code 0
|
||||
end
|
||||
|
||||
login_desc = <<-EOT.unindent
|
||||
Generates authentication proxy. The last argument specifies
|
||||
the expiration time in seconds
|
||||
EOT
|
||||
|
||||
command 'login', login_desc, :userid, :text do
|
||||
user=args[0]
|
||||
time=args[1]
|
||||
pp args
|
||||
if time
|
||||
time=time.to_i
|
||||
else
|
||||
time=3600
|
||||
end
|
||||
|
||||
ssh=SshAuth.new
|
||||
ssh.login(user, time)
|
||||
exit_with_code 0
|
||||
end
|
||||
|
||||
command 'key', 'Gets public key' do
|
||||
ssh=SshAuth.new
|
||||
puts ssh.extract_public_key
|
||||
exit_with_code 0
|
||||
end
|
||||
end
|
@ -1,141 +0,0 @@
|
||||
# -------------------------------------------------------------------------- #
|
||||
# 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 'one_usage'
|
||||
|
||||
# Quota functionality for auth driver. Stores in database limits for each
|
||||
# user and using OneUsage is able to retrieve resource usage from
|
||||
# OpenNebula daemon and check if it is below limits
|
||||
class Quota
|
||||
attr_accessor :defaults
|
||||
|
||||
TABLE_NAME=:quotas
|
||||
|
||||
# 'db' is a Sequel database where to store user limits and client
|
||||
# is OpenNebula::Client used to connect to OpenNebula daemon
|
||||
def initialize(db, client, conf={})
|
||||
@db=db
|
||||
@client=client
|
||||
@conf={
|
||||
:defaults => {
|
||||
:cpu => nil,
|
||||
:memory => nil,
|
||||
:num_vms => nil
|
||||
}
|
||||
}.merge(conf)
|
||||
|
||||
@defaults=@conf[:defaults]
|
||||
|
||||
@usage=OneUsage.new(@client)
|
||||
|
||||
create_table
|
||||
@table=@db[TABLE_NAME]
|
||||
end
|
||||
|
||||
# Creates database quota table if it does not exist
|
||||
def create_table
|
||||
@db.create_table?(TABLE_NAME) do
|
||||
primary_key :id
|
||||
Integer :uid
|
||||
Float :cpu
|
||||
Integer :memory
|
||||
Integer :num_vms
|
||||
index :uid
|
||||
end
|
||||
end
|
||||
|
||||
# Adds new user limits
|
||||
def set(uid, cpu, memory, num_vms)
|
||||
data={
|
||||
:cpu => cpu,
|
||||
:memory => memory,
|
||||
:num_vms => num_vms
|
||||
}
|
||||
|
||||
quotas=@table.filter(:uid => uid)
|
||||
|
||||
if quotas.first
|
||||
#STDERR.puts "updating"
|
||||
quotas.update(data)
|
||||
else
|
||||
#STDERR.puts "inserting"
|
||||
@table.insert(data.merge!(:uid => uid))
|
||||
end
|
||||
end
|
||||
|
||||
# Gets user limits
|
||||
def get(uid)
|
||||
limit=@table.filter(:uid => uid).first
|
||||
if limit
|
||||
limit
|
||||
else
|
||||
@defaults
|
||||
end
|
||||
end
|
||||
|
||||
# Checks if the user is below resource limits. If new_vm is defined
|
||||
# checks if its requirements fit in limits
|
||||
def check(user, new_vm=nil)
|
||||
use=@usage.total(user)
|
||||
use_after=use.clone
|
||||
user_quota=get(user)
|
||||
if new_vm
|
||||
use_after.cpu+=new_vm.cpu.to_f
|
||||
use_after.memory+=new_vm.memory.to_i
|
||||
use_after.num_vms+=1
|
||||
end
|
||||
|
||||
STDERR.puts [user_quota, use_after, new_vm].inspect
|
||||
|
||||
error_message=""
|
||||
|
||||
if !(!user_quota[:cpu] || use_after.cpu<=user_quota[:cpu])
|
||||
error_message<<"Cpu quota exceeded (Quota: #{user_quota[:cpu]}, "+
|
||||
"Used: #{use.cpu}"
|
||||
error_message<<", asked: #{new_vm.cpu.to_f}" if new_vm
|
||||
error_message<<")."
|
||||
end
|
||||
|
||||
if !(!user_quota[:memory] || use_after.memory<=user_quota[:memory])
|
||||
error_message<<" Memory quota exceeded (Quota: "+
|
||||
"#{user_quota[:memory]}, Used: #{use.memory}"
|
||||
error_message<<", asked: #{new_vm.memory.to_i}" if new_vm
|
||||
error_message<<")."
|
||||
end
|
||||
|
||||
if !(!user_quota[:num_vms] || use_after.num_vms<=user_quota[:num_vms])
|
||||
error_message<<" Num VMS quota exceeded (Quota: "+
|
||||
"#{user_quota[:memory]}, Used: #{use.num_vms})."
|
||||
end
|
||||
|
||||
if error_message==""
|
||||
false
|
||||
else
|
||||
error_message.strip
|
||||
end
|
||||
end
|
||||
|
||||
# Updates user resource consuption
|
||||
def update(user)
|
||||
@usage.update_user(user)
|
||||
end
|
||||
|
||||
# Get cache for the user
|
||||
def get_user(user)
|
||||
@usage.vms(user)
|
||||
end
|
||||
end
|
||||
|
24
src/authm_mad/remotes/dummy/authenticate
Executable file
24
src/authm_mad/remotes/dummy/authenticate
Executable file
@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# -------------------------------------------------------------------------- #
|
||||
# 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. #
|
||||
#--------------------------------------------------------------------------- #
|
||||
|
||||
# $1 = username
|
||||
# $2 = password
|
||||
echo $*
|
||||
|
||||
|
||||
|
41
src/authm_mad/simple_auth.rb → src/authm_mad/remotes/plain/authenticate
Normal file → Executable file
41
src/authm_mad/simple_auth.rb → src/authm_mad/remotes/plain/authenticate
Normal file → Executable file
@ -1,3 +1,5 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
# -------------------------------------------------------------------------- #
|
||||
# Copyright 2002-2011, OpenNebula Project Leads (OpenNebula.org) #
|
||||
# #
|
||||
@ -14,22 +16,29 @@
|
||||
# limitations under the License. #
|
||||
#--------------------------------------------------------------------------- #
|
||||
|
||||
# Password authentication module. This one just compares stored password
|
||||
# with the token sent by the client.
|
||||
class SimpleAuth
|
||||
# Method called by authentication driver. It should awnser true if
|
||||
# successful or a string with the error message if failure. All
|
||||
# parameters are strings extracted from the authorization message.
|
||||
#
|
||||
# * user_id: OpenNebula user identifier
|
||||
# * user: user name
|
||||
# * password: password stored in OpenNebula dabatase
|
||||
# * token: password sent by the client trying to connect
|
||||
def auth(user_id, user, password, token)
|
||||
auth=(password==token)
|
||||
auth="Invalid credentials" if auth!=true or token=='-'
|
||||
auth
|
||||
end
|
||||
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 'scripts_common'
|
||||
|
||||
user = ARGV[0]
|
||||
pass = ARGV[1]
|
||||
secret = ARGV[2]
|
||||
|
||||
#OpenNebula.log_debug("Authenticating #{user}, with password #{pass} (#{secret})")
|
||||
|
||||
if pass == secret
|
||||
exit 0
|
||||
else
|
||||
OpenNebula.error_message "Invalid credentials"
|
||||
exit -1
|
||||
end
|
55
src/authm_mad/remotes/server/authenticate
Executable file
55
src/authm_mad/remotes/server/authenticate
Executable file
@ -0,0 +1,55 @@
|
||||
#!/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 'server_auth'
|
||||
require 'scripts_common'
|
||||
|
||||
user = ARGV[0] # username as registered in OpenNebula
|
||||
pass = ARGV[1] # password for this user
|
||||
secret = ARGV[2] # Base64 encoded secret as obtained from login_token
|
||||
|
||||
#OpenNebula.log_debug("Authenticating #{user}, with password #{pass} (#{secret})")
|
||||
|
||||
begin
|
||||
server_auth = ServerAuth.new
|
||||
dsecret = Base64::decode64(secret)
|
||||
|
||||
rc = server_auth.authenticate(user, pass, dsecret)
|
||||
rescue => e
|
||||
OpenNebula.error_message e.message
|
||||
exit -1
|
||||
end
|
||||
|
||||
if rc == true
|
||||
exit 0
|
||||
else
|
||||
OpenNebula.error_message rc
|
||||
exit -1
|
||||
end
|
4
src/authm_mad/remotes/server/server_auth.conf
Normal file
4
src/authm_mad/remotes/server/server_auth.conf
Normal file
@ -0,0 +1,4 @@
|
||||
# Path to the certificate used by the OpenNebula Services
|
||||
# Certificates must be in PEM format
|
||||
:one_cert: "/etc/one/auth/cert.pem"
|
||||
:one_key: "/etc/one/auth/pk.pem"
|
96
src/authm_mad/remotes/server/server_auth.rb
Normal file
96
src/authm_mad/remotes/server/server_auth.rb
Normal file
@ -0,0 +1,96 @@
|
||||
# -------------------------------------------------------------------------- #
|
||||
# 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'
|
||||
|
||||
require 'x509_auth'
|
||||
|
||||
# Server authentication class. This authmethod can be used by opennebula services
|
||||
# to let access authenticated users by other means. It is based on x509 server
|
||||
# certificates
|
||||
class ServerAuth < X509Auth
|
||||
###########################################################################
|
||||
#Constants with paths to relevant files and defaults
|
||||
###########################################################################
|
||||
|
||||
SERVER_AUTH_CONF_PATH = ETC_LOCATION + "/auth/server_auth.conf"
|
||||
|
||||
SERVER_DEFAULTS = {
|
||||
:one_cert => ETC_LOCATION + "/auth/cert.pem",
|
||||
:one_key => ETC_LOCATION + "/auth/key.pem"
|
||||
}
|
||||
|
||||
###########################################################################
|
||||
|
||||
def initialize()
|
||||
@options = SERVER_DEFAULTS
|
||||
|
||||
load_options(SERVER_AUTH_CONF_PATH)
|
||||
|
||||
begin
|
||||
certs = [ File.read(@options[:one_cert]) ]
|
||||
key = File.read(@options[:one_key])
|
||||
|
||||
super(:certs_pem => certs,
|
||||
:key_pem => key)
|
||||
rescue
|
||||
raise
|
||||
end
|
||||
end
|
||||
|
||||
# Generates a login token in the form:
|
||||
# user_name:server:user_name:user_pass:time_expires
|
||||
# - user_name:user_pass:time_expires is encrypted with the server certificate
|
||||
def login_token(user, user_pass, expire)
|
||||
|
||||
expires = Time.now.to_i+expire
|
||||
|
||||
token_txt = "#{user}:#{user_pass}:#{expires}"
|
||||
|
||||
token = encrypt(token_txt)
|
||||
token64 = Base64::encode64(token).strip.delete("\n")
|
||||
|
||||
login_out = "#{user}:server:#{token64}"
|
||||
|
||||
login_out
|
||||
end
|
||||
|
||||
###########################################################################
|
||||
# Server side
|
||||
###########################################################################
|
||||
# auth method for auth_mad
|
||||
def authenticate(user, pass, signed_text)
|
||||
begin
|
||||
# Decryption demonstrates that the user posessed the private key.
|
||||
_user, user_pass, expires = decrypt(signed_text).split(':')
|
||||
|
||||
return "User name missmatch" if user != _user
|
||||
|
||||
return "login token expired" if Time.now.to_i >= expires.to_i
|
||||
|
||||
# Check that the signed password matches one for the user.
|
||||
if !pass.split('|').include?(user_pass)
|
||||
return "User password missmatch"
|
||||
end
|
||||
|
||||
return true
|
||||
rescue => e
|
||||
return e.message
|
||||
end
|
||||
end
|
||||
end
|
53
src/authm_mad/remotes/ssh/authenticate
Executable file
53
src/authm_mad/remotes/ssh/authenticate
Executable file
@ -0,0 +1,53 @@
|
||||
#!/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 'ssh_auth'
|
||||
require 'scripts_common'
|
||||
|
||||
user = ARGV[0]
|
||||
pass = ARGV[1]
|
||||
secret = ARGV[2]
|
||||
|
||||
#OpenNebula.log_debug("Authenticating #{user}, with password #{pass} (#{secret})")
|
||||
begin
|
||||
ssh_auth = SshAuth.new(:public_key=>pass)
|
||||
rescue Exception => e
|
||||
OpenNebula.error_message e.message
|
||||
exit -1
|
||||
end
|
||||
|
||||
rc = ssh_auth.authenticate(user,secret)
|
||||
|
||||
if rc == true
|
||||
exit 0
|
||||
else
|
||||
OpenNebula.error_message rc
|
||||
exit -1
|
||||
end
|
131
src/authm_mad/remotes/ssh/ssh_auth.rb
Normal file
131
src/authm_mad/remotes/ssh/ssh_auth.rb
Normal file
@ -0,0 +1,131 @@
|
||||
# -------------------------------------------------------------------------- #
|
||||
# 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 'pp'
|
||||
require 'openssl'
|
||||
require 'base64'
|
||||
require 'fileutils'
|
||||
|
||||
# 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
|
||||
LOGIN_PATH = ENV['HOME']+'/.one/one_ssh'
|
||||
|
||||
attr_reader :public_key
|
||||
|
||||
# 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
|
||||
|
||||
if options[:private_key]
|
||||
begin
|
||||
@private_key = File.read(options[:private_key])
|
||||
rescue Exception => e
|
||||
raise "Cannot read #{options[:private_key]}"
|
||||
end
|
||||
end
|
||||
|
||||
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 RSA PUBLIC KEY ----" and is in a single line
|
||||
key = OpenSSL::PKey::RSA.new(@private_key)
|
||||
|
||||
@public_key = key.public_key.to_pem.split("\n")
|
||||
@public_key = @public_key.reject {|l| l.match(/RSA PUBLIC KEY/) }.join('')
|
||||
end
|
||||
|
||||
if @private_key.nil? && @public_key.nil?
|
||||
raise "You have to define at least one of the keys"
|
||||
end
|
||||
end
|
||||
|
||||
# Creates the login file for ssh authentication at ~/.one/one_ssh.
|
||||
# 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)
|
||||
expire ||= 3600
|
||||
|
||||
# Init proxy file path and creates ~/.one directory if needed
|
||||
proxy_dir = File.dirname(LOGIN_PATH)
|
||||
|
||||
begin
|
||||
FileUtils.mkdir_p(proxy_dir)
|
||||
rescue Errno::EEXIST
|
||||
end
|
||||
|
||||
# Generate security token
|
||||
time = Time.now.to_i + expire.to_i
|
||||
|
||||
secret_plain = "#{user}:#{time}"
|
||||
secret_crypted = encrypt(secret_plain)
|
||||
|
||||
proxy = "#{user}:ssh:#{secret_crypted}"
|
||||
|
||||
file = File.open(LOGIN_PATH, "w")
|
||||
file.write(proxy)
|
||||
file.close
|
||||
|
||||
secret_crypted
|
||||
end
|
||||
|
||||
# Checks the proxy created with the login method
|
||||
def authenticate(user, token)
|
||||
begin
|
||||
token_plain = decrypt(token)
|
||||
_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"
|
||||
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)
|
||||
rsa=OpenSSL::PKey::RSA.new(@private_key)
|
||||
Base64::encode64(rsa.private_encrypt(data)).gsub!(/\n/, '').strip
|
||||
end
|
||||
|
||||
# Decrypts base 64 encoded data with pub_key (public key)
|
||||
def decrypt(data)
|
||||
rsa=OpenSSL::PKey::RSA.new(Base64::decode64(@public_key))
|
||||
rsa.public_decrypt(Base64::decode64(data))
|
||||
end
|
||||
end
|
60
src/authm_mad/remotes/x509/authenticate
Executable file
60
src/authm_mad/remotes/x509/authenticate
Executable file
@ -0,0 +1,60 @@
|
||||
#!/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 encoded text and certificate chain text:cert_0:cert_1:..., certs in pem format
|
||||
|
||||
#OpenNebula.log_debug("Authenticating #{user}, with password #{pass} (#{secret})")
|
||||
|
||||
begin
|
||||
dsecret = Base64::decode64(secret)
|
||||
asecret = dsecret.split(':')
|
||||
|
||||
token = asecret[0]
|
||||
certs = asecret[1..-1]
|
||||
|
||||
x509_auth = X509Auth.new(:certs_pem=>certs)
|
||||
|
||||
rc = x509_auth.authenticate(user, pass, token)
|
||||
rescue => e
|
||||
OpenNebula.error_message e.message
|
||||
exit -1
|
||||
end
|
||||
|
||||
if rc == true
|
||||
exit 0
|
||||
else
|
||||
OpenNebula.error_message rc
|
||||
exit -1
|
||||
end
|
3
src/authm_mad/remotes/x509/x509_auth.conf
Normal file
3
src/authm_mad/remotes/x509/x509_auth.conf
Normal file
@ -0,0 +1,3 @@
|
||||
# Path to the trusted CA directory. It should contain the trusted CA's for
|
||||
# the server, each CA certificate shoud be name CA_hash.0
|
||||
:ca_dir: "/etc/one/auth/certificates"
|
236
src/authm_mad/remotes/x509/x509_auth.rb
Normal file
236
src/authm_mad/remotes/x509/x509_auth.rb
Normal file
@ -0,0 +1,236 @@
|
||||
# -------------------------------------------------------------------------- #
|
||||
# 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'
|
||||
require 'yaml'
|
||||
|
||||
# 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
|
||||
###########################################################################
|
||||
#Constants with paths to relevant files and defaults
|
||||
###########################################################################
|
||||
if !ENV["ONE_LOCATION"]
|
||||
ETC_LOCATION = "/etc/one"
|
||||
else
|
||||
ETC_LOCATION = ENV["ONE_LOCATION"] + "/etc"
|
||||
end
|
||||
|
||||
LOGIN_PATH = ENV['HOME']+'/.one/one_x509'
|
||||
|
||||
X509_AUTH_CONF_PATH = ETC_LOCATION + "/auth/x509_auth.conf"
|
||||
|
||||
X509_DEFAULTS = {
|
||||
:ca_dir => ETC_LOCATION + "/auth/certificates"
|
||||
}
|
||||
|
||||
###########################################################################
|
||||
# Initialize x509Auth object
|
||||
#
|
||||
# @param [Hash] default options for path
|
||||
# @option options [String] :certs_pem
|
||||
# cert chain array in colon-separated pem format
|
||||
# @option options [String] :key_pem
|
||||
# key in pem format
|
||||
# @option options [String] :ca_dir
|
||||
# directory of trusted CA's. Needed for auth method, not for login.
|
||||
def initialize(options={})
|
||||
@options={
|
||||
:certs_pem => nil,
|
||||
:key_pem => nil,
|
||||
:ca_dir => X509_DEFAULTS[:ca_dir]
|
||||
}.merge!(options)
|
||||
|
||||
load_options(X509_AUTH_CONF_PATH)
|
||||
|
||||
@cert_chain = @options[:certs_pem].collect do |cert_pem|
|
||||
OpenSSL::X509::Certificate.new(cert_pem)
|
||||
end
|
||||
|
||||
if @options[:key_pem]
|
||||
@key = OpenSSL::PKey::RSA.new(@options[:key_pem])
|
||||
end
|
||||
end
|
||||
|
||||
###########################################################################
|
||||
# Client side
|
||||
###########################################################################
|
||||
|
||||
# Creates the login file for x509 authentication at ~/.one/one_x509.
|
||||
# By default it is valid as long as the certificate is valid. It can
|
||||
# be changed to any number of seconds with expire parameter (sec.)
|
||||
def login(user, expire=0)
|
||||
write_login(login_token(user,expire))
|
||||
end
|
||||
|
||||
# Returns the dn of the user certificate
|
||||
def dn
|
||||
@cert_chain[0].subject.to_s
|
||||
end
|
||||
|
||||
# Generates a login token in the form:
|
||||
# user_name:x509:user_name:time_expires:cert_chain
|
||||
# - user_name:time_expires is encrypted with the user certificate
|
||||
# - user_name:time_expires:cert_chain is base64 encoded
|
||||
def login_token(user, expire)
|
||||
if expire != 0
|
||||
expires = Time.now.to_i + expire.to_i
|
||||
else
|
||||
expires = @cert_chain[0].not_after.to_i
|
||||
end
|
||||
|
||||
text_to_sign = "#{user}:#{expires}"
|
||||
signed_text = encrypt(text_to_sign)
|
||||
|
||||
certs_pem = @cert_chain.collect{|cert| cert.to_pem}.join(":")
|
||||
|
||||
token = "#{signed_text}:#{certs_pem}"
|
||||
token64 = Base64::encode64(token).strip.delete("\n")
|
||||
|
||||
login_out = "#{user}:x509:#{token64}"
|
||||
|
||||
login_out
|
||||
end
|
||||
|
||||
###########################################################################
|
||||
# Server side
|
||||
###########################################################################
|
||||
# auth method for auth_mad
|
||||
def authenticate(user, pass, signed_text)
|
||||
begin
|
||||
# Decryption demonstrates that the user posessed the private key.
|
||||
_user, expires = decrypt(signed_text).split(':')
|
||||
|
||||
return "User name missmatch" if user != _user
|
||||
|
||||
return "x509 proxy expired" if Time.now.to_i >= expires.to_i
|
||||
|
||||
# Some DN in the chain must match a DN in the password
|
||||
dn_ok = @cert_chain.each do |cert|
|
||||
break true if pass.split('|').include?(cert.subject.to_s.delete("\s"))
|
||||
end
|
||||
|
||||
unless dn_ok == true
|
||||
return "Certificate subject missmatch"
|
||||
end
|
||||
|
||||
validate
|
||||
|
||||
return true
|
||||
rescue => e
|
||||
return e.message
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
# Writes a login_txt to the login file as defined in LOGIN_PATH
|
||||
# constant
|
||||
def write_login(login_txt)
|
||||
# Inits login file path and creates ~/.one directory if needed
|
||||
# Set instance variables
|
||||
login_dir = File.dirname(LOGIN_PATH)
|
||||
|
||||
begin
|
||||
FileUtils.mkdir_p(login_dir)
|
||||
rescue Errno::EEXIST
|
||||
end
|
||||
|
||||
file = File.open(LOGIN_PATH, "w")
|
||||
file.write(login_txt)
|
||||
file.close
|
||||
end
|
||||
|
||||
# Load class options form a configuration file (yaml syntax)
|
||||
def load_options(conf_file)
|
||||
if File.readable?(conf_file)
|
||||
config = File.read(conf_file)
|
||||
|
||||
@options.merge!(YAML::load(config))
|
||||
end
|
||||
end
|
||||
|
||||
###########################################################################
|
||||
# Methods to encrpyt/decrypt 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_chain[0].public_key.public_decrypt(Base64::decode64(data))
|
||||
end
|
||||
|
||||
###########################################################################
|
||||
# Validate the user certificate
|
||||
###########################################################################
|
||||
def validate
|
||||
now = Time.now
|
||||
failed = "Could not validate user credentials: "
|
||||
|
||||
# Check start time and end time of certificates
|
||||
@cert_chain.each do |cert|
|
||||
if cert.not_before > now || cert.not_after < now
|
||||
raise failed + "Certificate not valid. Current time is " +
|
||||
now.localtime.to_s + "."
|
||||
end
|
||||
end
|
||||
|
||||
begin
|
||||
# Validate the proxy certifcates
|
||||
signee = @cert_chain[0]
|
||||
|
||||
@cert_chain[1..-1].each do |cert|
|
||||
if !((signee.issuer.to_s == cert.subject.to_s) &&
|
||||
(signee.verify(cert.public_key)))
|
||||
raise failed + signee.subject.to_s + " with issuer " +
|
||||
signee.issuer.to_s + " was not verified by " +
|
||||
cert.subject.to_s + "."
|
||||
end
|
||||
signee = cert
|
||||
end
|
||||
|
||||
# Validate the End Entity certificate
|
||||
if !@options[:ca_dir]
|
||||
raise failed + "No certifcate authority directory was specified."
|
||||
end
|
||||
|
||||
begin
|
||||
ca_hash = signee.issuer.hash.to_s(16)
|
||||
ca_path = @options[:ca_dir] + '/' + ca_hash + '.0'
|
||||
|
||||
ca_cert = OpenSSL::X509::Certificate.new(File.read(ca_path))
|
||||
|
||||
if !((signee.issuer.to_s == ca_cert.subject.to_s) &&
|
||||
(signee.verify(ca_cert.public_key)))
|
||||
raise failed + signee.subject.to_s + " with issuer " +
|
||||
signee.issuer.to_s + " was not verified by " +
|
||||
ca_cert.subject.to_s + "."
|
||||
end
|
||||
|
||||
signee = ca_cert
|
||||
end while ca_cert.subject.to_s != ca_cert.issuer.to_s
|
||||
rescue
|
||||
raise
|
||||
end
|
||||
end
|
||||
end
|
@ -1,112 +0,0 @@
|
||||
# -------------------------------------------------------------------------- #
|
||||
# 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 'quota'
|
||||
require 'base64'
|
||||
|
||||
class SimplePermissions
|
||||
|
||||
def initialize(database, client, conf={})
|
||||
@quota=Quota.new(database, client, conf[:quota] || {})
|
||||
@quota_enabled=conf[:quota][:enabled]
|
||||
end
|
||||
|
||||
# Returns message if result is false, true otherwise
|
||||
def auth_message(result, message)
|
||||
result ? true : message
|
||||
end
|
||||
|
||||
# Extracts cpu and memory resources from the VM template sent in
|
||||
# authorization message
|
||||
def get_vm_usage(data)
|
||||
vm_xml=Base64::decode64(data)
|
||||
vm=OpenNebula::VirtualMachine.new(
|
||||
OpenNebula::XMLElement.build_xml(vm_xml, 'TEMPLATE'),
|
||||
OpenNebula::Client.new)
|
||||
|
||||
# Should set more sensible defaults or get driver configuration
|
||||
cpu=vm['CPU']
|
||||
cpu||=1.0
|
||||
cpu=cpu.to_f
|
||||
|
||||
memory=vm['MEMORY']
|
||||
memory||=64
|
||||
memory=memory.to_f
|
||||
|
||||
VmUsage.new(cpu, memory)
|
||||
end
|
||||
|
||||
# Checks if the quota is enabled, and if it is not exceeded
|
||||
def check_quota_enabled(uid, object, id, auth_result)
|
||||
if @quota_enabled and object=='VM' and auth_result
|
||||
STDERR.puts 'quota enabled'
|
||||
@quota.update(uid.to_i)
|
||||
if message=@quota.check(uid.to_i, get_vm_usage(id))
|
||||
auth_result=message
|
||||
end
|
||||
end
|
||||
|
||||
return auth_result
|
||||
end
|
||||
|
||||
# Method called by authorization driver
|
||||
def auth(uid, tokens)
|
||||
result=true
|
||||
|
||||
tokens.each do |token|
|
||||
object, id, action, owner, pub=token.split(':')
|
||||
result=auth_object(uid.to_s, object, id, action, owner, pub)
|
||||
break result if result!=true
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
# Authorizes each of the tokens. All parameters are strings. Pub
|
||||
# means public when "1" and private when "0"
|
||||
def auth_object(uid, object, id, action, owner, pub)
|
||||
return true if uid=='0'
|
||||
|
||||
auth_result=false
|
||||
|
||||
case action
|
||||
when 'CREATE'
|
||||
auth_result=true if %w{VM NET IMAGE TEMPLATE}.include? object
|
||||
auth_result = check_quota_enabled(uid, object, id, auth_result)
|
||||
|
||||
when 'INSTANTIATE'
|
||||
auth_result = true if %w{VM}.include? object
|
||||
auth_result = check_quota_enabled(uid, object, id, auth_result)
|
||||
|
||||
when 'DELETE'
|
||||
auth_result = (owner == uid)
|
||||
|
||||
when 'USE'
|
||||
if %w{VM NET IMAGE TEMPLATE}.include? object
|
||||
auth_result = ((owner == uid) | (pub=='1'))
|
||||
elsif object == 'HOST'
|
||||
auth_result=true
|
||||
end
|
||||
|
||||
when 'MANAGE'
|
||||
auth_result = (owner == uid)
|
||||
|
||||
when 'INFO'
|
||||
end
|
||||
|
||||
return auth_result
|
||||
end
|
||||
end
|
@ -1,114 +0,0 @@
|
||||
# -------------------------------------------------------------------------- #
|
||||
# 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 'pp'
|
||||
require 'openssl'
|
||||
require 'base64'
|
||||
require 'fileutils'
|
||||
|
||||
# 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
|
||||
# Reads id_rsa file from user .ssh directory
|
||||
def get_priv_key
|
||||
path=ENV['HOME']+'/.ssh/id_rsa'
|
||||
File.read(path)
|
||||
end
|
||||
|
||||
# Returns an opened file object to ~/.one/one_ssh
|
||||
def get_proxy_file
|
||||
proxy_dir=ENV['HOME']+'/.one'
|
||||
|
||||
# Creates ~/.one directory if it does not exist
|
||||
begin
|
||||
FileUtils.mkdir_p(proxy_dir)
|
||||
rescue Errno::EEXIST
|
||||
end
|
||||
|
||||
File.open(proxy_dir+'/one_ssh', "w")
|
||||
end
|
||||
|
||||
# Encrypts data with the private key of the user and returns
|
||||
# base 64 encoded output
|
||||
def encrypt(data)
|
||||
rsa=OpenSSL::PKey::RSA.new(get_priv_key)
|
||||
# base 64 output is joined into a single line as opennebula
|
||||
# ascii protocol ends messages with newline
|
||||
Base64::encode64(rsa.private_encrypt(data)).gsub!(/\n/, '').strip
|
||||
end
|
||||
|
||||
# Decrypts base 64 encoded data with pub_key (public key)
|
||||
def decrypt(data, pub_key)
|
||||
rsa=OpenSSL::PKey::RSA.new(Base64::decode64(pub_key))
|
||||
rsa.public_decrypt(Base64::decode64(data))
|
||||
end
|
||||
|
||||
# Gets public key from user's private key file. ssh private keys are
|
||||
# stored in a format not compatible with openssl. This method
|
||||
# will be used by oneauth so users can extrar their public keys
|
||||
# that the administrator then can add to the database
|
||||
def extract_public_key
|
||||
key=OpenSSL::PKey::RSA.new(get_priv_key)
|
||||
public_key=key.public_key.to_pem.split("\n")
|
||||
# gets rid of "---- BEGIN/END RSA PUBLIC KEY ----" lines and
|
||||
# joins resuklt into a single line
|
||||
public_key.reject {|l| l.match(/RSA PUBLIC KEY/) }.join('')
|
||||
end
|
||||
|
||||
# Creates the login file for ssh authentication at ~/.one/one_ssh.
|
||||
# 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)
|
||||
time=Time.now.to_i+expire
|
||||
proxy_text="#{user}:#{time}"
|
||||
proxy_crypted=encrypt(proxy_text)
|
||||
proxy="#{user}:plain:#{proxy_crypted}"
|
||||
file=get_proxy_file
|
||||
file.write(proxy)
|
||||
file.close
|
||||
|
||||
# Help string
|
||||
puts "export ONE_AUTH=#{ENV['HOME']}/.one/one_ssh"
|
||||
|
||||
proxy_crypted
|
||||
end
|
||||
|
||||
# auth method for auth_mad
|
||||
def auth(user_id, user, password, token)
|
||||
begin
|
||||
decrypted=decrypt(token, password)
|
||||
|
||||
username, time=decrypted.split(':')
|
||||
|
||||
pp [username, time]
|
||||
|
||||
if user==username
|
||||
if Time.now.to_i>=time.to_i
|
||||
"proxy expired, login again"
|
||||
else
|
||||
true
|
||||
end
|
||||
else
|
||||
"invalid credentials"
|
||||
end
|
||||
rescue
|
||||
"error"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
@ -166,6 +166,7 @@ EOT
|
||||
|
||||
extra_options = comm[:options] if comm
|
||||
parse(extra_options)
|
||||
|
||||
if comm
|
||||
check_args!(comm_name, comm[:arity], comm[:args_format])
|
||||
|
||||
@ -209,15 +210,23 @@ EOT
|
||||
next
|
||||
else
|
||||
shown_opts << o[:name]
|
||||
short = o[:short].split(' ').first
|
||||
printf opt_format, "#{short}, #{o[:large]}", o[:description]
|
||||
|
||||
str = ""
|
||||
str << o[:short].split(' ').first << ', ' if o[:short]
|
||||
str << o[:large]
|
||||
|
||||
printf opt_format, str, o[:description]
|
||||
puts
|
||||
end
|
||||
}
|
||||
}
|
||||
|
||||
@opts.each{ |o|
|
||||
printf opt_format, "#{o[:short]}, #{o[:large]}", o[:description]
|
||||
str = ""
|
||||
str << o[:short] if o[:short]
|
||||
str << o[:large]
|
||||
|
||||
printf opt_format, str, o[:description]
|
||||
puts
|
||||
}
|
||||
end
|
||||
@ -292,7 +301,13 @@ EOT
|
||||
merge = @opts
|
||||
merge = @opts + extra_options if extra_options
|
||||
merge.flatten.each do |e|
|
||||
opts.on(e[:short],e[:large], e[:format],e[:description]) do |o|
|
||||
args = []
|
||||
args << e[:short] if e[:short]
|
||||
args << e[:large]
|
||||
args << e[:format]
|
||||
args << e[:description]
|
||||
|
||||
opts.on(*args) do |o|
|
||||
if e[:proc]
|
||||
e[:proc].call(o, @options)
|
||||
elsif e[:name]=="help"
|
||||
|
@ -47,6 +47,92 @@ class OneUserHelper < OpenNebulaHelper::OneHelper
|
||||
return 0, password
|
||||
end
|
||||
|
||||
def password(options)
|
||||
if options[:ssh]
|
||||
require 'ssh_auth'
|
||||
|
||||
options[:key] ||= ENV['HOME']+'/.ssh/id_rsa'
|
||||
|
||||
begin
|
||||
sshauth = SshAuth.new(:private_key=>options[:key])
|
||||
rescue Exception => e
|
||||
return -1, e.message
|
||||
end
|
||||
|
||||
return 0, sshauth.public_key
|
||||
elsif options[:x509]
|
||||
require 'x509_auth'
|
||||
|
||||
options[:cert] ||= ENV['X509_USER_CERT']
|
||||
|
||||
begin
|
||||
cert = [File.read(options[:cert])]
|
||||
x509auth = X509Auth.new(:certs_pem=>cert)
|
||||
rescue Exception => e
|
||||
return -1, e.message
|
||||
end
|
||||
|
||||
return 0, x509auth.dn
|
||||
else
|
||||
return -1, "You have to specify an Auth method or define a password"
|
||||
end
|
||||
end
|
||||
|
||||
def login(username, options)
|
||||
if options[:ssh]
|
||||
require 'ssh_auth'
|
||||
|
||||
options[:key] ||= ENV['HOME']+'/.ssh/id_rsa'
|
||||
|
||||
begin
|
||||
auth = SshAuth.new(:private_key=>options[:key])
|
||||
rescue Exception => e
|
||||
return -1, e.message
|
||||
end
|
||||
elsif options[:x509]
|
||||
require 'x509_auth'
|
||||
|
||||
options[:cert] ||= ENV['X509_USER_CERT']
|
||||
options[:key] ||= ENV['X509_USER_KEY']
|
||||
|
||||
begin
|
||||
certs = [File.read(options[:cert])]
|
||||
key = File.read(options[:key])
|
||||
|
||||
auth = X509Auth.new(:certs_pem=>certs, :key_pem=>key)
|
||||
rescue Exception => e
|
||||
return -1, e.message
|
||||
end
|
||||
elsif options[:x509_proxy]
|
||||
require 'x509_auth'
|
||||
|
||||
options[:proxy] ||= ENV['X509_PROXY_CERT']
|
||||
|
||||
begin
|
||||
proxy = File.read(options[:proxy])
|
||||
|
||||
rc = proxy.scan(/(-+BEGIN CERTIFICATE-+\n[^-]*\n-+END CERTIFICATE-+)/)
|
||||
certs = rc.flatten!
|
||||
|
||||
rc = proxy.match(/(-+BEGIN RSA PRIVATE KEY-+\n[^-]*\n-+END RSA PRIVATE KEY-+)/)
|
||||
|
||||
key = rc[1]
|
||||
|
||||
auth = X509Auth.new(:certs_pem=>certs, :key_pem=>key)
|
||||
rescue => e
|
||||
return -1, e.message
|
||||
end
|
||||
else
|
||||
return -1, "You have to specify an Auth method"
|
||||
end
|
||||
|
||||
options[:time] ||= 3600
|
||||
|
||||
auth.login(username, options[:time])
|
||||
|
||||
return 0, 'export ONE_AUTH=' << auth.class::LOGIN_PATH
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def factory(id=nil)
|
||||
|
@ -92,8 +92,9 @@ cmd=CommandParser::CmdParser.new(ARGV) do
|
||||
Shows information for the given Group
|
||||
EOT
|
||||
|
||||
command :show, show_desc, :groupid, :options=>OpenNebulaHelper::XML do
|
||||
helper.show_resource(args[0],options)
|
||||
command :show, show_desc,[:groupid, nil], :options=>OpenNebulaHelper::XML do
|
||||
group = args[0] || OpenNebula::Group::SELF
|
||||
helper.show_resource(group,options)
|
||||
end
|
||||
|
||||
end
|
||||
|
104
src/cli/oneuser
104
src/cli/oneuser
@ -59,7 +59,56 @@ cmd=CommandParser::CmdParser.new(ARGV) do
|
||||
:description => "Store plain password"
|
||||
}
|
||||
|
||||
create_options = [READ_FILE, PLAIN]
|
||||
SSH={
|
||||
:name => "ssh",
|
||||
:large => "--ssh",
|
||||
:description => "SSH Auth system"
|
||||
}
|
||||
|
||||
X509={
|
||||
:name => "x509",
|
||||
:large => "--x509",
|
||||
:description => "x509 Auth system for x509 certificates"
|
||||
}
|
||||
|
||||
X509_PROXY={
|
||||
:name => "x509_proxy",
|
||||
:large => "--x509_proxy",
|
||||
:description => "x509 Auth system based on x509 proxy certificates"
|
||||
}
|
||||
|
||||
KEY={
|
||||
:name => "key",
|
||||
:short => "-k path_to_private_key_pem",
|
||||
:large => "--key path_to_private_key_pem",
|
||||
:format => String,
|
||||
:description => "Path to the Private Key of the User"
|
||||
}
|
||||
|
||||
CERT={
|
||||
:name => "cert",
|
||||
:short => "-c path_to_user_cert_pem",
|
||||
:large => "--cert path_to_user_cert_pem",
|
||||
:format => String,
|
||||
:description => "Path to the Certificate of the User"
|
||||
}
|
||||
|
||||
PROXY={
|
||||
:name => "proxy",
|
||||
:large => "--proxy path_to_user_proxy_pem",
|
||||
:format => String,
|
||||
:description => "Path to the user proxy certificate"
|
||||
}
|
||||
|
||||
TIME={
|
||||
:name => "time",
|
||||
:large => "--time x",
|
||||
:format => Integer,
|
||||
:description => "Token duration in hours, (default 1)"
|
||||
}
|
||||
|
||||
create_options = [READ_FILE, PLAIN, SSH, X509, KEY, CERT]
|
||||
login_options = [SSH, X509, X509_PROXY, KEY, CERT, PROXY, TIME]
|
||||
|
||||
########################################################################
|
||||
# Formatters for arguments
|
||||
@ -86,13 +135,44 @@ cmd=CommandParser::CmdParser.new(ARGV) do
|
||||
|
||||
create_desc = <<-EOT.unindent
|
||||
Creates a new User
|
||||
Examples:
|
||||
oneuser create my_user my_password
|
||||
oneuser create my_user /tmp/mypass -r
|
||||
oneuser create my_user --ssh --key /tmp/id_rsa
|
||||
oneuser create my_user --x509 --cert /tmp/my_cert.pem
|
||||
EOT
|
||||
|
||||
command :create, create_desc, :username, :password,
|
||||
command :create, create_desc, :username, [:password, nil],
|
||||
:options=>create_options do
|
||||
helper.create_resource(options) do |user|
|
||||
user.allocate(args[0], args[1])
|
||||
if options[:ssh] or options[:x509]
|
||||
rc = helper.password(options)
|
||||
if rc.first == 0
|
||||
pass = rc[1]
|
||||
else
|
||||
exit_with_code *rc
|
||||
end
|
||||
else
|
||||
pass = args[1]
|
||||
end
|
||||
|
||||
helper.create_resource(options) do |user|
|
||||
user.allocate(args[0], pass)
|
||||
end
|
||||
end
|
||||
|
||||
login_desc = <<-EOT.unindent
|
||||
Creates the Login token for authentication
|
||||
Examples:
|
||||
oneuser login my_user --ssh --key /tmp/id_rsa --time 72000
|
||||
oneuser login my_user --x509 --cert /tmp/my_cert.pem \
|
||||
--key /tmp/my_key.pk --time 72000
|
||||
oneuser login my_user --x509_proxy --proxy /tmp/my_cert.pem \
|
||||
--time 72000
|
||||
EOT
|
||||
|
||||
command :login, login_desc, :username, [:password, nil],
|
||||
:options=>login_options do
|
||||
helper.login(args[0], options)
|
||||
end
|
||||
|
||||
delete_desc = <<-EOT.unindent
|
||||
@ -109,9 +189,21 @@ cmd=CommandParser::CmdParser.new(ARGV) do
|
||||
Changes the given User's password
|
||||
EOT
|
||||
|
||||
command :passwd, passwd_desc, :userid, :password do
|
||||
command :passwd, passwd_desc, :userid, :password,
|
||||
:options=>create_options do
|
||||
if options[:ssh] or options[:x509]
|
||||
rc = helper.password(options)
|
||||
if rc.first == 0
|
||||
pass = rc[1]
|
||||
else
|
||||
exit_with_code *rc
|
||||
end
|
||||
else
|
||||
pass = args[1]
|
||||
end
|
||||
|
||||
helper.perform_action(args[0],options,"Password changed") do |user|
|
||||
user.passwd(args[1])
|
||||
user.passwd(pass)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -90,20 +90,22 @@ class OpenNebulaDriver < ActionManager
|
||||
def initialize(directory, options={})
|
||||
@options={
|
||||
:concurrency => 10,
|
||||
:threaded => true,
|
||||
:retries => 0,
|
||||
:threaded => true,
|
||||
:retries => 0,
|
||||
:local_actions => {}
|
||||
}.merge!(options)
|
||||
|
||||
super(@options[:concurrency], @options[:threaded])
|
||||
|
||||
@retries = @options[:retries]
|
||||
@send_mutex=Mutex.new
|
||||
@local_actions=@options[:local_actions]
|
||||
@retries = @options[:retries]
|
||||
@local_actions = @options[:local_actions]
|
||||
|
||||
@send_mutex = Mutex.new
|
||||
|
||||
# set default values
|
||||
@config = read_configuration
|
||||
@remote_scripts_base_path=@config['SCRIPTS_REMOTE_DIR']
|
||||
@remote_scripts_base_path = @config['SCRIPTS_REMOTE_DIR']
|
||||
|
||||
if ENV['ONE_LOCATION'] == nil
|
||||
@local_scripts_base_path = "/var/lib/one/remotes"
|
||||
else
|
||||
@ -111,8 +113,8 @@ class OpenNebulaDriver < ActionManager
|
||||
end
|
||||
|
||||
# dummy paths
|
||||
@remote_scripts_path=File.join(@remote_scripts_base_path, directory)
|
||||
@local_scripts_path=File.join(@local_scripts_base_path, directory)
|
||||
@remote_scripts_path = File.join(@remote_scripts_base_path, directory)
|
||||
@local_scripts_path = File.join(@local_scripts_base_path, directory)
|
||||
|
||||
register_action(:INIT, method("init"))
|
||||
end
|
||||
|
@ -96,19 +96,14 @@ module OpenNebula
|
||||
raise "ONE_AUTH file not present"
|
||||
end
|
||||
|
||||
if !one_secret.match(".+:.+")
|
||||
raise "Authorization file malformed"
|
||||
end
|
||||
tokens = one_secret.chomp.split(':')
|
||||
|
||||
|
||||
one_secret=~/^(.+?):(.+)$/
|
||||
user=$1
|
||||
password=$2
|
||||
|
||||
if password.match(/^plain:/)
|
||||
@one_auth = "#{user}:#{password.split(':').last}"
|
||||
if tokens.length > 2
|
||||
@one_auth = one_secret
|
||||
elsif tokens.length == 2
|
||||
@one_auth = "#{tokens[0]}:#{Digest::SHA1.hexdigest(tokens[1])}"
|
||||
else
|
||||
@one_auth = "#{user}:#{Digest::SHA1.hexdigest(password)}"
|
||||
raise "Authorization file malformed"
|
||||
end
|
||||
|
||||
if endpoint
|
||||
|
@ -27,6 +27,9 @@ module OpenNebula
|
||||
:delete => "group.delete"
|
||||
}
|
||||
|
||||
# Flag for requesting connected user's group info
|
||||
SELF = -1
|
||||
|
||||
# Creates a Group description with just its identifier
|
||||
# this method should be used to create plain Group objects.
|
||||
# +id+ the id of the user
|
||||
|
@ -101,11 +101,11 @@ module OpenNebula
|
||||
element.text
|
||||
end
|
||||
end
|
||||
|
||||
# Gets an array of text from elemenets extracted
|
||||
|
||||
# Gets an array of text from elemenets extracted
|
||||
# using the XPATH expression passed as filter
|
||||
def retrieve_elements(filter)
|
||||
elements_array = Array.new
|
||||
elements_array = Array.new
|
||||
|
||||
if NOKOGIRI
|
||||
@xml.xpath(filter.to_s).each { |pelem|
|
||||
@ -116,13 +116,13 @@ module OpenNebula
|
||||
elements_array << pelem.text if pelem.text
|
||||
}
|
||||
end
|
||||
|
||||
if elements_array.size == 0
|
||||
|
||||
if elements_array.size == 0
|
||||
return nil
|
||||
else
|
||||
return elements_array
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
# Gets an attribute from an elemenT
|
||||
@ -164,6 +164,18 @@ module OpenNebula
|
||||
end
|
||||
end
|
||||
|
||||
def each_xpath(xpath_str,&block)
|
||||
if NOKOGIRI
|
||||
@xml.xpath(xpath_str).each { |pelem|
|
||||
block.call pelem.text
|
||||
}
|
||||
else
|
||||
@xml.elements.each(xpath_str) { |pelem|
|
||||
block.call pelem.text
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def name
|
||||
@xml.name
|
||||
end
|
||||
@ -285,7 +297,7 @@ module OpenNebula
|
||||
|
||||
hash
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
def attr_to_str(attr)
|
||||
attr.gsub!('"',"\\\"")
|
||||
|
@ -77,6 +77,14 @@ int UserDelete::drop(int oid, PoolObjectSQL * object, string& error_msg)
|
||||
User * user = static_cast<User *>(object);
|
||||
int group_id = user->get_gid();
|
||||
|
||||
if (oid == 0)
|
||||
{
|
||||
error_msg = "oneadmin can not be deleted.";
|
||||
|
||||
object->unlock();
|
||||
return -1;
|
||||
}
|
||||
|
||||
int rc = pool->drop(object, error_msg);
|
||||
|
||||
object->unlock();
|
||||
|
@ -253,20 +253,7 @@ bool UserPool::authenticate(const string& session,
|
||||
|
||||
ar.add_authenticate(username,u_pass,secret);
|
||||
|
||||
if ( uid == 0 ) //oneadmin
|
||||
{
|
||||
if (ar.plain_authenticate())
|
||||
{
|
||||
user_id = uid;
|
||||
group_id = gid;
|
||||
|
||||
uname = tuname;
|
||||
gname = tgname;
|
||||
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
else if (authm == 0) //plain auth
|
||||
if (authm == 0) //plain auth
|
||||
{
|
||||
if ( user != 0 && ar.plain_authenticate()) //no plain for external users
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user