Commit 7056da55 authored by Leigh Stoller's avatar Leigh Stoller Committed by Gary Wong

First checkin of shellinabox support for APT.

parent 1b16a4bd
......@@ -55,6 +55,16 @@ body {
margin-left: auto;
margin-right: auto;
}
/* These two so the nav tab X looks right. */
.nav-tabs > li .close {
display: inline;
margin: -5px 0 0 5px;
font-size: 15px;
}
/* Smaller nav tabs. */
.nav > li > a {
padding: 5px 5px;
}
/* Styles for svg diagrams */
.topomap {
......
......@@ -25,7 +25,7 @@ chdir("..");
include("defs.php3");
include_once("osinfo_defs.php");
include_once("geni_defs.php");
chdir("aptlbs");
chdir("apt");
include("quickvm_sup.php");
$dblink = GetDBLink("sa");
......
......@@ -25,7 +25,7 @@ chdir("..");
include("defs.php3");
include_once("osinfo_defs.php");
include_once("geni_defs.php");
chdir("aptlbs");
chdir("apt");
include("quickvm_sup.php");
$ajax_request = 0;
......@@ -130,8 +130,8 @@ if (isset($ajax_request)) {
$eid = $experiment->eid();
SPITAJAX_RESPONSE(GetTopoMap($creator->uid(), $pid, $eid));
}
elseif ($ajax_method == "gateone_authobject") {
SPITAJAX_RESPONSE(GateOneAuthObject($creator->uid()));
elseif ($ajax_method == "ssh_authobject") {
SPITAJAX_RESPONSE(SSHAuthObject($creator->uid(), $ajax_argument));
}
elseif ($ajax_method == "request_extension") {
# Only extend for 24 hours. More later.
......@@ -266,19 +266,21 @@ echo "</div>\n";
# The topo diagram goes inside this div, when it becomes available.
#
echo "<div class='row'>
<div class='col-lg-6 col-lg-offset-3
col-md-8 col-md-offset-2
col-sm-8 col-sm-offset-2
<div class='col-lg-10 col-lg-offset-1
col-md-10 col-md-offset-1
col-sm-10 col-sm-offset-1
col-xs-12 col-xs-offset-0'>\n";
echo "<div class='panel panel-default invisible' id='showtopo_container'>\n";
echo "<div class='panel-body'>\n";
echo "<div id='quicktabs_div'>\n";
echo "<div id='showtopo_div'></div>\n";
SpitToolTip("Click on a node to SSH to that node.\n".
"Click and drag on a node to move things around.");
echo "</div>\n";
echo "</div>\n";
echo "</div>\n";
echo "</div>\n";
echo "</div>\n"; # showtopo
echo "</div>\n"; # quicktabs
echo "</div>\n"; # container
echo "</div>\n"; # cols
echo "</div>\n"; # row
#
# A modal to tell people how to register
......@@ -379,25 +381,9 @@ echo "<!-- This is a modal -->
</div>
</div>\n";
if (0) {
echo "<div class='uk-panel uk-panel-box uk-panel-header
uk-container-center'>\n";
echo " <div id='gateone_container'
style='font-family: monospace'>
<div id='gateone' style='height: 20em; font-family: monospace'>
</div>
</div>\n";
echo "</div>\n";
}
$location = uniqid("loc");
$auth_object = GateOneAuthObject($creator_uid);
echo "<script src='d3.v3.js'></script>\n";
echo "<SCRIPT LANGUAGE=JavaScript>
InitQuickVM('$uuid', '$slice_expires',
'$location', '$auth_object');
InitQuickVM('$uuid', '$slice_expires');
</SCRIPT>\n";
SPITFOOTER();
......
var myuuid = null;
function ShowModal(which)
{
console.log('Showing modal ' + which);
// console.log('Showing modal ' + which);
$( which ).modal('show');
}
function HideModal(which)
{
console.log('Hide modal ' + which);
// console.log('Hide modal ' + which);
$( which ).modal('hide');
}
......@@ -59,7 +61,6 @@ function StatusWatchCallBack(uuid, json)
if (json.code) {
status = "terminated";
}
console.log(status);
if (status != StatusWatchCallBack.laststatus) {
status_html = status;
......@@ -87,6 +88,7 @@ function StatusWatchCallBack(uuid, json)
$("#quickvm_progress").addClass("progress-bar-danger");
$("#quickvm_progress_bar").width("100%");
}
$("#terminate_button").prop("disabled", false);
}
else if (status == 'terminating' || status == 'terminated') {
status_html = "<font color=red>" + status + "</font>";
......@@ -126,8 +128,7 @@ function ShowTopo(uuid)
$("#showtopo_container").removeClass("invisible");
maketopmap("#showtopo_div",
$("#showtopo_div").width() - 30,
300, topo);
$("#showtopo_div").width(), 300, topo);
}
console.log(uuid);
......@@ -233,7 +234,6 @@ function Setsshurl(uuid)
var href = "<a href='" + url + "'>" + url + "</a>";
console.log(url);
$("#quickvm_sshurl").html(href);
// StartGateOne(url);
}
var $xmlthing = CallMethod("manifest", null, uuid, null);
$xmlthing.done(callback);
......@@ -298,51 +298,146 @@ function RegisterAccount(uid, email)
win.focus();
}
var gateone_authobject = null;
var gateone_location = null;
function InitQuickVM(uuid, slice_expires, location, auth_object)
function InitQuickVM(uuid, slice_expires)
{
// This activates the popover subsystem.
$('[data-toggle="popover"]').popover({
trigger: 'hover',
'placement': 'top'
});
gateone_authobject = auth_object;
gateone_location = location;
StartResizeWatchdog(uuid);
StartCountdownClock(slice_expires);
GetStatus(uuid);
myuuid = uuid;
}
function resetForm($form) {
$form.find('input:text, select, textarea').val('');
}
function StartSSH(uuid, sshurl)
function StartSSH(id, authobject)
{
var current_date = new Date().getTime();
GateOne.location = "loc" + current_date;
var callback = function(json) {
console.log(json.value);
var jsonauth = $.parseJSON(authobject);
var callback = function(stuff) {
var split = stuff.split(':');
var session = split[0];
var port = split[1];
var url = jsonauth.baseurl + ':' + port + '/' + '#' +
encodeURIComponent(document.location.href) + ',' + session;
console.log(url);
var iwidth = $('#' + id).width();
var iheight = 300;
$('#' + id).html('<iframe id="' + id + '_iframe" ' +
'width=' + iwidth + ' ' +
'height=' + iheight + ' ' +
'src=\'' + url + '\'>');
}
var $xmlthing = CallMethod("gateone_authobject", null, uuid, null);
$xmlthing.done(callback);
var xmlthing = $.ajax({
// the URL for the request
url: jsonauth.baseurl + '/d77e8041d1ad',
// the data to send (will be converted to a query string)
data: {
auth: authobject,
},
// Needs to be a POST to send the auth object.
type: 'POST',
// Ask for plain text for easier parsing.
dataType : 'text',
});
xmlthing.done(callback);
}
function StartGateOne(sshurl)
//
// User clicked on a node, so we want to create a tab to hold
// the ssh tab with a panel in it, and then call StartSSH above
// to get things going.
//
function NewSSHTab(hostport, client_id)
{
GateOne.location = gateone_location;
// Initialize Gate One:
GateOne.init({"url" : 'https://users.emulab.net:1090/gateone',
"autoConnectURL" : sshurl,
"fillContainer" : false,
"showToolbar" : false,
"terminalFont" : 'monospace',
"auth" : gateone_authobject});
var pair = hostport.split(":");
var host = pair[0];
var port = pair[1];
//
// On first ssh, we convert the topo div into a tabs array,
// and place each ssh session as a new tab.
//
if (! $("#quicktabs").length) {
var html =
"<ul id='quicktabs' class='nav nav-tabs'>\n" +
" <li><a href='#profile' data-toggle='tab'>Profile</a></li>\n" +
"</ul>\n" +
"<div id='quicktabs_content' class='tab-content'>\n" +
" <div class='tab-pane' id='profile'>" +
" <div id='showtopo_div'>\n" +
$('#showtopo_div').html() +
" </div>\n" +
" </div>\n" +
"</div>\n";
$('#quicktabs_div').html(html);
}
//
// Need to create the tab before we can create the topo, since
// we need to know the dimensions of the tab.
//
var tabname = client_id + "_tab";
if (! $("#" + tabname).length) {
// The tab.
var html = "<li><a href='#" + tabname + "' data-toggle='tab'>" +
client_id + "" +
"<button class='close' type='button' " +
" id='" + tabname + "_kill'>x</button>" +
"</a>" +
"</li>";
// Append to end of tabs
$("#quicktabs_div ul").append(html);
// Install a click handler for the X button.
$("#" + tabname + "_kill").click(function(e) {
e.preventDefault();
// remove the li from the ul.
$(this).parent().remove();
// Remove the content div.
$("#" + tabname).remove();
// Activate the "profile" tab.
$('#quicktabs a[href="#profile"]').tab('show');
})
// The content div.
html = "<div class='tab-pane' id='" + tabname + "'></div>";
$("#quicktabs_content").append(html);
// And make it active
$('#quicktabs a:last').tab('show') // Select last tab
}
else {
// Switch back to it.
$('#quicktabs a[href="#' + tabname + '"]').tab('show');
return;
}
// Ask the server for an authentication object that allows
// to start an ssh shell.
var callback = function(json) {
console.log(json.value);
if (json.code) {
alert("Failed to gain authentication for ssh.");
}
else {
StartSSH(tabname, json.value);
}
}
var xmlthing = CallMethod("ssh_authobject", null, myuuid, hostport);
xmlthing.done(callback);
}
//
......@@ -538,11 +633,15 @@ function maketopmap(divname, width, height, json)
.enter().append("svg:g")
.call(node_drag);
var nodea = nodeg.append("svg:a")
.attr("xlink:href", function(d) { return d.sshurl });
// var nodea = nodeg.append("svg:a")
// .attr("xlink:href", function(d) { return d.sshurl });
var node = nodea.append("svg:rect")
var node = nodeg.append("svg:rect")
.attr("class", "nodebox")
.attr("onclick",
function(d) {
return "NewSSHTab('" + d.hostport + "', " +
" '" + d.client_id + "')" })
.attr("x", "-10px")
.attr("y", "-10px")
.attr("width", "20px")
......@@ -556,7 +655,6 @@ function maketopmap(divname, width, height, json)
function tick(e) {
if (e && e.alpha < 0.05) {
console.log("Cooled");
vis.style("visibility", "visible")
force.stop();
return;
......@@ -626,7 +724,9 @@ function ConvertManifestToJSON(name, xml)
var port = login.attr("port");
var sshurl = "ssh://" + user + "@" + host + ":" + port + "/";
jobj.sshurl = sshurl;
jobj.client_id = client_id;
jobj.hostport = host + ":" + port;
jobj.sshurl = sshurl;
}
json.nodes.push(jobj);
});
......
......@@ -104,37 +104,50 @@ function SpitToolTip($info)
"</a>\n";
}
function GateOneAuthObject($uid)
#
# Generate an authentication object to pass to the browser that
# is passed to the web server on boss. This is used to grant
# permission to the user to invoke ssh to a local node using their
# emulab generated (no passphrase) key. This is basically a clone
# of what GateOne does, but that code was a mess.
#
function SSHAuthObject($uid, $nodeid)
{
global $USERNODE;
$file = "/usr/testbed/etc/sshauth.key";
#
# We need the secret that is shared with ops.
#
$fp = fopen("/usr/testbed/etc/gateone.key", "r");
$fp = fopen($file, "r");
if (! $fp) {
TBERROR("Error opening /usr/testbed/etc/gateone.key", 0);
TBERROR("Error opening $file", 0);
return null;
}
list($api_key,$secret) = preg_split('/:/', fread($fp, 128));
$key = fread($fp, 128);
fclose($fp);
if (!($secret && $api_key)) {
TBERROR("Could not get kets from gateone.key", 0);
if (!$key) {
TBERROR("Could not get key from $file", 0);
return null;
}
$secret = chop($secret);
$key = chop($key);
$stuff = GENHASH();
$now = time();
$authobj = array(
'api_key' => $api_key,
'upn' => $uid,
'timestamp' => time() . '000',
'signature_method' => 'HMAC-SHA1',
'api_version' => '1.0'
);
$authobj['signature'] = hash_hmac('sha1',
$authobj['api_key'] . $authobj['upn'] .
$authobj['timestamp'], $secret);
$valid_json_auth_object = json_encode($authobj);
return $valid_json_auth_object;
$authobj = array('uid' => $uid,
'stuff' => $stuff,
'nodeid' => $nodeid,
'timestamp' => $now,
'baseurl' => "https://${USERNODE}",
'signature_method' => 'HMAC-SHA1',
'api_version' => '1.0',
'signature' => hash_hmac('sha1',
$uid . $stuff . $nodeid . $now,
$key),
);
return json_encode($authobj);
}
#
......
......@@ -23,66 +23,37 @@
#
chdir("..");
include("defs.php3");
chdir("aptui-mockup");
chdir("apt");
include("quickvm_sup.php");
SPITHEADER();
#$node_id = "localhost";
#$uid = "stoller";
#$auth_object = SSHAuthObject($uid, $node_id);
#
# We need the secret that is shared with ops.
#
$fp = fopen("/usr/testbed/etc/gateone.key", "r");
if (! $fp) {
TBERROR("Error opening /usr/testbed/etc/gateone.key", 1);
}
list($api_key,$secret) = preg_split('/:/', fread($fp, 128));
fclose($fp);
if (!($secret && $api_key)) {
TBERROR("Could not get kets from gateone.key", 1);
}
$secret = chop($secret);
SPITHEADER();
$authobj = array(
'api_key' => $api_key,
'upn' => 'stoller',
'timestamp' => time() . '000',
'signature_method' => 'HMAC-SHA1',
'api_version' => '1.0'
);
$authobj['signature'] = hash_hmac('sha1',
$authobj['api_key'] . $authobj['upn'] .
$authobj['timestamp'], $secret);
$valid_json_auth_object = json_encode($authobj);
echo "<div class='panel panel-default'>\n";
echo "<div class='panel-body'>\n";
echo "<ul class='nav nav-tabs'>
<li><a href='#home' data-toggle='tab'>Home</a></li>
<li><a href='#sshpanel' data-toggle='tab'>Profile</a></li>
<li><a href='#messages' data-toggle='tab'>Messages</a></li>
<li><a href='#settings' data-toggle='tab'>Settings</a></li>
</ul>
<div class='tab-content'>
<div class='tab-pane' id='home'>...</div>
<div class='tab-pane active' id='sshpanel'>...</div>
<div class='tab-pane' id='messages'>...</div>
<div class='tab-pane' id='settings'>...</div>
</div>\n";
#
# Oh jeez, by default multiple instances share the same session and
# you end up with two tabs talking to the same terminal. Dumb. I have
# to actually tell the gateone code to not do this by providing a
# unique location parameter.
#
$location = uniqid("loc");
echo "</div>\n";
echo "</div>\n";
echo "<SCRIPT LANGUAGE=JavaScript>
window.onload = function() {
GateOne.location = '$location';
// Initialize Gate One:
GateOne.init({url: 'https://users.emulab.net:1090/gateone',
autoConnectURL: 'ssh://stoller@pc493',
showToolbar: false,
terminalFont: 'monospace',
auth: $valid_json_auth_object});
}
</Script>\n";
echo "<div class='uk-panel uk-panel-box uk-panel-header
uk-container-center uk-margin-bottom'>\n";
echo "<div id='gateone_container'
style='width: 60em; height: 30em; ".
"font-family: monospace'>
<div id='gateone'></div></div>\n";
echo "</div>\n";
StartSSH('sshpanel', '$auth_object');
</script>\n";
SPITFOOTER();
?>
......
......@@ -624,6 +624,48 @@ function CleanString($string)
return htmlspecialchars($string, ENT_QUOTES);
}
#
# Generate an authentication object to pass to the browser that
# is passed to the web server on boss. This is used to grant
# permission to the user to invoke ssh to a local node using their
# emulab generated (no passphrase) key. This is basically a clone
# of what GateOne does, but that code was a mess.
#
function SSHAuthObject($uid)
{
$file = "/usr/testbed/etc/sshauth.key";
#
# We need the secret that is shared with ops.
#
$fp = fopen($file, "r");
if (! $fp) {
TBERROR("Error opening $file", 0);
return null;
}
list($api_key,$secret) = preg_split('/:/', fread($fp, 128));
fclose($fp);
if (!($secret && $api_key)) {
TBERROR("Could not get key from $file", 0);
return null;
}
$secret = chop($secret);
$authobj = array(
'api_key' => $api_key,
'upn' => $uid,
'timestamp' => time() . '000',
'signature_method' => 'HMAC-SHA1',
'api_version' => '1.0'
);
$authobj['signature'] = hash_hmac('sha1',
$authobj['api_key'] . $authobj['upn'] .
$authobj['timestamp'], $secret);
$valid_json_auth_object = json_encode($authobj);
return $valid_json_auth_object;
}
#
# Beware empty spaces (cookies)!
#
......
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