Goodbye ZNC, hello pounce.


(last time edited: 2021-04-22)

tags: irc, communication, bouncer


pounce is a multi-client, TLS-only IRC bouncer. It takes a simple approach, using a multiple-consumer ring buffer and the IRCv3.2 server-time extension to communicate with clients.

Looking around the Void Linux repositories I found out this little gem. It's a simple open-source IRC bouncer program written in C by June Bug, a programmer who's very into IRC communication. There are other bouncers around, including ZNC, which is the top notch bouncer all around. But I was also dissatisfied with the ZNC experience.

I want simpler things, I do not want all features that a bunch of programmers mentally masturbate of. I don't care about API, plugins that would break eventually when programming language interpreters get updated with a new version. I do not care about administration web panels and NGINX proxy pass to configure it. Fuck all that, it's stupid and the attack surface grows larger. Maybe I'm just too dumb to understand the complexity of big spaghetti code, maybe not. Maybe I don't wanna waste my time. Maybe I don't need all those features because I don't make profit by offering web services to Internet people. Maybe I just want to get things done.

Anyways. Pounce is good, pounce is love. Let's get the setup done.

First create a user in your personal server/workstation that will be running the bouncer connected 24/7.

# useradd -m -k '' pounce

And completely lock the account. Only root will have access to the account now.

# passwd -l pounce

It's better to run pounce via configuration files rather than using CLI arguments, but I will explain the basic usage for both ways.

Launch Pounce using a configuration file

local-host = # default value is localhost, meaning you'll access via IP, but you can choose your own domain
local-port = 6697 # default port for all TLS/SSL irc connections
local-priv = /path/to/letsencrypt/privkey.pem # preferably pointing to the letsencrypt certs
local-cert = /path/to/letsencrypt/fullchain.pem # same as above
local-pass = $6$f246kLc4kvK0gqCy$fsFGbhEefxe..................... # get a hashed password by using pounce -x, you will be prompted to enter a plain text password

host = # server we are connecting to
port = 6697 # default port, there are servers who don't accept TLS/SSL connections and you'll have to use 6669
join = ##linux,#voidlinux, # any channel you want the bouncer to join, comma-separated values
nick = your_username
real = your_realname
away = something something not here
quit = leaving

We need to save this configuration file inside this directory ~/.config/pounce. Filename can be whatever you want. ~/.config/pounce/freenode.conf or ~/.config/pounce/myfirstbouncyboomboom.

Once we are done, run pounce as the pounce user.

$ pounce freenode.conf &

The bouncer will now be running in the background. How can we double check if everything is working correctly?

$ pgrep pounce

Now you can connect to your bouncer from your workstation by opening Irssi or a similar IRC client, for example WeeChat. Connect to using port 6697, TLS activated and the server password will be the unhashed password you entered before in the local-pass directive.

Using Irssi:

/connect -tls -w hellothisisthepassword

Optional: Launch Pounce using command-line arguments

Some sysadmins prefer to launch their programs via command-line with a bunch of arguments making it really cumbersome to understand at first glimpse, but this method is very useful for scripting situations. It really depends.

The same configuration I posted above can be achieved by running this command.

$ pounce -H -P 6697 -K /path/to/letsencrypt/privkey.pem -C /path/to/letsencrypt/fullchain.pem -h -p 6697 -j ##linux -u your_username

Replacing password authentication with self-signed TLS certificates

As I explained before, when we wanna login to our bouncer instance we input and send a plain-text password to the server, the server receives this password and compares it to the hashed string we obtained before. If the string matches, you get logged in.

Instead of entering a long ass password everytime we want to login to the bouncer, we can generate our client private key.

On your server side, in pounce user account, generate a private key. Warning! Do not share your privkey at all. (I'll explain it how to transfer it to your workstation later.). Optional for multi-users: You can create whatever quantity of clients you want.

$ pounce -g pounceprivkey.pem

Again on your server side, keep the public keys and save them in your ~/.config/pounce directory. Optional for multi-users: You can concatenate whatever quantity of generated public keys you want to into the pouncecert.pem file.

$ openssl x509 -in pounceprivkey.pem > ~/.config/pounce/pouncecert.pem

Now in your pounce configuration directory you can remove the local-pass directive and change it to local-ca.

local-ca = /home/pounce/.config/pounce/pouncecert.pem

or as alternative use the command-line argument -A to deploy your pounce.

$ pounce -H irc.ourdomain -P 6697 -K /path/to/letsencrypt/privkey.pem -C /path/to/letsencrypt/fullchain.pem -h -p 6697 -j ##linux -u your_username -A pounceprivkey.pem

Now we need to transfer to our workstation the private key pounceprivkey.pem we've made before; otherwise we won't be able to login.

From your workstation do this:

$ scp ~

Now it's very easy to login to our bouncer.

Using catgirl. This is an IRC client also made by June Bug:

$ catgirl -e -h -c /home/your_username/pounceprivkey.pem

On Irssi:

/connect -tls -tls_cert ~/pounceprivkey.pem

Identifying to IRC servers using CertFP (SASL external)

You can identify yourself to NickServ using a plain password, or you can opt using CertFP which is a more secure way to do.

First create a private key.

$ pounce -g ~/.config/pounce/freenode-username.pem

Then add this to your instance configuration.

client-cert = /home/pounce/.config/pounce/freenode-username.pem

or alternatively via command line argument using -c /path/to/freenode-username.pem

Once you are connected to the server contact NickServ.

In Irssi:

/query NickServ CERT ADD

You can list all added certificates:

/query NickServ CERT LIST

Now once everything has been configured, activate SASL.

Your configuration should look like this.

client-cert = /home/pounce/.config/pounce/freenode-username.pem

Launch multiple Pounce instances using Calico

The calico daemon dispatches incoming TLS connections to instances of pounce by Server Name Indication (SNI). Instances of pounce should be configured with -U to bind to UNIX-domain sockets in the directory passed to calico. Note that calico is not a proxy. Incoming connections are passed directly to instances of pounce, which handle TLS negotiation. Instances of pounce and calico can be restarted independently of each other.

This program is also written by June Bug in addition to pounce. It's an extension that helps us deploy multiple bouncers.

I prefer using subdomain wildcard (* certificates for my domain. Since it's an experimental VPS I don't need to be so strict. This simplifies deployment a lot. I don't need to extend my certificates for specific subdomain names everytime I wanna add a bouncer or start a new service. I talk about more about that here.

First log in into your pounce user account and create a new temporal directory. It will act as a socket.

$ mkdir /tmp/calico

Now run calico. It's not necessary to add a subdomain into the -H (host) argument. Calico will always be running in the background. You can then add and remove pounce instances.

$ calico -H -P 6697 /tmp/calico &

Now if you have already set up multiple configurations launch them via commandline.

$ pounce -U /tmp/calico freenode.conf & pounce -U /tmp/calico rizon.conf &

Generating Let's Encrypt TLS/SSL certificates with Certbot

Certbot is a free, open source software tool for automatically using Let’s Encrypt certificates on manually-administrated websites to enable HTTPS. Certbot is made by the Electronic Frontier Foundation (EFF), a 501(c)3 nonprofit based in San Francisco, CA, that defends digital privacy, free speech, and innovation.

Creating certificates is not a big deal as it may seem. You can use Certbot plugins to deploy the certificates to specific web servers such as Apache or NGINX but we aren't gonna need that. Our bouncer doesn't need of any web server for anything. We just need the cert files. Create them manually with this command.

# certbot certonly --cert-name certs -d

They will be placed inside /etc/letsencrypt/live/certs.

Now we encounter a new problem. Our pounce user won't be able to read the certificates. They were generated under root account and placed in root directories under root permissions.

One solution among many is to create a separate UNIX user group. This solves a situation where many non-root programs need to access the certificates, such as uMurmur.

# groupadd tls-ssl-certs

Add the user pounce to the tls-ssl-certs group.

# usermod -aG tls-ssl-certs pounce

Change group recursively to every file and directory inside /etc/letsencrypt.

# chgrp -R tls-ssl-certs /etc/letsencrypt

Modify permissions recursively so users inside tls-ssl-certs group can only read and execute.

# chmod -R g=rx /etc/letsencrypt

Modify permissions so Others cannot read, write, nor execute. This is done for better security but it's not completely necessary. Just a little detail.

# chmod -R o-rwx /etc/letsencrypt

Don't forget to relog-in into your pounce account in order to refresh permissions.

Creating a runit service for calico and pounce

Create a directory for the new calico runit service.

# mkdir /etc/sv/calico

Create a runit run file/script.

# touch /etc/sv/calico/run

Open the run file with a text editor.

# vim /etc/sv/calico/run

Copy and paste the following script. It checks if the temporal calico directory socket is created and then executes the calico daemon.

[ ! "$(pgrep calico)" ] && [ -d /tmp/calico ] && rm -rf /tmp/calico
[ ! -d /tmp/calico ] && su pounce -c "mkdir -p /tmp/calico"
exec chpst -u pounce calico -H -P 6697 /tmp/calico

Add executable permissions.

# chmod +x /etc/sv/calico/run

Start the service.

# sv force-restart /var/service/calico

Now create the pounce service for an specific IRC server. The procedure is similar.

First create a service directory.

# mkdir /etc/sv/pounce-freenode

Create a run runit file/script.

# touch /etc/sv/pounce-freenode/run

Open the file using a text editor.

# vim /etc/sv/pounce/run

Add the following text.

sv start calico || exit 1
exec chpst -u pounce:tls-ssl-certs pounce -U /tmp/calico /home/pounce/.config/pounce/freenode.conf

Add executable permissions to the run file.

# chmod +x /etc/sv/pounce/run

Repeat the same for other bouncer instances.

Some tips

Some IRC servers won't let you connect via their subdomain. That is fine. Use their Certificate Common Name instead of using the Subject Alternative Name. For example: instead of

Pounce and Calico should always be run as non-root.

Pounce uses only TLS (Transport Layer Security) by default which is a modern and updated implementation of SSL (Secure Sockets Layer). That's why in the tech community they refer it most of the time as TLS/SSL. SSL is deprecated.

Pounce has lots of potential for automation. I haven't seen any running service for multiple users yet but I'm willing to test and create one just for fun.

Save your configuration files somewhere.

Bouncer password and NickServ identification password should be different from each other as a basic security measure.

Happy chatting!