💾 Archived View for thrig.me › blog › 2023 › 08 › 14 › what-if-gem80.gmi captured on 2023-12-28 at 15:43:03. Gemini links have been rewritten to link to archived content

View Raw

More Information

⬅️ Previous capture (2023-11-14)

-=-=-=-=-=-=-

gemini://betabube.xyz/gemini80.gmi

What if Gemini could be served on port 80?

It can be, in which case you'll have "gemini://example.org:80" links instead of the shorter "gemini://example.org". Downsides include the need to deal with the usual "only root can listen on ports <1024" restriction, or more problematic that lots of web scanners will be poking at your gemini service. This may fill the logs and will waste CPU.

Also client software will tend to expect HTTP at TCP/80, and will probably throw weird errors if a client ever points their normie browser to your http://example.org:80 that runs gemini. Probably not the best experience. Good luck getting the browser company to support Gemini?

Also some Internet Service Providers block random ports. Probably not TCP/80, but running a gemini server on TCP/25 might very likely be blocked by ISP. Another risk is that you may run into weird traffic shaping rules. Again probably not for TCP/80, but if you pick some port that is usually for Voice-over-IP and that port turns out to have some traffic policy on it suitable for VoIP your gemini surfing could needlessly suck. Why not use the usual ports?

But nobody is stopping you from running Gemini on TCP/80 (or TCP/443, or whatever). It's not the usual way things are done, and may cause problems for you and others.

    $ lwp-request https://thrig.me:1965
    Can't connect to thrig.me:1965 (certificate verify failed)

    SSL connect attempt failed error:14FFF086:SSL routines:(UNKNOWN)SSL_internal:certificate verify failed at /usr/local/libdata/perl5/site_perl/LWP/Protocol/http.pm line 50.
    $ PERL_LWP_SSL_VERIFY_HOSTNAME=0 lwp-request https://thrig.me:1965
    59 bad request

And then amfora pointed at gemini://thrig.me:443 (to say less of TCP/80) throws up a nice

                     ╔═════ URL Fetch Error ══════╗
                     ║                            ║
                     ║   Failed to get header:    ║
                     ║  unexpected status value   ║
                     ║  HTTP/1.0: strconv.Atoi:   ║
                     ║    parsing "HTTP/1.0":     ║
                     ║      invalid syntax.       ║
                     ║                            ║
                     ║             Ok             ║
                     ║                            ║
                     ╚════════════════════════════╝

Maybe you can make this work better with a custom server, and maybe also custom clients? Again, good luck modifying the normie browser. Google doubtless has other plans.

I'm by no means an expert and there must be a reason solderpunk chose Gemini to get its own port. But I'm also a web developer by day and a strong advocatee of "Progressive enhancement".

New services always get new ports, unless you're doing something terribly clever like running SSH on port 443 (or from within a web server) to get around some ISP restriction or the other. Exceptions involve modifications to existing protocols, such as when the Simple Mail Transfer Protocol added a "EHLO" command to indicate extended SMTP. Cisco security devices used to helpfully autocorrect the EHLO to HELO, and thus disabled the extensions, such as STARTTLS and SMTPAUTH. Whoops. Or TCP/443 (among others) has gotten a series of evolutions to the protocol formerly known as SSL[1], all on that same port (or ports: SMTPS, LDAPS, etc).

Put another way, IMAP servers do not "fall back" to POP3 at TCP/143, nor would a SMTP server "fall back" to UUCP on the same port. Maybe someone can think of an example where this actually happens?

What if Gemini could rely on existing infrastructure? What if Gemini could be some sort of a graceful degradated version to HTTP served on port 80?

Web servers would need to support this, which would add some complexity for something that probably wouldn't be used much. Not too difficult if you're writing the web server yourself: look for a "gemini://" from the client and then branch off to the gemini code, otherwise do the HTTP thing, but that would be custom code doing custom things with the potential for custom bugs and time spent implementing and debugging all that (e.g. what happens if a gemini request shows up amid some HTTP pipeline thing?). Probably it is much easier to simply run a web server and a gemini server on their own dedicated ports, and then some other layer can handle the "formulate the content as HTML or gemini, if need be". Doing that all in one server would be even more complexity, which usually correlates with more bugs and more security whoopsies.

But nobody is stopping you from writing a server that is hybrid HTTPS and Gemini. Mixing encrypted (Gemini) with unencrypted (HTTP) might be tricky. I haven't thought much about that because it's never come up before. Generally protocols start plaintext and then upgrade to SSL (e.g. via the STARTTLS command of ESMTP) or start with SSL directly (TCP/443 for HTTPS, or TCP/465 for SMPTS, etc) and then do whatever over the SSL pipe. Maybe you could peek and guess whether to use SSL, or do something terribly clever during ClientHello, or hijack the port somehow in a Go service? Or you could run different services on different ports, which might be easier to debug and maintain than one huge thing that tries to do everything at one port. Like, any security bug gets you everything, while a HTTP and a Gemini server could be run as different users under different chroots, so breaking into one does not automatically give them access to your entire kingdom, or sink your entire boat.

    #!/usr/bin/env tclsh8.6
    # hybrid.tcl - a minimal hybrid HTTPS and Gemini server
    #   doas pkg_add tcl-8.6.13 tcltls-1.7.22
    package require tls

    proc response {socket} {
        gets $socket request
        if {[string equal -length 9 $request gemini://]} {
            # TODO serve up *.gmi or whatever here
            puts $socket "20 "
            puts $socket "get off my lawn"
            close $socket
        } elseif {[string equal -length 3 $request GET]} {
            # TODO serve up *.html or whatever here
            puts $socket "Content-Type: text/plain"
            puts $socket ""
            puts $socket "get off my lawn"
            close $socket
        }
    }

    proc setup-response {socket addr port} {
        fconfigure $socket -blocking 0 -translation crlf
        fileevent $socket readable [list response $socket]
    }

    # TWEAK your server certificate and key and custom port here
    #
    # However, gemini does TOFU while a HTTPS client will generally try to
    # verify the certificate, so that probably means you're stuck doing a
    # "Let's Encrypt" certificate and dealing with rotations on the gemini
    # side; with separate servers one could use different certificates for
    # the services. So that's another argument against mixing the two.
    tls::socket -server setup-response \
     -tls1 true -ssl2 false -ssl3 false -require 0 \
     -certfile host.cert -keyfile host.key 1234

    vwait godot

[1] Microsoft wouldn't take a knee and use a Netscape protocol, so SSL got renamed to TLS, while still being a Netscape protocol.