diff --git a/README.md b/README.md index f2dac28..584e7b9 100644 --- a/README.md +++ b/README.md @@ -39,9 +39,10 @@ Use the ``--help`` flag to view command-line options: ```bash $ jetforce --help -usage: jetforce [-h] [--host HOST] [--port PORT] [--tls-certfile FILE] - [--tls-keyfile FILE] [--hostname HOSTNAME] [--dir DIR] - [--cgi-dir DIR] [--index-file FILE] +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] An Experimental Gemini Protocol Server @@ -49,63 +50,68 @@ optional arguments: -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) + --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) - --hostname HOSTNAME Server hostname (default: localhost) - --dir DIR Local directory to serve (default: /var/gemini) + --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) - -If the TLS cert/keyfile is not provided, a self-signed certificate will -automatically be generated and saved to your temporary directory. ``` +### 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``. + ### TLS Certificates The gemini specification *requires* that all connections be sent over TLS. -Before you deploy jetforce, you should either generate your own self-signed -certificate, or obtain one from a Certificate Authority like -[Let's Encrypt](https://letsencrypt.org). -In order to make local development easier, if you do not specify the certificate -arguments, jetforce will automatically generate a temporary ad-hoc TLS certificate -to use. Here's the OpenSSL command that jetforce uses internally: +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). +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}" ``` -There are currently no plans to support transient self-signed client certificates. -This is due to a technical limitation of the python standard library's ``ssl`` -module, which is described in detail +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 [here](https://portal.mozz.us/?url=gemini%3A%2F%2Fmozz.us%2Fjournal%2F2019-08-21.txt). -Support for verified client certificates will be added in a future version. +### Static Files -### Hostname +Jetforce will serve static files in the ``/var/gemini/`` directory: -Because the gemini protocol sends the *whole* URL in the request, it's required -that you 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``. - -Using python, you can modify this behavior to do fancy things like building a -proxy server for HTTP requests. See [http_proxy.py](examples/http_proxy.py) for -an example of how this is done. - -### Serving Files - -Jetforce serves files from the ``/var/gemini/`` directory by default: - -- Files with the **.gmi** extension will be interpreted as *text/gemini*. -- Other files will have their *mimetype* guessed based on their file extension. -- Directories will look for a file with the name **index.gmi**. -- If an index file does not exist, a directory listing will be generated. +- 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 @@ -113,14 +119,17 @@ 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. -The main difference in this implementation is that the CGI script is expected -to write the entire gemini response *verbatim* to stdout: +The main difference in jetforce's implementation is that the CGI script is +expected to write the entire gemini response *verbatim* to stdout: 1. The status code and meta on the first line -2. Any additional response body on subsequent lines +2. The optional response body on subsequent lines -Unlike HTTP's CGI, there are no request/response headers or other special -fields to perform actions like redirects. +Unlike CGI v1.1, there are no headers or other special values that the script +can respond with. + +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. ## License diff --git a/jetforce.py b/jetforce.py index 704d49c..e499c71 100755 --- a/jetforce.py +++ b/jetforce.py @@ -39,11 +39,6 @@ An Experimental Gemini Server, v{__version__} https://github.com/michael-lazar/jetforce """ -EPILOG = """ -If the TLS cert/keyfile is not provided, a self-signed certificate will -automatically be generated and saved to your temporary directory. -""" - class Status: """ @@ -616,7 +611,6 @@ def command_line_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser( prog="jetforce", description="An Experimental Gemini Protocol Server", - epilog=EPILOG, formatter_class=argparse.ArgumentDefaultsHelpFormatter, ) parser.add_argument("--host", help="Server address to bind to", default="127.0.0.1")