# ServNest installation ## Notable prerequisites * sudo 1.9.10+ (available in Debian 12+) * SFTPGo, is usually not available from most distributions (as of january 2023) * Ports 22, 53 and 443 on public IPv6 and IPv4 addresses (not required for a local development/testing setup) ## Steps [The `servnest-mkosi` repository](https://code.antopie.org/servnest/servnest-mkosi) can automatically build a system image for ServNest using configuration files and scripts provided in it. **Configuration files referred to in this document are in its `install/` subdirectory.** ### DNS resolution A caching, DNSSEC-validating and TLS-forwarding local stub resolver is recommended, e.g. systemd-resolved, Knot Resolver or Unbound. For systemd-resolved, `ResolveUnicastSingleLabel=yes` is required. ### sudo / sudoers For the HTTP hosting service, ServNest requires to execute some commands as other users through sudo. The required sudoers configuration is `sudoers` and can be placed at `/etc/sudoers.d/servnest`. ### Tor Install the `torrc` file as your Tor configuration. The `%include` statement inside it includes configuration files that will be placed inside any subdirectory of `/srv/servnest/tor-config/`, and is central to the way ServNest uses Tor. ```shell mkdir /srv/servnest/tor-config chown -R servnest:tor /srv/servnest/tor-config chmod -R u=rwX,g=rX,o= /srv/servnest/tor-config mkdir /srv/servnest/tor-keys chown -R tor: /srv/servnest/tor-keys chmod -R u=rwX,g=,o= /srv/servnest/tor-keys ``` If you're using systemd, you might need to override your distribution configuration by placing `tor.service.override.conf` inside `/etc/systemd/system/tor.service.d/`. ### Knot DNS A local primary Knot DNS server is used for both the registry and name server services. Knot DNS configuration is inside `knot.conf`. Change `42053` port to `53` and local IPs to `::` and `0.0.0.0` (or specific ones). For a public server, at least one secondary server should be set up. As zones can be dynamically added and deleted from the primary server, [catalog zones](https://zones.cat/) should be used. Configuration for a primary and a secondary server can be found respectively at `mkosi.extra/etc/knot/knot-primary.conf` and `mkosi.extra/etc/knot/knot-secondary.conf`. Add user `servnest` to group `knot` to allow ServNest to send commands to Knot: ```shell usermod -aG knot servnest ``` #### Database configuration Knot configuration must be dynamic, therefore the configuration must stored in database, using: ```shell sudo -u knot knotc conf-import /etc/knot/knot.conf ``` The configuration file won't be used by Knot anymore. #### Database configuration edition Database configuration can be changed using `knotc conf-*` commands, see [Knot DNS 3.2 documentation > Operation > Dynamic configuration](https://www.knot-dns.cz/docs/3.2/html/operation.html#dynamic-configuration). If you don't want to use that and don't want the best uptime possible, you can do the following steps to edit configuration through a plaintext file: 1. Set `enabled` to `false` in `[reg]` and `[ns]` sections of `config.ini` 2. `knotc conf-export /etc/knot/knot.conf` 3. Edit `/etc/knot/knot.conf` 4. Stop the Knot DNS daemon 5. `sudo -u knot knotc conf-import /etc/knot/knot.conf` 6. Restart the Knot DNS daemon 7. Check for errors in logs: `cat /var/log/knot/knot.log` 8. Reverse the first step to `true` #### Directories ```shell mkdir /srv/servnest/reg /srv/servnest/ns chown -R servnest:knot /srv/servnest/reg /srv/servnest/ns chmod -R u=rwX,g=rwX,o= /srv/servnest/reg /srv/servnest/ns ``` #### Registry files initialisation In addition to being described in configuration, registry zone files need to be initialized (i.e. SOA and NS records) inside `/srv/servnest/reg/`. ### ServNest core Set up the source code inside `/srv/servnest/core/`: ```shell git clone https://code.antopie.org/servnest/servnest/ /srv/servnest/core ``` Set permissions (except for `.git/` and `db/`): ```shell chmod -R u=rX,g=rX,o= $(find /srv/servnest/core -mindepth 1 -maxdepth 1 ! -name .git ! -name db) chown -R servnest:nginx $(find /srv/servnest/core -mindepth 1 -maxdepth 1 ! -name .git ! -name db) ``` Generate new SQLite database: ```shell sqlite3 /srv/servnest/core/db/servnest.db < /srv/servnest/core/db/schema.sql ``` Set permissions for database: ```shell chmod -R u=rwX,g=,o= /srv/servnest/core/db chown -R servnest: /srv/servnest/core/db ``` Initialize database secret keys: ```shell echo "UPDATE params SET value = '$(openssl rand -hex 16)' WHERE name = 'username_salt';" | sqlite3 /srv/servnest/core/db/servnest.db ``` Generate gettext translations: ```shell msgfmt /srv/servnest/core/locales/fr/C/LC_MESSAGES/messages.po -o /srv/servnest/core/locales/fr/C/LC_MESSAGES/messages.mo chmod u=r,g=,o= /srv/servnest/core/locales/fr/C/LC_MESSAGES/messages.mo chown servnest: /srv/servnest/core/locales/fr/C/LC_MESSAGES/messages.mo ``` ### PHP In addition to PHP itself, the following PHP extensions are required and their packages probably needs to be installed: * pdo * pdo_sqlite * libsodium * gettext * curl (only for the `check.php` script) You might also want to enable the OPcache extension to improve performance. #### `php.ini` Set appropriately your `php.ini` to either `php.ini-production` or `php.ini-development` (distributions usually ship `php.ini-production` as the default `php.ini`). Use `php.ini` as additional PHP configuration (e.g. in `/etc/php/conf.d/servnest.ini`). #### `php-fpm.conf` Use `php-fpm.conf` as the PHP-FPM configuration (e.g. in `/etc/php/php-fpm.d/servnest.conf`). ##### For systemd `php-fpm.service.override.conf` may be required as the PHP-FPM service configuration override. ### Certbot If you are setting up a testing environment, running `certbot` commands in this document without `--test-cert` is probably useless. Register an ACME account for Let's Encrypt (production and staging): ```shell certbot register --no-eff-email certbot register --no-eff-email --test-cert ``` Copy and adapt `certbot.ini` in `/etc/letsencrypt/servnest.ini` Install the Certbot deploy hook: ```shell cp certbot-deploy-hook.sh /root/ chmod +x /root/certbot-deploy-hook.sh ``` Getting a Let's Encrypt certificate for a wildcard domain requires an ACME [DNS challenge](https://letsencrypt.org/docs/challenge-types/#dns-01-challenge). ```shell cp certbot-dns-challenge-hook.sh /root/ cp certbot-dns-cleanup-hook.sh /root/ chmod +x /root/certbot-dns-challenge-hook.sh /root/certbot-dns-cleanup-hook.sh certbot certonly --manual -d "*.ht.servnest.example" --non-interactive --manual-auth-hook /root/certbot-dns-challenge-hook.sh --manual-cleanup-hook /root/certbot-dns-cleanup-hook.sh ``` ### nginx nginx is used for 2 purposes: * serving the PHP interface * acting as a reverse proxy before Apache, terminating TLS and enforcing headers policy Create the ACME HTTP challenge directory used by Certbot: ```shell mkdir /srv/servnest/acme chown nginx: /srv/servnest/acme chmod u=rX,g=,o= /srv/servnest/acme ``` Generate default self-signed certificates: ```shell openssl req -subj '/' -new -newkey RSA:3072 -days 3650 -nodes -x509 -keyout /etc/ssl/private/servnest.key -out /etc/ssl/certs/servnest.crt openssl req -subj '/CN=servnest.test' -new -newkey RSA:3072 -days 3650 -nodes -x509 -keyout /etc/ssl/private/servnest.test.key -out /etc/ssl/certs/servnest.test.crt openssl req -subj '/CN=ht.servnest.test' -new -newkey RSA:3072 -days 3650 -nodes -x509 -keyout /etc/ssl/private/ht.servnest.test.key -out /etc/ssl/certs/ht.servnest.test.crt openssl req -subj '/CN=*.ht.servnest.test' -new -newkey RSA:3072 -days 3650 -nodes -x509 -keyout /etc/ssl/private/wildcard.ht.servnest.test.key -out /etc/ssl/certs/wildcard.ht.servnest.test.crt ``` A precise configuration is inside the `nginx/` directory. It requires the *headers more* nginx module. This configuration listens on `[::1]:42443`, `127.0.0.1:42443`, `[::1]:42080` and `127.0.0.1:42080`. For a public server, these should be replaced respectively by `[::]:443`, `0.0.0.0:443`, `[::]:80` and `0.0.0.0:80`. Other addresses (i.e for Onion services and SFTPGo authentication) are not meant to be publicly exposed. Once this configuration is put in place, replace self-signed certificates by Let's Encrypt certificates: ```shell certbot certonly --config "/etc/letsencrypt/servnest.ini" -d "ht.servnest.example" certbot certonly --config "/etc/letsencrypt/servnest.ini" -d "servnest.example" ``` The nginx configuration provided above uses the self-signed key pair at the locations set in the `openssl` command above. Replace those by the ones Certbot told you and reload nginx configuration. Allow nginx to access certificates: ```shell mkdir -p /etc/letsencrypt/archive/ /etc/letsencrypt/live/ chmod 710 /etc/letsencrypt/archive/ /etc/letsencrypt/live/ chown root:nginx /etc/letsencrypt/archive/ /etc/letsencrypt/live/ /root/certbot-deploy-hook.sh ``` ### Apache HTTP Server Apache in distributions is usually named `httpd`, `apache` or `apache2`. Adapt the following instructions as appropriate. Apache configuration is inside the `apache/` directory. It runs Apache inside a chroot, though it is not required by the ServNest design. Some paths may need adaptation according to the distribution used (e.g. modules or logs). Set up the directory where Apache will be chrooted: ```shell mkdir /srv/servnest/ht cp -r /install/http-messages /srv/servnest/ht/http-messages chown -R root:root /srv/servnest/ht chmod -R u=rX,g=rX,o=rX /srv/servnest/ht ``` Set up the directory managed by SFTPGo users: ```shell mkdir /srv/servnest/ht/fs chown -R apache:sftpgo /srv/servnest/ht/fs chmod -R u=rX,g=rwX,o= /srv/servnest/ht/fs ``` Set up the directory accessed by Apache and managed by ServNest that maps Web addresses to users directories using links: ```shell mkdir /srv/servnest/ht/uri mkdir /srv/servnest/ht/uri/ht.servnest.test # Subpath access chown -R servnest:apache /srv/servnest/ht/uri chmod -R u=rwX,g=rX,o= /srv/servnest/ht/uri ``` For Apache to work in a chroot, hardlinking some system dependencies inside the chroot may be needed: ```shell # Display dependencies paths ldd $(which httpd) # Create hardlink's parent directory mkdir -p /srv/servnest/ht/usr/lib # Hardlink (with a specific example) ln /usr/lib/libc.so.6 /srv/servnest/ht/usr/lib/libc.so.6 ``` ### SFTPGo #### Install SFTPGo The script at `../root/sftpgo.sh` can be used to build SFTPGo from source. You can use other methods to get SFTPGo builds. Create a directory for configuration: `mkdir /etc/sftpgo` Copy the systemd service: `cp /install/sftpgo.service /etc/systemd/system/sftpgo.service` Allow listening on privileged ports: `setcap 'cap_net_bind_service=+ep' /usr/local/bin/sftpgo` #### Configure SFTPGo for ServNest Generate a key pair using `ssh-keygen -f /etc/sftpgo/ed25519 -t ed25519 -N "" -C ""` Compute key pair fingerprints: ```shell fp=($(ssh-keygen -l -f /etc/sftpgo/ed25519)) echo ${fp[1]} > /etc/sftpgo/ed25519.fp ssh-keygen -lv -f /etc/sftpgo/ed25519 | tail -n +2 > /etc/sftpgo/ed25519.asciiart ``` Copy the SFTPGo configuration: `cp /install/sftpgo.toml /etc/sftpgo/sftpgo.toml`. For a public setup, change `[[sftpd.bindings]]` sections in it to public IPs and port 22. You can optionally set up in `/etc/sftpgo/banner.txt` a message displayed to users when logging in. Add user `servnest` to group `sftpgo`: ```shell usermod -aG sftpgo servnest ``` Permissions for `/etc/sftpgo`: ```shell chown -R sftpgo: /etc/sftpgo chmod -R u=rX,g=rX,o= /etc/sftpgo chmod u=r,g=,o= /etc/sftpgo/ed25519 ``` Generate and add SSHFP record for the public SFTP domain: ```shell echo sftp.servnest.test. 86400 SSHFP 4 2 $(cut -d ' ' -f 2 /etc/sftpgo/ed25519.pub | base64 -d | sha256sum | cut -d ' ' -f 1) >> /srv/servnest/reg/servnest.test.zone ``` ### ServNest core configuration Copy the configuration template to the actual configuration file and adapt it according to [the ServNest configuration reference](configuration.md): ```shell cp /srv/servnest/core/config.template.ini /srv/servnest/core/config.ini vim /srv/servnest/core/config.ini ``` ### Cronie Set the `cronie` file as `/etc/cron.d/servnest`.