2019-08-24 05:36:24 +02:00
|
|
|
# Jetforce
|
|
|
|
|
2019-08-28 04:55:05 +02:00
|
|
|
An experimental TCP server for the new, under development Gemini Protocol.
|
2019-08-28 04:55:27 +02:00
|
|
|
Learn more [here](https://gopher.commons.host/gopher://zaibatsu.circumlunar.space/1/~solderpunk/gemini).
|
2019-08-24 05:36:24 +02:00
|
|
|
|
|
|
|
![Rocket Launch](resources/rocket.jpg)
|
2019-08-09 03:45:26 +02:00
|
|
|
|
2019-08-05 04:57:34 +02:00
|
|
|
## Features
|
|
|
|
|
2019-08-28 04:52:38 +02:00
|
|
|
- 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.
|
2019-08-05 04:57:34 +02:00
|
|
|
- Supports concurrent connections using an asynchronous event loop.
|
2019-08-28 04:52:38 +02:00
|
|
|
- Extendable components that loosely implement the [WSGI](https://en.wikipedia.org/wiki/Web_Server_Gateway_Interface)
|
2019-08-24 06:34:05 +02:00
|
|
|
server/application pattern.
|
2019-08-05 04:57:34 +02:00
|
|
|
|
|
|
|
## Installation
|
|
|
|
|
2019-08-28 04:52:38 +02:00
|
|
|
Requires Python 3.7+
|
2019-08-05 04:57:34 +02:00
|
|
|
|
2019-08-28 04:52:38 +02:00
|
|
|
The latest release can be installed from [PyPI](https://pypi.org/project/Jetforce/):
|
2019-08-24 06:34:05 +02:00
|
|
|
|
2019-08-28 04:52:38 +02:00
|
|
|
```bash
|
2019-08-05 04:57:34 +02:00
|
|
|
$ pip install jetforce
|
|
|
|
```
|
|
|
|
|
2019-08-28 04:52:38 +02:00
|
|
|
Or, clone the repository and run the script directly:
|
2019-08-05 04:57:34 +02:00
|
|
|
|
2019-08-28 04:52:38 +02:00
|
|
|
```bash
|
2019-08-05 04:57:34 +02:00
|
|
|
$ git clone https://github.com/michael-lazar/jetforce
|
|
|
|
$ cd jetforce
|
2019-08-28 04:52:38 +02:00
|
|
|
$ python3 jetforce.py
|
2019-08-05 04:57:34 +02:00
|
|
|
```
|
2019-08-24 06:34:05 +02:00
|
|
|
|
|
|
|
## Usage
|
|
|
|
|
|
|
|
Use the ``--help`` flag to view command-line options:
|
|
|
|
|
2019-08-28 04:52:38 +02:00
|
|
|
```bash
|
2019-08-24 06:34:05 +02:00
|
|
|
$ jetforce --help
|
2019-08-30 05:35:29 +02:00
|
|
|
usage: jetforce [-h] [--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]
|
2019-08-24 06:34:05 +02:00
|
|
|
|
|
|
|
An Experimental Gemini Protocol Server
|
|
|
|
|
|
|
|
optional arguments:
|
2019-08-28 04:52:38 +02:00
|
|
|
-h, --help show this help message and exit
|
|
|
|
--host HOST Server address to bind to (default: 127.0.0.1)
|
|
|
|
--port PORT Server port to bind to (default: 1965)
|
2019-08-30 05:35:29 +02:00
|
|
|
--hostname HOSTNAME Server hostname (default: localhost)
|
2019-08-28 04:52:38 +02:00
|
|
|
--tls-certfile FILE Server TLS certificate file (default: None)
|
|
|
|
--tls-keyfile FILE Server TLS private key file (default: None)
|
2019-08-30 05:35:29 +02:00
|
|
|
--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)
|
2019-08-28 04:52:38 +02:00
|
|
|
--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)
|
2019-08-24 06:34:05 +02:00
|
|
|
```
|
|
|
|
|
2019-08-30 05:35:29 +02:00
|
|
|
### Hostname
|
|
|
|
|
|
|
|
Because the gemini protocol sends the whole URL in the request, it's necessary
|
|
|
|
to declare the hostname that your server is expecting to receive traffic under.
|
|
|
|
Jetforce will reject any request that doesn't match your hostname with a status
|
|
|
|
of ``Proxy Request Refused``.
|
|
|
|
|
2019-08-24 06:34:05 +02:00
|
|
|
### TLS Certificates
|
|
|
|
|
|
|
|
The gemini specification *requires* that all connections be sent over TLS.
|
|
|
|
|
2019-08-30 05:35:29 +02:00
|
|
|
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](https://letsencrypt.org).
|
2019-08-24 06:34:05 +02:00
|
|
|
|
2019-08-30 05:35:29 +02:00
|
|
|
Here's the OpenSSL command that jetforce uses to generate a self-signed cert:
|
2019-08-24 06:34:05 +02:00
|
|
|
|
|
|
|
```
|
|
|
|
$ openssl req -newkey rsa:2048 -nodes -keyout {hostname}.key \
|
|
|
|
-nodes -x509 -out {hostname}.crt -subj "/CN={hostname}"
|
|
|
|
```
|
|
|
|
|
2019-08-30 05:35:29 +02:00
|
|
|
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](https://portal.mozz.us/?url=gemini%3A%2F%2Fmozz.us%2Fjournal%2F2019-08-21.txt).
|
|
|
|
|
|
|
|
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
|
2019-08-24 20:38:57 +02:00
|
|
|
[here](https://portal.mozz.us/?url=gemini%3A%2F%2Fmozz.us%2Fjournal%2F2019-08-21.txt).
|
|
|
|
|
2019-08-30 05:35:29 +02:00
|
|
|
### Static Files
|
2019-08-24 20:38:57 +02:00
|
|
|
|
2019-08-30 05:35:29 +02:00
|
|
|
Jetforce will serve static files in the ``/var/gemini/`` directory:
|
2019-08-24 06:54:53 +02:00
|
|
|
|
2019-08-30 05:35:29 +02:00
|
|
|
- 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
|
2019-08-24 06:54:53 +02:00
|
|
|
|
2019-08-28 04:52:38 +02:00
|
|
|
### CGI Scripts
|
|
|
|
|
|
|
|
Jetforce implements a slightly modified version of the official CGI
|
|
|
|
specification. Because Gemini is a less complex than HTTP, the CGI interface is
|
|
|
|
also inherently easier and more straightforward to use.
|
|
|
|
|
2019-08-30 05:35:29 +02:00
|
|
|
The main difference in jetforce's implementation is that the CGI script is
|
|
|
|
expected to write the entire gemini response *verbatim* to stdout:
|
2019-08-28 04:52:38 +02:00
|
|
|
|
|
|
|
1. The status code and meta on the first line
|
2019-08-30 05:35:29 +02:00
|
|
|
2. The optional response body on subsequent lines
|
|
|
|
|
|
|
|
Unlike CGI v1.1, there are no headers or other special values that the script
|
|
|
|
can respond with.
|
2019-08-28 04:52:38 +02:00
|
|
|
|
2019-08-30 05:35:29 +02:00
|
|
|
Some of the HTTP-specific environment variables like ``REQUEST_METHOD`` are not
|
|
|
|
used, because they don't make sense in the context of a Gemini request.
|
2019-08-24 07:05:45 +02:00
|
|
|
|
|
|
|
## License
|
|
|
|
|
|
|
|
This project is licensed under the [Floodgap Free Software License](https://www.floodgap.com/software/ffsl/license.html).
|
|
|
|
|
|
|
|
> 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.
|