Commit f8c38a66 authored by Mike Hibler's avatar Mike Hibler

Merge branch 'master' into newports

parents a01156e0 66057123
......@@ -29,10 +29,15 @@ use English;
use Data::Dumper;
use POSIX qw(tmpnam);
use Exporter;
use vars qw(@ISA @EXPORT $AUTOLOAD);
use vars qw(@ISA @EXPORT $AUTOLOAD
$RECORDHISTORY_TERMINATED $RECORDHISTORY_EXPIRED);
@ISA = "Exporter";
@EXPORT = qw ( );
# RecordHistory flags.
$RECORDHISTORY_TERMINATED = 0x00;
$RECORDHISTORY_EXPIRED = 0x01;
@EXPORT = qw($RECORDHISTORY_TERMINATED $RECORDHISTORY_EXPIRED);
# Must come after package declaration!
use EmulabConstants;
......@@ -469,10 +474,12 @@ sub Profile($)
# Record a history entry.
# We do this when the instance is terminated.
#
sub RecordHistory($)
sub RecordHistory($$)
{
my ($self) = @_;
my ($self, $flags) = @_;
my $uuid = $self->uuid();
my $expired = ($flags ? 1 : 0);
DBQueryWarn("replace into apt_instance_aggregate_history ".
"select * from apt_instance_aggregates where uuid='$uuid'")
......@@ -481,7 +488,7 @@ sub RecordHistory($)
DBQueryWarn("replace into apt_instance_history ".
"select uuid,name,profile_id,profile_version,slice_uuid, ".
" creator,creator_idx,creator_uuid,pid,pid_idx, ".
" aggregate_urn,public_url,created,now(), ".
" aggregate_urn,public_url,created,now(),$expired, ".
" extension_count,extension_days, ".
" physnode_count,virtnode_count, ".
" servername,rspec,params,manifest ".
......
......@@ -38,7 +38,7 @@ WEB_BIN_SCRIPTS = webmanage_profile webmanage_instance webmanage_dataset \
webcreate_instance webrungenilib
WEB_SBIN_SCRIPTS=
LIBEXEC_SCRIPTS = $(WEB_BIN_SCRIPTS) $(WEB_SBIN_SCRIPTS)
USERLIBEXEC = rungenilib.proxy
USERLIBEXEC = rungenilib.proxy genilib-jail genilib-iocage
# These scripts installed setuid, with sudo.
SETUID_BIN_SCRIPTS = rungenilib
......
......@@ -254,7 +254,7 @@ sub ExpireInstances()
}
# Use debug option to keep it from going into the background.
my $output = emutil::ExecQuiet("$SUDO -u $PROTOUSER ".
"$MANAGEINSTANCE -d terminate $uuid");
"$MANAGEINSTANCE -d terminate $uuid -e");
my $status = $?;
print STDERR "$output\n"
if ($output ne "");
......
......@@ -191,7 +191,8 @@ if (!defined($this_user) || !$this_user->IsAdmin()) {
# Email record.
if (! $debug) {
AuditStart(0, undef, LIBAUDIT_LOGTBLOGS()|LIBAUDIT_LOGONLY());
my $opts = LIBAUDIT_LOGTBLOGS()|LIBAUDIT_LOGONLY();
AuditStart(0, undef, $opts);
#
# Once we determine the project, we can add the appropriate log CC
#
......@@ -495,7 +496,9 @@ if ($localuser) {
#
system("$UPDATEGENIUSER -p " . $emulab_user->uid());
if ($?) {
fatal("Not a member of any projects");
UserError("Could not get your project membership from your ".
"member authority. It is probably offline, please try ".
"again later.");
}
# Nonlocal users get the holding project.
......@@ -578,6 +581,10 @@ elsif (!$localuser) {
#
if (!$debug) {
AddAuditInfo("cc", $project->LogsEmailAddress());
if ($MAINSITE && $project->isEmulab()) {
# Mostly people use the Cloudlab UI.
AddAuditInfo("cc", "cloudlab-logs\@cloudlab.us");
}
}
# There will be "internal" keys cause we pass the flag asking for them.
......@@ -766,13 +773,12 @@ print STDERR "$rspecstr\n";
# Exit and let caller poll for status.
#
if (!$debug) {
libaudit::AuditPrefork();
my $child = fork();
if ($child) {
# Parent exits but avoid libaudit email.
exit(0);
}
# Let parent exit;
sleep(2);
# All of the logging magic happens in here.
libaudit::AuditFork();
}
......
#!/usr/bin/perl -w
#
# Copyright (c) 2015 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
# This file is part of the Emulab network testbed software.
#
# This file is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
#
# This file is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
# License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this file. If not, see <http://www.gnu.org/licenses/>.
#
# }}}
#
use strict;
use English;
use Getopt::Std;
use BSD::Resource;
use POSIX qw(:signal_h);
use File::Basename;
#
# Fire up an instance of a FreeBSD jail to run a geni-lib script.
# Since we must be invoked as root, the caller is responsible for all
# permission checks.
#
# We use iocage to manage jails. We have a template jail instance that
# has python, geni-lib, etc. in it. We clone that per-call to execute
# the geni-lib script. Pretty fast since ZFS is used to clone the filesystem
# and the jail runs essentially no daemons.
#
# XXX Note that although iocage supports resource limits, we don't use them
# right now because it requires a non-standard option to the kernel (that
# we don't have compiled in). So for the moment, we let our caller apply
# any CPU limits. Here are the limits set on the template (in the event we
# someday start using them):
# cputime="600:sigkill"
# maxproc="100:deny"
# memoryuse="1G:deny"
# see rctl(8) for details.
#
# XXX Note that we don't use the passed-in user either. We just run the
# script as "nobody" in the jail. We can add the user's account to the
# jail if that proves necessary.
#
my $CMD_TIMEOUT = 600;
my $CMD_QUOTA = "2G";
my $IOCAGE = "/usr/local/sbin/iocage";
my $PROG = $0;
my $INTERP = "nice -15 /usr/local/bin/python";
#my $INTERP = "/bin/sh";
# this is a ZFS snapshot that we make of the base FS
my $JAILROOT = "/iocage/jails";
my $jailattrs = "compression=off quota=$CMD_QUOTA exec_timeout=$CMD_TIMEOUT exec_clean=0";
# XXX for now
$jailattrs .= " rlimits=off";
# XXX speed things up
$jailattrs .= " vnet=off ip6=disable";
# XXX when cloning, hostname is not set
#$jailattrs .= " hostname=py-cage.emulab.net host_hostname=py-cage.emulab.net";
# XXX maybe not a good idea
#$jailattrs .= " exec_start=/usr/bin/true exec_stop=/usr/bin/true";
my $starttime = time();
sub usage()
{
print STDOUT
"Usage: $PROG [-d] [-n jailname] [-u user] [-p paramfile] [-o outfile] script\n".
" or: $PROG [-CR] [-n jailname]\n".
"Execute the given geni-lib script in a jail or just create/remove a jail\n".
"Options:\n".
" -u user User to run script as.\n".
" -p paramfile JSON params to pass to script.\n".
" -o outfile File in which to place script results.\n".
" -n jailname Name of jail; default is 'py-cage-<pid>'.\n".
" -d Turn on debugging.\n".
" -C Just create the jail; use 'jexec' to run commands.\n".
" -R Remove an existing (left-over) jail; must specify a name (-n)\n";
exit(-1);
}
my $optlist = "du:p:o:n:CR";
my $jailname = "py-cage-$$";
my $user = "nobody";
my $uid;
my $gid;
my $pfile;
my $ofile;
my $ifile;
# action: 1: create, 2: destroy, 4: run script
my $action = 4;
sub msg(@);
#
# Configure variables
#
my $TBROOT = "@prefix@";
my $GENILIB = "$TBROOT/lib/geni-lib/";
my $debug = 0;
#
# Turn off line buffering on output
#
$| = 1;
#
# Untaint the path
#
$ENV{'PATH'} = "/bin:/usr/bin:/sbin:/usr/sbin";
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
# Where geni-lib should be mounted in the jail
my $GENILIB_MNT = "/usr/testbed/lib/geni-lib/";
$ENV{"PYTHONPATH"} = $GENILIB_MNT;
#
# Testbed Support libraries
#
use lib "@prefix@/lib";
use libtestbed;
my $jailtag;
my $jailstate = 0;
my $jailuuid;
END {
if ($jailstate) {
my $ecode = $?;
if ($ecode) {
print STDERR "Unexpected exit, cleaning up...\n";
}
if ($action != 1 || $ecode) {
if ($jailstate == 2) {
msg("Stopping jail");
if (system("$IOCAGE stop $jailtag")) {
print STDERR "*** could not stop jail $jailtag\n";
}
msg("Stopping done");
$jailstate = 1;
}
if ($jailstate == 1) {
msg("Destroying jail");
# XXX make sure special FSes get unmounted
system("umount $JAILROOT/$jailuuid/root$GENILIB_MNT >/dev/null 2>&1")
if ($jailuuid);
if (system("$IOCAGE destroy -f $jailtag")) {
print STDERR "*** could not destroy jail $jailtag\n";
}
msg("Destroying done");
$jailstate = 0;
}
}
$? = $ecode;
}
}
#
# Parse command arguments. Once we return from getopts, all that should
# left are the required arguments.
#
my %options = ();
if (! getopts($optlist, \%options)) {
usage();
}
if (defined($options{"C"})) {
$action = 1;
}
if (defined($options{"R"})) {
$action = 2;
}
if (defined($options{"d"})) {
$debug = 1;
}
if (defined($options{"p"})) {
$pfile = $options{"p"};
}
if (defined($options{"o"})) {
$ofile = $options{"o"};
}
if (defined($options{"u"})) {
$user = $options{"u"};
}
if (defined($options{"n"})) {
$jailname = $options{"n"};
if ($jailname !~ /^[-\w]+$/) {
print STDERR "Come on, keep the name simple...\n";
usage();
}
}
#
# Extract params from the environment (if invoked via rungenilib.proxy).
#
if (!$ofile && exists($ENV{'GENILIB_PORTAL_REQUEST_PATH'})) {
$ofile = $ENV{'GENILIB_PORTAL_REQUEST_PATH'};
}
if (!$pfile && exists($ENV{'GENILIB_PORTAL_PARAMS_PATH'})) {
$pfile = $ENV{'GENILIB_PORTAL_PARAMS_PATH'};
}
if ($action == 4) {
if (@ARGV < 1) {
print STDERR "Must specify a script\n";
usage();
}
$ifile = $ARGV[0];
if (!$ofile) {
print STDERR "Must specify an output file (-o)\n";
usage();
}
} else {
if (@ARGV != 0) {
print STDERR "Too many args for create/destroy\n";
usage();
}
}
# Must be a legit user
(undef,undef,$uid,$gid) = getpwnam($user) or
die("*** $0:\n".
" Invalid user '$user'!");
# Must run as root
if ($UID != 0) {
die("*** $0:\n".
" Must be root to run this script!");
}
# Make sure jail infrastructure is there
if (! -d "$JAILROOT") {
die("*** $0:\n".
" $JAILROOT does not exist; is iocage installed?");
}
#
# Action checks.
# If only creating (-C) or running normally, then jail should not exist.
# If only removing (-R), then jail should exist.
#
# XXX we have to do an 'iocage list' to determine existance and that is
# expensive, so we just don't do it.
#
#if ($action != 2 && -e "$JAILROOT/$jailname") {
# die("*** $0:\n $jailname already exists");
#}
#
# Removing an existing jail, just set the jailstate and exit.
#
if ($action == 2) {
my $info = `$IOCAGE list | grep $jailname`;
if ($info =~ /\s(\w{8}-\w{4}-\w{4}-\w{4}-\w{12})\s/) {
$jailuuid = $1;
} else {
die("*** $0:\n $jailname does not exist");
}
$jailtag = $jailname;
$jailstate = 2;
exit(0);
}
#
# Fire up a jail instance
#
msg("Cloning py-cage");
if (system("$IOCAGE clone py-cage tag=$jailname $jailattrs")) {
print STDERR "Could not create geni-lib jail\n";
exit(1);
}
msg("Cloning done");
$jailstate = 1;
$jailtag = $jailname;
# XXX we have to explicitly set this after cloning!
system("$IOCAGE set hostname=$jailtag.emulab.net $jailtag");
#
# Find the UUID so we can locate the ZFS
# list output:
# - 2b0ecbff-3f6c-11e5-a21b-38eaa71273f9 off down py-cage-34420
#
msg("Finding UUID");
my $info = `$IOCAGE list | grep $jailtag`;
msg("Finding done");
if ($info =~ /\s(\w{8}-\w{4}-\w{4}-\w{4}-\w{12})\s/) {
$jailuuid = $1;
} else {
print STDERR "Could not find UUID for geni-lib jail\n";
exit(1);
}
my $jailrootdir = "$JAILROOT/$jailuuid/root";
if (! -d $jailrootdir) {
print STDERR "Could not find root FS for $jailtag ($jailuuid)\n";
exit(1);
}
#
# XXX mount host geni-lib in jail
# This way we don't have to copy in the geni-lib files and they
# will always be up-to-date. Also make dev trees work.
#
if (system("mount -t nullfs -o ro $GENILIB $jailrootdir$GENILIB_MNT")) {
print STDERR "Could not mount $GENILIB in $jailtag\n";
exit(1);
}
#
# Drop our files into the jail FS
# We create a protected directory owned by the user so that people
# cannot snoop from outside the jail.
#
my ($j_ifile,$j_ofile,$j_pfile);
if ($action != 1) {
my $tempdir = "/tmp/genilib/";
if (!mkdir("$jailrootdir$tempdir", 0700)) {
print STDERR "Could not create geni-lib jail tempdir\n";
exit(1);
}
$j_ifile = $tempdir . basename($ifile);
$j_ofile = $tempdir . basename($ofile);
$j_pfile = $tempdir . basename($pfile)
if ($pfile);
msg("Stashing files");
if (system("cp -p $ifile $jailrootdir$j_ifile") ||
($pfile && system("cp -p $pfile $jailrootdir$j_pfile")) ||
system("chown -R $uid:$gid $jailrootdir$tempdir")) {
print STDERR "Could not populate jail\n";
exit(1);
}
#
# XXX adjust the environment for the portal module to reflect the jail.
#
$ENV{'GENILIB_PORTAL_REQUEST_PATH'} = $j_ofile;
if (exists($ENV{'GENILIB_PORTAL_DUMPPARAMS_PATH'})) {
$ENV{'GENILIB_PORTAL_DUMPPARAMS_PATH'} = $j_ofile;
}
if ($pfile) {
$ENV{'GENILIB_PORTAL_PARAMS_PATH'} = $j_pfile;
}
#
# Make other aspects of the environment appear a little bit sane.
# XXX okay, not really THAT sane since the user will not exist in the jail.
#
$ENV{'SHELL'} = "/bin/sh";
$ENV{'USER'} = $user;
$ENV{'USERNAME'} = $user;
$ENV{'LOGNAME'} = $user;
$ENV{'HOME'} = $tempdir;
delete $ENV{'DISPLAY'};
delete @ENV{'SUDO_UID', 'SUDO_GID', 'SUDO_USER', 'SUDO_COMMAND'};
delete @ENV{'SSH_AUTH_SOCK', 'SSH_CLIENT', 'SSH_CONNECTION', 'SSH_TTY'};
}
#
# Fire up the jail
#
msg("Start jail");
if (system("$IOCAGE start $jailtag")) {
print STDERR "Could not start jail $jailtag\n";
exit(1);
}
msg("Starting done");
$jailstate = 2;
#
# And execute the command as the indicated jail user.
#
# XXX currently we run the command as the real user (-u), but note that
# they will have no passwd entry inside the jail. May cause problems.
# If so, we can run as nobody in the jail (-U nobody) or add the real
# user to the jail (but that is a lot of work for one command...)
#
my $status = -1;
if ($action != 1) {
msg("Execing command");
$status = system("$IOCAGE exec -u $user $jailtag $INTERP $j_ifile");
msg("Execing done");
} else {
$status = 0;
}
if ($status) {
if ($status == -1) {
print STDERR "Could not run jail $jailtag\n";
} elsif ($status & 127) {
$status &= 127;
print STDERR "Jail $jailtag execution died with signal $status\n";
$status = 2;
} else {
$status >>= 8;
print STDERR "Jail $jailtag execution failed with exit code $status\n";
}
# XXX odd semantics: if debug is set, don't remove jail on error
if ($debug) {
print STDERR "WARNING: not destroying jail, you will need to do it:\n".
" sudo $PROG -R -n $jailtag\n";
$jailstate = 0;
}
}
if ($action != 1) {
#
# Oh the joys of running as root. Now we need to take away user
# permission from the jail directory (recall the user can access
# it from outside) and then verify that the source file isn't a
# symlink (a cheap-o realpath check). Our caller is responsible
# for defending the target file.
#
my $tempdir = "/tmp/genilib";
if (-l "$jailrootdir$tempdir" ||
chown(0, -1, "$jailrootdir$tempdir") != 1) {
print STDERR "Could not copy back results of command\n";
exit(1);
}
if (-e "$jailrootdir$j_ofile") {
if (-l "$jailrootdir$j_ofile" ||
system("cp $jailrootdir$j_ofile $ofile")) {
print STDERR "Could not copy back results of command\n";
exit(1);
}
}
} else {
print STDERR "Jail '$jailtag' running. Root FS at '$jailrootdir'.\n";
}
exit($status);
sub msg(@)
{
if ($debug) {
my $stamp = time() - $starttime;
printf STDERR "[%3d] ", $stamp;
print STDERR @_, "\n";
}
}
#!/usr/bin/perl -w
#
# Copyright (c) 2015 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
# This file is part of the Emulab network testbed software.
#
# This file is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
#
# This file is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
# License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this file. If not, see <http://www.gnu.org/licenses/>.
#
# }}}
#
use strict;
use English;
use Getopt::Std;
use BSD::Resource;
use POSIX qw(:signal_h);
use File::Basename;
#
# Fire up an instance of a FreeBSD jail to run a geni-lib script.
# Since we must be invoked as root, the caller is responsible for all
# permission checks.
#
# This is the simple roll-our-own version. We take advantage of a template
# jail filesystem setup by iocage, but don't use iocage for anything else
# as it is toooo slooooow. We clone this FS per-call to execute the geni-lib
# script and setup a basic jail with no daemons, no network, etc.
#
# XXX This version does not support any resource limits other that a quota
# on disk use (ZFS property) and a per-command time limit (jail config).
#
# XXX Note that we don't use the passed-in user. We just run the script as
# "nobody" in the jail.
#
my $USEJAILRUN = 0;
my $CMD_TIMEOUT = 600;
my $CMD_QUOTA = "2G";
my $PROG = $0;
my $INTERP = "nice -15 /usr/local/bin/python";
#my $INTERP = "/bin/sh";
# this is a ZFS snapshot that we make of the base FS
my $JAILROOT = "/iocage/jails";
my $ZFSBASE = "z/iocage/jails";
my $zfsattrs = "-o compression=off -o quota=$CMD_QUOTA";
my $starttime = time();