I've attached a patch to OpenSSH 3.0.2p1 that will allow the client
side of local port forwarding to be bound to a single address. For
my purposes, binding to 127.0.0.1 or (via GatewayPorts) all addresses
would not work. I overloaded the "-b local_host" option so that
it's address will be used when "-L port:remote_host:remote_port"
is also specified.
Today is the first day I played with OpenSSH 3.x or port forwarding.
Therefore I am not sure if my use of -b with -L will break any
existing use cases. If it does, please speak up.
In case you are curious, here is the scenario I have...
On FreeBSD there is the idea of a jail. A jail takes the
old idea of chroot and extends it quite a bit. Aside from
limiting the privileges of root in a jail, each jail can
also have its own IP address. Therefore, one host can easily
have two instances of (for example) Apache, each bound to
port 80. (Yes, this can be done simply by multi-homing the
host, but there are other reasons for a jail.)
On one host on my LAN, I have two jails. Call them jail_1
(127.74.0.1) and jail_2 (127.74.0.2). I want to provide
a remote friend access to those jails even though they are
not routable addresses for him.
With my patch, my friend can add two IP aliases on a host
on his LAN. Let's call them mirror_1 (127.77.0.1) and
mirror_2 (127.77.0.2). If "remote" is in root's ~/.ssh/config
file pointing to my system, then the following two commands
will provide two unique tunnels linking each mirror with
its corresponding jail:
root# ssh -b mirror_1 -N -f -L 80:jail_1:80 remote
root# ssh -b mirror_2 -N -f -L 80:jail_2:80 remote
Either the jails or the mirrors could use 10.* instead of
127.* if it is safe to allow general access on the LAN.
Using the use case of Apache, my friend can then use
http://mirror_1:80/ and http://mirror_2:80/ and reach my
jails transparently. If we both then aliased mirror_1 and
jail_1 to be virtual_1, we could even use the same host
names.
The commands below show my local test case.
:
: Set up two aliases, one representing a FreeBSD jail and the
: other a remote mirror.
:
root# grep 127.7 /etc/hosts
127.74.0.1 localjail_1
127.77.0.1 localmirror_1
root# ifconfig lo0 inet localjail_1 netmask 0xffffffff alias
root# ifconfig lo0 inet localmirror_1 netmask 0xffffffff alias
:
: Make sure (ssh remote) is a login into localhost.
:
user% tail -4 ~/.ssh/config
Host remote
Protocol 2
IdentityFile /home/scott/.ssh/id_dsa
HostName 127.0.0.1
:
: Start the tunnel so that it is locally bound to the host
: address of localmirror_1. The 'remote' end happens to be local
: for this example, but it easily could be half way around the
: world.
:
user% ssh -b localmirror_1 -N -f -L 1111:localjail_1:2222 remote
:
: Run a test script that listens on a host:port and prints out
: what it sees. Note the last line below was actually printed
: after portsend was called.
:
user% portlisten.pl -a localjail_1 -p 2222 -w 600 &
[Server ./portlisten.pl accepting clients on port 2222.]
[Connect from 127.74.0.1:3521]
read "in the mirror, out the jail"
:
: Now send a test message in one port of the mirror and see it
: come out the other port in the jail.
:
user% ~/WIP-perl/portsend.pl -a localmirror_1 -p 1111 in the mirror, out the
jail
:
: Verify that the client side of the tunnel is bound to a single
: address.
:
root# netstat -a
Active Internet connections (including servers)
Proto Recv-Q Send-Q Local Address Foreign Address (state)
tcp4 0 0 localjail_1.3521 localjail_1.2222 TIME_WAIT
tcp4 0 0 localmirror_1.3520 localmirror_1.1111 TIME_WAIT
tcp4 0 0 localjail_1.2222 *.* LISTEN
tcp4 0 0 localmirror_1.1111 *.* LISTEN
tcp4 0 0 localhost.ssh localmirror_1.3517 ESTABLISHED
tcp4 0 0 localmirror_1.3517 localhost.ssh ESTABLISHED
...
So folks, what do I need to do to have this feature added to the
mainline of OpenSSH?
Thanks,
Scott
diff -ru openssh-3.0.2p1/channels.c openssh-3.0.2p1-NewFeature/channels.c
--- openssh-3.0.2p1/channels.c Thu Oct 11 20:35:05 2001
+++ openssh-3.0.2p1-NewFeature/channels.c Sat Jan 5 15:36:10 2002
@@ -2057,11 +2057,11 @@
* channel to host:port from remote side.
*/
int
-channel_request_local_forwarding(u_short listen_port, const char
*host_to_connect,
- u_short port_to_connect, int gateway_ports)
+channel_request_local_forwarding(const char *listen_host, u_short listen_port,
+ const char *host_to_connect, u_short port_to_connect, int gateway_ports)
{
return channel_request_forwarding(
- NULL, listen_port,
+ listen_host, listen_port,
host_to_connect, port_to_connect,
gateway_ports, /*remote_fwd*/ 0);
}
@@ -2080,7 +2080,7 @@
int success, sock, on = 1, type;
struct addrinfo hints, *ai, *aitop;
char ntop[NI_MAXHOST], strport[NI_MAXSERV];
- const char *host;
+ const char *host, *listen_host = NULL;
struct linger linger;
success = 0;
@@ -2089,6 +2089,7 @@
host = listen_address;
type = SSH_CHANNEL_RPORT_LISTENER;
} else {
+ listen_host = listen_address;
host = host_to_connect;
type = SSH_CHANNEL_PORT_LISTENER;
}
@@ -2108,7 +2109,7 @@
hints.ai_flags = gateway_ports ? AI_PASSIVE : 0;
hints.ai_socktype = SOCK_STREAM;
snprintf(strport, sizeof strport, "%d", listen_port);
- if (getaddrinfo(NULL, strport, &hints, &aitop) != 0)
+ if (getaddrinfo(listen_host, strport, &hints, &aitop) != 0)
packet_disconnect("getaddrinfo: fatal error");
for (ai = aitop; ai; ai = ai->ai_next) {
@@ -2259,7 +2260,7 @@
port);
#endif
/* Initiate forwarding */
- channel_request_local_forwarding(port, hostname, host_port, gateway_ports);
+ channel_request_local_forwarding(NULL, port, hostname, host_port,
gateway_ports);
/* Free the argument string. */
xfree(hostname);
diff -ru openssh-3.0.2p1/channels.h openssh-3.0.2p1-NewFeature/channels.h
--- openssh-3.0.2p1/channels.h Sun Nov 11 18:04:55 2001
+++ openssh-3.0.2p1-NewFeature/channels.h Sat Jan 5 14:34:05 2002
@@ -189,7 +189,7 @@
int channel_connect_to(const char *, u_short);
int channel_connect_by_listen_address(u_short);
void channel_request_remote_forwarding(u_short, const char *, u_short);
-int channel_request_local_forwarding(u_short, const char *, u_short, int);
+int channel_request_local_forwarding(const char *, u_short, const char *,
u_short, int);
int
channel_request_forwarding(const char *, u_short, const char *, u_short, int,
int);
diff -ru openssh-3.0.2p1/ssh.c openssh-3.0.2p1-NewFeature/ssh.c
--- openssh-3.0.2p1/ssh.c Sun Nov 11 17:52:04 2001
+++ openssh-3.0.2p1-NewFeature/ssh.c Sat Jan 5 14:37:50 2002
@@ -830,14 +830,27 @@
{
int success = 0;
int i;
+ char *local_host;
+
+ if (options.bind_address == NULL) {
+ if (options.gateway_ports == 0) {
+ local_host = "localhost";
+ } else {
+ local_host = "{ANY}";
+ }
+ } else {
+ local_host = options.bind_address;
+ }
/* Initiate local TCP/IP port forwardings. */
for (i = 0; i < options.num_local_forwards; i++) {
- debug("Connections to local port %d forwarded to remote address
%.200s:%d",
+ debug("Connections to local port %.200s:%d forwarded to remote address
%.200s:%d",
+ local_host,
options.local_forwards[i].port,
options.local_forwards[i].host,
options.local_forwards[i].host_port);
success += channel_request_local_forwarding(
+ options.bind_address,
options.local_forwards[i].port,
options.local_forwards[i].host,
options.local_forwards[i].host_port,