www.nico.schottelius.org/blog/openssh-6.2-add-callback-functionality-using-dynamic-remote-port-forwarding.mdwn
Nico Schottelius ed66866b47 update openssh url
Signed-off-by: Nico Schottelius <nico@wurzel.schottelius.org>
2016-03-18 09:05:42 +01:00

149 lines
5 KiB
Markdown

[[!meta title="OpenSSH 6.2: Add callback functionality (using dynamic remote port forwarding)"]]
## Introduction
This article describes a patch to OpenSSH 6.2 that I wrote to enable
**ssh callback** using dynamic ports. This is rather useful to have
for various types of software, including [[cdist|software/cdist]]
and [[ccollect|software/ccollect]].
## Background
Assume you have two hosts:
* A **target host**
* A **control host**
(backup server in case of [[ccollect|software/ccollect]],
configuration server in case of [[cdist|software/cdist]])
Assume further that the target host can directly reach the control
host, but the control host cannot connect to the target host directly.
For instance, it's the case,
when the target host is hidden by NAT
or protected by a firewall.
## Approaches
### Create a tunnel from the target host to the control host
A very simple solution is to create a static tunnel
from the target host to the control host, which allows
the control host to connect back:
targethost% ssh -R 42523:localhost:22 controlhost
controlhost% ssh -p 42523 localhost
The drawback is that the remote port needs to be defined
beforehand and both sides needs to know about it.
This is especially nasty, if you have a lot of
target hosts that need to be backed up / configured.
### Use dynamic port allocation
The [OpenSSH](http://www.openssh.com/) developers seem to have
spotted this problem and include an option to use a random
free port: If port 0 is chosen as the remote
forwarding port, the port is dynamically chosen by the
ssh server, which in our case runs on the controlhost.
Even better, the port information is also displayed on stdout:
targethost% ssh -R 0:localhost:22 controlhost
Allocated port 59818 for remote forward to localhost:22
The problem here is: The shell on the remote side does not
know which port was chosen, as it is only printed on stdout
by the **ssh client**.
### Expose remote forwarding ports
[[This patch|openssh-6.2p1-expose-remote-port-forwarding.diff]]
against OpenSSH 6.2p1 creates a new environment variable
***SSH_REMOTE_FORWARDING_PORTS***, which contains all ports
that are used for remote forwarding:
targethost % ssh -R 1234:localhost:22 controlhost
controlhost % echo $SSH_REMOTE_FORWARDING_PORTS
1234
As this works for all remotely forwarded ports, this can
also be used for dynamic port assignments:
targethost % ssh -R 0:localhost:22 controlhost
controlhost % echo $SSH_REMOTE_FORWARDING_PORTS
54294
If more than one port forwarding definition is given, they are listed
space separated:
targethost % ssh -R 0:localhost:22 -R 1234:localhost:22 controlhost
controlhost % echo $SSH_REMOTE_FORWARDING_PORTS
59056 1234
### Use socat
Adapted from a proposal of
[Philipp Marek](http://lists.mindrot.org/pipermail/openssh-unix-dev/2013-May/031350.html).
A different approach is using socat like this:
targethost% socat TCP:localhost:22,retry=forever "EXEC:ssh controlhost"
controlhost% cat .ssh/authorized_keys
command="~/myscript 1224" ssh-rsa ...
controlhost% cat ~/myscript
socat - TCP-LISTEN:1234 &
ssh -p 1234 ...
The drawback with this solution is to use pre-defined ports
as well as socat on the targethost exiting after the
first connection has been closed. It works for a single shot
callback, though.
### Use ProxyCommand with stdin/stdout
As proposed by
[Darren Tucker](http://lists.mindrot.org/pipermail/openssh-unix-dev/2013-May/031353.html) (some parts are copied & pasted from his original mail):
# Create fifo/named pipe for sshd
targethost% mkfifo sshd_in sshd_out
# Start ssh on the controlhost from the targethost
# and create a control socket. Use ProxyCommand=-
# to make use of stdin/stdout for proxying packets through.
targethost$ ssh <sshd_in >sshd_out -T -y controlhost "ssh -y -N -T -MS/tmp/ctl -oProxyCommand=- targethost" &
# Start a new sshd on the client, which listens on the newly
# created fifos
targethost$ /usr/sbin/sshd -i -f < sshd_in > sshd_out
# on the server, use the control socket to talk to the
# sshd running on the targethost
controlhost% ssh -S /tmp/ctl targethost
Drawback: Quite complicated setup required, thus probably error prone on day-to-day use.
Advantage: Very beautiful use of FIFOs, ssh, controlsockets and proxycommand. A setup
every geek must love.
## Limitations
The given patch has some known limitations:
* The destination of the remote forwarding is not shown.
Debugging the ssh server shows that this information was present
in ssh1, but is absent in ssh2.
* The number of listed ports is limited by the buffer size of 256 characters
* Includes only remote port forwardings specified at startup, not the ones added later
## Future
The patch
[has been submitted](http://lists.mindrot.org/pipermail/openssh-unix-dev/2013-May/031337.html)
to the
[openssh-unix-dev mailinglist](https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev) for discussion.
[[!tag net unix]]