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 *);
On 23/05/15 14.42, Kasper Dupont wrote:> 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.I have put a copy of the patch here: http://share.kasperd.net/openssh-6.6p1-sni.patch And an example of how a proxy using this feature could be implemented here: http://share.kasperd.net/ssh-sni.py -- 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);
On Sat, 23 May 2015, Kasper Dupont wrote:> 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?I'm not sure it should be part of the banner exchange, though there is no other trivial way to do it and maintain backwards compatibility. I don't much like it because it reveals host identity information in the clear. A better way would be to exchange this after the connection has been keyed, but that would require extensive changes to the key exchange protocol. I don't really want to implement a half-measure in OpenSSH... -d
On 25/05/15 09.51, Damien Miller wrote:> I'm not sure it should be part of the banner exchange, though there is > no other trivial way to do it and maintain backwards compatibility.Even if backwards compatibility wasn't a requirement, I don't see any better way it could be done.> I don't much like it because it reveals host identity information > in the clear.So does the DNS lookup performed before the TCP connection is being established. So that can hardly be considered a secret.> > A better way would be to exchange this after the connection has > been keyed, but that would require extensive changes to the key > exchange protocol.How would that work? The proxy doesn't hold the server key. The proxy doesn't even know which server holds the key.> > I don't really want to implement a half-measure in OpenSSH...All the proxy needs to know is the hostname which was previously send in clear to multiple DNS servers. And the only concern you have brought up is that you don't want this to be send in clear. I need a little bit of help to understand what your concern is here. I'll try to explain my usage scenario in a bit more detail. I have a number of servers each running IPv6 only. Since some clients will only have access to IPv4, I have deployed a proxy on a dual stack host. But the proxy only has a single IPv4 address. Clients connect to this address, and the proxy performs a DNS lookup to find the IPv6 address which this client wants to connect to. Currently this works for HTTP, HTTPS, SMTP, and DNS. -- 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);
Kasper Dupont wrote:> +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);You propose introducing JSON injection. Really? Aside from all the other valid criticism, JSON is a bad fit. //Peter
On 26/05/15 01.50, Peter Stuge wrote:> Kasper Dupont wrote: > > +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); > > You propose introducing JSON injection. Really?Felt like a better idea than XML.> > Aside from all the other valid criticism, JSON is a bad fit.Frankly. I don't care about the format. JSON was the best I could come up with given that it is supposed to be human readable and it is a good idea to use an established standard which is extensible. If you can suggest a better format, I'd happily change it. -- 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);