All new accounts created on Gitlab now require administrator approval. If you invite any collaborators, please let Flux staff know so they can approve the accounts.

Commit 3ebffb34 authored by Leigh B Stoller's avatar Leigh B Stoller

Some tweaks to credential handling:

1) Anytime we need to generate a slice credential, and the slice has
   expired, bump the slice expiration so we can create a valid credential
   and then reset the expiration. Consider if the slice expires but we
   missed it and its still active; we gotta be able to control it.

2) From the beginning, we have done almost all RPC operations as the
   creator of the experiment. Made sense when the portal interface was not
   project aware, but now other users in the project can see and mess with
   experiments in their project. But we are still doing all the RPC
   operations as the creator of the experiment, which will need to change
   at some point, but in the short term I am seeing a lot of credential
   errors caused by an expired speaks-for credential for that creator (if
   they have not logged into the portal in a while). When this happens,
   lets generate a plain slice credential, issued to the SA, so that we can
   complete the operation. Eventually we have to make the backend project
   aware, and issue the operations as the web user doing the driving.
   Maybe as part of the larger portalization project.
parent b6f68b20
......@@ -56,10 +56,10 @@ my $USEABACCREDS = 0;
#
# Generate the credentials we need.
#
sub GenCredentials($$;$)
sub GenCredentials($$;$$)
{
my ($target, $geniuser, $privs) = @_;
my ($speaksfor, $credential);
my ($target, $geniuser, $privs, $allowexpiredspeaksfor) = @_;
my ($speaksfor, $credential, $oldexpires);
# If the caller does not want a speaksfor, do not generate.
my $wantspeaksfor = wantarray;
......@@ -72,11 +72,21 @@ sub GenCredentials($$;$)
if (!$geniuser->IsLocal() && $MAINSITE) {
$speaker_signer = "/usr/testbed/etc/utah-apt.sa";
}
my $cachetag = $target->urn() . "::" . $geniuser->urn();
#
# If the target is a slice, and expired, change the expiration so
# that the credential we generate is not also expired (and invalid).
#
if (ref($target) eq "GeniSlice" && $target->IsExpired()) {
$oldexpires = $target->expires();
$target->SetExpiration(time() + 600);
delete($credcache{$cachetag})
if (exists($credcache{$cachetag}));
}
#
# Check cache.
#
my $cachetag = $target->urn() . "::" . $geniuser->urn();
if (exists($credcache{$cachetag})) {
($credential,$speaksfor) = @{ $credcache{$cachetag} };
goto cached;
......@@ -102,6 +112,25 @@ sub GenCredentials($$;$)
goto bad;
}
}
#
# Ick, if the speaks for credential has expired, we cannot
# operate as the user. We have no choice but to throw away
# these credentials and generate a new one issued to the local
# SA instead of the user and not bother with a speaksfor.
#
if ($speaksfor->IsExpired()) {
print STDERR "speaksfor credential for $geniuser has expired\n";
goto bad
if (!$allowexpiredspeaksfor);
print STDERR "-> Generating an SA credential instead\n";
$credential = APT_Geni::GenAuthCredential($target, $privs);
if (!defined($credential)) {
print STDERR "-> Could not generate SA credential!\n";
goto bad;
}
goto cached;
}
my $certificate =
GeniCertificate->LoadFromString($certificate_string);
if (!defined($certificate)) {
......@@ -167,11 +196,15 @@ sub GenCredentials($$;$)
$credcache{$cachetag} = [$credential, $speaksfor];
}
cached:
$target->SetExpiration($oldexpires)
if (defined($oldexpires));
if (wantarray) {
return ($credential, $speaksfor);
}
return $credential;
bad:
$target->SetExpiration($oldexpires)
if (defined($oldexpires));
return ();
}
......@@ -212,16 +245,25 @@ sub GeniContext()
sub GenAuthCredential($;$)
{
my ($target, $privs) = @_;
my $oldexpires;
my $owner = GeniCertificate->LoadFromFile($SACERT);
if (!defined($owner)) {
print STDERR "Could not load certificate from $SACERT\n";
return undef;
}
#
# If the target is a slice, and expired, change the expiration so
# that the credential we generate is not also expired (and invalid).
#
if (ref($target) eq "GeniSlice" && $target->IsExpired()) {
$oldexpires = $target->expires();
$target->SetExpiration(time() + 600);
}
my $credential = GeniCredential->Create($target, $owner);
if (!defined($credential)) {
print STDERR "Could not create credential for $target\n";
return undef;
goto bad;
}
# Add optional privs.
if (defined($privs)) {
......@@ -233,9 +275,15 @@ sub GenAuthCredential($;$)
if ($credential->Sign($GeniCredential::LOCALSA_FLAG) != 0) {
$credential->Delete();
print STDERR "Could not sign $target credential\n";
return undef
goto bad;
}
$target->SetExpiration($oldexpires)
if (defined($oldexpires));
return $credential;
bad:
$target->SetExpiration($oldexpires)
if (defined($oldexpires));
return undef;
}
#
......
This diff is collapsed.
......@@ -682,7 +682,7 @@ if ($tmp) {
# Generate credentials we need.
#
my ($slice_credential, $speaksfor_credential) =
APT_Geni::GenCredentials($slice, $geniuser);
APT_Geni::GenCredentials($slice, $geniuser, undef, 0);
if (! (defined($speaksfor_credential) &&
defined($slice_credential))) {
fatal("Could not generate credentials");
......
......@@ -46,6 +46,8 @@ my $optlist = "dt:";
my $debug = 0;
my $webtask_id;
my $webtask;
my $this_user;
my $geniuser;
#
# Configure variables
......@@ -80,6 +82,7 @@ use WebTask;
use Blockstore;
use GeniResponse;
use GeniXML;
use GeniUser;
# Protos
sub fatal($);
......@@ -117,16 +120,19 @@ if (defined($options{"t"})) {
if (@ARGV < 1) {
usage();
}
my $action = shift(@ARGV);
# The web interface (and in the future the xmlrpc interface) sets this.
my $this_user = User->ImpliedUser();
if (! defined($this_user)) {
if (getpwuid($UID) eq "nobody") {
$this_user = User->ImpliedUser();
}
else {
$this_user = User->ThisUser();
if (!defined($this_user)) {
fatal("You ($UID) do not exist!");
}
}
my $action = shift(@ARGV);
# No guests allowed.
if (! defined($this_user)) {
fatal("You ($UID) do not exist!");
}
$geniuser = GeniUser->CreateFromLocal($this_user);
if ($action eq "create") {
exit(DoCreate());
......
......@@ -56,6 +56,8 @@ my $debug = 0;
my $silent = 0;
my $webtask_id;
my $webtask;
my $this_user;
my $geniuser;
#
# Configure variables
......@@ -101,6 +103,7 @@ use EmulabFeatures;
# Protos
sub fatal($);
sub UserError($);
sub DoSnapshot();
sub DoConsole();
sub DoTerminate();
......@@ -147,6 +150,16 @@ if (!defined($instance)) {
if (!defined($instance)) {
fatal("No such instance $uuid");
}
if (getpwuid($UID) eq "nobody") {
$this_user = User->ImpliedUser();
}
else {
$this_user = User->ThisUser();
}
# If a guest user, we will not have an actual user, which is okay.
if (defined($this_user)) {
$geniuser = GeniUser->CreateFromLocal($this_user);
}
if ($action eq "snapshot") {
DoSnapshot();
......@@ -252,15 +265,6 @@ sub DoSnapshot()
fatal("No slice for quick VM: $uuid");
}
# The web interface (and in the future the xmlrpc interface) sets this.
my $this_user = User->ImpliedUser();
if (! defined($this_user)) {
$this_user = User->ThisUser();
if (!defined($this_user)) {
fatal("You ($UID) do not exist!");
}
}
#
# Might be a clone (manage_profile).
#
......@@ -1183,11 +1187,6 @@ sub DoExtend()
if (!defined($slice)) {
fatal("No slice for instance!");
}
# The web interface (and in the future the xmlrpc interface) sets this.
my $this_user = User->ImpliedUser();
if (! defined($this_user)) {
$this_user = User->ThisUser();
}
#
# Lock the slice in case it is doing something else, like taking
......@@ -1795,12 +1794,6 @@ sub DoLockdownInternal($$)
{
my ($setclr,$which) = @_;
# The web interface (and in the future the xmlrpc interface) sets this.
my $this_user = User->ImpliedUser();
if (! defined($this_user)) {
$this_user = User->ThisUser();
}
my $slice = $instance->GetGeniSlice();
if (!defined($slice)) {
fatal("No slice for instance");
......@@ -2021,11 +2014,6 @@ sub DoLinktest()
if (!defined($slice)) {
fatal("No slice for instance!");
}
# The web interface (and in the future the xmlrpc interface) sets this.
my $this_user = User->ImpliedUser();
if (! defined($this_user)) {
$this_user = User->ThisUser();
}
#
# Lock the slice in case it is doing something else, like taking
......@@ -2232,11 +2220,6 @@ sub DoUpdateKeys()
if (!defined($slice)) {
fatal("No slice for instance!");
}
# The web interface (and in the future the xmlrpc interface) sets this.
my $this_user = User->ImpliedUser();
if (! defined($this_user)) {
$this_user = User->ThisUser();
}
# This returns in CM format.
my $sshkeys;
......@@ -2369,6 +2352,19 @@ sub fatal($)
exit(-1);
}
sub UserError($)
{
my ($mesg) = @_;
if (defined($webtask)) {
$webtask->output($mesg);
$webtask->code(1);
}
print STDERR "*** $0:\n".
" $mesg\n";
exit(1);
}
sub escapeshellarg($)
{
my ($str) = @_;
......
......@@ -934,15 +934,11 @@ function Do_Refresh()
#
# Guest users can do this.
#
if (StatusSetupAjax(1)) {
if (StatusSetupAjax(0)) {
return;
}
$this_idx = $this_user->uid_idx();
$uuid = $ajax_args["uuid"];
if ($this_idx != $instance->creator_idx() && !ISADMIN()) {
SPITAJAX_ERROR(1, "Not enough permission. Maybe Clone instead?");
return;
}
#
# Call out to the backend.
#
......@@ -1014,7 +1010,7 @@ function Do_RebootOrReload($which)
# Call out to the backend.
#
$webtask_id = WebTask::GenerateID();
$retval = SUEXEC($this_user->uid(), "nobody",
$retval = SUEXEC("nobody", "nobody",
"webmanage_instance -t $webtask_id -- ".
" $which $uuid " . escapeshellarg($node_id),
SUEXEC_ACTION_IGNORE);
......@@ -1243,7 +1239,7 @@ function Do_Linktest()
return;
}
$webtask_id = WebTask::GenerateID();
$retval = SUEXEC($this_user->uid(), "nobody",
$retval = SUEXEC("nobody", "nobody",
"webmanage_instance -t $webtask_id -- ".
"linktest $uuid $level",
SUEXEC_ACTION_IGNORE);
......@@ -1296,7 +1292,7 @@ function Do_Linktest()
return;
}
$webtask_id = WebTask::GenerateID();
$retval = SUEXEC($this_user->uid(), "nobody",
$retval = SUEXEC("nobody", "nobody",
"webmanage_instance -t $webtask_id -- ".
"linktest $uuid -k",
SUEXEC_ACTION_IGNORE);
......
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