From 07013d3326fec085d662497f6e93d4e78b7892ca Mon Sep 17 00:00:00 2001 From: Tim Ambler Date: Thu, 2 Aug 2018 15:12:26 +0000 Subject: [PATCH 1/2] Configuration file / token-based auth support --- README.md | 16 +++++++++++++++- bin/server | 10 +++++++--- lib/config.js | 32 ++++++++++++++++++++++++++++++++ package.json | 5 +++++ server.js | 25 +++++++++++++++++++++++++ yarn.lock | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 132 insertions(+), 4 deletions(-) create mode 100644 lib/config.js diff --git a/README.md b/README.md index 9c829f0..15d5e94 100644 --- a/README.md +++ b/README.md @@ -43,11 +43,25 @@ You will be assigned a URL similar to `heavy-puma-9.sub.example.com:1234`. If your server is acting as a reverse proxy (i.e. nginx) and is able to listen on port 80, then you do not need the `:1234` part of the hostname for the `lt` client. +### configuration + +The localtunnel server can also be configured with an _optional_, YAML-based configuration file (`localtunnel.yml`). When launched, the server will work its way up the file tree in search of this file (starting from the current working directory). An example is shown below. + +``` +port: 3000 +domain: server.com +secure: true +require_token: true +tokens: + # John Doe + - VJtD3D6BXi9mZmqFT9ezAYq9fLjwUQ4WPZWUiaopRsy3HvMotRdhgbwiCuzrnhYv +``` + ## REST API ### POST /api/tunnels -Create a new tunnel. A LocalTunnel client posts to this enpoint to request a new tunnel with a specific name or a randomly assigned name. +Create a new tunnel. A LocalTunnel client posts to this endpoint to request a new tunnel with a specific name or a randomly assigned name. ### GET /api/status diff --git a/bin/server b/bin/server index a6bba9d..9e88016 100755 --- a/bin/server +++ b/bin/server @@ -2,15 +2,15 @@ import 'localenv'; import optimist from 'optimist'; - import log from 'book'; import Debug from 'debug'; - import CreateServer from '../server'; +import config from '../lib/config'; +import _ from 'lodash'; const debug = Debug('localtunnel'); -const argv = optimist +let argv = optimist .usage('Usage: $0 --port [num]') .options('secure', { default: false, @@ -38,10 +38,14 @@ if (argv.help) { process.exit(); } +argv = _.merge(argv, config); + const server = CreateServer({ max_tcp_sockets: argv['max-sockets'], secure: argv.secure, domain: argv.domain, + require_token: argv.require_token, + tokens: argv.tokens }); server.listen(argv.port, argv.address, () => { diff --git a/lib/config.js b/lib/config.js new file mode 100644 index 0000000..bb718c3 --- /dev/null +++ b/lib/config.js @@ -0,0 +1,32 @@ +'use strict'; + +import findup from 'find-up'; +import yaml from 'js-yaml'; +import deepFreeze from 'deep-freeze'; +import fs from 'fs'; +import _ from 'lodash'; + +let config = {}; + +let configFile = findup.sync('localtunnel.yaml') || findup.sync('localtunnel.yml'); + +if (configFile) { + + try { + config = yaml.safeLoad(fs.readFileSync(configFile, 'utf8')); + } catch(e) { + const err = new Error(`Unable to load config file: ${configFile}`); + err.code = 'CONFIG_LOAD_ERROR'; + throw err; + } + +} + +_.defaultsDeep(config, { + 'require_token': false, + 'tokens': [] +}); + +config = deepFreeze(config || {}); + +export default config; \ No newline at end of file diff --git a/package.json b/package.json index 0693b8d..d53f91e 100644 --- a/package.json +++ b/package.json @@ -9,13 +9,18 @@ "url": "git://github.com/localtunnel/server.git" }, "dependencies": { + "bluebird": "3.5.1", "book": "1.3.3", "debug": "3.1.0", + "deep-freeze": "0.0.1", "esm": "3.0.34", + "findup": "0.1.5", "human-readable-ids": "1.0.3", + "js-yaml": "3.12.0", "koa": "2.5.1", "koa-router": "7.4.0", "localenv": "0.2.2", + "lodash": "4.17.10", "optimist": "0.6.1", "pump": "3.0.0", "tldjs": "2.3.1" diff --git a/server.js b/server.js index 5a53c47..011061d 100644 --- a/server.js +++ b/server.js @@ -11,6 +11,7 @@ import ClientManager from './lib/ClientManager'; const debug = Debug('localtunnel:server'); export default function(opt) { + opt = opt || {}; const validHosts = (opt.domain) ? [opt.domain] : undefined; @@ -27,6 +28,30 @@ export default function(opt) { const app = new Koa(); const router = new Router(); + + app.use(async (ctx, next) => { + try { + await next(); + } catch (err) { + ctx.status = err.status || 500; + ctx.body = err.message; + ctx.app.emit('error', err, ctx); + } + }); + + if (opt.require_token) { + app.use((ctx, next) => { + const token = ctx.request.query.token; + if (!token || opt.tokens.indexOf(token) === -1) { + const err = new Error(`Missing or unknown token.`); + err.status = 511; + err.code = 'TOKEN_ERROR'; + throw err; + } else { + return next(); + } + }); + } router.get('/api/status', async (ctx, next) => { const stats = manager.stats; diff --git a/yarn.lock b/yarn.lock index 9ad744b..fb5a451 100644 --- a/yarn.lock +++ b/yarn.lock @@ -25,6 +25,12 @@ any-promise@^1.1.0: version "1.3.0" resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + dependencies: + sprintf-js "~1.0.2" + array-find-index@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" @@ -41,6 +47,10 @@ balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" +bluebird@3.5.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" + book@1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/book/-/book-1.3.3.tgz#53654254bfe083db102e461544ae9f84be199985" @@ -111,6 +121,10 @@ colors@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" +colors@~0.6.0-1: + version "0.6.2" + resolved "https://registry.yarnpkg.com/colors/-/colors-0.6.2.tgz#2423fe6678ac0c5dae8852e5d0e5be08c997abcc" + combined-stream@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.6.tgz#723e7df6e801ac5613113a7e445a9b69cb632818" @@ -125,6 +139,10 @@ commander@2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.5.0.tgz#d777b6a4d847d423e5d475da864294ac1ff5aa9d" +commander@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.1.0.tgz#d121bbae860d9992a3d517ba96f56588e47c6781" + component-emitter@^1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" @@ -187,6 +205,10 @@ deep-equal@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" +deep-freeze@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/deep-freeze/-/deep-freeze-0.0.1.tgz#3a0b0005de18672819dfd38cd31f91179c893e84" + delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -251,6 +273,10 @@ esm@3.0.34: version "3.0.34" resolved "https://registry.yarnpkg.com/esm/-/esm-3.0.34.tgz#f86afa35c83a0f535da01d15e625e45794ebddc8" +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + esprima@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.0.0.tgz#53cf247acda77313e551c3aa2e73342d3fb4f7d9" @@ -272,6 +298,13 @@ find-up@^1.0.0: path-exists "^2.0.0" pinkie-promise "^2.0.0" +findup@0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/findup/-/findup-0.1.5.tgz#8ad929a3393bac627957a7e5de4623b06b0e2ceb" + dependencies: + colors "~0.6.0-1" + commander "~2.1.0" + foreach@~2.0.1: version "2.0.5" resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" @@ -428,6 +461,13 @@ isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" +js-yaml@3.12.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1" + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + keygrip@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/keygrip/-/keygrip-1.0.2.tgz#ad3297c557069dea8bcfe7a4fa491b75c5ddeb91" @@ -586,6 +626,10 @@ lodash.toarray@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561" +lodash@4.17.10: + version "4.17.10" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" + loud-rejection@^1.0.0: version "1.6.0" resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" @@ -938,6 +982,10 @@ spdx-license-ids@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz#7a7cd28470cc6d3a1cfe6d66886f6bc430d3ac87" +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + stackframe@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-0.3.1.tgz#33aa84f1177a5548c8935533cbfeb3420975f5a4" -- 2.54.0 From 78c06f8fc43ce0eacea2aeb88f2d1d63afaf4e19 Mon Sep 17 00:00:00 2001 From: Jo Hyun Woo Date: Wed, 27 Mar 2019 00:56:17 +0900 Subject: [PATCH 2/2] fix typo in server.js (#109) `sock` is not defined in the scope. --- server.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server.js b/server.js index 5a53c47..21a4af6 100644 --- a/server.js +++ b/server.js @@ -145,13 +145,13 @@ export default function(opt) { server.on('upgrade', (req, socket, head) => { const hostname = req.headers.host; if (!hostname) { - sock.destroy(); + socket.destroy(); return; } const clientId = GetClientIdFromHostname(hostname); if (!clientId) { - sock.destroy(); + socket.destroy(); return; } -- 2.54.0