Jetforce
一个实验性的TCP服务器,用于新开发的Gemini协议。 在这里了解更多关于Gemini的信息。
目录
特性
- 内置静态文件服务器,支持gemini目录和CGI脚本。
- 一个完整的框架,用于编写健壮的gemini应用,如astrobotany。
- 精简、现代的代码库,带有类型提示和black格式化。
- 基于twisted异步网络引擎构建的坚实基础。
安装
需要Python 3.7或更高版本。
最新的稳定版本可以从PyPI安装:
$ pip install jetforce
或者,从源码安装:
$ git clone https://github.com/michael-lazar/jetforce
$ cd jetforce
$ pip install .
或者,安装到Python虚拟环境中:
# 在某处创建一个项目目录
$ mkdir /opt/jetforce
# 激活虚拟环境并安装jetforce
$ python3 -m virtualenv /opt/jetforce/venv
$ source /opt/jetforce/venv/bin/activate
$ pip install jetforce
# 启动脚本将安装在这里
$ /opt/jetforce/venv/bin/jetforce
用法
使用--help
标志查看命令行选项:
用法: 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]
一个实验性的Gemini协议服务器
可选参数:
-h, --help 显示此帮助信息并退出
-V, --version 显示程序的版本号并退出
服务器配置:
--host HOST 服务器要绑定的地址 (默认: 127.0.0.1)
--port PORT 服务器要绑定的端口 (默认: 1965)
--hostname HOSTNAME 服务器主机名 (默认: localhost)
--tls-certfile FILE 服务器TLS证书文件 (默认: None)
--tls-keyfile FILE 服务器TLS私钥文件 (默认: None)
--tls-cafile FILE 用于验证客户端的CA文件 (默认: None)
--tls-capath DIR 包含用于验证客户端的CA文件的目录 (默认: None)
文件服务器配置:
--dir DIR 文件系统上要提供服务的根目录 (默认: /var/gemini)
--cgi-dir DIR CGI脚本目录,相对于服务器的根目录 (默认: cgi-bin)
--index-file FILE 如果目录包含此名称的文件,将提供该文件而不是自动生成索引页面 (默认: index.gmi)
--default-lang DEFAULT_LANG
用于所有text/gemini响应的lang参数 (默认: None)
--rate-limit RATE_LIMIT
启用IP速率限制,例如 '60/5m' (5分钟内60个请求) (默认: None)
设置hostname
服务器的主机名应设置为您期望接收流量的DNS名称。例如,如果您的jetforce服务器在"gemini://cats.com"上运行,您应该将主机名设置为"cats.com"。任何不匹配此主机名的URL都将被服务器拒绝,包括使用直接IP地址的URL,如"gemini://174.138.124.169"。
IDN(包含unicode字符的域名)应使用其ASCII punycode表示形式定义。例如,域名café.mozz.us应表示为--hostname xn--caf-dma.mozz.us
。
设置host
服务器的主机应设置为您想要绑定的本地套接字:
--host "127.0.0.1"
- 仅接受本地连接--host "0.0.0.0"
- 接受IPv4远程连接--host "::"
- 接受IPv6远程连接--host ""
- 接受任何接口上的远程连接(IPv4 + IPv6)
TLS证书
gemini规范要求所有连接都通过TLS发送。
如果您没有使用--tls-certfile
标志提供TLS证书文件,jetforce将自动为您生成一个临时证书。这对于简化开发非常有用,但在将服务器暴露给公共互联网之前,您应该设置更永久的证书。您可以生成自己的自签名服务器证书,或从Let's Encrypt等证书颁发机构获取证书。
以下是您可以用来生成自签名证书的openssl
命令示例:
$ openssl req -newkey rsa:2048 -nodes -keyout {hostname}.key \
-nodes -x509 -out {hostname}.crt -subj "/CN={hostname}"
Jetforce还支持TLS客户端证书(自签名和CA授权)。使用客户端证书发出的请求将包含额外的CGI/环境变量,其中包含有关TLS连接的信息。
您可以使用--tls-cafile
或--tls-capath
标志指定用于客户端验证的CA。由CA验证的连接将设置TLS_CLIENT_AUTHORISED
环境变量为True。生成CA的说明超出了本自述文件的范围,但您可以在在线找到许多有用的教程。
静态文件
默认情况下,Jetforce将在/var/gemini/
目录中提供静态文件。以***.gmi结尾的文件将被解释为text/gemini类型。如果请求一个目录,jetforce将在该目录中查找名为index.gmi**的文件以返回。否则,将自动生成目录文件列表。
虚拟主机
为了保持命令行参数简单易懂,通过命令行配置虚拟主机不受支持。但是,使用几行Python代码和一个 自定义启动脚本。查看examples/vhost.py获取更多信息。
Jetforce 目前还不支持使用 SNI 在 TLS 层面进行虚拟主机托管。这意味着你无法为不同的域名返回不同的服务器 TLS 证书。建议的解决方法是使用带有多个 "subjectAltName" 属性的单一证书。服务器代码库中还有一个 sni_callback() 钩子,可以通过子类化来实现自定义 TLS 行为。
CGI
Jetforce 支持简化版的 CGI 脚本。它并不完全遵循 CGI 的 RFC 3875 规范,但对 Gemini 的目的来说已经足够了。
服务器 "cgi-bin/" 目录中的任何可执行文件都将被视为 CGI 脚本。当 Gemini 客户端请求 CGI 脚本时,Jetforce 服务器将执行该脚本,并通过环境变量传递请求相关信息。
CGI 脚本必须将 Gemini 响应写入标准输出流。这包括第一行的状态码和元字符串,以及后续行的可选响应正文。CGI 脚本生成的字节将原样转发给 Gemini 客户端,服务器不会进行任何额外修改。
CGI 环境变量
名称 | 示例值 |
---|---|
GATEWAY_INTERFACE | CGI/1.1 (为兼容 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 环境变量 - 已认证
当客户端连接使用 TLS 客户端证书时,将包含以下额外的 CGI 变量:
名称 | 示例值 |
---|---|
AUTH_TYPE | CERTIFICATE |
REMOTE_USER | mozz123 (证书主题 CN 属性) |
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 (未授权) / 1 (已授权) † |
† 需要将服务器配置为使用 CA 来验证客户端证书。
部署
Jetforce 旨在运行于进程管理器之后,由进程管理器处理脚本的守护进程化、将输出重定向到系统日志等。我倾向于使用 systemd,因为它已安装在我的操作系统上且易于设置。
以下是我在 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
允许服务器通过 IPv4 接受来自任何 IP 地址的外部连接。PYTHONUNBUFFERED=1
禁用stderr
缓冲,有时这对日志记录正常工作是必要的。--tls-certfile
和--tls-keyfile
指向我的 WWW 服务器的 Let's Encrypt 证书链。--tls-cafile
指向我创建的自签名 CA,用于测试接受客户端 TLS 连接。
安装此服务后,我可以使用以下命令启动和停止服务器:
systemctl start jetforce
systemctl stop jetforce
我可以使用以下命令查看服务器日志:
journalctl -u jetforce -f
警告
你正在将服务器暴露于互联网。你(是的,就是你!)有责任保护你的服务器并设置适当的访问权限。这可能意味着不要以 root 用户身份运行 jetforce。安全最佳实践超出了本文档的范围,并在很大程度上取决于你个人的威胁模型。
发布
要查看项目的发布历史,请参阅 CHANGELOG 文件。
许可
本项目采用 Floodgap 自由软件许可证。
Floodgap 自由软件许可证(FFSL)有一个首要要求:使用它的软件或基于使用它的软件的衍生作品必须是免费的。我们所说的免费仅仅是指"免费如同免费的啤酒"——你可以根据自己的意愿将你的作品放入开源或闭源包中,无论你是否选择公开发布你的更改或更新,但你不得要求任何费用。