Author: acoburn
Date: Mon Sep 24 20:05:17 2012
New Revision: 1389548
URL: http://svn.apache.org/viewvc?rev=1389548&view=rev
Log:
VCL-560
For installations using the vmprofile.password value in the database, this
value is stored in cleartext. That means that if there are multiple management
nodes, each
management node will have access to this password, which may not be desirable.
The password is also passed in clear text along the network between the mysql
database and the
management node(s).
This implements the ability to set an asymmetric key so that the password can
be set in the website but only be decypherable from the management node(s) with
the private key.
Modified:
vcl/trunk/managementnode/bin/install_perl_libs.pl
vcl/trunk/managementnode/lib/VCL/utils.pm
vcl/trunk/mysql/update-vcl.sql
vcl/trunk/mysql/vcl.sql
vcl/trunk/web/.ht-inc/utils.php
vcl/trunk/web/.ht-inc/vm.php
vcl/trunk/web/js/vm.js
Modified: vcl/trunk/managementnode/bin/install_perl_libs.pl
URL:
http://svn.apache.org/viewvc/vcl/trunk/managementnode/bin/install_perl_libs.pl?rev=1389548&r1=1389547&r2=1389548&view=diff
==============================================================================
--- vcl/trunk/managementnode/bin/install_perl_libs.pl (original)
+++ vcl/trunk/managementnode/bin/install_perl_libs.pl Mon Sep 24 20:05:17 2012
@@ -192,6 +192,7 @@ sub install_linux_packages {
'openssl-devel',
'perl-Archive-Tar',
'perl-CPAN',
+ 'perl-Crypt-OpenSSL-RSA',
'perl-DBD-MySQL',
'perl-DBI',
'perl-Digest-SHA1',
Modified: vcl/trunk/managementnode/lib/VCL/utils.pm
URL:
http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/utils.pm?rev=1389548&r1=1389547&r2=1389548&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/utils.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/utils.pm Mon Sep 24 20:05:17 2012
@@ -74,6 +74,7 @@ use Data::Dumper;
use Cwd;
use Sys::Hostname;
use XML::Simple;
+use Crypt::OpenSSL::RSA;
#use Date::Calc qw(Delta_DHMS Time_to_Date Date_to_Time);
@@ -4785,6 +4786,34 @@ EOF
$vmhost_info->{vmprofile}{username} = '' if
!$vmhost_info->{vmprofile}{username};
$vmhost_info->{vmprofile}{password} = '' if
!$vmhost_info->{vmprofile}{password};
+ # Decrypt the vmhost password
+ if(-f $vmhost_info->{vmprofile}{rsakey} &&
+ $vmhost_info->{vmprofile}{encryptedpasswd}){
+ # Read the private keyfile into a string
+ local $/ = undef;
+ open FH, $vmhost_info->{vmprofile}{rsakey} or
+ notify($ERRORS{'WARNING'}, 0, "Could not read private keyfile
(" . $vmhost_info->{vmprofile}{rsakey} . "): $!");
+ my $key = <FH>;
+ close FH;
+ if($key){
+ my $encrypted = $vmhost_info->{vmprofile}{encryptedpasswd};
+ my $rsa = Crypt::OpenSSL::RSA->new_private_key($key);
+ # Croak on an invalid key
+ $rsa->check_key;
+ # Use the same padding algorithm as the PHP code
+ $rsa->use_pkcs1_oaep_padding;
+ # Convert password from hex to binary, decrypt
+ # and store in the vmprofile.password field
+ $vmhost_info->{vmprofile}{password} = $rsa->decrypt(pack("H*",
$encrypted));
+ notify($ERRORS{'DEBUG'}, 0, "decrypted vmprofile password with
key: " . $vmhost_info->{vmprofile}{rsakey});
+ } else {
+ notify($ERRORS{'WARNING'}, 0, "unable to decrypt vmprofile
password");
+ }
+ }
+ # Clean up the extraneous data
+ delete $vmhost_info->{vmprofile}{rsakey};
+ delete $vmhost_info->{vmprofile}{encryptedpassword};
+
$vmhost_info->{vmprofile}{vmpath} =
$vmhost_info->{vmprofile}{datastorepath} if !$vmhost_info->{vmprofile}{vmpath};
$vmhost_info->{vmprofile}{virtualdiskpath} =
$vmhost_info->{vmprofile}{vmpath} if
!$vmhost_info->{vmprofile}{virtualdiskpath};
Modified: vcl/trunk/mysql/update-vcl.sql
URL:
http://svn.apache.org/viewvc/vcl/trunk/mysql/update-vcl.sql?rev=1389548&r1=1389547&r2=1389548&view=diff
==============================================================================
--- vcl/trunk/mysql/update-vcl.sql (original)
+++ vcl/trunk/mysql/update-vcl.sql Mon Sep 24 20:05:17 2012
@@ -901,6 +901,9 @@ CALL AddColumnIfNotExists('vmprofile', '
CALL AddColumnIfNotExists('vmprofile', 'datastoreimagetypeid', "smallint(5)
unsigned NOT NULL default '1' AFTER datastorepath");
CALL AddColumnIfNotExists('vmprofile', 'virtualswitch2', "varchar(80) NULL
default NULL AFTER `virtualswitch1`");
CALL AddColumnIfNotExists('vmprofile', 'virtualswitch3', "varchar(80) NULL
default NULL AFTER `virtualswitch2`");
+CALL AddColumnIfNotExists('vmprofile', 'rsapub', "text NULL default NULL AFTER
`virtualswitch3`");
+CALL AddColumnIfNotExists('vmprofile', 'rsakey', "varchar(256) NULL default
NULL AFTER `rsa_pub`");
+CALL AddColumnIfNotExists('vmprofile', 'encryptedpasswd', "text NULL default
NULL AFTER `rsa_key`");
CALL AddOrRenameColumn('vmprofile', 'vmware_mac_eth0_generated',
'eth0generated', "tinyint(1) unsigned NOT NULL default '0'");
CALL AddOrRenameColumn('vmprofile', 'vmware_mac_eth1_generated',
'eth1generated', "tinyint(1) unsigned NOT NULL default '0'");
Modified: vcl/trunk/mysql/vcl.sql
URL:
http://svn.apache.org/viewvc/vcl/trunk/mysql/vcl.sql?rev=1389548&r1=1389547&r2=1389548&view=diff
==============================================================================
--- vcl/trunk/mysql/vcl.sql (original)
+++ vcl/trunk/mysql/vcl.sql Mon Sep 24 20:05:17 2012
@@ -1237,6 +1237,9 @@ CREATE TABLE IF NOT EXISTS `vmprofile` (
`password` varchar(256) NULL default NULL,
`eth0generated` tinyint(1) unsigned NOT NULL default '0',
`eth1generated` tinyint(1) unsigned NOT NULL default '0',
+ `rsapub` text NULL default NULL,
+ `rsakey` varchar(256) NULL default NULL,
+ `encryptedpasswd` text NULL default NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `profilename` (`profilename`),
KEY `imageid` (`imageid`),
Modified: vcl/trunk/web/.ht-inc/utils.php
URL:
http://svn.apache.org/viewvc/vcl/trunk/web/.ht-inc/utils.php?rev=1389548&r1=1389547&r2=1389548&view=diff
==============================================================================
--- vcl/trunk/web/.ht-inc/utils.php (original)
+++ vcl/trunk/web/.ht-inc/utils.php Mon Sep 24 20:05:17 2012
@@ -2361,6 +2361,33 @@ function decryptData($data) {
}
////////////////////////////////////////////////////////////////////////////////
+//
+// \fn encryptDataAsymmetric($data, $public_key)
+//
+// \param $data - a string
+//
+// \param $public_key - either a filename for a public key or the public key
itself
+//
+// \return hex-encoded, encrypted form of $data
+//
+// \brief generate public key encrypted data
+//
+////////////////////////////////////////////////////////////////////////////////
+function encryptDataAsymmetric($data, $public_key){
+ if(file_exists($public_key)){
+ $key = openssl_pkey_get_public(file_get_contents($public_key));
+ } else {
+ $key = openssl_pkey_get_public($public_key);
+ }
+
+ openssl_public_encrypt($data, $encrypted, $key,
OPENSSL_PKCS1_OAEP_PADDING);
+ openssl_free_key($key);
+
+ $hexformatted = unpack("H*hex", $encrypted);
+ return $hexformatted['hex'];
+}
+
+////////////////////////////////////////////////////////////////////////////////
///
/// \fn getParentNodes($node)
///
@@ -9433,6 +9460,8 @@ function getVMProfiles($id="") {
. "vp.vmdisk, "
. "vp.username, "
. "vp.password, "
+ . "vp.rsakey, "
+ . "vp.rsapub, "
. "vp.eth0generated, "
. "vp.eth1generated "
. "FROM vmprofile vp "
@@ -10757,6 +10786,7 @@ function getDojoHTML($refresh) {
'dijit.form.NumberSpinner',
'dijit.form.Button',
'dijit.form.TextBox',
+ 'dijit.form.Textarea',
'dijit.form.FilteringSelect',
'dijit.form.Select',
'dijit.TitlePane',
Modified: vcl/trunk/web/.ht-inc/vm.php
URL:
http://svn.apache.org/viewvc/vcl/trunk/web/.ht-inc/vm.php?rev=1389548&r1=1389547&r2=1389548&view=diff
==============================================================================
--- vcl/trunk/web/.ht-inc/vm.php (original)
+++ vcl/trunk/web/.ht-inc/vm.php Mon Sep 24 20:05:17 2012
@@ -279,6 +279,20 @@ function editVMInfo() {
print " </select><img tabindex=0 src=\"images/helpicon.png\"
id=\"genmac1help\" /></td>\n";
print " </tr>\n";
print " <tr>\n";
+ print " <th align=right>RSA Public Key:</th>\n";
+ print " <td>\n";
+ print " <span id=prsapub dojoType=\"dijit.InlineEditBox\"
editor=\"dijit.form.Textarea\"
onChange=\"updateProfile('prsapub','rsapub')\"></span>\n";
+ print " <img tabindex=0 src=\"images/helpicon.png\" id=\"rsapubhelp\"
/>\n";
+ print " </td>\n";
+ print " </tr>\n";
+ print " <tr>\n";
+ print " <th align=right>RSA Private Key File:</th>\n";
+ print " <td>\n";
+ print " <span id=prsakey dojoType=\"dijit.InlineEditBox\"
onChange=\"updateProfile('prsakey','rsakey');\"></span>\n";
+ print " <img tabindex=0 src=\"images/helpicon.png\" id=\"rsakeyhelp\"
/>\n";
+ print " </td>\n";
+ print " </tr>\n";
+ print " <tr>\n";
print " <th align=right>Username:</th>\n";
print " <td><span id=pusername dojoType=\"dijit.InlineEditBox\"
onChange=\"updateProfile('pusername', 'username');\"></span><img tabindex=0
src=\"images/helpicon.png\" id=\"usernamehelp\" /></td>\n";
print " </tr>\n";
@@ -350,6 +364,12 @@ function editVMInfo() {
print "<div dojoType=\"dijit.Tooltip\" connectId=\"genmac1help\">\n";
print _("Specifies whether VMs are assigned MAC addresses defined in
the VCL database or if random MAC addresses should be assigned.");
print "</div>\n";
+ print "<div dojoType=\"dijit.Tooltip\" connectId=\"rsapubhelp\">\n";
+ print _("(Optional) In order to encrypt the VM Host password in the
database, create an RSA public/private key pair on the relevant management
node. Enter the public key here. Note that while this value will be available
to every management node in your system, only those management nodes with the
designated private key will be able to decrypt the password.");
+ print "</div>\n";
+ print "<div dojoType=\"dijit.Tooltip\" connectId=\"rsakeyhelp\">\n";
+ print _("(Optional) In order to decrypt an encrypted VM Host password,
enter the path to a private key on the management node. Any management node
without this private key will not be able to decrypt the password.");
+ print "</div>\n";
print "<div dojoType=\"dijit.Tooltip\" connectId=\"usernamehelp\">\n";
print _("Name of the administrative or root user residing on the VM
host.");
print "</div>\n";
@@ -872,7 +892,7 @@ function AJupdateVMprofileItem() {
}
$profileid = processInputVar('profileid', ARG_NUMERIC);
$item = processInputVar('item', ARG_STRING);
- if(!
preg_match('/^(profilename|imageid|resourcepath|repositorypath|repositoryimagetypeid|datastorepath|datastoreimagetypeid|vmdisk|vmpath|virtualswitch[0-3]|username|password|eth0generated|eth1generated)$/',
$item)) {
+ if(!
preg_match('/^(profilename|imageid|resourcepath|repositorypath|repositoryimagetypeid|datastorepath|datastoreimagetypeid|vmdisk|vmpath|virtualswitch[0-3]|username|password|eth0generated|eth1generated|rsakey|rsapub)$/',
$item)) {
print "alert('Invalid data submitted.');";
return;
}
@@ -908,8 +928,19 @@ function AJupdateVMprofileItem() {
$item = mysql_real_escape_string($item);
$profile = getVMProfiles($profileid);
- if($profile[$profileid][$item] == $newvalue)
+ if($item == 'password' && $profile[$profileid]['rsapub']){
+ $encrypted = encryptDataAsymmetric($newvalue,
$profile[$profileid]['rsapub']);
+ $escaped = mysql_real_escape_string($encrypted);
+ $query = "UPDATE vmprofile "
+ . "SET `encryptedpasswd` = '$escaped' "
+ . "WHERE id=$profileid";
+ doQuery($query, 101);
+ # don't store the unencrypted password
+ $newvalue2 = 'NULL';
+ $newvalue = '';
+ } else if($profile[$profileid][$item] == $newvalue){
return;
+ }
$query = "UPDATE vmprofile "
. "SET `$item` = $newvalue2 "
. "WHERE id = $profileid";
Modified: vcl/trunk/web/js/vm.js
URL:
http://svn.apache.org/viewvc/vcl/trunk/web/js/vm.js?rev=1389548&r1=1389547&r2=1389548&view=diff
==============================================================================
--- vcl/trunk/web/js/vm.js (original)
+++ vcl/trunk/web/js/vm.js Mon Sep 24 20:05:17 2012
@@ -598,6 +598,8 @@ function getVMprofileDataCB(data, ioArgs
dijit.byId('pvs2').noValueIndicator = '(empty)';
dijit.byId('pvs3').noValueIndicator = '(empty)';
dijit.byId('pusername').noValueIndicator = '(empty)';
+ dijit.byId('prsakey').noValueIndicator = '(empty)';
+ dijit.byId('prsapub').noValueIndicator = '(empty)';
dijit.byId('pname').setValue(curprofile.profilename);
dijit.byId('presourcepath').setValue(curprofile.resourcepath);
@@ -611,6 +613,8 @@ function getVMprofileDataCB(data, ioArgs
dijit.byId('pusername').setValue(curprofile.username);
dijit.byId('pgenmac0').setValue(curprofile.eth0generated);
dijit.byId('pgenmac1').setValue(curprofile.eth1generated);
+ dijit.byId('prsapub').setValue(curprofile.rsapub);
+ dijit.byId('prsakey').setValue(curprofile.rsakey);
dojo.byId('ppassword').value = curprofile.password;
dojo.byId('ppwdconfirm').value = curprofile.password;
checkProfilePassword();
@@ -779,7 +783,7 @@ function updateProfile(id, field) {
var newvalue = dijit.byId(id).value;
else
var newvalue = dojo.byId(id).value;
- if(curprofile[field] == newvalue)
+ if(curprofile[field] == newvalue && field != 'password')
return;
if(field == 'password')
dojo.byId('savestatus').innerHTML = 'Saving...';