This project is under development. Unless you are explicitly asked to, do not use it for now!
With tnnl you can make IoT devices that are behind a private network, accessible from the internet.
tnnl can be used as an application that makes other applications accessible from the internet.
npm install --global tnnl
You can now start tnnl using
#if you don't have a token, you should not try it for now.
TOKEN="********************" tnnl
This will start tnnl with the default rule :*-*:*
.
This will allow unrestricted access to all ports on localhost!
You can provide your own rules using the environment variable RULE
in the format: RULE="$IN_HOST:$IN_PORT-$OUT_HOST:$OUT_PORT"
.
Multiple rules can be seperated by ,
.
The rules are evaluated in the order they are provided and the first matching rule will be used.
If no rule is matching the connection is rejected.
For the $IN_HOST
there are two special values.
- An empty string is the default host which will only match requests to the default host. Requests to the default host will be redirected to
localhost
if not specified. -
*
which is a wildcard that allows access to all devices (unsupported for now, tnnl requests will only be sent to the default host for now).
Here are a few example rules:
Rule | Description |
---|---|
:*-*:* |
Unrestricted access to all ports on localhost |
:80-*:* |
Allow access to port 80 |
:80-*:4000 |
Allow access to port 80 but forward it to port 4000 |
:*-192.168.1.1:* |
Allow access to all ports on 192.168.1.1
|
:80-192.168.1.1:* |
Allow access to port 80 on 192.168.1.1
|
:80-192.168.1.1:4000 |
Allow access to port 80 on 192.168.1.1 but forward it to port 4000 |
:80-*:*,:22-*:* |
Allow access to port 80 and port 22 |
Example with a rule:
#if you don't have a token, you should not try it for now.
RULE=":80-*:*" TOKEN="********************" tnnl
tnnl can also be embedded in your own application directly.
npm install --save tnnl
This creates a simple http server which can be accessed using your assigned domain name.
const os = require('os');
const http = require('http');
const { ClientHandler } = require('tnnl');
const main = async () => {
const server = http.createServer((req, res) => {
res.write(`Hello World from ${os.hostname}`);
res.end();
});
const clientHandler = new ClientHandler({
// if you don't have a token, you should not try it for now.
token: '********************',
onConnect: await ClientHandler.connectHttpServer(server),
onStateChanged: console.log,
});
process.on('SIGINT', async () => {
await clientHandler.stop();
server.close();
});
await clientHandler.start();
};
main().catch(console.error);
This makes the local device accessible.
If you have a http server running on port 80 you can access is now using your assigned domain.
If you have a ssh server running you can access it now using ssh -o ProxyCommand='nc -x access.websof.de:1080 %h %p' $YOUR_ASSIGNED_DOMAIN_NAME
.
Please note that every open port on the device can be accessed using the proxy!
const tnnl = require('tnnl');
const ClientHandler = tnnl.ClientHandler;
const clientHandler = new ClientHandler({
// if you don't have a token, you should not try it for now.
token: '********************',
onConnect: ClientHandler.connectLocalhost(),
onStateChanged: console.log,
});
process.on('SIGINT', () => {
clientHandler.stop();
});
clientHandler.start();
You can provide your own onConnect
callback to rewrite ports or filter access to different ports and even forward requests to other hosts.
const tnnl = require('tnnl');
const ClientHandler = tnnl.ClientHandler;
const connect = ClientHandler.connectLocalhost();
const onConnect = (port) => {
// forward requests to port 80 to port 4000
if (port === 80) {
return connect(4000);
// you could also forward them to another host
// return connect(80, '192.168.1.1');
}
// forward ssh traffic
if (port === 22) {
return connect(22);
}
// block all other ports
return null;
};
const clientHandler = new ClientHandler({
// if you don't have a token, you should not try it for now.
token: '********************',
onConnect: onConnect,
onStateChanged: console.log,
});
process.on('SIGINT', () => {
clientHandler.stop();
});
clientHandler.start();