Skip to main content

An Experimental Gemini Server

Project description

Jetforce

An experimental TCP server for the new, under development Gemini Protocol. Learn more about Gemini here.

Rocket Launch

Features

  • A built-in static file server with support for gemini directories and CGI scripts.
  • Lightweight, single-file framework with zero external dependencies.
  • Modern python codebase with type hinting and black style formatting.
  • Supports concurrent connections using an asynchronous event loop.
  • Extendable components that loosely implement the WSGI server/application pattern.

Installation

Requires Python 3.7+

The latest release can be installed from PyPI:

$ pip install jetforce

Or, clone the repository and run the script directly:

$ git clone https://github.com/michael-lazar/jetforce
$ cd jetforce
$ python3 jetforce.py

Usage

Use the --help flag to view command-line options:

$ jetforce --help
usage: jetforce [-h] [-V] [--host HOST] [--port PORT] [--hostname HOSTNAME]
                [--tls-certfile FILE] [--tls-keyfile FILE] [--tls-cafile FILE]
                [--tls-capath DIR] [--dir DIR] [--cgi-dir DIR]
                [--index-file FILE]

An Experimental Gemini Protocol Server

optional arguments:
  -h, --help           show this help message and exit
  -V, --version        show program's version number and exit
  --host HOST          Server address to bind to (default: 127.0.0.1)
  --port PORT          Server port to bind to (default: 1965)
  --hostname HOSTNAME  Server hostname (default: localhost)
  --tls-certfile FILE  Server TLS certificate file (default: None)
  --tls-keyfile FILE   Server TLS private key file (default: None)
  --tls-cafile FILE    A CA file to use for validating clients (default: None)
  --tls-capath DIR     A directory containing CA files for validating clients
                       (default: None)
  --dir DIR            Root directory on the filesystem to serve (default:
                       /var/gemini)
  --cgi-dir DIR        CGI script directory, relative to the server's root
                       directory (default: cgi-bin)
  --index-file FILE    If a directory contains a file with this name, that
                       file will be served instead of auto-generating an index
                       page (default: index.gmi)

Setting the hostname

The server's hostname should be set to the DNS name that you expect to receive traffic from. For example, if your jetforce server is running on "gemini://cats.com", you should set the hostname to "cats.com". Any URLs that do not match this hostname will be refused by the server, including URLs that use a direct IP address such as "gemini://174.138.124.169".

Setting the host

The server's host should be set to the local socket address that you want to bind to:

  • --host 127.0.0.1 - Accept local connections only
  • --host 0.0.0.0 - Accept remote connections over IPv4
  • --host :: - Accept remote connections over IPv6
  • --host "" - Accept remote connections over any interface (IPv4 + IPv6)

TLS Certificates

The gemini specification requires that all connections be sent over TLS.

If you do not provide a TLS certificate file using the --tls-certfile flag, jetforce will automatically generate a temporary cert for you to use. This is great for making development easier, but before you expose your server to the public internet you should configure something more permanent. You can generate your own self-signed server certificate, or obtain one from a Certificate Authority like Let's Encrypt.

Here's the OpenSSL command that jetforce uses to generate a self-signed cert:

$ openssl req -newkey rsa:2048 -nodes -keyout {hostname}.key \
    -nodes -x509 -out {hostname}.crt -subj "/CN={hostname}"

Jetforce also supports verified client TLS certificates. You can specify your client CA with the --tls-cafile or --tls-capath flags. Verified connections will have the REMOTE_USER variable added to their environment, which contains the client certificate's CN attribute. Instructions on how to generate TLS client certificates are outside of the scope of this readme, but you can find many helpful tutorials online.

There are currently no plans to support unverified (transient) client certificates. This is due to a technical limitation of the python standard library's ssl module, which is described in detail here.

Static Files

Jetforce will serve static files in the /var/gemini/ directory:

  • Files ending with .gmi will be interpreted as the text/gemini type
  • If a directory is requested, jetforce will look for a file in that directory with the name of index.gmi
    • If it exists, the index file will be returned
    • Otherwise, jetforce will generate a directory listing

CGI Scripts

Jetforce supports a simplified version of CGI scripting. It doesn't exactly follow the RFC 3875 specification for CGI, but it gets the job done for the purposes of Gemini.

Any executable file placed in the server's cgi-bin/ directory will be considered a CGI script. When a CGI script is requested by a gemini client, the jetforce server will execute the script and pass along information about the request using environment variables:

Variable Name Example
GATEWAY_INTERFACE GCI/1.1
GEMINI_URL gemini://mozz.us/cgi-bin/debug.cgi?foobar
HOSTNAME mozz.us
PATH_INFO /cgi-bin/debug.cgi
QUERY_STRING foobar
REMOTE_ADDR 10.10.0.2
REMOTE_HOST 10.10.0.2
SCRIPT_NAME /usr/local/www/mozz/gemini/cgi-bin/debug.cgi
SERVER_NAME mozz.us
SERVER_PORT 1965
SERVER_PROTOCOL GEMINI
SERVER_SOFTWARE jetforce/0.0.7

The CGI script must then write the gemini response to the stdout stream. This includes the status code and meta string on the first line, and the optional response body on subsequent lines. The bytes generated by the CGI script will be forwarded verbatim to the gemini client, without any additional modificiation by the server.

Deployment

Jetforce is intended to be run behind a process manager that handles daemonizing the script, redirecting output to system logs, etc. I prefer to use systemd for this because it's installed on my operating system and easy to set up.

Here's how I configure my server over at gemini://mozz.us:

# /etc/systemd/system/jetforce.service
[Unit]
Description=Jetforce Server

[Service]
Type=simple
Restart=always
RestartSec=5
User=jetforce
Group=jetforce
Environment="PYTHONUNBUFFERED=1"
ExecStart=/usr/local/bin/jetforce \
    --host 0.0.0.0 \
    --port 1965 \
    --hostname mozz.us \
    --dir /var/gemini \
    --tls-certfile /etc/letsencrypt/live/mozz.us/fullchain.pem \
    --tls-keyfile /etc/letsencrypt/live/mozz.us/privkey.pem \

[Install]
WantedBy=default.target
  • --host 0.0.0.0 allows the server to accept external connections from any IP address over IPv4.
  • PYTHONUNBUFFERED=1 disables buffering stderr and is sometimes necessary for logging to work.
  • --tls-certfile and --tls-keyfile point to my WWW server's Let's Encrypt certificate chain.

I created a separate jetforce user for my server to restrict file access:

useradd jetforce

mkdir -p /var/gemini/
chown -R root:jetforce /var/gemini/

# Set the GID so that any new files created in /var/gemini will
# be automatically assigned to the `jetforce` group permission.
chmod -R g+s /var/gemini/

With this service installed, I can start and stop the server using

systemctl start jetforce
systemctl stop jetforce

And I can view the server logs using

journalctl -u jetforce -f

License

This project is licensed under the Floodgap Free Software License.

The Floodgap Free Software License (FFSL) has one overriding mandate: that software using it, or derivative works based on software that uses it, must be free. By free we mean simply "free as in beer" -- you may put your work into open or closed source packages as you see fit, whether or not you choose to release your changes or updates publicly, but you must not ask any fee for it.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

Jetforce-0.2.0.tar.gz (18.9 kB view details)

Uploaded Source

Built Distribution

Jetforce-0.2.0-py3-none-any.whl (25.1 kB view details)

Uploaded Python 3

File details

Details for the file Jetforce-0.2.0.tar.gz.

File metadata

  • Download URL: Jetforce-0.2.0.tar.gz
  • Upload date:
  • Size: 18.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.13.0 pkginfo/1.4.2 requests/2.22.0 setuptools/41.6.0 requests-toolbelt/0.8.0 tqdm/4.24.0 CPython/3.7.5

File hashes

Hashes for Jetforce-0.2.0.tar.gz
Algorithm Hash digest
SHA256 06fddac811fc7891a902ca523ab2fd706201ce82c7d843dbefe2f83342e1874e
MD5 83ffc3ae29a3eb37514a12acdb336c05
BLAKE2b-256 a775a0098911243dbab64d2883ffd0ecf17ed0804cb02f268045b865efd30bb6

See more details on using hashes here.

File details

Details for the file Jetforce-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: Jetforce-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 25.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.13.0 pkginfo/1.4.2 requests/2.22.0 setuptools/41.6.0 requests-toolbelt/0.8.0 tqdm/4.24.0 CPython/3.7.5

File hashes

Hashes for Jetforce-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c7112f8daad67369920be4cbe7a6a1b6d2e2ba165c5d75660410622722dda9f4
MD5 98fbbb46168260cbd58e34da6157263e
BLAKE2b-256 944a68cf260bab4c6d43da43eabe03d377ddcb8269cabe151f5e7c980f243cd4

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page