diff --git a/Dockerfile b/Dockerfile index 5beca9d..0e92eb7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,8 +2,9 @@ FROM node:8-alpine MAINTAINER butlerx@notthe.cloud WORKDIR /app RUN adduser -D -h /home/term -s /bin/sh term && \ - echo "term:term" | chpasswd + ( echo "term:term" | chpasswd ) && \ + apk add --update build-base python openssh-client EXPOSE 3000 COPY . /app -RUN apk add --update build-base python openssh-client && yarn +RUN yarn CMD node bin diff --git a/cli.mjs b/cli.mjs index 7ec34f1..c3e8ea3 100644 --- a/cli.mjs +++ b/cli.mjs @@ -29,6 +29,10 @@ const opts = optimist demand : false, description: 'defaults to "password", you can use "publickey,password" instead', }, + sshkey: { + demand : false, + description: 'path to an optional client private key (connection will be password-less and insecure!)', + }, port: { demand : false, alias : 'p', @@ -51,6 +55,7 @@ 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.SSHPORT || 22; +const sshkey = opts.sshkey || process.env.SSHKEY || ''; const port = opts.port || process.env.PORT || 3000; loadSSL(opts) @@ -62,11 +67,21 @@ loadSSL(opts) process.exit(1); }); +const sshkeyWarning = +`!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +! Password-less auth enabled using private key from \'%s\'. +! This is dangerous, anything that reaches the wetty server +! will be able to run remote operations without authentication. +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!`; +if(sshkey) { + console.warn(sshkeyWarning); +} + process.on('uncaughtException', err => { console.error(`Error: ${err}`); }); -const tty = wetty(port, sshuser, sshhost, sshport, sshauth, opts.ssl); +const tty = wetty(port, sshuser, sshhost, sshport, sshauth, sshkey, opts.ssl); tty.on('exit', code => { console.log(`exit with code: ${code}`); }); diff --git a/wetty.mjs b/wetty.mjs index b5181e1..2f66e29 100644 --- a/wetty.mjs +++ b/wetty.mjs @@ -36,33 +36,37 @@ function createServer(port, sslopts) { }); } -function getCommand(socket, sshuser, sshhost, sshport, sshauth) { +function getCommand(socket, sshuser, sshhost, sshport, sshauth, sshkey) { 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; + const sshRemoteOptsBase = [ + path.join(__dirname, 'bin/ssh'), + ssh, + '-p', + sshport, + '-o', + `PreferredAuthentications=${sshauth}`, + ] + const sshRemoteOpts = sshkey ? sshRemoteOptsBase.concat(['-i', sshkey]) + : sshRemoteOptsBase 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}`, - ], + : sshRemoteOpts + , ssh, ]; } -export default function start(port, sshuser, sshhost, sshport, sshauth, sslopts) { +export default function start(port, sshuser, sshhost, sshport, sshauth, sshkey, 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 [args, ssh] = getCommand(socket, sshuser, sshhost, sshport, sshauth, sshkey); const term = spawn('/usr/bin/env', args, { name: 'xterm-256color', cols: 80,