Compare commits

...

21 Commits

Author SHA1 Message Date
AJ ONeal bb018c538d v0.20.0: use proxy-packer v2.x 2018-08-08 09:08:56 +00:00
AJ ONeal 06cc7bbaeb v0.13.1 2018-06-21 20:16:39 +00:00
AJ ONeal 82aeffd67c bugfix tcp disconnect 2018-06-21 20:16:08 +00:00
AJ ONeal 1873cdea50 v0.13.0 2018-06-21 20:03:08 +00:00
AJ ONeal af63322aca minor logging cleanup 2018-06-21 20:00:02 +00:00
AJ ONeal 8be76e1eb2 major refactor (only briefly tested) 2018-06-19 23:43:28 +00:00
AJ ONeal cb8261fd31 add index.json 2018-06-15 22:53:30 +00:00
AJ ONeal 92f37a515a Merge branch 'master' of ssh://git.coolaj86.com:22042/coolaj86/telebit-relay.js 2018-06-15 08:46:55 +00:00
AJ ONeal e619018276 show meaningful errors 2018-06-15 08:46:43 +00:00
AJ ONeal c8c06d1b99 use sharedDomain from config 2018-06-15 06:13:26 +00:00
AJ ONeal 13a9e77998 Merge branch 'master' of ssh://git.coolaj86.com:22042/coolaj86/telebit-relay.js 2018-06-15 06:09:55 +00:00
AJ ONeal f94e01917e update example config 2018-06-15 06:09:46 +00:00
AJ ONeal 33c3bc9876 Merge branch 'master' of ssh://git.coolaj86.com:22042/coolaj86/telebit-relay.js 2018-06-15 05:16:46 +00:00
AJ ONeal f2cd8158f7 ignore node files 2018-06-15 05:16:23 +00:00
AJ ONeal 02c3836883 change user permissions 2018-06-14 22:22:58 +00:00
AJ ONeal 6cfad96323 adduser telebit 2018-06-14 21:23:08 +00:00
AJ ONeal 24b42e1ed9 manual install instructions for linux 2018-06-14 21:21:48 +00:00
AJ ONeal 39b01fca47 rename telebitd => telebit-relay 2018-06-14 21:19:27 +00:00
AJ ONeal 4a3a395c14 de-hard-code TELEBIT_RELAY_PATH 2018-06-14 21:13:36 +00:00
AJ ONeal 7e83b0c3c2 whitespace 2018-06-14 21:13:09 +00:00
AJ ONeal c8d974884b read from /dev/tty direct 2018-06-14 20:46:41 +00:00
11 changed files with 645 additions and 538 deletions

6
.gitignore vendored
View File

@ -1,4 +1,10 @@
node_modules.* node_modules.*
include
bin/node
bin/npm
bin/npx
share
etc
# Logs # Logs
logs logs

View File

@ -63,6 +63,41 @@ Windows & Node.js
There is [a bug](https://github.com/nodejs/node/issues/20241) in node v9.x that causes telebit-relay to crash. There is [a bug](https://github.com/nodejs/node/issues/20241) in node v9.x that causes telebit-relay to crash.
Manually Install
-----------
```bash
git clone https://git.coolaj86.com/coolaj86/telebit-relay.js.git telebit-relay
# we're very picky to due to bugs in various versions of v8, v9, and v10
export NODEJS_VER="v10.2.1"
# We can keep everything self-contained
export NPM_CONFIG_PREFIX=/opt/telebit-relay
export NODE_PATH=/opt/telebit-relay/lib/node_modules
curl -fsSL https://bit.ly/node-installer | bash -s -- --no-dev-deps
pushd /opt/telebit-relay
bin/node bin/npm install
rsync -a examples/telebit-relay.yml etc/telebit-relay.yml
rsync -a dist/etc/systemd/system/telebit-relay.service /etc/systemd/system/telebit-relay.service
popd
# IMPORTANT: Season the config file to taste
# IMPORTANT: change your email address and domain
edit /opt/telebit-relay/etc/telebit-relay.yml
adduser --home /opt/telebit-relay --gecos '' --disabled-password telebit >/dev/null 2>&1
sudo chown -R telebit:telebit /opt/telebit-relay/
systemctl daemon-reload
systemctl restart telebit-relay
systemctl status telebit-relay
journalctl -xefu telebit-relay
```
Usage Usage
==== ====

View File

@ -0,0 +1,7 @@
{ "terms_of_service": ":hostname/tos/"
, "api_host": ":hostname"
, "tunnel": {
"method": "wss"
, "pathname": ""
}
}

View File

@ -134,7 +134,7 @@ function applyConfig(config) {
// TODO specify extensions in config file // TODO specify extensions in config file
state.extensions = require('../lib/extensions'); state.extensions = require('../lib/extensions');
} catch(e) { } catch(e) {
if (state.debug) { console.log('[DEBUG] no extensions loaded', e); } if ('ENOENT' !== e.code || state.debug) { console.log('[DEBUG] no extensions loaded', e); }
state.extensions = {}; state.extensions = {};
} }
require('../lib/handlers').create(state); // adds directly to config for now... require('../lib/handlers').create(state); // adds directly to config for now...

View File

@ -2,6 +2,8 @@ email: 'jon@example.com' # must be valid (for certificate recovery and sec
agree_tos: true # agree to the Telebit, Greenlock, and Let's Encrypt TOSes agree_tos: true # agree to the Telebit, Greenlock, and Let's Encrypt TOSes
community_member: true # receive infrequent relevant updates community_member: true # receive infrequent relevant updates
telemetry: true # contribute to project telemetric data telemetry: true # contribute to project telemetric data
webmin_domain: example.com
shared_domain: xm.pl
servernames: # hostnames that direct to the Telebit Relay admin console servernames: # hostnames that direct to the Telebit Relay admin console
- telebit.example.com - telebit.example.com
- telebit.example.net - telebit.example.net

View File

@ -68,6 +68,7 @@ my_app="telebit-relay"
my_bin="telebit-relay.js" my_bin="telebit-relay.js"
my_name="Telebit Relay" my_name="Telebit Relay"
my_repo="telebit-relay.js" my_repo="telebit-relay.js"
exec 3<>/dev/tty
if [ -z "${my_email}" ]; then if [ -z "${my_email}" ]; then
echo "" echo ""
@ -77,7 +78,7 @@ if [ -z "${my_email}" ]; then
echo "To accept the Terms of Service for Telebit, Greenlock and Let's Encrypt," echo "To accept the Terms of Service for Telebit, Greenlock and Let's Encrypt,"
echo "please enter your email." echo "please enter your email."
echo "" echo ""
read -p "email: " my_email read -u 3 -p "email: " my_email
echo "" echo ""
# UX - just want a smooth transition # UX - just want a smooth transition
sleep 0.5 sleep 0.5
@ -86,7 +87,7 @@ fi
if [ -z "${my_servername}" ]; then if [ -z "${my_servername}" ]; then
echo "What is the domain of this server (for admin interface)?" echo "What is the domain of this server (for admin interface)?"
echo "" echo ""
read -p "domain (ex: telebit-relay.example.com): " my_servername read -u 3 -p "domain (ex: telebit-relay.example.com): " my_servername
echo "" echo ""
# UX - just want a smooth transition # UX - just want a smooth transition
sleep 0.5 sleep 0.5
@ -120,8 +121,8 @@ mkdir -p $my_tmp
echo "sudo mkdir -p '$TELEBIT_RELAY_PATH'" echo "sudo mkdir -p '$TELEBIT_RELAY_PATH'"
sudo mkdir -p "$TELEBIT_RELAY_PATH" sudo mkdir -p "$TELEBIT_RELAY_PATH"
echo "sudo mkdir -p '/opt/$my_app/etc'" echo "sudo mkdir -p '$TELEBIT_RELAY_PATH/etc'"
sudo mkdir -p "/opt/$my_app/etc/" sudo mkdir -p "$TELEBIT_RELAY_PATH/etc/"
set +e set +e
#https://git.coolaj86.com/coolaj86/telebit-relay.js.git #https://git.coolaj86.com/coolaj86/telebit-relay.js.git
@ -172,17 +173,17 @@ if [ -z "$(cat /etc/passwd | grep $my_user)" ]; then
sudo adduser --home $TELEBIT_RELAY_PATH --gecos '' --disabled-password $my_user >/dev/null 2>&1 sudo adduser --home $TELEBIT_RELAY_PATH --gecos '' --disabled-password $my_user >/dev/null 2>&1
fi fi
if [ ! -f "/opt/$my_app/etc/$my_app.yml" ]; then if [ ! -f "$TELEBIT_RELAY_PATH/etc/$my_app.yml" ]; then
echo "### Creating config file from template. sudo may be required" echo "### Creating config file from template. sudo may be required"
#echo "sudo rsync -a examples/$my_app.yml /opt/$my_app/etc/$my_app.yml" #echo "sudo rsync -a examples/$my_app.yml $TELEBIT_RELAY_PATH/etc/$my_app.yml"
sudo bash -c "echo 'email: $my_email' >> /opt/$my_app/etc/$my_app.yml" sudo bash -c "echo 'email: $my_email' >> $TELEBIT_RELAY_PATH/etc/$my_app.yml"
sudo bash -c "echo 'secret: $my_secret' >> /opt/$my_app/etc/$my_app.yml" sudo bash -c "echo 'secret: $my_secret' >> $TELEBIT_RELAY_PATH/etc/$my_app.yml"
sudo bash -c "echo 'servernames: [ $my_servername ]' >> /opt/$my_app/etc/$my_app.yml" sudo bash -c "echo 'servernames: [ $my_servername ]' >> $TELEBIT_RELAY_PATH/etc/$my_app.yml"
sudo bash -c "cat examples/$my_app.yml.tpl >> /opt/$my_app/etc/$my_app.yml" sudo bash -c "cat $TELEBIT_RELAY_PATH/examples/$my_app.yml.tpl >> $TELEBIT_RELAY_PATH/etc/$my_app.yml"
fi fi
echo "sudo chown -R $my_user '$TELEBIT_RELAY_PATH' '/opt/$my_app/etc'" echo "sudo chown -R $my_user '$TELEBIT_RELAY_PATH'"
sudo chown -R $my_user "$TELEBIT_RELAY_PATH" "/opt/$my_app/etc" sudo chown -R $my_user "$TELEBIT_RELAY_PATH"
echo "### Adding $my_app is a system service" echo "### Adding $my_app is a system service"
echo "sudo rsync -a $TELEBIT_RELAY_PATH/dist/etc/systemd/system/$my_app.service /etc/systemd/system/$my_app.service" echo "sudo rsync -a $TELEBIT_RELAY_PATH/dist/etc/systemd/system/$my_app.service /etc/systemd/system/$my_app.service"
@ -201,7 +202,7 @@ echo "=============================================="
echo " Privacy Settings in Config" echo " Privacy Settings in Config"
echo "==============================================" echo "=============================================="
echo "" echo ""
echo "The example config file /opt/$my_app/etc/$my_app.yml opts-in to" echo "The example config file $TELEBIT_RELAY_PATH/etc/$my_app.yml opts-in to"
echo "contributing telemetrics and receiving infrequent relevant updates" echo "contributing telemetrics and receiving infrequent relevant updates"
echo "(probably once per quarter or less) such as important notes on" echo "(probably once per quarter or less) such as important notes on"
echo "a new release, an important API change, etc. No spam." echo "a new release, an important API change, etc. No spam."
@ -218,13 +219,13 @@ echo "=============================================="
echo "" echo ""
echo "Edit the config and restart, if desired:" echo "Edit the config and restart, if desired:"
echo "" echo ""
echo " sudo vim /opt/$my_app/etc/$my_app.yml" echo " sudo vim $TELEBIT_RELAY_PATH/etc/$my_app.yml"
echo " sudo systemctl restart $my_app" echo " sudo systemctl restart $my_app"
echo "" echo ""
echo "Or disabled the service and start manually:" echo "Or disabled the service and start manually:"
echo "" echo ""
echo " sudo systemctl stop $my_app" echo " sudo systemctl stop $my_app"
echo " sudo systemctl disable $my_app" echo " sudo systemctl disable $my_app"
echo " $my_app --config /opt/$my_app/etc/$my_app.yml" echo " $my_app --config $TELEBIT_RELAY_PATH/etc/$my_app.yml"
echo "" echo ""
sleep 1 sleep 1

View File

@ -52,7 +52,7 @@ module.exports.create = function (state) {
|| redirectHttpsAndClose || redirectHttpsAndClose
); );
state.handleInsecureHttp = function (servername, socket) { state.handleInsecureHttp = function (servername, socket) {
console.log("handleInsecureHttp('" + servername + "', socket)"); console.log("[handlers] insecure http for '" + servername + "'");
socket.__my_servername = servername; socket.__my_servername = servername;
state.httpInsecureServer.emit('connection', socket); state.httpInsecureServer.emit('connection', socket);
}; };

View File

@ -2,42 +2,55 @@
var Packer = require('proxy-packer'); var Packer = require('proxy-packer');
module.exports = function pipeWs(servername, service, conn, remote, serviceport) { module.exports = function pipeWs(servername, service, srv, conn, serviceport) {
var browserAddr = Packer.socketToAddr(conn); var browserAddr = Packer.socketToAddr(conn);
var cid = Packer.addrToId(browserAddr); var cid = Packer.addrToId(browserAddr);
browserAddr.service = service; browserAddr.service = service;
browserAddr.serviceport = serviceport; browserAddr.serviceport = serviceport;
browserAddr.name = servername; browserAddr.name = servername;
conn.tunnelCid = cid; conn.tunnelCid = cid;
var rid = Packer.socketToId(remote.upgradeReq.socket); var rid = Packer.socketToId(srv.upgradeReq.socket);
//if (state.debug) { console.log('[pipeWs] client', cid, '=> remote', rid, 'for', servername, 'via', service); } //if (state.debug) { console.log('[pipeWs] client', cid, '=> remote', rid, 'for', servername, 'via', service); }
function sendWs(data, serviceOverride) { function sendWs(data, serviceOverride) {
if (remote.ws && (!conn.tunnelClosing || serviceOverride)) { if (srv.ws && (!conn.tunnelClosing || serviceOverride)) {
try { try {
remote.ws.send(Packer.pack(browserAddr, data, serviceOverride), { binary: true }); if (data && !Buffer.isBuffer(data)) {
data = Buffer.from(JSON.stringify(data));
}
srv.ws.send(Packer.packHeader(browserAddr, data, serviceOverride), { binary: true });
if (data) {
srv.ws.send(data, { binary: true });
}
// If we can't send data over the websocket as fast as this connection can send it to us // If we can't send data over the websocket as fast as this connection can send it to us
// (or there are a lot of connections trying to send over the same websocket) then we // (or there are a lot of connections trying to send over the same websocket) then we
// need to pause the connection for a little. We pause all connections if any are paused // need to pause the connection for a little. We pause all connections if any are paused
// to make things more fair so a connection doesn't get stuck waiting for everyone else // to make things more fair so a connection doesn't get stuck waiting for everyone else
// to finish because it got caught on the boundary. Also if serviceOverride is set it // to finish because it got caught on the boundary. Also if serviceOverride is set it
// means the connection is over, so no need to pause it. // means the connection is over, so no need to pause it.
if (!serviceOverride && (remote.pausedConns.length || remote.ws.bufferedAmount > 1024*1024)) { if (!serviceOverride && (srv.pausedConns.length || srv.ws.bufferedAmount > 1024*1024)) {
// console.log('pausing', cid, 'to allow web socket to catch up'); // console.log('pausing', cid, 'to allow web socket to catch up');
conn.pause(); conn.pause();
remote.pausedConns.push(conn); srv.pausedConns.push(conn);
} }
} catch (err) { } catch (err) {
console.warn('[pipeWs] remote', rid, ' => client', cid, 'error sending websocket message', err); console.warn('[pipeWs] srv', rid, ' => client', cid, 'error sending websocket message', err);
} }
} }
} }
remote.clients[cid] = conn; srv.clients[cid] = conn;
conn.servername = servername;
conn.serviceport = serviceport;
conn.service = service;
// send peek at data too?
srv.ws.send(Packer.packHeader(browserAddr, null, 'connection'), { binary: true });
// TODO convert to read stream?
conn.on('data', function (chunk) { conn.on('data', function (chunk) {
//if (state.debug) { console.log('[pipeWs] client', cid, ' => remote', rid, chunk.byteLength, 'bytes'); } //if (state.debug) { console.log('[pipeWs] client', cid, ' => srv', rid, chunk.byteLength, 'bytes'); }
sendWs(chunk); sendWs(chunk);
}); });
@ -48,7 +61,7 @@ module.exports = function pipeWs(servername, service, conn, remote, serviceport)
conn.on('close', function (hadErr) { conn.on('close', function (hadErr) {
//if (state.debug) { console.log('[pipeWs] client', cid, 'closing'); } //if (state.debug) { console.log('[pipeWs] client', cid, 'closing'); }
sendWs(null, hadErr ? 'error': 'end'); sendWs(null, hadErr ? 'error': 'end');
delete remote.clients[cid]; delete srv.clients[cid];
}); });
}; };

File diff suppressed because it is too large Load Diff

View File

@ -19,6 +19,11 @@ module.exports.createTcpConnectionHandler = function (state) {
//return; //return;
conn.once('data', function (firstChunk) { conn.once('data', function (firstChunk) {
var service = 'tcp';
var servername;
var str;
var m;
conn.pause(); conn.pause();
conn.unshift(firstChunk); conn.unshift(firstChunk);
@ -31,18 +36,13 @@ module.exports.createTcpConnectionHandler = function (state) {
// defer after return (instead of being in many places) // defer after return (instead of being in many places)
function deferData(fn) { function deferData(fn) {
if (fn) { if (fn) {
state[fn](servername, conn) state[fn](servername, conn);
} }
process.nextTick(function () { process.nextTick(function () {
conn.resume(); conn.resume();
}); });
} }
var service = 'tcp';
var servername;
var str;
var m;
function tryTls() { function tryTls() {
var vhost; var vhost;
@ -76,9 +76,9 @@ module.exports.createTcpConnectionHandler = function (state) {
return; return;
} }
if (state.debug) { console.log("pipeWs(servername, service, socket, deviceLists['" + servername + "'])"); } if (state.debug) { console.log("pipeWs(servername, service, deviceLists['" + servername + "'], socket)"); }
deferData(); deferData();
pipeWs(servername, service, conn, nextDevice, serviceport); pipeWs(servername, service, nextDevice, conn, serviceport);
} }
// TODO don't run an fs check if we already know this is working elsewhere // TODO don't run an fs check if we already know this is working elsewhere
@ -90,7 +90,7 @@ module.exports.createTcpConnectionHandler = function (state) {
//return; //return;
require('fs').readdir(vhost, function (err, nodes) { require('fs').readdir(vhost, function (err, nodes) {
if (state.debug && err) { console.log("VHOST error", err); } if (state.debug && err) { console.log("VHOST error", err); }
if (err) { run(); return; } if (err || !nodes) { run(); return; }
//if (nodes) { deferData('httpsVhost'); return; } //if (nodes) { deferData('httpsVhost'); return; }
deferData('httpsVhost'); deferData('httpsVhost');
}); });
@ -131,7 +131,7 @@ module.exports.createTcpConnectionHandler = function (state) {
// HTTP // HTTP
if (Devices.exist(state.deviceLists, servername)) { if (Devices.exist(state.deviceLists, servername)) {
deferData(); deferData();
pipeWs(servername, service, conn, Devices.next(state.deviceLists, servername), serviceport); pipeWs(servername, service, Devices.next(state.deviceLists, servername), conn, serviceport);
return; return;
} }
deferData('handleHttp'); deferData('handleHttp');

View File

@ -1,6 +1,6 @@
{ {
"name": "telebit-relay", "name": "telebit-relay",
"version": "0.12.1", "version": "0.20.0",
"description": "Friends don't let friends localhost. Expose your bits with a secure connection even from behind NAT, Firewalls, in a box, with a fox, on a train or in a plane... or a Raspberry Pi in your closet. An attempt to create a better localtunnel.me server, a more open ngrok. Uses Automated HTTPS (Free SSL) via ServerName Indication (SNI). Can also tunnel tls and plain tcp.", "description": "Friends don't let friends localhost. Expose your bits with a secure connection even from behind NAT, Firewalls, in a box, with a fox, on a train or in a plane... or a Raspberry Pi in your closet. An attempt to create a better localtunnel.me server, a more open ngrok. Uses Automated HTTPS (Free SSL) via ServerName Indication (SNI). Can also tunnel tls and plain tcp.",
"main": "lib/relay.js", "main": "lib/relay.js",
"bin": { "bin": {
@ -43,15 +43,15 @@
"greenlock": "^2.2.4", "greenlock": "^2.2.4",
"human-readable-ids": "^1.0.4", "human-readable-ids": "^1.0.4",
"js-yaml": "^3.11.0", "js-yaml": "^3.11.0",
"jsonwebtoken": "^8.2.1", "jsonwebtoken": "^8.3.0",
"proxy-packer": "^1.4.3", "proxy-packer": "^2.0.0",
"recase": "^1.0.4", "recase": "^1.0.4",
"redirect-https": "^1.1.5", "redirect-https": "^1.1.5",
"serve-static": "^1.13.2", "serve-static": "^1.13.2",
"sni": "^1.0.0", "sni": "^1.0.0",
"ws": "^5.1.1" "ws": "^5.1.1"
}, },
"engineStrict" : true, "engineStrict": true,
"engines": { "engines": {
"node": "10.2.1" "node": "10.2.1"
} }