Gemini server for CGI
This repository has been archived on 2024-08-20. You can view files and clone it, but cannot push or open issues or pull requests.
Go to file
dece 237959fb42 use OpenSSL bindings instead of Rustls
Rustls is fine but it's hard to change the default "secure" behaviour
for Gemini usage. Not that Gemini is unsecure but things like
self-signed client certificates should not be such a stupidly hard thing
to accept.
2021-11-24 12:15:08 +01:00
examples/cgi init 2021-11-23 12:53:37 +01:00
src use OpenSSL bindings instead of Rustls 2021-11-24 12:15:08 +01:00
.gitignore init 2021-11-23 12:53:37 +01:00
Cargo.lock use OpenSSL bindings instead of Rustls 2021-11-24 12:15:08 +01:00
Cargo.toml use OpenSSL bindings instead of Rustls 2021-11-24 12:15:08 +01:00
README.md use OpenSSL bindings instead of Rustls 2021-11-24 12:15:08 +01:00

Opal

Opal is a Gemini server written in Rust. It is meant to serve dynamic content through CGI and does not serve static files. In a way, it is a companion project to the Agate Gemini server which only serves static files, trying to focus on a smaller set of features but do them correctly.

Opal uses the openssl Rust bindings, which work with OpenSSL and LibreSSL, so it should work properly on those platforms. I only support Linux systems but feel free to patch stuff!

Usage

Use opal -h to get a list of options. There is no config file.

CGI support

Opal tries to implement RFC 3875 (CGI 1.1) and provides all the required environment variables to processes. It also add a bunch of Gemini specific variables, like a lot of other servers (Gemserv, Gmid, Gmnisrv, …). The environment for the subprocess is cleaned and should only contain those variables.

Presence Variable Description
always GATEWAY_INTERFACE "CGI/1.1"
always REMOTE_ADDR Peer IP address and port
always REMOTE_HOST Same as REMOTE_ADDR
always REQUEST_METHOD Empty string for compatibility
always SCRIPT_NAME Script name part of the URL path
always SERVER_NAME Hostname used for SNI
always SERVER_PORT Port where the request has been received
always SERVER_PROTOCOL "GEMINI"
always SERVER_SOFTWARE "opal/version", e.g. "opal/0.1.0"
always GEMINI_DOCUMENT_ROOT CGI root
always GEMINI_SCRIPT_FILENAME CGI script that matched the URL path
always GEMINI_URL Full URL, normalized
always GEMINI_URL_PATH URL path, normalized
always TLS_VERSION TLS version, e.g. "TLSv1.3"
always TLS_CIPHER TLS cipher suite, e.g. "TLS_AES_256_GCM_SHA384"
optional PATH_INFO Path passed to the CGI process after the script name
optional QUERY_STRING Query string if provided, still URL-encoded
client cert AUTH_TYPE "CERTIFICATE" if one is provided
client cert REMOTE_USER Subject common name (empty if unavailable)
client cert TLS_CLIENT_ISSUER Issuer common name (empty if unavailable)
client cert TLS_CLIENT_HASH Digest of the DER reprensetation of the cert
client cert TLS_CLIENT_NOT_AFTER Validity end date, RFC 3339 format
client cert TLS_CLIENT_NOT_BEFORE Validity start date, RFC 3339 format

Opal does not provide CONTENT_LENGTH, CONTENT_TYPE, REMOTE_IDENT because they do not make much sense in Gemini. PATH_TRANSLATED is also not implemented by pure laziness.

The TLS_CLIENT_HASH is a string that starts with "SHA256:" followed by the SHA256 digest of the DER representation of the client certificate, as an uppercase hex-string.

It can be a bit confusing which variable represent what data, especially those related to the URL and the path. Take the following request as example: gemini://localhost/env/sub1/sub2?search=élément. Suppose our CGI root, in /cgi, contains the executable script named env. The variables will be:

GEMINI_DOCUMENT_ROOT=/cgi
GEMINI_SCRIPT_FILENAME=/cgi/env
GEMINI_URL=gemini://localhost/env/sub1/sub2?search=%C3%A9l%C3%A9ment
GEMINI_URL_PATH=/env/sub1/sub2
SCRIPT_NAME=/env
PATH_INFO=/sub1/sub2
QUERY_STRING=search=%C3%A9l%C3%A9ment