From d25f63dabe80b630d4ce7d6213fb782ce4a88d03 Mon Sep 17 00:00:00 2001 From: Denis Kramer Date: Sun, 10 Feb 2019 15:24:46 +0000 Subject: [PATCH 01/12] Added remote-user request header feature --- bin/nginx.template | 19 +++++++++++++++++++ src/server/term.ts | 11 +++++++++++ 2 files changed, 30 insertions(+) diff --git a/bin/nginx.template b/bin/nginx.template index e89cb69..20c6199 100644 --- a/bin/nginx.template +++ b/bin/nginx.template @@ -46,6 +46,25 @@ server { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_set_header X-NginX-Proxy true; + + # Authenticate user via other services (e.g., oauth2 end-points) + # + # Configuration : + # - Configure a 'auth_request' directive for this server block + # - Capture the authenticated username using 'auth_request_set' + # - Set the 'remote-user' request header accordingly + # + # Example (using lasso as authentication middleware): + # + # Add to server block: + # auth_request /lasso-validate + # auth_request_set $auth_user $upstream_http_x_lasso_user; + # + # Add to /wetty location block + # proxy_set_header remote-user $auth_user; + # + # And configure a '/lasso-validate' location. See this blog for further + # guidance: https://developer.okta.com/blog/2018/08/28/nginx-auth-request } # gzip diff --git a/src/server/term.ts b/src/server/term.ts index 8b8fd22..af5ba89 100644 --- a/src/server/term.ts +++ b/src/server/term.ts @@ -42,6 +42,17 @@ export default class Term { } public static login(socket: SocketIO.Socket): Promise { + + // Check request-header for username + let remoteUser = socket.request.headers['remote-user']; + if (remoteUser) { + return new Promise((resolve,reject) => { + resolve(remoteUser); + }); + } + + // Request carries no username information + // Create terminal and ask user for username const term = spawn( '/usr/bin/env', ['node', `${__dirname}/buffer.js`], From 65ec883e1c22fa84c4acbd0551557d49f401f978 Mon Sep 17 00:00:00 2001 From: Denis Kramer Date: Sun, 10 Feb 2019 15:25:06 +0000 Subject: [PATCH 02/12] fixed working dir path --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index bfaf519..28b21eb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,7 +6,7 @@ services: image: butlerx/wetty container_name: wetty tty: true - working_dir: /app + working_dir: /usr/src/app ports: - "3000:3000" environment: From 2efbfc1f4f53229c29b6922112e3934fb9a3c2c5 Mon Sep 17 00:00:00 2001 From: harryleesan Date: Tue, 19 Feb 2019 22:02:17 +0200 Subject: [PATCH 03/12] Updated working directory to match Dockerfile --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index bfaf519..28b21eb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,7 +6,7 @@ services: image: butlerx/wetty container_name: wetty tty: true - working_dir: /app + working_dir: /usr/src/app ports: - "3000:3000" environment: From 117583310a4477ccdf57ec7a1ad38c0ad5e21fdb Mon Sep 17 00:00:00 2001 From: Felix Pojtinger Date: Sat, 23 Feb 2019 11:07:29 +0100 Subject: [PATCH 04/12] fix wrong link to API docs --- docs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index c15adf1..ea15386 100644 --- a/docs/README.md +++ b/docs/README.md @@ -21,4 +21,4 @@ wetty.start(/* server settings, see Options */).then(() => { ## API -For WeTTy options and event details please refer to the [api docs](./api.md) +For WeTTy options and event details please refer to the [api docs](./API.md) From c32c4e5d2621e7df425d432efd877060fa4d114e Mon Sep 17 00:00:00 2001 From: Henri Date: Mon, 25 Feb 2019 17:10:33 +0000 Subject: [PATCH 05/12] use relative paths for html resources, to allow reverse proxying --- src/server/server.ts | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/server/server.ts b/src/server/server.ts index 4b789da..ed9540a 100644 --- a/src/server/server.ts +++ b/src/server/server.ts @@ -29,7 +29,8 @@ export default function createServer( const html = ( req: express.Request, res: express.Response - ): express.Response => + ): express.Response => { + const resourcePath = /^\/ssh\//.test(req.url) ? '../' : ''; res.send(` @@ -37,7 +38,7 @@ export default function createServer( WeTTy - The Web Terminal Emulator - +
@@ -47,9 +48,10 @@ export default function createServer(
- + `); + } const app = express(); app @@ -59,12 +61,7 @@ export default function createServer( .use(favicon(path.join(distDir, 'favicon.ico'))) .use(`${basePath}/public`, express.static(distDir)) .use((req, res, next) => { - if ( - req.url.substr(-1) === '/' && - req.url.length > 1 && - !/\?[^]*\//.test(req.url) - ) - res.redirect(301, req.url.slice(0, -1)); + if (req.url === basePath) res.redirect(301, req.url + '/'); else next(); }) .get(basePath, html) From bfb1abfb178ae62e501f266ff12ee204177c6481 Mon Sep 17 00:00:00 2001 From: Henri Date: Mon, 25 Feb 2019 17:40:52 +0000 Subject: [PATCH 06/12] new startup option: --title --- index.js | 6 ++++++ src/server/index.ts | 5 ++++- src/server/server.ts | 6 +++--- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index 73489a1..526d775 100755 --- a/index.js +++ b/index.js @@ -41,6 +41,12 @@ if (require.main === module) { type: 'string', default: process.env.SSHUSER || '', }, + title: { + demand: false, + description: 'window title', + type: 'string', + default: process.env.TITLE || 'WeTTy - The Web Terminal Emulator', + }, sshauth: { demand: false, description: diff --git a/src/server/index.ts b/src/server/index.ts index 4dfbe69..2274339 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -15,6 +15,7 @@ export interface Options { base: string; host: string; port: number; + title: string; command?: string; } @@ -33,6 +34,7 @@ export default class Server { base, host, port, + title, command, sslkey, sslcert, @@ -54,10 +56,11 @@ export default class Server { host: sshhost, auth: sshauth, port: sshport, + title: title, pass: sshpass, key: sshkey, }, - { base, host, port }, + { base, host, port, title }, command, { key: sslkey, cert: sslcert } ); diff --git a/src/server/server.ts b/src/server/server.ts index 4b789da..a8a83a3 100644 --- a/src/server/server.ts +++ b/src/server/server.ts @@ -17,13 +17,13 @@ const distDir = path.join(__dirname, 'client'); const trim = (str: string): string => str.replace(/\/*$/, ''); export default function createServer( - { base, port, host }: Server, + { base, port, host, title }: Server, { key, cert }: SSLBuffer ): SocketIO.Server { const basePath = trim(base); events.emit( 'debug', - `key: ${key}, cert: ${cert}, port: ${port}, base: ${base}` + `key: ${key}, cert: ${cert}, port: ${port}, base: ${base}, title: ${title}` ); const html = ( @@ -36,7 +36,7 @@ export default function createServer( - WeTTy - The Web Terminal Emulator + ${title} From 5cc2e68ca9ec43f11114b29fdb497eea29b3f6b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Suszy=C5=84ski?= Date: Sat, 23 Mar 2019 15:29:51 +0100 Subject: [PATCH 07/12] Adding configuration to wetty --- src/client/index.ts | 40 +++++++++++++++++++++++++++++++++- src/client/wetty.scss | 50 +++++++++++++++++++++++++++++++++++++++++++ src/server/server.ts | 7 ++++++ 3 files changed, 96 insertions(+), 1 deletion(-) diff --git a/src/client/index.ts b/src/client/index.ts index ed454ef..81f8cf4 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -15,8 +15,46 @@ const socket = io(window.location.origin, { socket.on('connect', () => { const term = new Terminal(); term.open(document.getElementById('terminal')); - term.setOption('fontSize', 14); + const defaultOptions = { fontSize: 14 }; + let options: any; + try { + if (localStorage.options === undefined) { + options = defaultOptions; + } else { + options = JSON.parse(localStorage.options); + } + } catch { + options = defaultOptions; + } + Object.keys(options).forEach(key => { + const value = options[key]; + term.setOption(key, value); + }); + const code = JSON.stringify(options, null, 2); + const editor = document.querySelector('#options .editor'); + editor.value = code; + editor.addEventListener('keyup', e => { + try { + const updated = JSON.parse(editor.value); + const updatedCode = JSON.stringify(updated, null, 2); + editor.value = updatedCode; + editor.classList.remove('error'); + localStorage.options = updatedCode; + Object.keys(updated).forEach(key => { + const value = updated[key]; + term.setOption(key, value); + }); + resize(); + } catch { + // skip + editor.classList.add('error'); + } + }); document.getElementById('overlay').style.display = 'none'; + document.querySelector('#options .toggler').addEventListener('click', e => { + document.getElementById('options').classList.toggle('opened'); + e.preventDefault(); + }); window.addEventListener('beforeunload', handler, false); /* term.scrollPort_.screen_.setAttribute('contenteditable', 'false'); diff --git a/src/client/wetty.scss b/src/client/wetty.scss index 2a5c131..c8ae18c 100644 --- a/src/client/wetty.scss +++ b/src/client/wetty.scss @@ -3,6 +3,7 @@ $black: #000; $grey: rgba(0, 0, 0, 0.75); $white: #fff; +$lgrey: #ccc; html, body { @@ -44,4 +45,53 @@ body { position: relative; width: 100%; } + + #options { + position: absolute; + top: 1em; + right: 1em; + z-index: 20; + height: 16px; + width: 16px; + + a.toggler { + display: inline-block; + position: absolute; + right: 1em; + top: 0em; + font-size: 16px; + color: $lgrey; + z-index: 20; + + :hover { + color: $white; + } + } + + .editor { + background-color: rgba(0, 0, 0, 0.85); + padding: 0.5em; + border-radius: 0.3em; + border-color: rgba(255, 255, 255, 0.25); + display: none; + position: relative; + height: 100%; + width: 100%; + top: 1em; + right: 2em; + color: #eee; + font-size: 24px; + } + .editor.error { + color: red; + } + } + + #options.opened { + height: 50%; + width: 50%; + .editor { + display: flex; + } + } } diff --git a/src/server/server.ts b/src/server/server.ts index a8a83a3..695a3d7 100644 --- a/src/server/server.ts +++ b/src/server/server.ts @@ -38,6 +38,7 @@ export default function createServer( ${title} +
@@ -46,6 +47,12 @@ export default function createServer(
+
+ + +
From 0e0949cf9b2b608c55a1aa7a038b56709755e276 Mon Sep 17 00:00:00 2001 From: Matthew Piercey Date: Mon, 27 May 2019 20:31:42 -0400 Subject: [PATCH 08/12] Tweak README Fix minor typo; change yarn installation to 'yarn global add wetty.js' since the current command does not work in the newest version of Yarn --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8915134..e230d9e 100644 --- a/README.md +++ b/README.md @@ -24,11 +24,11 @@ $ yarn build To install it globally from npm use yarn or npm: -- yarn, `yarn -g add wetty.js` +- yarn, `yarn global add wetty.js` - npm, `npm i -g wetty.js` -For auto-login feature you'll need sshpass installed(NOT required for rest of -the program". +For auto-login feature you'll need sshpass installed (NOT required for rest of +the program). - `apt-get install sshpass` (debian eg. Ubuntu) - `yum install sshpass` (red hat flavours eg. CentOs) From 981b0b2059a2858b2404912659991082e4acb9a1 Mon Sep 17 00:00:00 2001 From: Anthony Jund Date: Tue, 11 Jun 2019 09:02:29 -0400 Subject: [PATCH 09/12] Added startup option to disable helmet --- index.js | 6 ++++++ src/server/index.ts | 6 ++++-- src/server/interfaces.ts | 1 + src/server/server.ts | 9 ++++++--- 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/index.js b/index.js index 526d775..55beed2 100755 --- a/index.js +++ b/index.js @@ -94,6 +94,12 @@ if (require.main === module) { type: 'string', default: process.env.COMMAND || 'login', }, + 'no-helmet': { + demand: false, + description: 'disable helmet from placing security restrictions', + type: 'boolean', + default: false, + }, help: { demand: false, alias: 'h', diff --git a/src/server/index.ts b/src/server/index.ts index 2274339..41dbc46 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -17,6 +17,7 @@ export interface Options { port: number; title: string; command?: string; + disableHelmet?: boolean; } interface CLI extends Options { @@ -38,6 +39,7 @@ export default class Server { command, sslkey, sslcert, + disableHelmet, }: Options): Promise { wetty .on('exit', ({ code, msg }: { code: number; msg: string }) => { @@ -56,11 +58,11 @@ export default class Server { host: sshhost, auth: sshauth, port: sshport, - title: title, + title, pass: sshpass, key: sshkey, }, - { base, host, port, title }, + { base, host, port, title, disableHelmet }, command, { key: sslkey, cert: sslcert } ); diff --git a/src/server/interfaces.ts b/src/server/interfaces.ts index 4674ecf..adcda37 100644 --- a/src/server/interfaces.ts +++ b/src/server/interfaces.ts @@ -21,4 +21,5 @@ export interface Server { port: number; host: string; base: string; + disableHelmet: boolean; } diff --git a/src/server/server.ts b/src/server/server.ts index 695a3d7..f53e0c2 100644 --- a/src/server/server.ts +++ b/src/server/server.ts @@ -17,7 +17,7 @@ const distDir = path.join(__dirname, 'client'); const trim = (str: string): string => str.replace(/\/*$/, ''); export default function createServer( - { base, port, host, title }: Server, + { base, port, host, title, disableHelmet }: Server, { key, cert }: SSLBuffer ): SocketIO.Server { const basePath = trim(base); @@ -49,7 +49,7 @@ export default function createServer(
@@ -61,7 +61,6 @@ export default function createServer( const app = express(); app .use(morgan('combined', { stream: logger.stream })) - .use(helmet()) .use(compression()) .use(favicon(path.join(distDir, 'favicon.ico'))) .use(`${basePath}/public`, express.static(distDir)) @@ -77,6 +76,10 @@ export default function createServer( .get(basePath, html) .get(`${basePath}/ssh/:user`, html); + if (!disableHelmet) { + app.use(helmet()); + } + return socket( !isUndefined(key) && !isUndefined(cert) ? https.createServer({ key, cert }, app).listen(port, host, () => { From ca50c52674329c5e819796186cfbe7824ee8fdac Mon Sep 17 00:00:00 2001 From: Anthony Jund Date: Tue, 11 Jun 2019 09:03:06 -0400 Subject: [PATCH 10/12] Updated lint-staged to fix any-observable error --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4ec2b22..bf7ce9d 100644 --- a/package.json +++ b/package.json @@ -94,7 +94,7 @@ "eslint-plugin-typescript": "^1.0.0-rc.1", "file-loader": "^3.0.1", "husky": "^1.3.1", - "lint-staged": "^6.1.1", + "lint-staged": "~8.2.0", "mini-css-extract-plugin": "^0.5.0", "node-sass": "^4.11.0", "nodemon": "^1.14.10", From cce60629ec1c8065eaeda157b969c376f5089f22 Mon Sep 17 00:00:00 2001 From: Anthony Jund Date: Tue, 11 Jun 2019 09:28:32 -0400 Subject: [PATCH 11/12] Renamed option from --no-helmet to --bypasshelmet --- index.js | 2 +- src/server/index.ts | 4 ++-- src/server/interfaces.ts | 2 +- src/server/server.ts | 13 ++++++++----- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/index.js b/index.js index 55beed2..3caa5f9 100755 --- a/index.js +++ b/index.js @@ -94,7 +94,7 @@ if (require.main === module) { type: 'string', default: process.env.COMMAND || 'login', }, - 'no-helmet': { + bypasshelmet: { demand: false, description: 'disable helmet from placing security restrictions', type: 'boolean', diff --git a/src/server/index.ts b/src/server/index.ts index 41dbc46..af96308 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -39,7 +39,7 @@ export default class Server { command, sslkey, sslcert, - disableHelmet, + bypasshelmet, }: Options): Promise { wetty .on('exit', ({ code, msg }: { code: number; msg: string }) => { @@ -62,7 +62,7 @@ export default class Server { pass: sshpass, key: sshkey, }, - { base, host, port, title, disableHelmet }, + { base, host, port, title, bypasshelmet }, command, { key: sslkey, cert: sslcert } ); diff --git a/src/server/interfaces.ts b/src/server/interfaces.ts index adcda37..05b391c 100644 --- a/src/server/interfaces.ts +++ b/src/server/interfaces.ts @@ -21,5 +21,5 @@ export interface Server { port: number; host: string; base: string; - disableHelmet: boolean; + bypasshelmet: boolean; } diff --git a/src/server/server.ts b/src/server/server.ts index f53e0c2..1462586 100644 --- a/src/server/server.ts +++ b/src/server/server.ts @@ -17,7 +17,7 @@ const distDir = path.join(__dirname, 'client'); const trim = (str: string): string => str.replace(/\/*$/, ''); export default function createServer( - { base, port, host, title, disableHelmet }: Server, + { base, port, host, title, bypasshelmet }: Server, { key, cert }: SSLBuffer ): SocketIO.Server { const basePath = trim(base); @@ -72,14 +72,17 @@ export default function createServer( ) res.redirect(301, req.url.slice(0, -1)); else next(); - }) - .get(basePath, html) - .get(`${basePath}/ssh/:user`, html); + }); - if (!disableHelmet) { + // Allow helmet to be bypassed. + // Unfortunately, order matters with middleware + // which is why this is thrown in the middle + if (!bypasshelmet) { app.use(helmet()); } + app.get(basePath, html).get(`${basePath}/ssh/:user`, html); + return socket( !isUndefined(key) && !isUndefined(cert) ? https.createServer({ key, cert }, app).listen(port, host, () => { From 681064050bc9e7415ee315f540042df9dba96a24 Mon Sep 17 00:00:00 2001 From: Anthony Jund Date: Tue, 11 Jun 2019 09:39:16 -0400 Subject: [PATCH 12/12] Corrected type from renaming bypassHelmet --- src/server/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/index.ts b/src/server/index.ts index af96308..408fe4c 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -17,7 +17,7 @@ export interface Options { port: number; title: string; command?: string; - disableHelmet?: boolean; + bypasshelmet?: boolean; } interface CLI extends Options {