Gemini reverse proxy using Traefik HostSNI

How reverse proxies work in the HTTP world

As you may know if you hosted web servers, you can only have one webserver listening on the port 80 which is used web browsers. There has been two way around it for some time (other than buying another public IP or another VPS)

  • virtualhosts, in which one webserver serves content from a different folder depending on the hostname in the request
  • reverse proxy, in which a primary webserver dispatches requests to other backend webservers based on the hostname in the request

In the HTTP/web world, these systems use the Host header, and often perform TLS termination, so that the traffic between the reverse proxy and the content webservers is unencrypted.

How to do it with Gemini

In Gemini, there is no header to peek at the requested hostname, and we need to keep the traffic secure because Gemini is never unencrypted.

The solution: SNI

Server Name Indication is sent to the server with the TLS Client Hello, and contains the hostname used in the request, but isn't encrypted (encrypted traffic starts after the Server Hello).

Traefik allows us to not only route traffic based on the SNI, but also we are not required to perform TLS termination, so the Gemini backend does not suspect that we are using a reverse proxy. The TLS traffic is not modified by Traefik.

We use the HostSNI Rule to filter TCP traffic, and the transparent TLS option to tell traefik to not perform TLS termination.

Config

tcp:
  routers:
    capsule1:
      entrypoints:
        - "gemini"
      rule: "HostSNI(`capsule1.127.0.0.1.nip.dns.ribes.ovh`)"
      service: "capsule1"
      tls:
        passthrough: true

    capsule2:
      entrypoints:
        - "gemini"
      rule: "HostSNI(`capsule2.127.0.0.1.nip.dns.ribes.ovh`)"
      service: "capsule2"
      tls:
        passthrough: true