diff options
author | CoprDistGit <infra@openeuler.org> | 2023-05-10 09:31:36 +0000 |
---|---|---|
committer | CoprDistGit <infra@openeuler.org> | 2023-05-10 09:31:36 +0000 |
commit | 15c6e548f3f3b053f4d925cf26582a25563f1a65 (patch) | |
tree | 660732d1e61280116484c76bbd235cac7d6763c9 | |
parent | 1e6f7a460b8f8f109b781219df5d561e0b54495d (diff) |
automatic import of python-jetforce
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | python-jetforce.spec | 982 | ||||
-rw-r--r-- | sources | 1 |
3 files changed, 984 insertions, 0 deletions
@@ -0,0 +1 @@ +/Jetforce-0.9.1.tar.gz diff --git a/python-jetforce.spec b/python-jetforce.spec new file mode 100644 index 0000000..41f8e61 --- /dev/null +++ b/python-jetforce.spec @@ -0,0 +1,982 @@ +%global _empty_manifest_terminate_build 0 +Name: python-Jetforce +Version: 0.9.1 +Release: 1 +Summary: An Experimental Gemini Server +License: Other/Proprietary License +URL: https://github.com/michael-lazar/jetforce +Source0: https://mirrors.nju.edu.cn/pypi/web/packages/04/67/417aa93503f348d06e428ad91c3f79b453cd43cc2342939938c49da67a72/Jetforce-0.9.1.tar.gz +BuildArch: noarch + +Requires: python3-twisted +Requires: python3-service-identity +Requires: python3-idna +Requires: python3-pyopenssl + +%description +# Jetforce + +An experimental TCP server for the new, under development Gemini Protocol. +Learn more about Gemini [here](https://portal.mozz.us/). + + + +[](https://pypi.python.org/pypi/jetforce/) +[](https://pypi.python.org/pypi/jetforce/) +[](https://github.com/michael-lazar/jetforce/actions?query=workflow%3AJetforce) + +## Table of Contents + +* [Features](#features) +* [Installation](#installation) +* [Usage](#usage) +* [Deployment](#deployment) +* [Releases](#releases) +* [License](#license) + +## Features + +- A built-in static file server with support for gemini directories and CGI scripts. +- A complete framework for writing robust gemini applications like [astrobotany](https://github.com/michael-lazar/astrobotany). +- A lean, modern codebase with type hints and black formatting. +- A solid foundation built on top of the [twisted](https://twistedmatrix.com/trac/) asynchronous networking engine. + +## Installation + +Requires Python 3.7 or newer. + +The latest stable release can be installed from [PyPI](https://pypi.org/project/Jetforce/): + +```bash +$ pip install jetforce +``` + +Or, install from source: + +```bash +$ git clone https://github.com/michael-lazar/jetforce +$ cd jetforce +$ pip install . +``` + +Or, install into a python virtual environment: + +```bash +# Create a project directory somewhere +$ mkdir /opt/jetforce + +# Activate a virtual environment and install jetforce +$ python3 -m virtualenv /opt/jetforce/venv +$ source /opt/jetforce/venv/bin/activate +$ pip install jetforce + +# The launch script will be installed here +$ /opt/jetforce/venv/bin/jetforce +``` + +## Usage + +Use the ``--help`` flag to view command-line options: + +```bash +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] + [--default-lang DEFAULT_LANG] [--rate-limit RATE_LIMIT] + +An Experimental Gemini Protocol Server + +optional arguments: + -h, --help show this help message and exit + -V, --version show program's version number and exit + +server configuration: + --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) + +fileserver configuration: + --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) + --default-lang DEFAULT_LANG + A lang parameter that will be used for all text/gemini responses + (default: None) + --rate-limit RATE_LIMIT + Enable IP rate limiting, e.g. '60/5m' (60 requests per 5 minutes) + (default: None) +``` + +### 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". + +IDNs (domain names that contain unicode characters) should be defined using +their ASCII punycode representation. For example, the domain name +*café.mozz.us* should be represented as ``--hostname xn--caf-dma.mozz.us``. + +### Setting the ``host`` + +The server's host should be set to the local socket 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 setup 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 an example `openssl` command that you can use to generate a self-signed certificate: + +``` +$ openssl req -newkey rsa:2048 -nodes -keyout {hostname}.key \ + -nodes -x509 -out {hostname}.crt -subj "/CN={hostname}" +``` + +Jetforce also supports TLS client certificates (both self-signed and CA authorised). +Requests that are made with client certificates will include additional +CGI/environment variables with information about the TLS connection. + +You can specify a CA for client validation with the ``--tls-cafile`` or ``--tls-capath`` +flags. Connections validated by the CA will have the ``TLS_CLIENT_AUTHORISED`` environment +variable set to True. Instructions on how to generate CA's are outside of the scope of +this readme, but you can find many helpful tutorials +[online](https://www.makethenmakeinstall.com/2014/05/ssl-client-authentication-step-by-step/). + +### Static Files + +Jetforce will serve static files in the ``/var/gemini/`` directory by default. +Files ending with ***.gmi** will be interpreted as the *text/gemini* type. If +a directory is requested, jetforce will look for a file named **index.gmi** in that +directory to return. Otherwise, a directory file listing will be automatically +generated. + +### Virtual Hosting + +For the sake of keeping the command line arguments straightforward and easy +to understand, configuring virtual hosting is not supported via the command +line. However, it is readily available using only a few lines of python and a +custom launch script. Check out [examples/vhost.py](examples/vhost.py) for more +information. + +Jetforce does not (yet) support virtual hosting at the TLS-layer using SNI. +This means that you cannot return different server TLS certificates for +different domains. The suggested workaround is to use a single certificate with +multiple ``subjectAltName`` attributes. There is also an +[sni_callback()](https://github.com/michael-lazar/jetforce/blob/9ac80a986c6ed8a62951c857315ca04b6d127c32/jetforce/tls.py#L140) +hook in the server codebase that can be subclassed to implement custom TLS +behavior. + +### CGI + +Jetforce supports a simplified version of CGI scripting. It doesn't +exactly follow the [RFC 3875](https://tools.ietf.org/html/rfc3875) +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. + +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 modification by the server. + +#### CGI Environment Variables + +| Name | Example Value | +| --- | --- | +| GATEWAY_INTERFACE | ``CGI/1.1`` (for compatibility with RFC 3875) | +| SERVER_PROTOCOL | ``GEMINI`` | +| SERVER_SOFTWARE | ``jetforce/0.0.7`` | +| GEMINI_URL | ``gemini://mozz.us/cgi-bin/example.cgi/extra?hello%20world`` | +| SCRIPT_NAME | ``/cgi-bin/example.cgi`` | +| PATH_INFO | ``/extra`` | +| QUERY_STRING | ``hello%20world`` | +| SERVER_NAME | ``mozz.us`` | +| SERVER_PORT | ``1965`` | +| REMOTE_HOST | ``10.10.0.2`` | +| REMOTE_ADDR | ``10.10.0.2`` | +| TLS_CIPHER | ``TLS_AES_256_GCM_SHA384`` | +| TLS_VERSION | ``TLSv1.3`` | + + +#### CGI Environment Variables - Authenticated + +Additional CGI variables will be included only when the client connection uses a TLS client certificate: + +| Name | Example Value | +| --- | --- | +| AUTH_TYPE | ``CERTIFICATE`` | +| REMOTE_USER | ``mozz123`` (the certificate subject CN attribute) | +| TLS_CLIENT_HASH | ``SHA256:86341FB480BFE333C343530D75ABF99D1437F69338F36C684C8831B63C993A96`` | +| TLS_CLIENT_NOT_BEFORE | ``2020-04-05T04:18:22Z`` | +| TLS_CLIENT_NOT_AFTER | ``2021-04-05T04:18:22Z`` | +| TLS_CLIENT_SERIAL_NUMBER | ``73629018972631`` | +| TLS_CLIENT_AUTHORISED | ``0`` (not authorised) / ``1`` (authorised) † | + +† Requires the server to be configured with a CA for validating client certificates. + +## 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](https://www.freedesktop.org/wiki/Software/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 +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 \ + --tls-cafile /etc/pki/tls/jetforce_client/ca.cer + +[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. +- ``--tls-cafile`` points to a self-signed CA that I created in order to test + accepting client TLS connections. + +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 +``` + +*WARNING* + +You are exposing a server to the internet. You (yes you!) are responsible for +securing your server and setting up appropriate access permissions. This likely means +*not* running jetforce as the root user. Security best practices are outside of the +scope of this document and largely depend on your individual threat model. + +## Releases + +To view the project's release history, see the [CHANGELOG](CHANGELOG.md) file. + +## 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. + + + + +%package -n python3-Jetforce +Summary: An Experimental Gemini Server +Provides: python-Jetforce +BuildRequires: python3-devel +BuildRequires: python3-setuptools +BuildRequires: python3-pip +%description -n python3-Jetforce +# Jetforce + +An experimental TCP server for the new, under development Gemini Protocol. +Learn more about Gemini [here](https://portal.mozz.us/). + + + +[](https://pypi.python.org/pypi/jetforce/) +[](https://pypi.python.org/pypi/jetforce/) +[](https://github.com/michael-lazar/jetforce/actions?query=workflow%3AJetforce) + +## Table of Contents + +* [Features](#features) +* [Installation](#installation) +* [Usage](#usage) +* [Deployment](#deployment) +* [Releases](#releases) +* [License](#license) + +## Features + +- A built-in static file server with support for gemini directories and CGI scripts. +- A complete framework for writing robust gemini applications like [astrobotany](https://github.com/michael-lazar/astrobotany). +- A lean, modern codebase with type hints and black formatting. +- A solid foundation built on top of the [twisted](https://twistedmatrix.com/trac/) asynchronous networking engine. + +## Installation + +Requires Python 3.7 or newer. + +The latest stable release can be installed from [PyPI](https://pypi.org/project/Jetforce/): + +```bash +$ pip install jetforce +``` + +Or, install from source: + +```bash +$ git clone https://github.com/michael-lazar/jetforce +$ cd jetforce +$ pip install . +``` + +Or, install into a python virtual environment: + +```bash +# Create a project directory somewhere +$ mkdir /opt/jetforce + +# Activate a virtual environment and install jetforce +$ python3 -m virtualenv /opt/jetforce/venv +$ source /opt/jetforce/venv/bin/activate +$ pip install jetforce + +# The launch script will be installed here +$ /opt/jetforce/venv/bin/jetforce +``` + +## Usage + +Use the ``--help`` flag to view command-line options: + +```bash +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] + [--default-lang DEFAULT_LANG] [--rate-limit RATE_LIMIT] + +An Experimental Gemini Protocol Server + +optional arguments: + -h, --help show this help message and exit + -V, --version show program's version number and exit + +server configuration: + --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) + +fileserver configuration: + --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) + --default-lang DEFAULT_LANG + A lang parameter that will be used for all text/gemini responses + (default: None) + --rate-limit RATE_LIMIT + Enable IP rate limiting, e.g. '60/5m' (60 requests per 5 minutes) + (default: None) +``` + +### 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". + +IDNs (domain names that contain unicode characters) should be defined using +their ASCII punycode representation. For example, the domain name +*café.mozz.us* should be represented as ``--hostname xn--caf-dma.mozz.us``. + +### Setting the ``host`` + +The server's host should be set to the local socket 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 setup 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 an example `openssl` command that you can use to generate a self-signed certificate: + +``` +$ openssl req -newkey rsa:2048 -nodes -keyout {hostname}.key \ + -nodes -x509 -out {hostname}.crt -subj "/CN={hostname}" +``` + +Jetforce also supports TLS client certificates (both self-signed and CA authorised). +Requests that are made with client certificates will include additional +CGI/environment variables with information about the TLS connection. + +You can specify a CA for client validation with the ``--tls-cafile`` or ``--tls-capath`` +flags. Connections validated by the CA will have the ``TLS_CLIENT_AUTHORISED`` environment +variable set to True. Instructions on how to generate CA's are outside of the scope of +this readme, but you can find many helpful tutorials +[online](https://www.makethenmakeinstall.com/2014/05/ssl-client-authentication-step-by-step/). + +### Static Files + +Jetforce will serve static files in the ``/var/gemini/`` directory by default. +Files ending with ***.gmi** will be interpreted as the *text/gemini* type. If +a directory is requested, jetforce will look for a file named **index.gmi** in that +directory to return. Otherwise, a directory file listing will be automatically +generated. + +### Virtual Hosting + +For the sake of keeping the command line arguments straightforward and easy +to understand, configuring virtual hosting is not supported via the command +line. However, it is readily available using only a few lines of python and a +custom launch script. Check out [examples/vhost.py](examples/vhost.py) for more +information. + +Jetforce does not (yet) support virtual hosting at the TLS-layer using SNI. +This means that you cannot return different server TLS certificates for +different domains. The suggested workaround is to use a single certificate with +multiple ``subjectAltName`` attributes. There is also an +[sni_callback()](https://github.com/michael-lazar/jetforce/blob/9ac80a986c6ed8a62951c857315ca04b6d127c32/jetforce/tls.py#L140) +hook in the server codebase that can be subclassed to implement custom TLS +behavior. + +### CGI + +Jetforce supports a simplified version of CGI scripting. It doesn't +exactly follow the [RFC 3875](https://tools.ietf.org/html/rfc3875) +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. + +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 modification by the server. + +#### CGI Environment Variables + +| Name | Example Value | +| --- | --- | +| GATEWAY_INTERFACE | ``CGI/1.1`` (for compatibility with RFC 3875) | +| SERVER_PROTOCOL | ``GEMINI`` | +| SERVER_SOFTWARE | ``jetforce/0.0.7`` | +| GEMINI_URL | ``gemini://mozz.us/cgi-bin/example.cgi/extra?hello%20world`` | +| SCRIPT_NAME | ``/cgi-bin/example.cgi`` | +| PATH_INFO | ``/extra`` | +| QUERY_STRING | ``hello%20world`` | +| SERVER_NAME | ``mozz.us`` | +| SERVER_PORT | ``1965`` | +| REMOTE_HOST | ``10.10.0.2`` | +| REMOTE_ADDR | ``10.10.0.2`` | +| TLS_CIPHER | ``TLS_AES_256_GCM_SHA384`` | +| TLS_VERSION | ``TLSv1.3`` | + + +#### CGI Environment Variables - Authenticated + +Additional CGI variables will be included only when the client connection uses a TLS client certificate: + +| Name | Example Value | +| --- | --- | +| AUTH_TYPE | ``CERTIFICATE`` | +| REMOTE_USER | ``mozz123`` (the certificate subject CN attribute) | +| TLS_CLIENT_HASH | ``SHA256:86341FB480BFE333C343530D75ABF99D1437F69338F36C684C8831B63C993A96`` | +| TLS_CLIENT_NOT_BEFORE | ``2020-04-05T04:18:22Z`` | +| TLS_CLIENT_NOT_AFTER | ``2021-04-05T04:18:22Z`` | +| TLS_CLIENT_SERIAL_NUMBER | ``73629018972631`` | +| TLS_CLIENT_AUTHORISED | ``0`` (not authorised) / ``1`` (authorised) † | + +† Requires the server to be configured with a CA for validating client certificates. + +## 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](https://www.freedesktop.org/wiki/Software/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 +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 \ + --tls-cafile /etc/pki/tls/jetforce_client/ca.cer + +[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. +- ``--tls-cafile`` points to a self-signed CA that I created in order to test + accepting client TLS connections. + +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 +``` + +*WARNING* + +You are exposing a server to the internet. You (yes you!) are responsible for +securing your server and setting up appropriate access permissions. This likely means +*not* running jetforce as the root user. Security best practices are outside of the +scope of this document and largely depend on your individual threat model. + +## Releases + +To view the project's release history, see the [CHANGELOG](CHANGELOG.md) file. + +## 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. + + + + +%package help +Summary: Development documents and examples for Jetforce +Provides: python3-Jetforce-doc +%description help +# Jetforce + +An experimental TCP server for the new, under development Gemini Protocol. +Learn more about Gemini [here](https://portal.mozz.us/). + + + +[](https://pypi.python.org/pypi/jetforce/) +[](https://pypi.python.org/pypi/jetforce/) +[](https://github.com/michael-lazar/jetforce/actions?query=workflow%3AJetforce) + +## Table of Contents + +* [Features](#features) +* [Installation](#installation) +* [Usage](#usage) +* [Deployment](#deployment) +* [Releases](#releases) +* [License](#license) + +## Features + +- A built-in static file server with support for gemini directories and CGI scripts. +- A complete framework for writing robust gemini applications like [astrobotany](https://github.com/michael-lazar/astrobotany). +- A lean, modern codebase with type hints and black formatting. +- A solid foundation built on top of the [twisted](https://twistedmatrix.com/trac/) asynchronous networking engine. + +## Installation + +Requires Python 3.7 or newer. + +The latest stable release can be installed from [PyPI](https://pypi.org/project/Jetforce/): + +```bash +$ pip install jetforce +``` + +Or, install from source: + +```bash +$ git clone https://github.com/michael-lazar/jetforce +$ cd jetforce +$ pip install . +``` + +Or, install into a python virtual environment: + +```bash +# Create a project directory somewhere +$ mkdir /opt/jetforce + +# Activate a virtual environment and install jetforce +$ python3 -m virtualenv /opt/jetforce/venv +$ source /opt/jetforce/venv/bin/activate +$ pip install jetforce + +# The launch script will be installed here +$ /opt/jetforce/venv/bin/jetforce +``` + +## Usage + +Use the ``--help`` flag to view command-line options: + +```bash +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] + [--default-lang DEFAULT_LANG] [--rate-limit RATE_LIMIT] + +An Experimental Gemini Protocol Server + +optional arguments: + -h, --help show this help message and exit + -V, --version show program's version number and exit + +server configuration: + --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) + +fileserver configuration: + --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) + --default-lang DEFAULT_LANG + A lang parameter that will be used for all text/gemini responses + (default: None) + --rate-limit RATE_LIMIT + Enable IP rate limiting, e.g. '60/5m' (60 requests per 5 minutes) + (default: None) +``` + +### 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". + +IDNs (domain names that contain unicode characters) should be defined using +their ASCII punycode representation. For example, the domain name +*café.mozz.us* should be represented as ``--hostname xn--caf-dma.mozz.us``. + +### Setting the ``host`` + +The server's host should be set to the local socket 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 setup 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 an example `openssl` command that you can use to generate a self-signed certificate: + +``` +$ openssl req -newkey rsa:2048 -nodes -keyout {hostname}.key \ + -nodes -x509 -out {hostname}.crt -subj "/CN={hostname}" +``` + +Jetforce also supports TLS client certificates (both self-signed and CA authorised). +Requests that are made with client certificates will include additional +CGI/environment variables with information about the TLS connection. + +You can specify a CA for client validation with the ``--tls-cafile`` or ``--tls-capath`` +flags. Connections validated by the CA will have the ``TLS_CLIENT_AUTHORISED`` environment +variable set to True. Instructions on how to generate CA's are outside of the scope of +this readme, but you can find many helpful tutorials +[online](https://www.makethenmakeinstall.com/2014/05/ssl-client-authentication-step-by-step/). + +### Static Files + +Jetforce will serve static files in the ``/var/gemini/`` directory by default. +Files ending with ***.gmi** will be interpreted as the *text/gemini* type. If +a directory is requested, jetforce will look for a file named **index.gmi** in that +directory to return. Otherwise, a directory file listing will be automatically +generated. + +### Virtual Hosting + +For the sake of keeping the command line arguments straightforward and easy +to understand, configuring virtual hosting is not supported via the command +line. However, it is readily available using only a few lines of python and a +custom launch script. Check out [examples/vhost.py](examples/vhost.py) for more +information. + +Jetforce does not (yet) support virtual hosting at the TLS-layer using SNI. +This means that you cannot return different server TLS certificates for +different domains. The suggested workaround is to use a single certificate with +multiple ``subjectAltName`` attributes. There is also an +[sni_callback()](https://github.com/michael-lazar/jetforce/blob/9ac80a986c6ed8a62951c857315ca04b6d127c32/jetforce/tls.py#L140) +hook in the server codebase that can be subclassed to implement custom TLS +behavior. + +### CGI + +Jetforce supports a simplified version of CGI scripting. It doesn't +exactly follow the [RFC 3875](https://tools.ietf.org/html/rfc3875) +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. + +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 modification by the server. + +#### CGI Environment Variables + +| Name | Example Value | +| --- | --- | +| GATEWAY_INTERFACE | ``CGI/1.1`` (for compatibility with RFC 3875) | +| SERVER_PROTOCOL | ``GEMINI`` | +| SERVER_SOFTWARE | ``jetforce/0.0.7`` | +| GEMINI_URL | ``gemini://mozz.us/cgi-bin/example.cgi/extra?hello%20world`` | +| SCRIPT_NAME | ``/cgi-bin/example.cgi`` | +| PATH_INFO | ``/extra`` | +| QUERY_STRING | ``hello%20world`` | +| SERVER_NAME | ``mozz.us`` | +| SERVER_PORT | ``1965`` | +| REMOTE_HOST | ``10.10.0.2`` | +| REMOTE_ADDR | ``10.10.0.2`` | +| TLS_CIPHER | ``TLS_AES_256_GCM_SHA384`` | +| TLS_VERSION | ``TLSv1.3`` | + + +#### CGI Environment Variables - Authenticated + +Additional CGI variables will be included only when the client connection uses a TLS client certificate: + +| Name | Example Value | +| --- | --- | +| AUTH_TYPE | ``CERTIFICATE`` | +| REMOTE_USER | ``mozz123`` (the certificate subject CN attribute) | +| TLS_CLIENT_HASH | ``SHA256:86341FB480BFE333C343530D75ABF99D1437F69338F36C684C8831B63C993A96`` | +| TLS_CLIENT_NOT_BEFORE | ``2020-04-05T04:18:22Z`` | +| TLS_CLIENT_NOT_AFTER | ``2021-04-05T04:18:22Z`` | +| TLS_CLIENT_SERIAL_NUMBER | ``73629018972631`` | +| TLS_CLIENT_AUTHORISED | ``0`` (not authorised) / ``1`` (authorised) † | + +† Requires the server to be configured with a CA for validating client certificates. + +## 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](https://www.freedesktop.org/wiki/Software/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 +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 \ + --tls-cafile /etc/pki/tls/jetforce_client/ca.cer + +[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. +- ``--tls-cafile`` points to a self-signed CA that I created in order to test + accepting client TLS connections. + +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 +``` + +*WARNING* + +You are exposing a server to the internet. You (yes you!) are responsible for +securing your server and setting up appropriate access permissions. This likely means +*not* running jetforce as the root user. Security best practices are outside of the +scope of this document and largely depend on your individual threat model. + +## Releases + +To view the project's release history, see the [CHANGELOG](CHANGELOG.md) file. + +## 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. + + + + +%prep +%autosetup -n Jetforce-0.9.1 + +%build +%py3_build + +%install +%py3_install +install -d -m755 %{buildroot}/%{_pkgdocdir} +if [ -d doc ]; then cp -arf doc %{buildroot}/%{_pkgdocdir}; fi +if [ -d docs ]; then cp -arf docs %{buildroot}/%{_pkgdocdir}; fi +if [ -d example ]; then cp -arf example %{buildroot}/%{_pkgdocdir}; fi +if [ -d examples ]; then cp -arf examples %{buildroot}/%{_pkgdocdir}; fi +pushd %{buildroot} +if [ -d usr/lib ]; then + find usr/lib -type f -printf "/%h/%f\n" >> filelist.lst +fi +if [ -d usr/lib64 ]; then + find usr/lib64 -type f -printf "/%h/%f\n" >> filelist.lst +fi +if [ -d usr/bin ]; then + find usr/bin -type f -printf "/%h/%f\n" >> filelist.lst +fi +if [ -d usr/sbin ]; then + find usr/sbin -type f -printf "/%h/%f\n" >> filelist.lst +fi +touch doclist.lst +if [ -d usr/share/man ]; then + find usr/share/man -type f -printf "/%h/%f.gz\n" >> doclist.lst +fi +popd +mv %{buildroot}/filelist.lst . +mv %{buildroot}/doclist.lst . + +%files -n python3-Jetforce -f filelist.lst +%dir %{python3_sitelib}/* + +%files help -f doclist.lst +%{_docdir}/* + +%changelog +* Wed May 10 2023 Python_Bot <Python_Bot@openeuler.org> - 0.9.1-1 +- Package Spec generated @@ -0,0 +1 @@ +c5d347855371ebd64358d6bf79fe47ce Jetforce-0.9.1.tar.gz |