Commit 8be26639 authored by Leigh Stoller's avatar Leigh Stoller

Add new options to CreateSliver/Provision; supply an x509 certificate and

private key.

The goal is to distribute an experiment wide certificate and private
key. At the moment this is just a self signed x509 certificate and the
accompanying rsa key. In PEM format. The same cert/key will be distributed
across multiple aggregates.

An openssh key pair can be trivially derived from the private key. Or the
public part can be derived from the certificate. A quick google will show
show.

Initially, you will need to run tmcc directly to get them, using the
geni_certificate and geni_key commands.
parent ec07c10c
...@@ -1405,9 +1405,9 @@ sub GetManifest($) ...@@ -1405,9 +1405,9 @@ sub GetManifest($)
return $response->value()->{'manifest'}; return $response->value()->{'manifest'};
} }
sub Provision($$$) sub Provision($$$$)
{ {
my ($self, $perrmsg, $keys) = @_; my ($self, $perrmsg, $keys, $cert, $key) = @_;
my $authority = $self->GetGeniAuthority(); my $authority = $self->GetGeniAuthority();
my $urn = $self->aggregate_urn(); my $urn = $self->aggregate_urn();
my $geniuser = $self->instance()->GetGeniUser(); my $geniuser = $self->instance()->GetGeniUser();
...@@ -1433,12 +1433,14 @@ sub Provision($$$) ...@@ -1433,12 +1433,14 @@ sub Provision($$$)
{"geni_type" => "geni_sfa", {"geni_type" => "geni_sfa",
"geni_version" => 3, "geni_version" => 3,
"geni_value" => $slice_credential->asString()}, "geni_value" => $slice_credential->asString()},
], ],
# Options array. # Options array.
{"speaking_for" => $geniuser->urn(), {"speaking_for" => $geniuser->urn(),
"geni_speaking_for" => $geniuser->urn(), "geni_speaking_for" => $geniuser->urn(),
"geni_users" => [{'urn' => $geniuser->urn(), "geni_users" => [{'urn' => $geniuser->urn(),
'keys' => $keys }], 'keys' => $keys }],
"geni_certificate" => $cert,
"geni_key" => $key,
}); });
my $cmurl = $authority->url(); my $cmurl = $authority->url();
......
...@@ -89,6 +89,7 @@ my $SSHSETUP = "$TB/sbin/aptssh-setup"; ...@@ -89,6 +89,7 @@ my $SSHSETUP = "$TB/sbin/aptssh-setup";
my $ADDPUBKEY = "$TB/sbin/addpubkey"; my $ADDPUBKEY = "$TB/sbin/addpubkey";
my $UPDATEGENIUSER= "$TB/sbin/protogeni/updategeniuser"; my $UPDATEGENIUSER= "$TB/sbin/protogeni/updategeniuser";
my $STITCHER = "$TB/gcf/src/stitcher.py"; my $STITCHER = "$TB/gcf/src/stitcher.py";
my $OPENSSL = "/usr/bin/openssl";
# un-taint path # un-taint path
$ENV{'PATH'} = '/bin:/usr/bin:/usr/local/bin:/usr/site/bin'; $ENV{'PATH'} = '/bin:/usr/bin:/usr/local/bin:/usr/site/bin';
...@@ -589,6 +590,27 @@ if (defined($profile)) { ...@@ -589,6 +590,27 @@ if (defined($profile)) {
} }
} }
#
# Generate a new ssl key/cert to be used to derive an ssh key pair,
# or whatever else is needed. This is sent along as an option when the
# sliver is created (or provisioned, when stitching).
#
my $sslkeyfile = "/tmp/key$$.pem";
my $sslcrtfile = "/tmp/crt$$.pem";
system("$OPENSSL req -x509 -newkey rsa:2048 ".
"-keyout $sslkeyfile -out $sslcrtfile ".
"-days 2000 -nodes -subj '/CN=localhost' -text");
if ($?) {
unlink($sslkeyfile);
unlink($sslcrtfile);
fatal("Could not generate ssl key/cert");
}
my $sslkey = `cat $sslkeyfile`;
my $sslcrt = `cat $sslcrtfile`;
unlink($sslkeyfile);
unlink($sslcrtfile);
# #
# #
# Now generate a slice registration and credential # Now generate a slice registration and credential
...@@ -661,6 +683,8 @@ my $blob = {'uuid' => $quickvm_uuid, ...@@ -661,6 +683,8 @@ my $blob = {'uuid' => $quickvm_uuid,
'status' => "created", 'status' => "created",
'servername' => $SERVER_NAME, 'servername' => $SERVER_NAME,
'rspec' => $rspecstr, 'rspec' => $rspecstr,
'cert' => $sslcrt,
'privkey' => $sslkey,
}; };
if (defined($project)) { if (defined($project)) {
$blob->{"pid"} = $project->pid(); $blob->{"pid"} = $project->pid();
...@@ -1027,11 +1051,14 @@ sub CreateSliver($) ...@@ -1027,11 +1051,14 @@ sub CreateSliver($)
[{'urn' => $user_urn, [{'urn' => $user_urn,
'login' => $user_uid, 'login' => $user_uid,
'keys' => \@sshkeys }], 'keys' => \@sshkeys }],
"credentials" => "credentials" =>
[$slice_credential->asString(), [$slice_credential->asString(),
$speaksfor_credential->asString(), $speaksfor_credential->asString(),
@dataset_credentials @dataset_credentials
]}); ],
"certificate" => $sslcrt,
"key" => $sslkey,
});
if (!defined($response) || $response->code() != GENIRESPONSE_SUCCESS) { if (!defined($response) || $response->code() != GENIRESPONSE_SUCCESS) {
if (defined($response) && if (defined($response) &&
...@@ -1319,7 +1346,7 @@ sub RunStitcher() ...@@ -1319,7 +1346,7 @@ sub RunStitcher()
return 0; return 0;
} }
print "Provisioning at $urn\n"; print "Provisioning at $urn\n";
if ($aggobj->Provision(\$errmsg, \@sshkeys)) { if ($aggobj->Provision(\$errmsg, \@sshkeys, $sslcrt, $sslkey)) {
$aggobj->SetStatus("failed"); $aggobj->SetStatus("failed");
$webtask->output($errmsg); $webtask->output($errmsg);
$webtask->Exited(-1); $webtask->Exited(-1);
......
...@@ -222,6 +222,14 @@ CREATE TABLE `geni_userkeys` ( ...@@ -222,6 +222,14 @@ CREATE TABLE `geni_userkeys` (
INDEX `uuid` (`uuid`) INDEX `uuid` (`uuid`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1; ) ENGINE=MyISAM DEFAULT CHARSET=latin1;
DROP TABLE IF EXISTS `geni_slicecerts`;
CREATE TABLE `geni_slicecerts` (
`uuid` varchar(40) NOT NULL default '',
`cert` text,
`privkey` text,
INDEX `uuid` (`uuid`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
DROP TABLE IF EXISTS `geni_resources`; DROP TABLE IF EXISTS `geni_resources`;
CREATE TABLE `geni_resources` ( CREATE TABLE `geni_resources` (
`pid` varchar(12) NOT NULL default '', `pid` varchar(12) NOT NULL default '',
......
...@@ -385,6 +385,14 @@ sub CreateSliver() ...@@ -385,6 +385,14 @@ sub CreateSliver()
'credentials' => $credentials, 'credentials' => $credentials,
'keys' => $sliver_keys 'keys' => $sliver_keys
}; };
if (defined($options)) {
if (exists($options->{'geni_certificate'})) {
$create_args->{'certificate'} = $options->{'geni_certificate'};
}
if (exists($options->{'geni_key'})) {
$create_args->{'key'} = $options->{'geni_key'};
}
}
my $response = GeniCMV2::CreateSliver($create_args); my $response = GeniCMV2::CreateSliver($create_args);
if (!ref($response)) { if (!ref($response)) {
# This is cause GeniCMV2::CreateSliver does a fork, and the child # This is cause GeniCMV2::CreateSliver does a fork, and the child
...@@ -1027,7 +1035,14 @@ sub Provision ...@@ -1027,7 +1035,14 @@ sub Provision
'credentials' => $credentials, 'credentials' => $credentials,
'keys' => $sliver_keys 'keys' => $sliver_keys
}; };
if (defined($options)) {
if (exists($options->{'geni_certificate'})) {
$args->{'certificate'} = $options->{'geni_certificate'};
}
if (exists($options->{'geni_key'})) {
$args->{'key'} = $options->{'geni_key'};
}
}
my $response = GeniCMV2::RedeemTicket($args); my $response = GeniCMV2::RedeemTicket($args);
if (! GeniResponse::IsError($response)) { if (! GeniResponse::IsError($response)) {
my $description = Describe($urn_args, $credential_args, []); my $description = Describe($urn_args, $credential_args, []);
......
...@@ -482,6 +482,18 @@ sub CreateSliver($) ...@@ -482,6 +482,18 @@ sub CreateSliver($)
} }
main::AddLogfileMetaDataFromSlice($slice); main::AddLogfileMetaDataFromSlice($slice);
#
# If we got a cert/key, record them for the slice. This is a
# generic openssl key/cert that is stored on the nodes (and from
# which an ssh key pair can be derived).
#
if (exists($argref->{'certificate'}) || exists($argref->{'key'})) {
$slice->AddGenericCert((exists($argref->{'certificate'}) ?
$argref->{'certificate'} : undef),
(exists($argref->{'key'}) ?
$argref->{'key'} : undef));
}
# Make sure that the next phase sees all changes. # Make sure that the next phase sees all changes.
Experiment->FlushAll(); Experiment->FlushAll();
Node->FlushAll(); Node->FlushAll();
......
...@@ -44,7 +44,7 @@ use GeniUser; ...@@ -44,7 +44,7 @@ use GeniUser;
use GeniHRN; use GeniHRN;
use English; use English;
use libEmulab; use libEmulab;
use emutil qw(TBGetUniqueIndex); use emutil;
use Date::Parse; use Date::Parse;
use Data::Dumper; use Data::Dumper;
use vars qw(); use vars qw();
...@@ -354,6 +354,8 @@ sub Delete($) ...@@ -354,6 +354,8 @@ sub Delete($)
my $uuid = $self->uuid(); my $uuid = $self->uuid();
my $idx = $self->idx(); my $idx = $self->idx();
DBQueryWarn("delete from geni_slicecerts where uuid='$uuid'")
or return -1;
DBQueryWarn("delete from geni_bindings where slice_uuid='$uuid'") DBQueryWarn("delete from geni_bindings where slice_uuid='$uuid'")
or return -1; or return -1;
DBQueryWarn("delete from geni_certificates where uuid='$uuid'") DBQueryWarn("delete from geni_certificates where uuid='$uuid'")
...@@ -1452,6 +1454,30 @@ sub SetPublicID($) ...@@ -1452,6 +1454,30 @@ sub SetPublicID($)
return 0; return 0;
} }
#
# Add a generic certificate/key for a slice.
#
sub AddGenericCert($$$)
{
my ($self, $cert, $key) = @_;
my $uuid = $self->uuid();
my $query = "insert into geni_slicecerts set uuid='$uuid' ";
if (defined($cert)) {
return -1
if (!TBcheck_dbslot($cert, "default", "fulltext",
TBDB_CHECKDBSLOT_WARN|TBDB_CHECKDBSLOT_ERROR));
$query .= ",cert=" . DBQuoteSpecial($cert);
}
if (defined($key)) {
return -1
if (!TBcheck_dbslot($key, "default", "fulltext",
TBDB_CHECKDBSLOT_WARN|TBDB_CHECKDBSLOT_ERROR));
$query .= ",privkey=" . DBQuoteSpecial($key);
}
return DBQueryWarn($query);
}
########################################################################## ##########################################################################
# #
package GeniSlice::ClientSliver; package GeniSlice::ClientSliver;
......
#
# APT Changes.
#
use strict;
use GeniDB;
sub DoUpdate($$$)
my ($dbhandle, $dbname, $version) = @_;
DBSetDefault($dbhandle);
if (!DBTableExists("geni_slicecerts")) {
DBQueryFatal("CREATE TABLE `geni_slicecerts` ( ".
" `uuid` varchar(40) NOT NULL default '', ".
" `cert` text, ".
" `privkey` text, ".
" INDEX `uuid` (`uuid`)".
") ENGINE=MyISAM DEFAULT CHARSET=latin1");
}
return 0;
}
1;
# Local Variables:
# mode:perl
# End:
...@@ -398,6 +398,8 @@ COMMAND_PROTOTYPE(dogeniuserurn); ...@@ -398,6 +398,8 @@ COMMAND_PROTOTYPE(dogeniuserurn);
COMMAND_PROTOTYPE(dogeniuseremail); COMMAND_PROTOTYPE(dogeniuseremail);
COMMAND_PROTOTYPE(dogenigeniuser); COMMAND_PROTOTYPE(dogenigeniuser);
COMMAND_PROTOTYPE(dogenimanifest); COMMAND_PROTOTYPE(dogenimanifest);
COMMAND_PROTOTYPE(dogenicert);
COMMAND_PROTOTYPE(dogenikey);
COMMAND_PROTOTYPE(dogenicontrolmac); COMMAND_PROTOTYPE(dogenicontrolmac);
COMMAND_PROTOTYPE(dogeniversion); COMMAND_PROTOTYPE(dogeniversion);
COMMAND_PROTOTYPE(dogenigetversion); COMMAND_PROTOTYPE(dogenigetversion);
...@@ -532,6 +534,8 @@ struct command { ...@@ -532,6 +534,8 @@ struct command {
/* Yes, "geni_user" is a stupid name. Wasn't my idea. */ /* Yes, "geni_user" is a stupid name. Wasn't my idea. */
{ "geni_geni_user", FULLCONFIG_NONE, 0, dogenigeniuser }, { "geni_geni_user", FULLCONFIG_NONE, 0, dogenigeniuser },
{ "geni_manifest", FULLCONFIG_NONE, 0, dogenimanifest }, { "geni_manifest", FULLCONFIG_NONE, 0, dogenimanifest },
{ "geni_certificate", FULLCONFIG_NONE, 0, dogenicert },
{ "geni_key", FULLCONFIG_NONE, 0, dogenikey },
{ "geni_control_mac", FULLCONFIG_NONE, 0, dogenicontrolmac }, { "geni_control_mac", FULLCONFIG_NONE, 0, dogenicontrolmac },
{ "geni_version", FULLCONFIG_NONE, 0, dogeniversion }, { "geni_version", FULLCONFIG_NONE, 0, dogeniversion },
{ "geni_getversion", FULLCONFIG_NONE, 0, dogenigetversion }, { "geni_getversion", FULLCONFIG_NONE, 0, dogenigetversion },
...@@ -12411,6 +12415,68 @@ static char *getgenimanifest( tmcdreq_t *reqp ) { ...@@ -12411,6 +12415,68 @@ static char *getgenimanifest( tmcdreq_t *reqp ) {
return strdup( buf ); return strdup( buf );
} }
static char *getgenicert( tmcdreq_t *reqp ) {
MYSQL_RES *res;
char buf[ MAXTMCDPACKET ];
buf[0] = (char) NULL;
res = mydb_query( "SELECT c.cert FROM `geni-cm`.geni_slivers AS s, "
"`geni-cm`.geni_slicecerts AS c WHERE "
"s.resource_uuid='%s' AND "
"c.uuid = s.slice_uuid", 1, reqp->nodeuuid );
if( !res ) {
error( "getgenicert: %s: DB error getting certificate!\n",
reqp->nodeid );
return NULL;
}
if( mysql_num_rows( res ) ) {
MYSQL_ROW row = mysql_fetch_row( res );
GOUTPUT( buf, sizeof buf, "%s", row[ 0 ] );
}
mysql_free_result( res );
if( verbose )
info( "%s: getgenicert: %s", reqp->nodeid, buf );
return strdup( buf );
}
static char *getgenikey( tmcdreq_t *reqp ) {
MYSQL_RES *res;
char buf[ MAXTMCDPACKET ];
buf[0] = (char) NULL;
res = mydb_query( "SELECT c.privkey FROM `geni-cm`.geni_slivers AS s, "
"`geni-cm`.geni_slicecerts AS c WHERE "
"s.resource_uuid='%s' AND "
"c.uuid = s.slice_uuid", 1, reqp->nodeuuid );
if( !res ) {
error( "getgenikey: %s: DB error getting certificate!\n",
reqp->nodeid );
return NULL;
}
if( mysql_num_rows( res ) ) {
MYSQL_ROW row = mysql_fetch_row( res );
GOUTPUT( buf, sizeof buf, "%s", row[ 0 ] );
}
mysql_free_result( res );
if( verbose )
info( "%s: getgenikey: %s", reqp->nodeid, buf );
return strdup( buf );
}
static char *getgenigeniuser( tmcdreq_t *reqp ) { static char *getgenigeniuser( tmcdreq_t *reqp ) {
MYSQL_RES *res; MYSQL_RES *res;
...@@ -12691,6 +12757,8 @@ MAKEGENICOMMAND(userurn) ...@@ -12691,6 +12757,8 @@ MAKEGENICOMMAND(userurn)
MAKEGENICOMMAND(useremail) MAKEGENICOMMAND(useremail)
MAKEGENICOMMAND(geniuser) MAKEGENICOMMAND(geniuser)
MAKEGENICOMMAND(manifest) MAKEGENICOMMAND(manifest)
MAKEGENICOMMAND(cert)
MAKEGENICOMMAND(key)
MAKEGENICOMMAND(controlmac) MAKEGENICOMMAND(controlmac)
MAKEGENICOMMAND(version) MAKEGENICOMMAND(version)
MAKEGENICOMMAND(getversion) MAKEGENICOMMAND(getversion)
...@@ -12726,7 +12794,9 @@ struct genicommand { ...@@ -12726,7 +12794,9 @@ struct genicommand {
{ "user_email", getgeniuseremail, 1, "Show the e-mail address of this " { "user_email", getgeniuseremail, 1, "Show the e-mail address of this "
"sliver's creator" }, "sliver's creator" },
{ "user_urn", getgeniuserurn, 1, "Show the URN of this sliver's creator" }, { "user_urn", getgeniuserurn, 1, "Show the URN of this sliver's creator" },
{ "version", getgeniversion, 1, NULL } { "version", getgeniversion, 1, NULL },
{ "certificate", getgenicert, 1, NULL },
{ "key", getgenikey, 1, NULL },
}; };
COMMAND_PROTOTYPE(dogenicommands) COMMAND_PROTOTYPE(dogenicommands)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment