Add a TLS layer to your Gopher server

NILHi,

In this article I will explain how to setup a gopher server supporting

TLS. Gopher TLS support is not "official" as there is currently no RFC

to define it. It has been recently chose by the community how to make

it work, while keeping compatibility with old servers / clients.

The way to do it is really simple.

Client A tries to connects to Server B, Client A tries TLS handshake,

if Server B answers correctly to the TLS handshakes, then Client A

sends the gopher request and Server B answers the gopher requests. If

Server B doesn't understand the TLS handshakes, then it will probably

output a regular gopher page, then this is throwed and Client A

retries the connection using plaintext gopher and Server B answers the

gopher request.

This is easy to achieve because gopher protocol doesn't require the

server to send anything to the client before the client sends its

request.

The way to add the TLS layer and the dispatching can be achieved using

the latter is in OpenBSD base system so I will use it. Thanks parazyd

for sharing about sslh for this use case.

depending on what it receives, it will try to guess the protocol used

by the client and send it to the according backend. It's first purpose

was to make ssh available on port 443 while still having https daemon

working on that server.

Here is a schema of the setup

+→ relayd for TLS + forwarding

↑ ↓

↑ tls? ↓

client -> sslh TCP 70 → + ↓

↓ not tls ↓

↓ ↓

+→ → → → → → → gopher daemon on localhost

This method allows to wrap any server to make it TLS compatible. The

best case would be to have TLS compatibles servers which do all the

work without requiring sslh and something to add the TLS. But it's

currently a way to show TLS for gopher is real.

Relayd

The relayd(1) part is easy, you first need a x509 certificate for the

TLS part, I will not explain here how to get one, there are already

plenty of how-to and one can use let's encrypt with acme-client(1) to

get one on OpenBSD.

We will write our configuration in **/etc/relayd.conf**

log connection

relay "gopher" {

listen on 127.0.0.1 port 7000 tls

forward to 127.0.0.1 port 7070

}

In this example, relayd listens on port 7000 and our gopher daemon

listens on port 7070. According to relayd.conf(5), relayd will look

for the certificate at the following places:

`/etc/ssl/private/$LISTEN_ADDRESS:$PORT.key` and

`/etc/ssl/$LISTEN_ADDRESS:$PORT.crt`, with the current example you

will need the files: /etc/ssl/private/127.0.0.1:7000.key and

/etc/ssl/127.0.0.1:7000.crt

relayd can be enabled and started using rcctl:

# rcctl enable relayd

# rcctl start relayd

Gopher daemon

Choose your favorite gopher daemon, I recommend geomyidae but any

other valid daemon will work, just make it listening on the correct

address and port combination.

# pkg_add geomyidae

# rcctl enable geomyidae

# rcctl set geomyidae flags -p 7070

# rcctl start geomyidae

SSLH

We will use sslh_fork (but sslh_select would be valid too, they have

differents pros/cons). The `--tls` parameters tells where to forward a

TLS connection while `--ssh` will forward to the gopher daemon. This

is so because the protocol ssh is already configured within sslh and

acts exactly like a gopher daemon: the client doesn't expect the

server to be the first sending data.

# pkg_add sslh

# rcctl enable sslh_fork

# rcctl set sslh_fork flags --tls 127.0.0.1:7000 --ssh 127.0.0.1:7070 -p 0.0.0.0:70

# rcctl start sslh_fork

Client

You can easily test if this works using openssl to connect by hand to the port 70

$ openssl s_client -connect 127.0.0.1:7000

You should see a lot of output, which is the TLS handshake, then you

can send a gopher request like "/" and you should get a result. Using

telnet on the same address and port should give the same result.

My gopher client **clic** already supports gopher TLS and is available

at git://bitreich.org/clic and only requires the ecl common lisp

interpreter to compile.