Share My Creation Remote SSH and SCP access with TCP proxy server and MQTT Tunnel

I have several remote Linux "boxes" collecting sensor data that I occasionally need to SSH into for troubleshooting, etc. The problem is that all of these boxes are behind firewalls as is expected so it's impossible to SSH or SCP into the boxes without having the network administrator open Port 22 inbound to each box at each location. Of course I have never found a network administrator willing to do that for obvious reasons. I know that there are solutions out there but they all seem overly complicated to me and most are unreliable. This solution can be used stand-alone, or the code can easily be integrated into your B4J application to allow remote access to the host computer.

So this B4J Application allows me to transparently access my Linux boxes using standard Windows applications such as PUTTY and WINSCP so I can have access to a remote terminal and transfer files as if I were connecting directly to the box.

The program is basically a TCP "proxy" server. But it uses a MQTT tunnel at both ends for communications. No inbound ports need to be opened at the remote location. In most cases outbound connections to all ports are open so there should be no issues accessing the MQTT server.

Steps to Accomplish remote access.

1. Compile the program into a JAR file. I use Java8. Have not tried it with other Java versions. But I see no reason why it would not work.

2. Copy the mqttproxy.JAR and mqttproxy.CFG files to the remote computer and also copy it to the local computer that has the programs that you generally use for remote SSH and SCP access such as PUTTY and WINSCP.

3. On the remote computer edit the mqttproxy.cfg file:

dest_IP=localhost
dest_port=22
local_port=2222 # <- Not relevant
mqtt_server=mqtt.xyz.com:1883 # <- The mqtt server you will be using
mqtt_username=user # <- your mqtt username
mqtt_password=pass # <- your mqtt password
mqtt_topic=mqttproxy/ABC/ # <- mqtt root topic (suggest leaving it the same initially)
mqtt_qos=0
program_mode=remote # <- IMPORTANT!!

4. On the local computer edit the mqttproxy.cfg file:

dest_IP=localhost # <- Not relevant
dest_port=22 # <- Not relevant
local_port=2222 # <- This is the port you will be connecting to
mqtt_server=mqtt.xyz.com:1883 # <- The mqtt server you will be using
mqtt_username=user # <- your mqtt username
mqtt_password=pass # <- your mqtt password
mqtt_topic=mqttproxy/ABC/ # <- mqtt root topic (suggest leaving it the same initially)
mqtt_qos=0
program_mode=local # <- IMPORTANT!!


5. Run PUTTY or WINSCP on the local computer

You will connect to "localhost" Port 2222

This is a "proxy" to "localhost" Port 22 (SSH/SCP) on the remote computer. Communications will take place with the MQTT server acting as a "tunnel".



Notes:

* As mentioned above I have tested it with the JAR complied with Oracle Java 8, but it should would with any Java JDK.

* I am using my own MQTT server hosted on a VPS. Have not tested it on any of the "free" MQTT servers out there. It does generate a lot of traffic so there may be issues with the free ones. If anyone has success with the free servers I would love to know.

* I set the MQTT QOS to 0 which appears to be the fastest. This application needs a responsive mqtt server.

* The MQTT topic is the "root" topic. If you look at the code you will see that sub-topics are appended to it for each end of the connection. If you have multiple remote computers, you can identify each one by this root topic. In my example "ABC" identifies the remote computer.

* This version only allows ONE connection at a time. So for example, you cannot open a PUTTY and WINSCP session at the same time. Should be easy to overcome, but I did not have a need for it so I did not pursue it. Maybe someone else can add that feature.

* The program is not limited to just remote access. You can use it as a proxy tunnel for any two TCP connections behind firewalls without having to open inbound ports.

Hope you find it useful.

Enjoy!


Update - 2024-06-24
V1.1 - Revised ConnectToServer() so that it returns after making the connection. This reduces the possibility of multiple instances of the function running when connections are made.
Fixed "incoming packet was garbled" error when SSH connection is first made.
 

Attachments

  • MQTTProxy.zip
    39.4 KB · Views: 111
Last edited:

pixet

Member
Licensed User
Longtime User
I tried to implement the tunnel via MQTT as you indicated, the connection with SSH is established in the logs it tells me that it is connected but the ssh session is not established and after about 30 seconds Putty responds with timeout, and suggests if I want to try again, but even trying again it fails.
I tried to compile with jdk11, jdk14, jdk19 but from all versions I get the same result. I also tried to compile a package (".exe") in this case I get an error from java that does not find the network module.
On the remote linux server I launch the application:
"sudo java -jar mqttproxy.jar"
while on the local PC it is running from the B4J IDE, to which I connect with Putty in a Windows environment.
Do you have any suggestions to solve it?
 

aminoacid

Active Member
Licensed User
Longtime User
I assume that you are using the sample mqttproxy.cfg file mentioned above with the same MQTT topic, correct? That is important.

Are you sure that you are connecting to the MQTT server okay? Both at the PC and the Linux server? And packets are being sent and received? You may want to separately test this out at both ends just to make sure.
(what mqtt server are you using?)

In PUTTY you are connecting to localhost Port 2222, correct?

Have you tried Oracle Java8? That's the JDK/JRE I use, and I have been using this setup quite a lot and it works like a charm! But again, the code and concept are so simple, I don't see why it would not work with any Java version.

Attach the two cfg files (PC and server) and let me take a look at it.
 

pixet

Member
Licensed User
Longtime User
Yes,
I checked the configurations and they seem to be correct with my server parameters. I am using Mosquitto server on Debian 11.
I checked better using an MQTT monitor and this is the result below.
I open an MQTT monitor with "Subscribe: with wildcard "#" on the MQTT server I see the connection attempt but I also see a

message/payload "mqttproxy/ABC/fromclient : Invalid SSH identification string"

after a few attempts it connected with WinSCP, then nothing more.

On the Linux server side I am using OpenJDK runtime 11.0.24, apparently it seems a server-side buffer problem.
I managed to log these messages on the Linux terminal.


This is my terminal on Debian 11 server:
pi@srv-test:~/javapp $ sudo java -jar mqttproxy.jar
TCP Proxy Server via MQTT Tunnel V1.1
Program Mode: remote
Connecting to MQTT mqttserver: 192.168.1.14:1883
localhost - Connection Closed
Trying to connect to: localhost
Successful Connect to: localhost:22
Mqtt is connected
localhost - Connection Closed
Trying to connect to: localhost
Successful Connect to: localhost:22
java.net.SocketException: Connection reset
        at java.base/java.net.SocketInputStream.read(SocketInputStream.java:186)
        at java.base/java.net.SocketInputStream.read(SocketInputStream.java:140)
        at java.base/java.net.SocketInputStream.read(SocketInputStream.java:126)
        at anywheresoftware.b4a.randomaccessfile.AsyncStreams$AIN.run(AsyncStreams.java:232)
        at java.base/java.lang.Thread.run(Thread.java:829)
localhost - AST1-TCP Error
 sudo java -jar mqttproxy.jar
TCP Proxy Server via MQTT Tunnel V1.1
Program Mode: remote
Connecting to MQTT mqttserver: 192.168.1.14:1883
localhost - Connection Closed
Trying to connect to: localhost
Successful Connect to: localhost:22
Mqtt is connected
localhost - Connection Closed
Trying to connect to: localhost
Successful Connect to: localhost:22
java.net.SocketException: Connection reset
        at java.base/java.net.SocketInputStream.read(SocketInputStream.java:186)
        at java.base/java.net.SocketInputStream.read(SocketInputStream.java:140)
        at java.base/java.net.SocketInputStream.read(SocketInputStream.java:126)
        at anywheresoftware.b4a.randomaccessfile.AsyncStreams$AIN.run(AsyncStreams.java:232)
        at java.base/java.lang.Thread.run(Thread.java:829)
localhost - AST1-TCP Error
 

aminoacid

Active Member
Licensed User
Longtime User
Hmm... can you send me both the CFG files ?

On the server side, from your log, it looks like the program crashes and then you restart it. Is this correct?
 

pixet

Member
Licensed User
Longtime User
No, on the linux server the process is started as a daemon, as you can see from the log, it seems that when there is an error it does not crash
but handles the error by itself.
 

Attachments

  • my-cfg-MQTTProxy.zip
    523 bytes · Views: 50

aminoacid

Active Member
Licensed User
Longtime User
I see the issue (at least one issue):

In the remote CFG file, you should have:

program_mode=remote

You have "local" in both files.
 

aminoacid

Active Member
Licensed User
Longtime User
No, on the linux server the process is started as a daemon, as you can see from the log, it seems that when there is an error it does not crash
but handles the error by itself.

For now, I suggest you just run it at the command line and not as a service. That way we can see if and why it crashes. It should not do so.


[Ignore my previous reply. I see that you updated the ZIP file and the CFG files seem to be correct now.]
 

pixet

Member
Licensed User
Longtime User
I updated the CFG files because they were not the ones I use in the app.
Look at the ones that are online.
The MQTT server is behind a firewall. The public port is not standard, but this does not create problems, other services that I have use this port without any problem. The load on the broker does not exceed 1%
With winSCP it connects 1 time out of 2, while with Putty after many attempts. I also tried to compile the jar with jdk8.x.x but nothing changes.
 

pixet

Member
Licensed User
Longtime User
Yes,
I see the files from the remote, I tried to upload and also download and it works perfectly.
 

pixet

Member
Licensed User
Longtime User
How to clear the buffer (AsyncStreams) every time you close your session.

It seems to remain dirty, and when reopening a new session it finds characters that introduce the error.
Whether the buffer can be flushed()ed.
 

aminoacid

Active Member
Licensed User
Longtime User
What's strange is that if it works with WinSCP, it should also work with PUTTY. But just to be clear, are you saying that after many attempts, PUTTY will connect? Is that right? Once it connects, does everything work? If that's the case then the issue is definitely with the data transport taking place in MQTT.
 

aminoacid

Active Member
Licensed User
Longtime User
How to clear the buffer (AsyncStreams) every time you close your session.

It seems to remain dirty, and when reopening a new session it finds characters that introduce the error.
Whether the buffer can be flushed()ed.

The buffer should be cleared automatically when the session is closed. I don't have the same problem you are having - for me it connects immediately almost 100% of the time.
 

aminoacid

Active Member
Licensed User
Longtime User
I'm glad it worked with the "mosquitto.org" test server. You must have had some latency with your MQTT server that was causing a slight delay that was enough to disrupt the SSH handshake. It could have been the port forwarded IP address that you were using at the remote end - I have experienced that issue before as some routers are slow enough to cause it.

On the other hand, I would assume that "mosquitto.org" has a lot of traffic and one of my concerns was that it would not work properly with a public "free" server because of that. But since it works with their test server, it is apparent that MQTT is robust enough to handle it.
 
Last edited:
Top