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 410b56e0 authored by Leigh B Stoller's avatar Leigh B Stoller

Add shellinabox support for Quick VM ssh in the browser.

parent d8831438
......@@ -35,7 +35,8 @@ ISCLEARINGHOUSE = @PROTOGENI_ISCLEARINGHOUSE@
include $(OBJDIR)/Makeconf
SBIN_STUFF = cleanupslice gencabundle cleanupticket
SBIN_STUFF = cleanupslice gencabundle cleanupticket aptssh-setup
PSBIN_STUFF = register_resources expire_daemon gencrl postcrl \
addauthority getcacerts \
gencrlbundle shutdownslice remauthority listusage \
......@@ -53,7 +54,7 @@ endif
# These scripts installed setuid, with sudo.
SETUID_BIN_SCRIPTS =
SETUID_SBIN_SCRIPTS = cleanupslice gencabundle cleanupticket
SETUID_SBIN_SCRIPTS = cleanupslice gencabundle cleanupticket aptssh-setup
SETUID_LIBX_SCRIPTS =
#
......@@ -61,7 +62,8 @@ SETUID_LIBX_SCRIPTS =
# configure if the .in file is changed.
#
all: $(SBIN_STUFF) $(PSBIN_STUFF) \
initsite resolve resolvenode resolve-ch getversion genspeaksfor
initsite resolve resolvenode resolve-ch getversion genspeaksfor \
shellinabox.pl
include $(TESTBED_SRCDIR)/GNUmakerules
......@@ -72,6 +74,9 @@ install: $(addprefix $(INSTALL_SBINDIR)/, $(SBIN_STUFF)) \
$(INSTALL_LIBEXECDIR)/webmaptoslice
-rm -f $(INSTALL_SBINDIR)/protogeni/cleanupticket
apt-install: $(addprefix $(INSTALL_DIR)/opsdir/cgi-bin/, shellinabox.pl) \
$(addprefix $(INSTALL_SBINDIR)/, aptssh-setup)
control-install:
clean:
......@@ -82,5 +87,11 @@ $(INSTALL_SBINDIR)/protogeni/%: %
-mkdir -p $(INSTALL_SBINDIR)/protogeni
$(INSTALL) $< $@
$(INSTALL_DIR)/opsdir/cgi-bin/shellinabox.pl: shellinabox.pl
echo "Installing (link to wrapper) $<"
mkdir -p $(INSTALL_DIR)/opsdir/cgi-bin
-rm -f $@
ln -s $(INSTALL_LIBEXECDIR)/runsuid $@
echo "Installing (real script) $<"
-mkdir -p $(INSTALL_DIR)/opsdir/suidbin
$(SUDO) $(INSTALL_PROGRAM) $< $(INSTALL_DIR)/opsdir/suidbin/$<
#!/usr/bin/perl -wT
#
# Copyright (c) 2000-2013 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 English;
use Getopt::Std;
#
# Setup GateOne stuff for in browser SSH client.
#
sub usage()
{
print "Usage: gateone-setup [-d] ...\n";
print "Options:\n";
print " -d Turn on debugging\n";
print " -r Force a regenerate of initial key for user\n";
exit(-1);
}
my $optlist = "dr";
my $debug = 0;
my $regen = 0;
#
# Configure variables
#
my $TB = "@prefix@";
my $TBOPS = "@TBOPSEMAIL@";
my $OURDOMAIN = "@OURDOMAIN@";
my $CONTROL = "@USERNODE@";
my $PROTOGENI = @PROTOGENI_SUPPORT@;
my $KEYGEN = "/usr/bin/ssh-keygen";
my $APTDIR = "/var/apt/users";
my $USERSAPTDIR = "$TB/usersvar/apt/users";
my $SSH = "$TB/bin/sshtb";
my $SAVEUID = $UID;
# Locals
my $user;
#
# Testbed Support libraries
#
use lib "@prefix@/lib";
use GeniDB;
use GeniUser;
# Connect to the SA DB.
GeniDB::DBConnect(GeniDB::GENISA_DBNAME());
#
# Function prototypes
#
sub fatal($);
#
# Turn off line buffering on output
#
$| = 1;
#
# Untaint the path
#
$ENV{'PATH'} = "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin";
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
#
# We don't want to run this script unless its the real version.
#
if ($EUID != 0) {
die("*** $0:\n".
" Must be setuid! Maybe its a development version?\n");
}
#
# Please do not run it as root. Hard to track what has happened.
#
if ($UID == 0) {
die("*** $0:\n".
" Please do not run this as root!\n");
}
#
# Parse command arguments. Once we return from getopts, all that should be
# left are the required arguments.
#
%options = ();
if (! getopts($optlist, \%options)) {
usage();
}
if (defined($options{"d"})) {
$debug = 1;
}
if (defined($options{"r"})) {
$regen = 1;
}
usage()
if (@ARGV != 1);
my $geniuser = GeniUser->Lookup($ARGV[0], 0);
if (!defined($geniuser)) {
fatal("No such Geni user!");
}
my $uid = $geniuser->uid();
my $dir = "$APTDIR/$uid";
my $udir = "$USERSAPTDIR/$uid";
$UID = 0;
if (! -e "$udir") {
system("$SSH -host $CONTROL '/bin/mkdir -p -m 0750 $dir'")
== 0 or fatal("Could not mkdir $dir: $!");
}
system("$SSH -host $CONTROL ".
" 'rm -f $dir/id_rsa; $KEYGEN -q -t rsa -P \"\" ".
" -C \"${uid}" . "\@" . ${OURDOMAIN} . "\" ".
" -f $dir/id_rsa'")
== 0 or fatal("Failure in ssh-keygen!");
system("$SSH -host $CONTROL '/usr/sbin/chown -R nobody:nobody $dir'")
== 0 or fatal("Could not mkdir $dir: $!");
system("$SSH -host $CONTROL '/bin/chmod -R 700 $dir'")
== 0 or fatal("Could not mkdir $dir: $!");
system("/usr/bin/fsync $udir");
system("/usr/bin/fsync $udir/id_rsa.pub");
sleep(5);
$UID = $SAVEUID;
# Grab a copy for the DB.
my $pubkey = `cat $udir/id_rsa.pub`;
if ($?) {
fatal("Could not read new key from file");
}
chomp($pubkey);
# Only one.
$geniuser->DeleteInternalKeys();
$geniuser->AddInternalKey($pubkey) == 0
or fatal("Could not add new pub key to the database!");
exit(0);
sub fatal($) {
my($mesg) = $_[0];
die("*** $0:\n".
" $mesg\n");
}
......@@ -69,7 +69,7 @@ my $PGENIDOMAIN = "@PROTOGENI_DOMAIN@";
my $SACERT = "$TB/etc/genisa.pem";
my $CMCERT = "$TB/etc/genicm.pem";
my $SSHKEYGEN = "/usr/bin/ssh-keygen";
my $GATEONESETUP = "$TB/sbin/gateone-setup";
my $SSHSETUP = "$TB/sbin/aptssh-setup";
# un-taint path
$ENV{'PATH'} = '/bin:/usr/bin:/usr/local/bin:/usr/site/bin';
......@@ -354,13 +354,11 @@ if (!defined($geniuser)) {
$geniuser->SetAuthToken($auth_token);
#
# Setup GateOne browser ssh.
# Setup ssh browser ssh.
#
if (0) {
system("$GATEONESETUP -g " . $geniuser->uuid());
fatal("Could not create ssh key pair")
if ($?);
}
system("$SSHSETUP " . $geniuser->uuid());
fatal("Could not create ssh key pair")
if ($?);
}
my $user_uuid = $geniuser->uuid();
# So we know this user has dome something lately.
......@@ -653,7 +651,9 @@ sub Terminate($)
[$slice_credential->asString(),
$speaksfor_credential->asString()]});
if (!defined($response) || $response->code() != GENIRESPONSE_SUCCESS) {
if (!defined($response) ||
($response->code() != GENIRESPONSE_SUCCESS &&
$response->code() != GENIRESPONSE_SEARCHFAILED)) {
fatal("DeleteSlice failed: ".
(defined($response) ? $response->output() : "") . "\n");
}
......
#!/usr/bin/perl -w
#
# Copyright (c) 2008-2013 University of Utah and the Flux Group.
#
# {{{GENIPUBLIC-LICENSE
#
# GENI Public License
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and/or hardware specification (the "Work") to
# deal in the Work without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Work, and to permit persons to whom the Work
# is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Work.
#
# THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS
# IN THE WORK.
#
# }}}
#
#
# Simple CGI interface to shellinabox ...
#
use strict;
use English;
use Data::Dumper;
use CGI;
use JSON;
use Digest::HMAC_SHA1 qw(hmac_sha1 hmac_sha1_hex);
use Sys::Syslog;
use IO::Handle;
# Yack. apache does not close fds before the exec, and if this dies
# we are left with a giant mess.
BEGIN {
no warnings;
for (my $i = 3; $i < 1024; $i++) {
POSIX:close($i);
}
}
# Configure variables
my $TB = "@prefix@";
my $MAINSITE = @TBMAINSITE@;
my $TBOPS = "@TBOPSEMAIL@";
my $TBLOGS = "@TBLOGSEMAIL@";
my $TBBASE = "@TBBASE@";
my $TBLOGFACIL = "@TBLOGFACIL@";
my $CERTFILE = "/usr/local/etc/apache22/ssl.crt/users.emulab.net.crt";
my $KEYFILE = "/usr/local/etc/apache22/ssl.key/users.emulab.net.key";
my $APTDIR = "/var/apt/users";
# Testbed libraries.
use lib '@prefix@/lib';
#
# Turn off line buffering on output
#
$| = 1;
#
# Untaint the path
#
$ENV{'PATH'} = '/bin:/usr/bin:/usr/local/bin';
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
# Locals
my $debug = 0;
# Watch for apache restart; must disconnect and continue.
my $disconnected = 0;
#
# Only apache ...
#
if (getpwuid($UID) ne "nobody") {
printf STDERR "You are not allowed to run this script!\n";
exit(1);
}
sub info($)
{
my ($msg) = @_;
if ($debug) {
print STDERR "$msg\n";
}
else {
syslog("info", $msg);
}
}
sub fatal($)
{
my ($msg) = @_;
info($msg);
exit(1);
}
if ($debug) {
open(STDERR, "> /tmp/foo.log");
}
else {
# Set up syslog
openlog("shellinabox", "pid", $TBLOGFACIL);
}
# The query holds the authentication object.
my $query = new CGI();
my $authstuff = $query->param('auth');
if (!defined($authstuff)) {
fatal("No auth object provided");
}
#
# We need the shared key to recreate the SHA1 signature.
#
open(KEY, "/usr/testbed/etc/sshauth.key") or
fatal("Could not open sshauth.key");
my $sshauthkey = <KEY>;
chomp($sshauthkey);
#
# Dig out the authentication object. It is a json object.
#
my $auth = eval { decode_json($authstuff); };
if ($@) {
fatal("Could not decode auth object");
}
if ($debug) {
print STDERR Dumper($auth);
}
else {
syslog("info", $auth->{'uid'} . "," . $auth->{'nodeid'});
}
#
# Recreate the signature and compare.
#
my $sigtocheck =
$auth->{'uid'} . $auth->{'stuff'} . $auth->{'nodeid'} . $auth->{'timestamp'};
my $signature = hmac_sha1_hex($sigtocheck, $sshauthkey);
if ($signature ne $auth->{'signature'}) {
fatal("Bad signature: $signature");
}
my $uid = $auth->{'uid'};
my $nodeid = $auth->{'nodeid'};
my $port = "";
# Silly taint check stuff.
if ($uid =~ /^([-\w]*)$/) {
$uid = $1;
}
# Watch for port number in nodeid.
if ($nodeid =~ /^([-\.\w]*)$/) {
$nodeid = $1;
}
elsif ($nodeid =~ /^([-\.\w]*):(\d*)$/) {
$nodeid = $1;
$port = "-p $2";
}
my $who = "${uid}\@${nodeid}";
my $where = "HOME";
my $sshopts = "";
# shellinabox wants the gid to be the default for the user.
my (undef,undef,$gid) = getpwnam($uid);
# No gid, see if a phony user.
if (!defined($gid)) {
if (-e "$APTDIR/$uid") {
$sshopts = "-i $APTDIR/$uid/id_rsa ";
$sshopts .= "-q -o BatchMode=yes -o StrictHostKeyChecking=no ";
$sshopts .= "-o UserKnownHostsFile=${APTDIR}/$uid/known_hosts";
$where = "$APTDIR/$uid";
# Switch to nobody for below.
$uid = "nobody";
$gid = "nobody";
}
else {
fatal("$uid is not in the passwd file or $APTDIR");
}
}
# Silly taint check stuff.
if ($gid =~ /^([-\w]*)$/) {
$gid = $1;
}
# This is so shellinabox will not complain.
$UID = $EUID;
# Shove this header out so that we can do cross site xmlrpc.
print "Access-Control-Allow-Origin: $TBBASE\n";
my $cmd = "shellinaboxd " . ($debug ? "-d" : "-v") . " " .
"--certfile=${CERTFILE} --keyfile=${KEYFILE} ".
"--cgi -c $TB/etc -s '/:$uid:$gid:$where:/usr/bin/ssh $port $sshopts $who'";
info($cmd);
#
# The point of this is to capture the initial STDERR of shellinaboxd
# and send it out to syslog or file.
#
pipe(READER, WRITER);
WRITER->autoflush(1);
my $pid = fork();
if ($pid) {
close WRITER;
close STDOUT;
close STDIN;
while (<READER>) {
info($_);
}
info("Before waitpid");
waitpid($pid, 0);
info("After waitpid: $?");
exit($? >> 0);
}
else {
# Need the parent to run first so it can close STDOUT. Better
# ways to do this of course.
select(undef, undef, undef, 0.2);
close READER;
close STDERR;
open(STDERR, ">&WRITER") || fatal("can't dup to stderr");
close WRITER;
}
exec($cmd);
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