ssh in https
The wifi network at BSDcan, really the UOttawa network, blocks a bunch of ports. This makes it difficult to connect to outside machines using “exotic” protocols, basically anything except http or https. There are many ways to resolve this, here’s what I did.
Pick a port that’s allowed, such as 443. Unfortunately I’m already using port 443 for some very serious https business. But it’s possible to run two protocols over the same port with some smarts in the endpoint.
ssh connections start by having both client and server exchange version strings. You can observe this by running netcat.
> nc localhost 22
SSH-2.0-OpenSSH_8.0
Or start a server and observe the client, ssh -p 20002 localhost
.
> nc -l localhost 20002
SSH-2.0-OpenSSH_8.0
This is convenient because many other protocols typically begin with only one party. You can’t tell you’re talking to a web server, for instance, because the server doesn’t respond until it receives a request. The upside, for ssh, is that you can probably inject it into almost anything, given some control over the software.
In my case, I’m running a web server. My request loop used to look like this:
_, err := rw.Reader.Peek(1)
if err != nil {
return
}
req, err := http.ReadRequest(rw.Reader)
It’s reading one byte to check for timeouts and disconnects. If we read a little more, we can spot ssh connections.
sneak, err := rw.Reader.Peek(4)
if err != nil {
return
}
if conn.sshproxy && string(sneak) == "SSH-" {
sshconn, err := net.Dial("tcp", "127.0.0.1:22")
sneak, _ = rw.Reader.Peek(rw.Reader.Buffered())
sshconn.Write(sneak)
c := make(chan net.Conn)
go copyuntildone(netconn, sshconn, c)
go copyuntildone(sshconn, netconn, c)
closewhendone(c)
return
}
req, err := http.ReadRequest(rw.Reader)
From there, it’s a simple matter of copying data from one socket to the other.
On the client side, this requires a simple proxy command to connect via TLS first.
ProxyCommand nc -c %h 443
One may also consider something like sslh.