Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
FROM node:14-alpine
FROM node:15-alpine

WORKDIR /codebase
RUN apk update
RUN apk upgrade

COPY package.json /codebase/
COPY package-lock.json /codebase/
# setup run user
RUN apk add libcap
RUN setcap 'cap_net_bind_service=+ep' /usr/local/bin/node
RUN adduser appuser -D
USER appuser
WORKDIR /home/appuser

COPY --chown=appuser package.json /home/appuser
COPY --chown=appuser package-lock.json /home/appuser

RUN npm install --production

COPY . /codebase
COPY --chown=appuser . /home/appuser

ENV NODE_ENV production
ENTRYPOINT ["./bin/server.js"]
66 changes: 48 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# mytunnel-server
# fusetunnel-server

mytunnel exposes your localhost to the world for easy testing and sharing! No need to mess with DNS or deploy just to have others test out your changes.
Fusetunnel exposes your localhost to the world for easy testing and sharing! No need to mess with DNS or deploy just to have others test out your changes.

This repo is the server component. If you are just looking for the CLI mytunnel app, see (https://github.com/StyleT/mytunnel).
This repo is the server component. If you are just looking for the CLI fusetunnel app, see (https://github.com/fusebit/tunnel).

## Overview

You can easily set up and run your own server. In order to run your own mytunnel server you must ensure that your server can meet the following requirements:
You can easily set up and run your own server. In order to run your own fusetunnel server you must ensure that your server can meet the following requirements:

* You can set up DNS entries for your `domain.tld` and `*.domain.tld` (or `sub.domain.tld` and `*.sub.domain.tld`).
* The server can accept incoming TCP connections for any non-root TCP port (i.e. ports over 1000).
Expand All @@ -17,24 +17,24 @@ The above are important as the client will ask the server for a subdomain under

```shell
# pick a place where the files will live
git clone git://github.com/StyleT/mytunnel-server.git
cd mytunnel-server
git clone git://github.com/fusebit/tunnel-server.git
cd tunnel-server
npm install

# server set to run on port 1234
bin/server.js --port 1234
```

The mytunnel server is now running and waiting for client requests on port 1234. You will most likely want to set up a reverse proxy to listen on port 80 (or start mytunnel on port 80 directly).
The fusetunnel server is now running and waiting for client requests on port 1234. You will most likely want to set up a reverse proxy to listen on port 80 (or start fusetunnel on port 80 directly).

**NOTE** By default, mytunnel will use subdomains for clients, if you plan to host your mytunnel server itself on a subdomain you will need to use the _--domain_ option and specify the domain name behind which you are hosting mytunnel. (i.e. my-tunnel-server.example.com)
**NOTE** By default, fusetunnel will use subdomains for clients, if you plan to host your fusetunnel server itself on a subdomain you will need to use the _--domain_ option and specify the domain name behind which you are hosting fusetunnel. (i.e. my-tunnel-server.example.com)

### Systemd config

Sample systemd config file:
```
[Unit]
Description=MyTunnel service
Description=FuseTunnel service
After=network.target
StartLimitIntervalSec=0

Expand All @@ -53,18 +53,47 @@ WantedBy=multi-user.target
Setup flow:
```bash
sudo setcap CAP_NET_BIND_SERVICE=+eip /usr/bin/node # allow non root access to bind below port number 1024
sudo vi /etc/systemd/system/mytunnel.service
sudo systemctl start mytunnel
sudo systemctl enable mytunnel # Add to autoload
journalctl -f -u mytunnel # See logs
sudo vi /etc/systemd/system/fusetunnel.service
sudo systemctl start fusetunnel
sudo systemctl enable fusetunnel # Add to autoload
journalctl -f -u fusetunnel # See logs
```
## Enable TLS

You can enable TLS support for both API plane and data plane natively

To enable data plane TLS, enable these flags on fusetunnel-server

`--tunnel-ssl-cert` (the SSL certificate of the tunnel)

`--tunnel-ssl-key` (the SSL key of the tunnel)

`--tunnel-ssl-ca` (optional: the certificate authority/full chain certificate of the tunnel)

`--auto-generate-cert` (optional: automatically generate self signed certificate and key for tunnel data plane)

To enable control plane TLS, enable these flags in fusetunnel-server

`--web-cert` (the SSL cert of the API server)

`--web-key` (the SSL key of the API server)

`--web-ca` (the certificate authority/full chain certificate of the tunnel)

## Use behind reverse proxy/load balancer

If you need to override the IP sockets connect to for fuse-tunnel, enable these flags on fusetunnel-server

`--override-tunnel-ip` (optional: override the IP fusetunnel return to client)

`--auto-discover-tunnel-ip` (optional: automatically discover the public ip of the fusetunnel server through AWS' public API)

## Use your server

You can now use your domain with the `--host` flag for the `lt` client.

```shell
npx mytunnel --host http://tunnel.example.com --port 9000
npx fusetunnel --host http://tunnel.example.com --port 9000
```

You will be assigned a URL similar to `http://heavy-puma-9.tunnel.example.com`.
Expand All @@ -75,24 +104,25 @@ If your server is acting as a reverse proxy (i.e. nginx) and is able to listen o

### POST /api/tunnels

Create a new tunnel. A MyTunnel client posts to this enpoint to request a new tunnel with a specific name or a randomly assigned name.
Create a new tunnel. A FuseTunnel client posts to this enpoint to request a new tunnel with a specific name or a randomly assigned name.

### GET /api/status

General server information.

## Deploy

You can deploy your own mytunnel server using the prebuilt docker image.
You can deploy your own fusetunnel server using the prebuilt docker image.

**Note** This assumes that you have a proxy in front of the server to handle the http(s) requests and forward them to the mytunnel server on port 3000. You can use our [localtunnel-nginx](https://github.com/localtunnel/nginx) to accomplish this.
**Note** This assumes that you have a proxy in front of the server to handle the http(s) requests and forward them to the fusetunnel server on port 3000. You can use our [localtunnel-nginx](https://github.com/localtunnel/nginx) to accomplish this.

If you do not want ssl support for your own tunnel (not recommended), then you can just run the below with `--port 80` instead.


```
docker run -d \
--restart always \
--name mytunnel \
--name fusetunnel \
--net host \
defunctzombie/localtunnel-server:latest --port 3000
```
68 changes: 65 additions & 3 deletions bin/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import optimist from 'optimist';

import log from 'book';
import Debug from 'debug';

import pemPromise from 'pem-promise';
import CreateServer from '../server.js';

const debug = Debug('mytunnel');
import fs from 'fs';
import axios from 'axios';
const debug = Debug('fusetunnel');

const argv = optimist
.usage('Usage: $0 --port [num]')
Expand All @@ -31,17 +32,78 @@ const argv = optimist
default: 10,
describe: 'maximum number of tcp sockets each client is allowed to establish at one time (the tunnels)'
})
.options('tunnel-ssl-cert', {
describe: 'the ssl cert for encrypting the tunnel'
})
.options('tunnel-ssl-key', {
describe: 'the ssl key for encrypting the tunnel'
})
.options('tunnel-ssl-ca', {
describe: 'the certificate authority chain for the ssl cert that is used to encrypt the tunnel'
})
.options('enable-tunnel-ssl', {
describe: 'tells the tunnel to enable SSL'
})
.options('web-cert', {
describe: 'the ssl cert for the main web page'
})
.options('web-key', {
describe: 'the ssl key for the main web page'
})
.options('web-ca', {
describe: 'the ssl ca for the main web page'
})
.options('auto-generate-cert', {
describe: 'enabling this flag will automatically generate the server certs for you'
})
.options('override-tunnel-ip', {
describe: 'override the ip to connect to when establishing sockets, usually meant to be used if you are using a external load balancer'
})
.options('auto-discover-tunnel-ip', {
describe: 'automatically descover the tunnel public ip via the AWS checkip endpoint'
})
.options("secret", {
describe:
"the secret that clients need to pass to open a tunnel connection with the server",
})
.argv;

if (argv.help) {
optimist.showHelp();
process.exit();
}
if (!argv['auto-generate-cert'] && argv['enable-tunnel-ssl']) {
argv['tunnel-ssl-key'] = fs.readFileSync(argv['tunnel-ssl-key'])
argv['tunnel-ssl-cert'] = fs.readFileSync(argv['tunnel-ssl-cert'])
argv['tunnel-ssl-ca'] = fs.readFileSync(argv['tunnel-ssl-ca'])
}
if (argv['auto-generate-cert']) {
await pemPromise.createCertificate().then((keys) => {
argv['tunnel-ssl-key'] = keys.serviceKey
argv['tunnel-ssl-cert'] = keys.certificate
argv['tunnel-ssl-ca'] = keys.certificate
}
);

}
if (argv['auto-discover-tunnel-ip']) {
await axios.get("http://checkip.amazonaws.com")
.then((response) => {
argv['override-tunnel-ip'] = response.data.split("\n")[0]
})
}
const server = CreateServer({
max_tcp_sockets: argv['max-sockets'],
secure: argv.secure,
domain: argv.domain,
key: argv['tunnel-ssl-key'],
cert: argv['tunnel-ssl-cert'],
ca: argv['tunnel-ssl-ca'],
webcert: argv['web-cert'],
webkey: argv['web-key'],
webca: argv['web-ca'],
OverrideTunnelIp: argv['override-tunnel-ip'],
secret: argv.secret,
});

server.listen(argv.port, argv.address, () => {
Expand Down
19 changes: 19 additions & 0 deletions keys/test.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDETCCAfkCFEh5+sIJi3pEG2H3ycSK30r3LUlkMA0GCSqGSIb3DQEBCwUAMEUx
CzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl
cm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMjEwMzIyMDMzMjQ3WhcNMjEwNDIxMDMz
MjQ3WjBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UE
CgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEAxO5pBm6kNOVwQrnfJonNsdEBI6q2nomQ2mA4hticJ33jG+Pm
4Mj1UHx+sL/FcEVRgtfuTEc2D3YIyDQTg7bBrS2U3FmAgHBpfn0kYR4OG3kfquCK
bcjouzwIg+suqp/j90ASLl1+s/qLOhOgmNgEQAwv7z+DOQ6ISRgVz5LtBr4BWRGH
5jOwm+6JezBSRC+JyYMSciDrZz3SXz9cl5xmfA1gt9AFrZXYxTb+B0KMPVNiUNnl
oOmQ8S0IEO1lEtp99GoTUVp8fl4edAkqRG1KXrB3fOJ7UTAln9tQRi+ajgJdn1eM
YBEhLs3tFY5I8NwX2t7LQpzN7dDh5CBZd8G1UwIDAQABMA0GCSqGSIb3DQEBCwUA
A4IBAQCq2IeKsd7MG7mdZVbZfXuamn5QzOwJOk3D6x1IFc2YDAwd+Kx1nYNiqfqB
OiyRP04e81AYDue59rgifVtV5BGRraAHO1d2u6ZemHTO9U2XVIswQDD+XQDZCKmq
7tvKXiCY9h3t1L6k1AQHzPZv7m8ZAcnPjupbGXyjhcljkL4hkM5YeMgmYOgwuHAQ
XQM/jFIFipCwhLcFFInIOwwppeW1Ui7ySg1LS5kr2hJK4x1qksYSQ769tG/uVfhJ
2RHIix6JJrVfIHuB1znkpBT4PGZu5nM6yKmIc3vM8+X0z2vzC1SBbQsBPtCEA7nK
8kE+3yg6Sz6Dh+oNFH7ipgMe9DDg
-----END CERTIFICATE-----
16 changes: 16 additions & 0 deletions keys/test.csr
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
-----BEGIN CERTIFICATE REQUEST-----
MIICijCCAXICAQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx
ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBAMTuaQZupDTlcEK53yaJzbHRASOqtp6JkNpgOIbY
nCd94xvj5uDI9VB8frC/xXBFUYLX7kxHNg92CMg0E4O2wa0tlNxZgIBwaX59JGEe
Dht5H6rgim3I6Ls8CIPrLqqf4/dAEi5dfrP6izoToJjYBEAML+8/gzkOiEkYFc+S
7Qa+AVkRh+YzsJvuiXswUkQvicmDEnIg62c90l8/XJecZnwNYLfQBa2V2MU2/gdC
jD1TYlDZ5aDpkPEtCBDtZRLaffRqE1FafH5eHnQJKkRtSl6wd3zie1EwJZ/bUEYv
mo4CXZ9XjGARIS7N7RWOSPDcF9rey0Kcze3Q4eQgWXfBtVMCAwEAAaAAMA0GCSqG
SIb3DQEBCwUAA4IBAQCXJcZjImSryMYlvtiGJ5Xr2JH+yyhLYIgci6dolAoaDQDm
5QqByounrNC8goHA5pbTpxeL6KonjeY8cPJiVeSTMWGp372mSOFJKkDA2CkOiAlG
+pUOPo7+JxDZ4g179vzTBrkbEhNweEqp2QGJp+qTRj1KCMnyxsoqXhxAr+gXU7tr
vIhkBXgH9zvftxXFYl6juhaT3bfaed8klWfkuDaYpbEp+dRWZLUCqI3B2qYCTVNw
7dE2XrMoh9ZjwXG3bdbv/h1UAUCx6joPyEVQ38iFA9hfKrL+7HvRjqZal6A5vA5G
FE9xHiJi210a+L7anZ531JqkPcidg+wduulwgoBz
-----END CERTIFICATE REQUEST-----
27 changes: 27 additions & 0 deletions keys/test.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAxO5pBm6kNOVwQrnfJonNsdEBI6q2nomQ2mA4hticJ33jG+Pm
4Mj1UHx+sL/FcEVRgtfuTEc2D3YIyDQTg7bBrS2U3FmAgHBpfn0kYR4OG3kfquCK
bcjouzwIg+suqp/j90ASLl1+s/qLOhOgmNgEQAwv7z+DOQ6ISRgVz5LtBr4BWRGH
5jOwm+6JezBSRC+JyYMSciDrZz3SXz9cl5xmfA1gt9AFrZXYxTb+B0KMPVNiUNnl
oOmQ8S0IEO1lEtp99GoTUVp8fl4edAkqRG1KXrB3fOJ7UTAln9tQRi+ajgJdn1eM
YBEhLs3tFY5I8NwX2t7LQpzN7dDh5CBZd8G1UwIDAQABAoIBAFULWFOsyEEHrHlE
Z7fu3uOhjMpoWMESS3ni/8ZMGPN9C+zpeLUuU7JUcSnHR256oP7OXVyXjCMVKCm9
Q4goDm/JNXYLONE+unAjfuhp4FHA9woGOgG7N7ZT8KF3mQmaTwphovhe6+6xjhqg
i0Q2VW+LU9AQL40eV4CpZBHmM3IrLaCVlgPsMD4aIT0JH+blGLXuwHKX2Qq4Sshc
D8m7z3+iO3ZI5JilY/ffmKtlo2BDCWXd2iZ1Z+tBUZujPZRM1KZdVeRgTutIclyO
oHKyREaYY3ndMKrx+WVypYG4VfS0PJ3jMqHLaUlNd7vEcySRrFnXlBshbAr4595n
tMt2CKECgYEA7Cc25h1VdCzJA9ODZor8hyF3qNkHfxPz5zodIMpvoPasQPsql3Xn
MqtLmFnZtBmw0UYS/lcty6+mo/cbGM6kFKW4LZVxOYsG0Ql/KHndKYWNsja4K5mc
9x+ECf8Y+ia2yEitHF2Jsmfe1Kzg1Ke6KEcqxQUw2IBJI1Y7a8i2GzkCgYEA1XtY
Wy52ig7rWUSpkMgmsbpWyuUq5ubunquTTGkVUKSIqPxNxg0wAlVxBwlX2aji+N+6
Zxhi5/dAey0CByofSHGuqCgAK6vxdKCjnTtVUzkCjPSt4SVqSzdhk9jKnOJvqYIo
QL+OqMBAcHsXlWHa4sFPbEz3PuPjcB6kFLZ0eOsCgYEAxj/dfK1fkdoPHuEwTKOd
gCv+codQHcS4W4OT9HU95VlwHlU82k4mu4jGO64ib2mp4OX9B/xT+80IDlpiKx8+
6/57cfklRnOKU26vi2FS44q0+8moLyIUffm9NU8svsRFajpo6Yk1EcrVbKF9VclB
JJMqOqAy824u+DVqSQAEsTECgYEAj3knJ5ixaBWgzEzeHQS3JKkUHaYo0CgR5Kdx
BmB1R85cXh2680j3pAniPZVk6k6bxCEweIsmJbnBJ0dXXpNLlPd6J19Cxhb33qb7
bvdqX9Unxq3GklL//e8JcEC54vg3jVeBUAqtiPI0HKRNdELgxZaflyye2jB0MW0W
XylEW+MCgYEA30QKHxzWxB1+7JroBNJXgf3e0ERCW4i3KV6WiLtQ0Kr2spGuXaTm
8C7dR0Jucd1k3wQMC2v2sXEjT6q0Qpu04OdSvBrOvsXzfFOlrZ21SNJ/YYpq7N+8
PurZmp9Hwidr/RtHDKe1hRYzrgv0I//IxomABIx2EMeSFpQPp8ZEDmY=
-----END RSA PRIVATE KEY-----
12 changes: 4 additions & 8 deletions lib/Client.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class Client extends EventEmitter {
const agent = this.agent = options.agent;
const id = this.id = options.id;

this.debug = Debug(`mytunnel:Client[${this.id}]`);
this.debug = Debug(`fusetunnel:Client[${this.id}]`);

// client is given a grace period in which they can connect before they are _removed_
this.graceTimeout = setTimeout(() => {
Expand Down Expand Up @@ -57,22 +57,18 @@ class Client extends EventEmitter {

handleRequest(req, res) {
this.debug('> %s', req.url);
const opt = {
const clientReq = http.request({
path: req.url,
agent: this.agent,
method: req.method,
headers: req.headers
};

const clientReq = http.request(opt, (clientRes) => {
headers: req.headers,
}, (clientRes) => {
this.debug('< %s', req.url);
// write response code and headers
res.writeHead(clientRes.statusCode, clientRes.headers);

// using pump is deliberate - see the pump docs for why
pump(clientRes, res);
});

// this can happen when underlying agent produces an error
// in our case we 504 gateway error this?
// if we have already sent headers?
Expand Down
8 changes: 7 additions & 1 deletion lib/ClientManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class ClientManager {
tunnels: 0
};

this.debug = Debug('mytunnel:ClientManager');
this.debug = Debug('fusetunnel:ClientManager');
}

// create a new tunnel with `id`
Expand All @@ -35,6 +35,9 @@ class ClientManager {
const agent = new TunnelAgent({
clientId: id,
maxSockets: 10,
key: this.opt.key,
cert: this.opt.cert,
ca: this.opt.ca
});

const client = new Client({
Expand All @@ -57,6 +60,9 @@ class ClientManager {
return {
id: id,
port: info.port,
isTls: this.opt.cert ? true: false,
OverrideTunnelIp: this.opt.OverrideTunnelIp,
certificate: this.opt.cert,
max_conn_count: maxSockets,
};
}
Expand Down
Loading