150 lines
5 KiB
Markdown
150 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]]
|