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/ssmtp
using 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!!