-- Leo's gemini proxy

-- Connecting to esa.prk.st:1965...

-- Connected

-- Sending request

-- Meta line: 20 text/gemini; lang=en

Jetforce Gemini server on Alpine Linux

Categories: [IT] [Gemini] [Linux]

Back home


Jetforce[1] is a simple Gemini server based on Python3. It provides a lot of install modes, but none of them is adapted to "non-mainstream" OS like Alpine Linux[2].

This log is about my struggle to make it work (spoiler: it works :D).


EDIT may 2022


Well... It *used* to work; now it's totally broken due to python/alpine/bcrypt sheningans regarding libffi dependencies. Long story short: I'm running gemserv now :)


New setup with Gemserv


About Alpine Linux


Alpine Linux is a stripped-down Linux distro mostly used for lightweight virtual machines or containers (LXC, docker, etc.). It is pretty rough compared to mainstream distros like Ubuntu or even Arch, having a different set of base utils:


Libc is not provided by glibc as usual but by musl, which is a low-footprint implementation, lacking some exotic features. As such, it's not 100% compatible with some pieces of software depending on glibc features.

Standard binaries (mostly POSIX CLI utils) are provided by BusyBox, a compact implementation lacking some GNU extensions to POSIX requirements.

Init system is not Systemd as with the vast majority of current distros but by OpenRC, a BSD-inspired init system.

Package system is not deb or rpm but apk (not related to Android packages).


Being a lightweight distro, many "implied" requirements for running Jetforce must be considered and specific actions taken.


About Jetforce


To be honest, I don't know much about Jetforce except that it works. It depends on python3 with some specific modules which must be installed. This should be an easy job for pip (Python dependency manager) but some of these modules themselves require build steps implying specific tools and external dependencies.


While installing Jetforce, I met problems mostly with "silent" dependencies:


Rust and Cargo for some Python modules.

Wheel, a python module to manage compilations needed by some modules.

GCC specificaly as some module build step tries to run gcc and not any other C/C++ compiler.


Installation steps


As Alpine Linux is a lightweight distro usually deployed on docker, containers, etc. It is assumed that no other service (except SSH) will be running on this server.


Installing Alpine Linux


Just follow the doc, nothing to see here. Just make sure to have up-to-date packages.

# apk update
# apk upgrade

Install required packages


There are quite a lot of dependencies to install before Jetforce can run (or even install). It is mostly related to Python3, Wheel, Rust, Cargo, GCC. Here is the full install command:

# apk add git python3 py3-pip python3-dev py3-wheel \
          build-base gcc libffi libffi-dev \
          openssl openssl-dev ca-certificates openssh \
          rust cargo

Install Jetforce


We will install Jetforce in /opt/jetforce without any venv or other python environment trick. This directory will only be used for building/updating Jetforce, final binary will be at /usr/bin/jetforce

First we need to clone Jetforce repository:


# mkdir /opt
# cd /opt/
# git clone https://github.com/michael-lazar/jetforce

Jetforce installation itself is done by pip and the stuff we installed before:


# cd /opt/jetforce/
# pip install .

Installation takes some time (mostly building binaries for python wrappers).


Setup environment for Jetforce


First, we'll create config directory, data directories, a new user for our new service and a group to manage gmi files ownership:


# mkdir -p /var/gemini/public
# mkdir /etc/jetforce
# adduser -S -h /var/gemini jetforce
# addgroup -S gemini

Next we'll make sure Jetforce can read but not modify anything in our public directory:


# cd /var/gemini/
# chown root:nogroup .
# chown root:gemini pub
# chmod -R g+w .

Any Gemini server needs a keypair and certificate. Gemini doesn't care about self-signed certificates, so we'll take advantage of that. If you want to get better, cleaner keys, feel free to do so. Keypair and certificate generation is done this way (securing directories as we go. Don't forget to replace "esa.prk.st" with your server FQDN:


# mkdir -p /etc/jetforce/tls/
# chown jetforce:root /etc/jetforce/tls
# chmod 500 /etc/jetforce/tls
# cd /etc/jetforce/tls/
# openssl req -newkey rsa:2048 -nodes \
    -keyout esa.prk.st.key -nodes -x509 \
    -out esa.prk.st.crt -subj "/CN=esa.prk.st" \
    -days 365
# chown jetforce:root /etc/jetforce/tls/esa.*
# chmod 500 /etc/jetforce/tls/esa.*


We're almost done. Next step is setting up a user that will be allowed to update /var/gemini/pub (by adding this user to "gemini" group). Replace "whatever" with your desired username:


# adduser -G gemini whatever


First run


We'll test everything before setting up a service. You'll need to create a basic index.gmi file in /var/gemini/public as you new user (not root) :


$ echo test > /var/gemini/public/index.gmi

Next, we'll try to start Jetforce. This has to be done as user "jetforce", which only root can impersonate, so as root run:


# su jetforce -p -c "/usr/bin/jetforce \
    --host 0.0.0.0 --hostname esa.prk.st \
    --tls-certfile /etc/jetforce/tls/esa.prk.st.crt \
    --tls-keyfile /etc/jetforce/tls/esa.prk.st.key \
    --dir /var/gemini/pub"

Try connecting to your server using your favorite Gemini client, looking for any error message.


Setup an OpenRC service


Jetforce configuration is done entierly with CLI arguments, so there is no need for a config file. Everything will go in the OpenRC service setup. We will create a service file as /etc/init.d/jetforce and related directories. We add acces to the log directory to our user.


# mkdir /var/log/jetforce
# chown jetforce:gemini /var/log/jetforce
# touch /etc/init.d/jetforce
# chmod +x /etc/init.d/jetforce

Then edit /etc/init.d/jetforce and copy this script:


#!/sbin/openrc-run

name=$RC_SVCNAME
command="/usr/bin/jetforce"
command_args="--host 0.0.0.0 --hostname esa.prk.st \
 --tls-certfile /etc/jetforce/tls/esa.prk.st.crt \
 --tls-keyfile /etc/jetforce/tls/esa.prk.st.key \
 --dir /var/gemini/pub"
command_user="jetforce"
pidfile="/run/$RC_SVCNAME/$RC_SVCNAME.pid"
command_background="yes"
output_log="/var/log/$RC_SVCNAME/$RC_SVCNAME.log"
error_log="/var/log/$RC_SVCNAME/$RC_SVCNAME.err"

depend() {
        need net
}

start_pre() {
        checkpath --directory --owner $command_user:gemini --mode 0755 /var/log/$RC_SVCNAME
        checkpath --directory --owner root:gemini --mode 0755 /run/$RC_SVCNAME
}


This is a pretty simple OpenRC init script, using a pid file and a dedicated log directory.


Now time to start it all:


# rc-update add sshd
# rc-update add jetforce
# service sshd start
# service jetforce start

Your server should now be up&running.


Next steps


Log management is currently not handled by logrotate or other tools, please configure it as you see fit.

This setup is "mono-user", if you want to host multipe capsules, you'll have to repeat this process (except Jetforce building) with new directories.

Setting up some access filter like failtoban may be a good idea to prevent some issues.


Links


[1] Jetforce Github repository

[2] Alpine Linux


-- Response ended

-- Page fetched on Mon May 13 06:37:45 2024