Name based SSH proxy

Kasper Dupont kasperd at kdxdx.23.may.2015.kasperd.net
Sat May 23 22:42:06 AEST 2015


I am working on a proxy which can be hosted on a single
IP address and dispatch requests to different backends
depending on which hostname the client used to connect to
this IP address.

Currently such a proxy can be build to support HTTP, HTTPS,
SMTP, and DNS. However SSH support is impossible due to
the ssh client not sending the information such a proxy
would need.

I am not the first to want such a proxy:
http://serverfault.com/q/34552/214507
However my searches only found people talking about it,
and nobody doing anything about it.

I have attached a tiny patch for the openssh-client, which
I believe does everything openssh would need to do in order
to support this kind of proxies.

What are your thoughts on the attached patch?

Rationale behind the design of the patch:
A name based SSH proxy will need to accept connections from
clients and based on data send by the client choose a
backend server to connect to.

The proxy will not be able to forward the version
identification from the backend to the client until after
it has connected to the backend. Thus the proxy will need
to extract the hostname from the data send by the client
before any version identification has been send in the
other direction.

This leaves the version identification send from client
to server as the only place such a proxy could possibly
extract the hostname from. Thus the patch would have to
extend the format of the version identification to include
a hostname.

The version identification contains a comments field
which at the moment is a free form field send by client
and ignored by server. The intended purpose of this field
is to aid in debugging, thus it just needed to be huamn
redable.

Replacing the comments field with JSON formatted data will
allow it to serve both purposes. I picked JSON because it
is extensible and very simple.

The change amounts to modifying two lines of code in
send_client_banner and passing the hostname as function
argument where it is now necessary. No server side changes
are needed.

-- 
Kasper Dupont -- Rigtige mænd skriver deres egne backupprogrammer
#define _(_)"d.%.4s%."_"2s" /* This is my email address */
char*_="@2kaspner"_()"%03"_("4s%.")"t\n";printf(_+11,_+6,_,12,_+2,_+7,_+6);
-------------- next part --------------
diff -up openssh-6.6p1/roaming_client.c.original openssh-6.6p1/roaming_client.c
--- openssh-6.6p1/roaming_client.c.original	2014-01-10 00:58:53.000000000 +0100
+++ openssh-6.6p1/roaming_client.c	2015-05-23 12:58:12.193450191 +0200
@@ -147,7 +147,7 @@ roaming_resume(void)
 	resume_in_progress = 1;
 
 	/* Exchange banners */
-	ssh_exchange_identification(timeout_ms);
+	ssh_exchange_identification(timeout_ms, "");
 	packet_set_nonblocking();
 
 	/* Send a kexinit message with resume at appgate.com as only kex algo */
diff -up openssh-6.6p1/sshconnect.c.original openssh-6.6p1/sshconnect.c
--- openssh-6.6p1/sshconnect.c.original	2015-05-23 11:56:55.235217137 +0200
+++ openssh-6.6p1/sshconnect.c	2015-05-23 13:43:41.426983727 +0200
@@ -515,12 +515,13 @@ ssh_connect(const char *host, struct add
 }
 
 static void
-send_client_banner(int connection_out, int minor1)
+send_client_banner(int connection_out, int minor1, const char *host)
 {
 	/* Send our own protocol version identification. */
 	if (compat20) {
-		xasprintf(&client_version_string, "SSH-%d.%d-%.100s\r\n",
-		    PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, SSH_VERSION);
+		xasprintf(&client_version_string,
+		    "SSH-%d.%d-%.100s {\"SNI\": \"%.133s\"}\r\n",
+		    PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, SSH_VERSION, host);
 	} else {
 		xasprintf(&client_version_string, "SSH-%d.%d-%.100s\n",
 		    PROTOCOL_MAJOR_1, minor1, SSH_VERSION);
@@ -537,7 +538,7 @@ send_client_banner(int connection_out, i
  * identification string.
  */
 void
-ssh_exchange_identification(int timeout_ms)
+ssh_exchange_identification(int timeout_ms, const char *host)
 {
 	char buf[256], remote_version[256];	/* must be same size! */
 	int remote_major, remote_minor, mismatch;
@@ -559,7 +560,7 @@ ssh_exchange_identification(int timeout_
 	 */
 	if (options.protocol == SSH_PROTO_2) {
 		enable_compat20();
-		send_client_banner(connection_out, 0);
+		send_client_banner(connection_out, 0, host);
 		client_banner_sent = 1;
 	}
 
@@ -672,7 +673,7 @@ ssh_exchange_identification(int timeout_
 		logit("Server version \"%.100s\" uses unsafe RSA signature "
 		    "scheme; disabling use of RSA keys", remote_version);
 	if (!client_banner_sent)
-		send_client_banner(connection_out, minor1);
+		send_client_banner(connection_out, minor1, host);
 	chop(server_version_string);
 }
 
@@ -1286,7 +1287,7 @@ ssh_login(Sensitive *sensitive, const ch
 	lowercase(host);
 
 	/* Exchange protocol version identification strings with the server. */
-	ssh_exchange_identification(timeout_ms);
+	ssh_exchange_identification(timeout_ms, host);
 
 	/* Put the connection into non-blocking mode. */
 	packet_set_nonblocking();
diff -up openssh-6.6p1/sshconnect.h.original openssh-6.6p1/sshconnect.h
--- openssh-6.6p1/sshconnect.h.original	2013-10-17 02:47:24.000000000 +0200
+++ openssh-6.6p1/sshconnect.h	2015-05-23 12:57:16.129172189 +0200
@@ -39,7 +39,7 @@ void	 ssh_kill_proxy_command(void);
 void	 ssh_login(Sensitive *, const char *, struct sockaddr *, u_short,
     struct passwd *, int);
 
-void	 ssh_exchange_identification(int);
+void	 ssh_exchange_identification(int, const char *);
 
 int	 verify_host_key(char *, struct sockaddr *, Key *);
 


More information about the openssh-unix-dev mailing list