An app allows you to forward specific port of some machine inside network with no public IPs.
Time to time I have to connect via RDP, SSH, proxy, etc. to several machine inside private network. Sadly, tools like TeamViewer, Hamachi, SSH tunneling, VPN are blocked there…
So I decided to build a Node.js app to fulfil my needs.
Using the code
I’ll start with a minimal amount actions required to run app and play around with it.
Prerequisites: Node.js (tested with 8), git client
git clone https://github.com/mgrybyk/node-tunnel.git
Now, let’s create minimal config file, I’ll provide two examples: for people who has SSH, another one – for guys with browser 🙂
Create .env file with content:
SSH (you may change host/port to any other if you want)
N_T_AGENT_DATA_HOST=localhost N_T_AGENT_DATA_PORT=22 N_T_CLIENT_PORT=2222
http (you may change host/port to any other; most likely website won’t work due to certificates and other issues, but it’s enough as an example)
N_T_AGENT_DATA_HOST=inplainsite.org N_T_AGENT_DATA_PORT=80 N_T_CLIENT_PORT=8000
We are ready to launch it!
If you’ve chosen SSH way, connect to localhost:2222 -> ssh -p 2222 localhost
If you’ve chosen http way, open your browser and visit localhost:8000
Yeah! This is the minimal working example. All your traffic goes through client->server->agent and back.
Real life case, two PCs
In this example we have such situation. You have PC with public IP and another one without it. Goal – connect to it with SSH/RDP/whatever.
Let’s start with cloning repo and installing modules on each machine.
Now, go to remote PC and create .env file. In this example I’m using RDP port, feel free to change to anything else.
N_T_SERVER_HOST=ip of machine with public ip N_T_SERVER_PORT=1337 N_T_SERVER_PORTS_FROM=1338 N_T_SERVER_PORTS_TO=1340 N_T_AGENT_DATA_HOST=localhost N_T_AGENT_DATA_PORT=3389
Now, start agent here:
Switch to local PC, and create .env file:
N_T_SERVER_HOST=localhost N_T_SERVER_PORT=1337 N_T_SERVER_PORTS_FROM=1338 N_T_SERVER_PORTS_TO=1340 N_T_CLIENT_PORT=8000
Now, start server and client here:
Once you are done – you can connect with RPD client to localhost:8000 that will open connection to remote PC.
Real life case, two PCs and server
That looks great but what if yours local PC has no public IP? You have to forward all traffic though some PC with public IP. If you haven’t such – you may create free container on AWS.
Let’s do it! As usual, install node.js, clone repo and install modules on each machine.
Go to PC with public IP (server) and create .env file there:
N_T_SERVER_HOST=localhost N_T_SERVER_PORT=1337 N_T_SERVER_PORTS_FROM=1338 N_T_SERVER_PORTS_TO=1340
Great, run server:
On remote PC, create .env file. In this example I’m using SSH port, but you can change host/port to anything you want. Also, I would give some name to agent (that should match with client name).
N_T_SERVER_HOST=ip of machine with public ip N_T_SERVER_PORT=1337 N_T_AGENT_DATA_HOST=localhost N_T_AGENT_DATA_PORT=22 N_T_AGENT_NAME=test-ssh
Ready to launch agent:
Switch to client PC and create .env file. Client name should match agent name.
N_T_SERVER_HOST=ip of machine with public ip N_T_SERVER_PORT=1337 N_T_CLIENT_PORT=8000 N_T_CLIENT_NAME=test-ssh
Finally, start client:
Cool. Now we can open SSH connection to localhost:8000 that will open ssh connection to remote PC.
We have created tunnel though server machine, as we, actually, did before. All the data goes like this:
ssh client -> client -> server -> agent -> ssh server
and back ssh server -> agent -> server -> client -> ssh client
There is no direct connection between agent and client.
More agents and clients
Multiple agents and clients can go through one server. Example:
You can run one agent to handle SSH, another to handle RDP. Please note that each agent should have name (N_T_AGENT_NAME).
Each agent may work with multiple client, so you can run client on your machine and others to go to specific agent. Don’t forget to specify which agent client should use by providing name (N_T_CLIENT_NAME)
.env file per agent
If you need to run multiple agents/clients/servers you may create multiple .env files, example:
Having such .env.* files you can start server/client/agent by passing .env file name as argument:
node server .env.rdp
node agent .env.ssh
node client .env.test
How it works?
Let’s take a look at this example:
In an example with SSH the following happens:
- SSH client connects to client (that is listening on some port)
- client forwards all data to server
- server knows that it is required to forward data to specific agent
- agent opens connection to SSH server and forwards data from server to it
response from SSH server goes back to agent, server, client and reaches SSH client finally
Let me try to explain what each part of app does
- by default server listens for clients and agents connections
- once new agent connects – server creates a dedicated server for it
- once new client connects – server notifies it that there is a dedicated server for agent and its port
- client and agent names should match in order to start data forwarding, agent with same name are not allowed
- there may be multiple agents, and there may be multiple client per agent (there may be a low of clients with same name)
Dedicated server for agent behaves this way:
- on new connection to client server a new connection opens to agent dedicated server
- once client connected – a notification to agent is sent so it can open connection to server
in a meanwhile: client socket is now stored and waiting for agent socket
- once agent is connected – dedicated server pipes agent to client and back, and removes data listeners of client and agent sockets
- dedicated server sends notification to client saying that pipe is created and we are ready to go
Client creates server and listens for incoming connections on port provided in .env file.
On new connection client starts forwarding data to server (see server section for details).
Agent waits for notification regarding client connection. When such happened – agents connects to host:port specified in .env file and forwards it to server.
Encryption! Currently data is not encrypted, it’s not a problem for SSH, RDP but is an issue for plain text protocols.
Only service messages are encrypted at the moment.
And, of course, fix some defects, cleanup code, increase stability.
If you have any ideas – feel free share 🙂
As a conclusion
Thank you for reading!
I hope it was somehow interesting, somewhat understandable and maybe even useful 🙂
Points of Interest
Helpful module when working with pipes may be though2.
With this module you pass data though your worker and do stuff like: logging, error handling or changing data, let’s say change some keyword (ebay) to another one (amazon), or change all 0 to 1 🙂