Recently I decided it was time again to do some IT work on my colo host. Besides a nessesary upgrade of the Operating System I was finally ready to start using IPv6 (I know! This was long overdue!). And while tinkering around to get everything once more in prestine order, I decided it was also time to work on some other projects I had been delaying indefinitely. One of them is hosting a private cloud (perhaps I will dedicate a separate blog on that one); and the other one was having access to IRC from any device I work with (including 24/7 access to the joined channels/servers).

Sure enough I could just install a terminal app on my phone and iPads, use tmux or some other tool to enable attaching to and detaching from active sessions and run a standard IRC client in that, but I wanted something a bit less old sk00l (although I agree with you that using IRC is probably considered old sk00l in and by itself). So I asked around, and a few friends pointed me to a self-hosted web IRC client called The Lounge.

So I created a new jail, assigned IPv4 and IPv6 addresses to it and allowed HTTP and HTTPS traffic to communicate with whatever was listening. I use iocage to manage my jails on this machine. After the rewrite to Python it proofed a very convenient way to do all necessary maintenance on these environments.

Since there is no package or port for The Lounge I had to do some additional work in setting things up. Nothing fancy, but quite a bit more than install the ports collection using portsnap fetch; portsnap install followed by cd /usr/ports/irc/...; make install clean and modifying some configuration files, or directly install a pre-built binary package using pkg install .... As all users of FreeBSD know, you can install most applications from the ports collection using binary packages that are built using standard settings and can easily be upgraded (or downgraded as we will notice a bit further on), or use the ports to build the same applications on your own system. These days I would highly recommend using the binary packages even on a production systems since it is much faster and more efficient. But because this is just a pet project and the machine is mostly idling I used the ports to compile and install all necessary applications (and their dependencies).

A couple of applications I always want to have available are ports-mgmt/portmaster and security/sudo using:

cd /usr/ports/security/sudo; make install clean
cd /usr/ports/ports-mgmt/portmaster; make install clean

if needed modifying the configurations, and eventually answering y to the question if you really want to install that application and all its dependencies.

Next on the list are nginx, py-certbot-nginx and mail/ssmtpusing portmaster:

portmaster www/nginx
portmaster security/py-certbot-nginx
portmaster mail/ssmtp

Like I already mentioned there is no port or package for The Lounge. Reading the website reveals that this is a program written in JavaScript node and digging a bit further shows that there is a package manager called yarn to install and upgrade Node.js packages. So the next step is to install these as well:

portmaster www/node
portmaster www/yarn

Since I want to run The Lounge under its own userID I create one using:

pw adduser thelounge -d /nonexistent -s /usr/sbin/nologin -c "The Lounge Pseudo-User"

The next step is to install The Lounge using yarn, so I create a configuration directory, change its owner accordingly and get The Lounge from a repository:

mkdir /usr/local/etc/thelounge
chown thelounge /usr/local/etc/thelounge

yarn global add thelounge

Next we run The Lounge as thelounge user:

su -m thelounge -c "setenv THELOUNGE_HOME /usr/local/etc/thelounge; \
    /usr/local/bin/thelounge start"

As you may have noticed we need to set the THELOUNGE_HOME enviroment variable. This ensures that the configuration ends up in the correct directory. Once it stops generating outpu you can press Ctrl+C to stop it. A default configuration file is now stored in /usr/local/etc/thelounge. Next a The Lounge user (<name>) is created, which is the account you will use to connect:

su -m thelounge -c "setenv THELOUNGE_HOME /usr/local/etc/thelounge; \
    /usr/local/bin/thelounge add <name>"

All FreeBSD services are started and stopped using the service command which uses scripts stored in the either the /etc/rc.d or /usr/local/etc/rc.d directory. Therefore we create a basic script and store it in /usr/local/etc/rc.d/thelounge using the following commands:

mkdir -p /usr/local/etc/rc.d && vi /usr/local/etc/rc.d/thelounge

The code stored in that file reads as follows:

#!/bin/sh

. /etc/rc.subr

name="thelounge"
rcvar=thelounge_enable

load_rc_config ${name}

command=/usr/local/bin/thelounge

start_cmd="${name}_start"
stop_cmd="${name}_stop"
status_cmd="${name}_status"

: ${thelounge_enable="NO"}
: ${thelounge_user="thelounge"}
: ${thelounge_home="/usr/local/etc/thelounge"}

thelounge_start(){
        cmd="${command} start"
        if thelounge_running; then
                echo "The Lounge is already running!"
        else
                su -m ${thelounge_user} -c \
               "setenv THELOUNGE_HOME ${thelounge_home}; ${cmd} &" > /dev/null 2>&1
        fi
}

thelounge_stop(){
        if thelounge_running; then
                pgrep -u ${thelounge_user} | xargs -I _ kill -s SIGINT _
        else
                echo "The Lounge is not running!"
        fi
}

thelounge_status(){
        if thelounge_running; then
                echo "The Lounge is running."
        else
                echo "The Lounge is stopped."
        fi
}

thelounge_running(){
        pids=$(pgrep -u ${thelounge_user})
        [ ! -z "$pids" ] && return 0 || return 1
}

run_rc_command "$1"

Save this code and exit vi, mark the file as executable, enable the service and start The Lounge:

chmod +x /usr/local/etc/rc.d/thelounge
sysrc thelounge_enable="YES"
service thelounge start

By default The Lounge will listen on port 9000, and using sockstat you should now see a service listening on that port on all interfaces. To complete this basic setup I use certbot to generate a certificate and key used by the nginx reverse proxy webserver, so that The Lounge is accessible using an HTTPS connection. Only at the time I wrote this blog there was a problem. py-openssl which is used by certbot wanted to use a version of py-cryptograpy that was not yet available, resulting in a well known flurry of Python error messages. So before I could use certbot I needed to downgrade py-openssl to a previous version, by first removing the installed package/port from the system and then install the downgraded package:

portmaster -e py37-openssl
pkg install py37-openssl-19.1.0

Using the excellent howto available on the certbot website at https://certbot.eff.org/lets-encrypt/freebsd-nginx.html I generated the certificate:

certbot certonly --standalone

And created a cron job for automatic renewal:

echo "0 0,12 * * * root python3.7 \
    -c 'import random; import time; time.sleep(random.random() * 3600)' \
        && certbot renew -q" | sudo tee -a /etc/crontab > /dev/null

Now all that was left to do was to configure and enable nginx and ssmtp. First the relatively easy configuration of ssmtp to enable the system to send mail to an administrative user. For this we need to change the contents of /etc/mail/mailer.conf to read:

# $FreeBSD: releng/12.2/etc/mail/mailer.conf 363973 2020-08-06 18:13:45Z kevans $
#
# Execute the SSMTP program
#
sendmail        /usr/local/sbin/ssmtp
send-mail       /usr/local/sbin/ssmtp
mailq           /usr/bin/true
newaliases      /usr/bin/true
hoststat        /usr/bin/true
purgestat       /usr/bin/true

Next we disble the use of sendmail if this is not already done by editing the /etc/rc.conf file, and since we are already there we also enable the use of nginx. The content of this file needs to show something like:

cron_flags="$cron_flags -J 15"

# Disable Sendmail by default
sendmail_enable="NO"
sendmail_submit_enable="NO"
sendmail_outbound_enable="NO"
sendmail_msp_queue_enable="NO"

# Enable IPv6
ipv6_activate_all_interfaces="YES"
hostname="irc"
rtsold_enable="NO"

thelounge_enable="YES"
nginx_enable="YES"

And modify the /usr/local/etc/ssmtp/ssmtp.conf file to use your mail server. Afther that you can test it using a command like this:

sendmail -s "Test SSMTP" you@example.com < /usr/local/etc/ssmtp/ssmtp.conf

Now before we can start the nginx reverse proxy, we need to configure it as such, and also ensure that eventually all traffic is using strong encryption over HTTPS (port 443/tcp). This is done by modifying the /usr/local/etc/nginx/nginx.conf file. I want to point you to the `Moz://a SSL Configuration Generator website, where you find many up-to-date configurations to ensure that strong encryption is used. I modified this file to contain basically the following:

worker_processes  1;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;

    keepalive_timeout  65;

    server {
    # ENFORCE HTTPS
        listen       80;
        server_name  <My_Server_FQDN>;
    return 301 https://$server_name$request_uri;
   }

   server {
    listen 443 ssl;
    listen [::]:443 ssl;
    ssl_certificate /usr/local/etc/letsencrypt/live/<My_Server_FQDN>/fullchain.pem;
    ssl_certificate_key /usr/local/etc/letsencrypt/live/<My_Server_FQDN>/privkey.pem;

        include /usr/local/etc/letsencrypt/options-ssl-nginx.conf;

        ssl_dhparam /usr/local/etc/letsencrypt/ssl-dhparams.pem;

        add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" \
            always;

        ssl_trusted_certificate /usr/local/etc/letsencrypt/live/<My_Server_FQDN>/chain.pem;
        ssl_stapling on;
        ssl_stapling_verify on;

    client_max_body_size 10m;

        location / {
            proxy_pass http://<My_Server_FQDN>:9000/;
            proxy_http_version 1.1;
            proxy_set_header Connection "upgrade";
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header X-Forwarded-For $remote_addr;
            proxy_set_header X-Forwarded-Proto $scheme;
        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   /usr/local/www/nginx-dist;
        }
    }
}

In the file above the <My_Server_FQDN> is the full domain name of this server which the DNS service will translate into the used IPv4 and IPv6 addresses assigned to this jail. As you may haqve noticed there are two additional files mentioned in this configuration. The first is /usr/local/etc/letsencrypt/options-ssl-nginx.conf. It contains the SSL configuration, and should read something like this:

# This file contains important security parameters. If you modify this file
# manually, Certbot will be unable to automatically provide future security
# updates. Instead, Certbot will print and log an error message with a path to
# the up-to-date file that you will need to refer to when manually updating
# this file.

ssl_session_cache shared:le_nginx_SSL:1m;
ssl_session_timeout 1440m;
ssl_session_tickets off;
ssl_session_cache   shared:SSL:10m;
ssl_prefer_server_ciphers off;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ecdh_curve secp384r1:X25519:prime256v1;

ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256: \
             ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384: \
             ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305: \
             DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';

As I have modified this file I will probably receive error messages, but these parameters currently ensure an A+ rating on Qualis SSL Labs. The other one is /usr/local/etc/letsencrypt/ssl-dhparams.pem. It contains the used Diffie-Helman parameters for this server and is generated using the following command:

openssl dhparam -out /usr/local/etc/letsencrypt/ssl-dhparams.pem 4096

This will probably take a long time. After all this is in place you can now start nginx using:

service nginx start

And browse to https://<My_Server_FQDN>/ (if you use http://<My_Server_FQDN>/ you will automatically be redirected to the HTTPS version) and start using The Lounge. Enjoy!!