How to forward SSH port example / Just-In-Time Feature Request

I use boring proxy for a lot of self-hosted services and really like it. As an admin I want to use boring proxy to create temporary tunnels for SSH access to the machine where other services are hosted.

I tried all permutations but could not figure out how to forward the SSH port.

My guess is as follows:

Tunnel Port: 2244
Client Name: myclient
Client Address:
Client Port: 22
Allow External TCP: [x]
Password Protect: [ ]
TLS Termination: Client

chmod 0600 ~/Downloads/id_rsa
ssh -vvv -i ~/Downloads/id_rsa -p 2244

However, this does not work.

Could anyone please provide an example of how to connect to a SSH server? Thanks!

I know, that feature requests would be best placed on GitHub but it would be awesome to have separate tokens that would allow access to the boring proxy ui to just enable or disable a tunnel. Maybe even time based for one hour. That way, boring proxy could emulate Just-In-Time access as it gets used by cloud providers to tighten security.

Hey @reiro. boringproxy doesn’t currently support reverse-SSH connections. The main reason for this is because all routing is based off the host name from the TLS client hello, ie only TLS-based connections are supported. Since SSH uses it’s own encryption stack it won’t work. It might be possible to add this functionality, but it’s not something I need personally so probably wouldn’t happen for a while. I’d recommend checking some of the other tunneling options from my list:

You may be able to use one of those instead of boringproxy, or even alongside it.

It’s fine to ask for features on the forums. As far as disabling tunnels go, can you provide a real-world use case for this? Personally I just delete/add tunnels as necessary.

Thanks! That explains a lot :smiley:
As a workaround I could use something like Shellinabox.

Regarding the disabling of tunnels:
The main real-world usecase would be Just-In-Time access:

Just-in-Time (JIT) access is a fundamental security practice where the privilege granted to access applications or systems is limited to predetermined periods of time, on an as-needed basis. This helps to minimize the risk of standing privileges that attackers or malicious insiders can readily exploit.


This could be interesting with privilege separation:

  1. A tunnel gets prepared by an admin that has the proper permissions and knows the technical details.
  2. A user could use the same portal with their own access token that only allows to enable/disable a prepared tunnel.

It would also be beneficial for automating tunnels. A REST call could turn on/off an already existing tunnel.

Also when something is not working, this could be a quick fix for users to restart a tunnel without admin intervention.

1 Like

So I actually got to looking into reverse SSH a bit, and it might not be that hard to add to boringproxy. Adding raw TCP tunnels (including SSH) would be a useful feature. I’m going to see if I can hack this together soon and I’ll report back here how that goes.

As far as JIT access, the existence of such a feature makes sense, but I don’t think it’s a good fit for boringproxy, at least for now. It seems more useful in an enterprise setting, whereas boringproxy is focused on self-hosting.

That said, you might be able to implement something like this yourself by making a small webserver that simply accepts two requests: /on and /off (perhaps with a minimal HTML UI). /on would use the boringproxy API to create a specific tunnel. /off would delete it. You could then run this server behind boringproxy with a username and password. Then anyone with the credentials could access those functions, but that would be the only privileges they have. It’s hacky but should work.

Well that didn’t take very long. Turns out boringproxy already supports reverse SSH if you use the correct incantation. Definitely need to streamline this a bit and document it, but I just tested it and it worked.

Say your boringproxy server is running at and you want to be able to do ssh -p 8022 in order to ssh into a specific client.

Here’s the steps:

Create a tunnel with the following values:

Tunnel Port: 8022
Client Name: whatever client you want to ssh into
TLS Termination: Server
Client Port: 22 or whatever the client machine is running sshd on
Allow External: TCP true

That should do it. You’ll need to make sure port 8022 is open on the server. You can use whatever port you want, just make sure it’s open and matches what you use when you try to connect ssh.

Note: The key was setting TLS Termination to Server, because that opens up the code path for the client to support raw TCP connections. I need to change this to be a setting on the tunnel itself, so it will work with server or client termination. Or maybe add an option to disable TLS altogether for SSH tunnels since the domain and TLS will never be used.

EDIT: For anyone looking at this later, make sure you also have GatewayPorts clientspecified in your sshd_config file on the boringproxy server machine.

Thanks. After opening the port in iptables I could use the tunneled SSH connection.

You are right that your design is quite open and using the API should be quite easy to adapt it for special use cases.

1 Like

How did you manage to get the tunnelled SSH connection working? I added a new tunnel as @anders suggested and it is listening on port 8022. However, it is listening on localhost ( and not on

Did you make another change to allow routing of external traffic to localhost? As far as I know allowing the port through the firewall will only open the port if its already listening on local address Its possible to route 0.0.0. traffic to, but I don’t want to allow that system wide; or are all your BoringProxy ports listening on

Do you have GatewayPorts set correctly in your sshd_config on the boringproxy server machine? The instructions are buried at the bottom of the installation page:

I really need to make them more visible. Really it should be a note in the UI next to “Allow External TCP”. That option won’t work without it.

You were right. I did miss this in the installation instructions. It’s now working perfectly.