Cian Butler
6 years ago
committed by
GitHub
30 changed files with 8629 additions and 1830 deletions
@ -0,0 +1,11 @@ |
|||
{ |
|||
"presets": [ |
|||
[ |
|||
"es2015", |
|||
{ |
|||
"modules": false |
|||
} |
|||
] |
|||
], |
|||
"compact": true, |
|||
} |
@ -0,0 +1,2 @@ |
|||
node_modules |
|||
.esm-cache |
@ -0,0 +1,4 @@ |
|||
node_modules/ |
|||
public/ |
|||
.esm-cache |
|||
*hterm* |
@ -0,0 +1,36 @@ |
|||
module.exports = { |
|||
env: { |
|||
es6 : true, |
|||
node: true, |
|||
}, |
|||
extends: ['airbnb'], |
|||
rules : { |
|||
'linebreak-style' : ['error', 'unix'], |
|||
'arrow-parens' : ['error', 'as-needed'], |
|||
'no-param-reassign' : ['error', { props: false }], |
|||
'func-style' : ['error', 'declaration', { allowArrowFunctions: true }], |
|||
'no-use-before-define': ['error', { functions: false }], |
|||
'no-shadow' : [ |
|||
'error', |
|||
{ |
|||
builtinGlobals: true, |
|||
hoist : 'functions', |
|||
allow : ['resolve', 'reject', 'err'], |
|||
}, |
|||
], |
|||
'no-console': [ |
|||
'error', |
|||
{ |
|||
allow: ['warn', 'trace', 'log', 'error'], |
|||
}, |
|||
], |
|||
'consistent-return': 0, |
|||
'key-spacing' : [ |
|||
'error', |
|||
{ |
|||
multiLine: { beforeColon: false, afterColon: true }, |
|||
align : { beforeColon: false, afterColon: true, on: 'colon', mode: 'strict' }, |
|||
}, |
|||
], |
|||
}, |
|||
}; |
@ -0,0 +1,21 @@ |
|||
lib-cov |
|||
*.seed |
|||
*.log |
|||
*.csv |
|||
*.dat |
|||
*.out |
|||
*.pid |
|||
*.gz |
|||
|
|||
tmp |
|||
pids |
|||
logs |
|||
results |
|||
|
|||
npm-debug.log |
|||
node_modules/* |
|||
.esm-cache |
|||
src |
|||
*.yml |
|||
Dockerfile |
|||
*.png |
@ -1,15 +1,9 @@ |
|||
FROM node:0.10.38 |
|||
MAINTAINER Nathan LeClaire <nathan@docker.com> |
|||
|
|||
ADD . /app |
|||
FROM node:8-alpine |
|||
MAINTAINER butlerx@notthe.cloud |
|||
WORKDIR /app |
|||
RUN npm install |
|||
RUN apt-get update |
|||
RUN apt-get install -y vim |
|||
RUN useradd -d /home/term -m -s /bin/bash term |
|||
RUN echo 'term:term' | chpasswd |
|||
|
|||
RUN adduser -D -h /home/term -s /bin/sh term && \ |
|||
echo "term:term" | chpasswd |
|||
EXPOSE 3000 |
|||
|
|||
ENTRYPOINT ["node"] |
|||
CMD ["app.js", "-p", "3000"] |
|||
COPY . /app |
|||
RUN apk add --update build-base python openssh && yarn |
|||
CMD yarn start |
|||
|
@ -1,37 +0,0 @@ |
|||
module.exports = function (grunt) { |
|||
|
|||
require('load-grunt-tasks')(grunt); |
|||
|
|||
var config = { |
|||
mkdir: { |
|||
tmp: { |
|||
options: { |
|||
create: ['tmp'] |
|||
} |
|||
} |
|||
}, |
|||
gitclone: { |
|||
hterm: { |
|||
options: { |
|||
cwd: './tmp', |
|||
repository: 'https://chromium.googlesource.com/apps/libapps' |
|||
} |
|||
} |
|||
}, |
|||
shell: { |
|||
build_hterm: { |
|||
command: 'LIBDOT_SEARCH_PATH=$(pwd) ./libdot/bin/concat.sh -i ./hterm/concat/hterm_all.concat -o ../../public/wetty/hterm_all.js', |
|||
options: { |
|||
execOptions: { |
|||
cwd: './tmp/libapps' |
|||
} |
|||
} |
|||
} |
|||
}, |
|||
clean: ['./tmp'] |
|||
}; |
|||
|
|||
grunt.initConfig(config); |
|||
|
|||
grunt.registerTask('update-hterm', ['mkdir:tmp', 'gitclone:hterm', 'shell:build_hterm', 'clean']); |
|||
}; |
@ -1,134 +0,0 @@ |
|||
var express = require('express'); |
|||
var http = require('http'); |
|||
var https = require('https'); |
|||
var path = require('path'); |
|||
var server = require('socket.io'); |
|||
var pty = require('pty.js'); |
|||
var fs = require('fs'); |
|||
|
|||
var opts = require('optimist') |
|||
.options({ |
|||
sslkey: { |
|||
demand: false, |
|||
description: 'path to SSL key' |
|||
}, |
|||
sslcert: { |
|||
demand: false, |
|||
description: 'path to SSL certificate' |
|||
}, |
|||
sshhost: { |
|||
demand: false, |
|||
description: 'ssh server host' |
|||
}, |
|||
sshport: { |
|||
demand: false, |
|||
description: 'ssh server port' |
|||
}, |
|||
sshuser: { |
|||
demand: false, |
|||
description: 'ssh user' |
|||
}, |
|||
sshauth: { |
|||
demand: false, |
|||
description: 'defaults to "password", you can use "publickey,password" instead' |
|||
}, |
|||
port: { |
|||
demand: true, |
|||
alias: 'p', |
|||
description: 'wetty listen port' |
|||
}, |
|||
}).boolean('allow_discovery').argv; |
|||
|
|||
var runhttps = false; |
|||
var sshport = 22; |
|||
var sshhost = 'localhost'; |
|||
var sshauth = 'password,keyboard-interactive'; |
|||
var globalsshuser = ''; |
|||
|
|||
if (opts.sshport) { |
|||
sshport = opts.sshport; |
|||
} |
|||
|
|||
if (opts.sshhost) { |
|||
sshhost = opts.sshhost; |
|||
} |
|||
|
|||
if (opts.sshauth) { |
|||
sshauth = opts.sshauth |
|||
} |
|||
|
|||
if (opts.sshuser) { |
|||
globalsshuser = opts.sshuser; |
|||
} |
|||
|
|||
if (opts.sslkey && opts.sslcert) { |
|||
runhttps = true; |
|||
opts['ssl'] = {}; |
|||
opts.ssl['key'] = fs.readFileSync(path.resolve(opts.sslkey)); |
|||
opts.ssl['cert'] = fs.readFileSync(path.resolve(opts.sslcert)); |
|||
} |
|||
|
|||
process.on('uncaughtException', function(e) { |
|||
console.error('Error: ' + e); |
|||
}); |
|||
|
|||
var httpserv; |
|||
|
|||
var app = express(); |
|||
app.get('/wetty/ssh/:user', function(req, res) { |
|||
res.sendfile(__dirname + '/public/wetty/index.html'); |
|||
}); |
|||
app.use('/', express.static(path.join(__dirname, 'public'))); |
|||
|
|||
if (runhttps) { |
|||
httpserv = https.createServer(opts.ssl, app).listen(opts.port, function() { |
|||
console.log('https on port ' + opts.port); |
|||
}); |
|||
} else { |
|||
httpserv = http.createServer(app).listen(opts.port, function() { |
|||
console.log('http on port ' + opts.port); |
|||
}); |
|||
} |
|||
|
|||
var io = server(httpserv,{path: '/wetty/socket.io'}); |
|||
io.on('connection', function(socket){ |
|||
var sshuser = ''; |
|||
var request = socket.request; |
|||
console.log((new Date()) + ' Connection accepted.'); |
|||
if (match = request.headers.referer.match('/wetty/ssh/.+$')) { |
|||
sshuser = match[0].replace('/wetty/ssh/', '') + '@'; |
|||
} else if (globalsshuser) { |
|||
sshuser = globalsshuser + '@'; |
|||
} |
|||
|
|||
var term; |
|||
if (process.getuid() == 0) { |
|||
term = pty.spawn('/usr/bin/env', ['login'], { |
|||
name: 'xterm-256color', |
|||
cols: 80, |
|||
rows: 30 |
|||
}); |
|||
} else { |
|||
term = pty.spawn('ssh', [sshuser + sshhost, '-p', sshport, '-o', 'PreferredAuthentications=' + sshauth], { |
|||
name: 'xterm-256color', |
|||
cols: 80, |
|||
rows: 30 |
|||
}); |
|||
} |
|||
console.log((new Date()) + " PID=" + term.pid + " STARTED on behalf of user=" + sshuser) |
|||
term.on('data', function(data) { |
|||
socket.emit('output', data); |
|||
}); |
|||
term.on('exit', function(code) { |
|||
console.log((new Date()) + " PID=" + term.pid + " ENDED") |
|||
}); |
|||
socket.on('resize', function(data) { |
|||
term.resize(data.col, data.row); |
|||
}); |
|||
socket.on('input', function(data) { |
|||
term.write(data); |
|||
}); |
|||
socket.on('disconnect', function() { |
|||
term.end(); |
|||
}); |
|||
}) |
@ -0,0 +1,3 @@ |
|||
#! /usr/bin/env node
|
|||
require = require('@std/esm')(module); // eslint-disable-line no-global-assign
|
|||
require('../cli.mjs'); |
@ -0,0 +1,15 @@ |
|||
#!/usr/bin/env sh |
|||
|
|||
userAtAddress="$1" |
|||
USER=$(echo "$userAtAddress" | cut -d"@" -f1); |
|||
HOST=$(echo "$userAtAddress" | cut -d"@" -f2); |
|||
|
|||
if [ "$USER" = "$HOST" ] |
|||
then |
|||
printf "Enter your username: " |
|||
read -r USER |
|||
USER=$(echo "${USER}" | tr -d '[:space:]') |
|||
ssh "$USER"@"$HOST" |
|||
else |
|||
ssh "$userAtAddress" |
|||
fi |
@ -1,3 +0,0 @@ |
|||
#!/usr/bin/env node
|
|||
|
|||
require("../app"); |
@ -0,0 +1,95 @@ |
|||
import fs from 'fs-extra'; |
|||
import path from 'path'; |
|||
import optimist from 'optimist'; |
|||
import wetty from './wetty'; |
|||
|
|||
const opts = optimist |
|||
.options({ |
|||
sslkey: { |
|||
demand : false, |
|||
description: 'path to SSL key', |
|||
}, |
|||
sslcert: { |
|||
demand : false, |
|||
description: 'path to SSL certificate', |
|||
}, |
|||
sshhost: { |
|||
demand : false, |
|||
description: 'ssh server host', |
|||
}, |
|||
sshport: { |
|||
demand : false, |
|||
description: 'ssh server port', |
|||
}, |
|||
sshuser: { |
|||
demand : false, |
|||
description: 'ssh user', |
|||
}, |
|||
sshauth: { |
|||
demand : false, |
|||
description: 'defaults to "password", you can use "publickey,password" instead', |
|||
}, |
|||
port: { |
|||
demand : false, |
|||
alias : 'p', |
|||
description: 'wetty listen port', |
|||
}, |
|||
help: { |
|||
demand : false, |
|||
alias : 'h', |
|||
description: 'Print help message', |
|||
}, |
|||
}) |
|||
.boolean('allow_discovery').argv; |
|||
|
|||
if (opts.help) { |
|||
optimist.showHelp(); |
|||
process.exit(0); |
|||
} |
|||
|
|||
const sshuser = opts.sshuser || process.env.SSHUSER || ''; |
|||
const sshhost = opts.sshhost || process.env.SSHHOST || 'localhost'; |
|||
const sshauth = opts.sshauth || process.env.SSHAUTH || 'password,keyboard-interactive'; |
|||
const sshport = opts.sshport || process.env.SSHPOST || 22; |
|||
const port = opts.port || process.env.PORT || 3000; |
|||
|
|||
loadSSL(opts) |
|||
.then(ssl => { |
|||
opts.ssl = ssl; |
|||
}) |
|||
.catch(err => { |
|||
console.error(`Error: ${err}`); |
|||
process.exit(1); |
|||
}); |
|||
|
|||
process.on('uncaughtException', err => { |
|||
console.error(`Error: ${err}`); |
|||
}); |
|||
|
|||
const tty = wetty(port, sshuser, sshhost, sshport, sshauth, opts.ssl); |
|||
tty.on('exit', code => { |
|||
console.log(`exit with code: ${code}`); |
|||
}); |
|||
tty.on('disconnect', () => { |
|||
console.log('disconnect'); |
|||
}); |
|||
|
|||
function loadSSL({ sslkey, sslcert }) { |
|||
return new Promise((resolve, reject) => { |
|||
const ssl = {}; |
|||
if (sslkey && sslcert) { |
|||
fs |
|||
.readFile(path.resolve(sslkey)) |
|||
.then(key => { |
|||
ssl.key = key; |
|||
}) |
|||
.then(fs.readFile(path.resolve(sslcert))) |
|||
.then(cert => { |
|||
ssl.cert = cert; |
|||
}) |
|||
.then(resolve(ssl)) |
|||
.catch(reject); |
|||
} |
|||
resolve(ssl); |
|||
}); |
|||
} |
@ -0,0 +1,15 @@ |
|||
version: "3" |
|||
|
|||
services: |
|||
wetty: |
|||
build: . |
|||
image: butlerx/wetty |
|||
container_name: wetty |
|||
tty: true |
|||
working_dir: /app |
|||
ports: |
|||
- "3000:3000" |
|||
environment: |
|||
PORT: 3000 |
|||
SSHHOST: 'localhost' |
|||
SSHPORT: 22 |
@ -0,0 +1,40 @@ |
|||
const gulp = require('gulp'); |
|||
const concat = require('gulp-concat'); |
|||
const minify = require('gulp-minify'); |
|||
const babel = require('gulp-babel'); |
|||
const shell = require('gulp-shell'); |
|||
const del = require('del'); |
|||
|
|||
gulp.task('compress', [], () => |
|||
gulp |
|||
.src(['./src/hterm_all.js', './src/wetty.js']) |
|||
.pipe(concat('wetty.js')) |
|||
.pipe(babel()) |
|||
.pipe( |
|||
minify({ |
|||
ext: { |
|||
min: '.min.js', |
|||
}, |
|||
exclude : ['tasks'], |
|||
noSource : true, |
|||
ignoreFiles: ['.combo.js', '*.min.js'], |
|||
}), |
|||
) |
|||
.pipe(gulp.dest('./public/wetty')), |
|||
); |
|||
|
|||
gulp.task( |
|||
'hterm', |
|||
shell.task( |
|||
[ |
|||
'git clone https://chromium.googlesource.com/apps/libapps', |
|||
'LIBDOT_SEARCH_PATH=$(pwd)/libapps ./libapps/libdot/bin/concat.sh -i ./libapps/hterm/concat/hterm_all.concat -o ./src/hterm_all.js', |
|||
], |
|||
{ |
|||
verbose: true, |
|||
}, |
|||
), |
|||
); |
|||
|
|||
gulp.task('default', ['compress']); |
|||
gulp.task('upgrade', ['hterm'], () => del(['./libapps'])); |
@ -0,0 +1,2 @@ |
|||
require = require('@std/esm')(module); // eslint-disable-line no-global-assign
|
|||
module.exports = require('./wetty.mjs').default; |
After Width: | Height: | Size: 165 KiB |
@ -1,30 +0,0 @@ |
|||
<!doctype html> |
|||
<html lang="en"> |
|||
|
|||
<head> |
|||
<meta charset="UTF-8"> |
|||
<title>Wetty - The WebTTY Terminal Emulator</title> |
|||
<script src="/wetty/hterm_all.js"></script> |
|||
<script src="/wetty/socket.io/socket.io.js"></script> |
|||
<script src="/wetty/wetty.js"></script> |
|||
<style> |
|||
html, |
|||
body { |
|||
height: 100%; |
|||
width: 100%; |
|||
margin: 0px; |
|||
} |
|||
#terminal { |
|||
display: block; |
|||
position: relative; |
|||
width: 100%; |
|||
height: 100%; |
|||
} |
|||
</style> |
|||
</head> |
|||
|
|||
<body> |
|||
<div id="terminal"></div> |
|||
</body> |
|||
|
|||
</html> |
@ -1,64 +0,0 @@ |
|||
var term; |
|||
var socket = io(location.origin, {path: '/wetty/socket.io'}) |
|||
var buf = ''; |
|||
|
|||
function Wetty(argv) { |
|||
this.argv_ = argv; |
|||
this.io = null; |
|||
this.pid_ = -1; |
|||
} |
|||
|
|||
Wetty.prototype.run = function() { |
|||
this.io = this.argv_.io.push(); |
|||
|
|||
this.io.onVTKeystroke = this.sendString_.bind(this); |
|||
this.io.sendString = this.sendString_.bind(this); |
|||
this.io.onTerminalResize = this.onTerminalResize.bind(this); |
|||
} |
|||
|
|||
Wetty.prototype.sendString_ = function(str) { |
|||
socket.emit('input', str); |
|||
}; |
|||
|
|||
Wetty.prototype.onTerminalResize = function(col, row) { |
|||
socket.emit('resize', { col: col, row: row }); |
|||
}; |
|||
|
|||
socket.on('connect', function() { |
|||
lib.init(function() { |
|||
hterm.defaultStorage = new lib.Storage.Local(); |
|||
term = new hterm.Terminal(); |
|||
window.term = term; |
|||
term.decorate(document.getElementById('terminal')); |
|||
|
|||
term.setCursorPosition(0, 0); |
|||
term.setCursorVisible(true); |
|||
term.prefs_.set('ctrl-c-copy', true); |
|||
term.prefs_.set('ctrl-v-paste', true); |
|||
term.prefs_.set('use-default-window-copy', true); |
|||
|
|||
term.runCommandClass(Wetty, document.location.hash.substr(1)); |
|||
socket.emit('resize', { |
|||
col: term.screenSize.width, |
|||
row: term.screenSize.height |
|||
}); |
|||
|
|||
if (buf && buf != '') |
|||
{ |
|||
term.io.writeUTF16(buf); |
|||
buf = ''; |
|||
} |
|||
}); |
|||
}); |
|||
|
|||
socket.on('output', function(data) { |
|||
if (!term) { |
|||
buf += data; |
|||
return; |
|||
} |
|||
term.io.writeUTF16(data); |
|||
}); |
|||
|
|||
socket.on('disconnect', function() { |
|||
console.log("Socket.io connection closed"); |
|||
}); |
File diff suppressed because one or more lines are too long
@ -0,0 +1,37 @@ |
|||
module.exports = { |
|||
env: { |
|||
es6 : true, |
|||
browser: true, |
|||
}, |
|||
globals: { |
|||
hterm: true, |
|||
lib : true, |
|||
io : true, |
|||
}, |
|||
extends: ['airbnb'], |
|||
rules : { |
|||
'no-underscore-dangle': 0, |
|||
'class-methods-use-this': 0, |
|||
'linebreak-style' : ['error', 'unix'], |
|||
'arrow-parens' : ['error', 'as-needed'], |
|||
'no-param-reassign' : ['error', { props: false }], |
|||
'func-style' : ['error', 'declaration', { allowArrowFunctions: true }], |
|||
'no-use-before-define': ['error', { functions: false }], |
|||
'no-shadow' : [ |
|||
'error', |
|||
{ |
|||
builtinGlobals: true, |
|||
hoist : 'functions', |
|||
allow : ['resolve', 'reject', 'err'], |
|||
}, |
|||
], |
|||
'consistent-return': 0, |
|||
'key-spacing' : [ |
|||
'error', |
|||
{ |
|||
multiLine: { beforeColon: false, afterColon: true }, |
|||
align : { beforeColon: false, afterColon: true, on: 'colon', mode: 'strict' }, |
|||
}, |
|||
], |
|||
}, |
|||
}; |
File diff suppressed because it is too large
@ -0,0 +1,84 @@ |
|||
const socket = io(location.origin, { path: '/wetty/socket.io' }); |
|||
let term; |
|||
let buf = ''; |
|||
|
|||
class Wetty { |
|||
constructor(argv) { |
|||
this.argv_ = argv; |
|||
this.io = null; |
|||
this.pid_ = -1; |
|||
} |
|||
|
|||
run() { |
|||
this.io = this.argv_.io.push(); |
|||
this.io.onVTKeystroke = this.sendString_.bind(this); |
|||
this.io.sendString = this.sendString_.bind(this); |
|||
this.io.onTerminalResize = this.onTerminalResize.bind(this); |
|||
} |
|||
|
|||
sendString_(str) { |
|||
socket.emit('input', str); |
|||
} |
|||
|
|||
onTerminalResize(col, row) { |
|||
socket.emit('resize', { col, row }); |
|||
} |
|||
} |
|||
|
|||
socket.on('connect', () => { |
|||
document.getElementById('overlay').style.display = 'none'; |
|||
window.addEventListener('beforeunload', handler, false); |
|||
lib.init(() => { |
|||
hterm.defaultStorage = new lib.Storage.Local(); |
|||
term = new hterm.Terminal(); |
|||
window.term = term; |
|||
term.decorate(document.getElementById('terminal')); |
|||
|
|||
term.setCursorPosition(0, 0); |
|||
term.setCursorVisible(true); |
|||
term.prefs_.set('ctrl-c-copy', true); |
|||
term.prefs_.set('ctrl-v-paste', true); |
|||
term.prefs_.set('use-default-window-copy', true); |
|||
term.prefs_.set('send-encoding', 'raw'); |
|||
term.prefs_.set('receive-encoding', 'raw'); |
|||
term.prefs_.set('font-size', 14); |
|||
term.scrollPort_.screen_.setAttribute('spellcheck', 'false'); |
|||
term.scrollPort_.screen_.setAttribute('autocorrect', 'false'); |
|||
term.scrollPort_.screen_.setAttribute('autocomplete', 'false'); |
|||
term.scrollPort_.screen_.setAttribute('contenteditable', 'false'); |
|||
|
|||
term.runCommandClass(Wetty, document.location.hash.substr(1)); |
|||
socket.emit('resize', { |
|||
col: term.screenSize.width, |
|||
row: term.screenSize.height, |
|||
}); |
|||
|
|||
if (buf && buf !== '') { |
|||
term.io.writeUTF8(buf); |
|||
buf = ''; |
|||
} |
|||
}); |
|||
}); |
|||
|
|||
socket.on('output', data => { |
|||
if (!term) { |
|||
buf += data; |
|||
return; |
|||
} |
|||
term.io.writeUTF8(data); |
|||
}); |
|||
|
|||
socket.on('logout', () => { |
|||
document.getElementById('overlay').style.display = 'block'; |
|||
window.removeEventListener('beforeunload', handler, false); |
|||
}); |
|||
|
|||
socket.on('disconnect', () => { |
|||
document.getElementById('overlay').style.display = 'block'; |
|||
window.removeEventListener('beforeunload', handler, false); |
|||
}); |
|||
|
|||
function handler(e) { |
|||
e.returnValue = 'Are you sure?'; |
|||
return e.returnValue; |
|||
} |
@ -0,0 +1,88 @@ |
|||
import express from 'express'; |
|||
import http from 'http'; |
|||
import https from 'https'; |
|||
import path from 'path'; |
|||
import server from 'socket.io'; |
|||
import { spawn } from 'node-pty'; |
|||
import EventEmitter from 'events'; |
|||
import favicon from 'serve-favicon'; |
|||
|
|||
const app = express(); |
|||
app.use(favicon(`${__dirname}/public/favicon.ico`)); |
|||
// For using wetty at /wetty on a vhost
|
|||
app.get('/wetty/ssh/:user', (req, res) => { |
|||
res.sendFile(`${__dirname}/public/wetty/index.html`); |
|||
}); |
|||
app.get('/wetty/', (req, res) => { |
|||
res.sendFile(`${__dirname}/public/wetty/index.html`); |
|||
}); |
|||
// For using wetty on a vhost by itself
|
|||
app.get('/ssh/:user', (req, res) => { |
|||
res.sendFile(`${__dirname}/public/wetty/index.html`); |
|||
}); |
|||
app.get('/', (req, res) => { |
|||
res.sendFile(`${__dirname}/public/wetty/index.html`); |
|||
}); |
|||
// For serving css and javascript
|
|||
app.use('/', express.static(path.join(__dirname, 'public'))); |
|||
|
|||
function createServer(port, sslopts) { |
|||
return sslopts && sslopts.key && sslopts.cert |
|||
? https.createServer(sslopts, app).listen(port, () => { |
|||
console.log(`https on port ${port}`); |
|||
}) |
|||
: http.createServer(app).listen(port, () => { |
|||
console.log(`http on port ${port}`); |
|||
}); |
|||
} |
|||
|
|||
function getCommand(socket, sshuser, sshhost, sshport, sshauth) { |
|||
const { request } = socket; |
|||
const match = request.headers.referer.match('.+/ssh/.+$'); |
|||
const sshAddress = sshuser ? `${sshuser}@${sshhost}` : sshhost; |
|||
const ssh = match ? `${match[0].split('/ssh/').pop()}@${sshhost}` : sshAddress; |
|||
|
|||
return [ |
|||
process.getuid() === 0 && sshhost === 'localhost' |
|||
? ['login', '-h', socket.client.conn.remoteAddress.split(':')[3]] |
|||
: [ |
|||
path.join(__dirname, 'bin/ssh'), |
|||
ssh, |
|||
'-p', |
|||
sshport, |
|||
'-o', |
|||
`PreferredAuthentications=${sshauth}`, |
|||
], |
|||
ssh, |
|||
]; |
|||
} |
|||
|
|||
export default function start(port, sshuser, sshhost, sshport, sshauth, sslopts) { |
|||
const events = new EventEmitter(); |
|||
const io = server(createServer(port, sslopts), { path: '/wetty/socket.io' }); |
|||
io.on('connection', socket => { |
|||
console.log(`${new Date()} Connection accepted.`); |
|||
const [args, ssh] = getCommand(socket, sshuser, sshhost, sshport, sshauth); |
|||
const term = spawn('/usr/bin/env', args, { |
|||
name: 'xterm-256color', |
|||
cols: 80, |
|||
rows: 30, |
|||
}); |
|||
|
|||
console.log(`${new Date()} PID=${term.pid} STARTED on behalf of user=${ssh}`); |
|||
term.on('data', data => socket.emit('output', data)); |
|||
term.on('exit', code => { |
|||
console.log(`${new Date()} PID=${term.pid} ENDED`); |
|||
socket.emit('logout'); |
|||
events.emit('exit', code); |
|||
}); |
|||
socket.on('resize', ({ col, row }) => term.resize(col, row)); |
|||
socket.on('input', input => term.write(input)); |
|||
socket.on('disconnect', () => { |
|||
term.end(); |
|||
term.destroy(); |
|||
events.emit('disconnect'); |
|||
}); |
|||
}); |
|||
return events; |
|||
} |
File diff suppressed because it is too large
Loading…
Reference in new issue