How To... | Why not..? | Scripts | Patches | ![]() |
Last update: 2024-05-18
In this article I show you how you can run the IRC bouncer pounce(1) on a public reachable server. pounce is part of the IRC suite developed by June McEnroe. You may want to give the other pieces of the suite a try, especially the IRC client catgirl(1). In case you are looking for information about IRC - Internet Relay Chat - Wikipedia is a good starting point.
In this piece I assume that you have the following:
Beside that you will need to install git on the system. One of the components - kitd(1) - is not available (yet) as package on OpenBSD.
The architecture of the software requires you to run a single pounce
process per IRC network you want to connect to. Following this I will
use a naming scheme for the instances in DNS. Let’s say your domain is
example.net
. In this article I create a subdomain irc.example.net
.
In this subdomain I create A and/or AAAA records for the IP address(es)
the service will listen on. Assuming all pounce processes will run on
the same host. Then I create a CNAME record for each network name of one
pounce process that points to the subdomain name. In the zone file of
example.net
this looks like this:
irc A 192.0.2.97
AAAA 2001:db8::c0:0:2:61
libera.irc CNAME irc
oftc.irc CNAME irc
In this example I will run pounces twice: one to connect to libera.chat and one to connect to OFTC. Please make sure you publish the names in DNS first because later you will use acme-client(1) to obtain a certificate from Let’s Encrypt for these.
Next you will want to install the required packages on your system. As mentioned in the introduction kitd(1) is not available as a package. So first install the ones that are available:
$ doas pkg_add pounce git
Next, clone the git repository of kitd(1) and install it the old way:
$ git clone https://git.causal.agency/kitd
$ cd kitd/
$ make
$ doas make install
By the way, pounce brings along calico(1) which we will use to dispatch incoming clients to the right pounce process depending on the hostname the client wishes to connect to. Unlike most packages pounce doesn’t create a user, group or rc.d script for itself. That is because there are several different ways to use pounce, and not all of them require these elements. This means you must do some of the work manually.
The user account under which pounce processes will be run should be as
restricted as possible. You don’t want to use that account for logins of
any kind. First, you should create a login class for the account. Using
the login class, you can tune certain parameters for the account if
necessary. Create the file /etc/login.conf.d/pounce
with the following
content:
pounce:\
:hushlogin:\
:umask=027:\
:tc=default:
Then you can create the account using the following command:
# useradd -c "IRC bouncer" -g =uid -k /var/empty -L pounce -m -s /sbin/nologin -u 6697 _pounce
Make sure any mails for this user get redirected to root (or any other account that receives mails):
# echo "_pounce: root" >> /etc/mail/aliases
# newaliases
Some directories are required for pounce and calico, which must be owned by the new user:
# mkdir /var/run/calico
# chown _pounce:_pounce /var/run/calico
# chmod 0750 /var/run/calico
# mkdir -p /home/_pounce/.config/pounce
# mkdir -p /home/_pounce/.local/share/pounce
You should put the first three commands above into rc.local to make sure the directory is properly setup before pounce and calico start.
As pounce only supports connections over TLS you must obtain a valid
certificate for the host names you intend to use. Instead of creating one
certificate for each hostname (= IRC network) you can create one for the
base domain irc.example.net
and make all the host names appear as
subject alternative names in it. I use acme-client(1) to obtain the
certificate from Let’s Encrypt. For this add a new block to
/etc/acme-client.conf
:
domain irc.example.net {
alternative names {
libera.irc.example.net
oftc.irc.example.net
}
domain key "/home/_pounce/.config/pounce/irc.example.net.key"
domain full chain certificate "/home/_pounce/.config/pounce/irc.example.net.pem"
sign with letsencrypt
}
I assume that you already have a working acme-client.conf(5)
on your system. If not, you can use /etc/examples/acme-client.conf
as
starting point. In that case you should carefully read acme-client(1).
Obtain the certificate for the first time, thereby verifying that your
DNS entries are published:
# acme-client irc.example.net
If everything worked as expected, you must create a cronjob for root which takes care of renewing the certificate as well as informing pounce whenever a new certificate is available:
# crontab -e
~ * * * * acme-client irc.example.net && pkill -USR1 pounce
Make sure that pounce can read the private key of the certificate:
# chown _pounce /home/_pounce/.config/pounce/irc.example.net.key
For each instance of pounce (= each IRC network to connect to) you will
require a configuration file. The example here shows the config file to
connect to libera.chat using the nick sample
.
Create the file /home/_pounce/.config/pounce/libera.conf
with the
following content:
client-cert = sample.pem
host = irc.libera.chat
join = #openbsd
local-ca = auth.pem
local-cert = irc.example.net.pem
local-host = libera.irc.example.net
local-path = /var/run/calico
local-priv = irc.example.net.key
nick = sample
sasl-external
save = libera.buffer
The file sample.pem
is the user certificate for logging in as sample
at libera.chat. Place this file in /home/_pounce/.config/pounce/
and
make it owned by _pounce:
$ doas -u _pounce cp sample.pem /home/_pounce/.config/pounce/
The other file, /home/_pounce/.config/pounce/auth.pem
, is created in
the next chapter.
Your client will use CertFP to identify themselves to pounce. With the method shown here certificate management gets easy for a handful of clients. If you need more, you probably want to implement a proper CA. For each client you need to execute the following steps to create and activate a certificate for it:
$ pounce -g client.example.net.pem
$ openssl x509 -subject -in client.example.net.pem | doas -u _pounce tee -a /home/_pounce/.config/pounce/auth.pem > /dev/null
The file client.example.net.pem
created above contains the private
key, so you want to handle it with care. You want to move it from your
server to the client system it belongs to as soon as possible.
Repeat the steps above for each client you want be able to connect to
pounce. In case you want to / have to revoke a certificate just remove
it from /home/_pounce/.config/pounce/auth.pem
. Each certificate in the
file can easily be identified because it is headed by a line containing
its subject:
$ doas -u _pounce vi /home/_pounce/.config/pounce/auth.pem
subject= /CN=client
-----BEGIN CERTIFICATE-----
MIIEojCCAooCCQDS/rz9gvX5QTAN...
-----END CERTIFICATE-----
After modifying the file /home/_pounce/.config/pounce/auth.pem
you must
notify all running pounce instances about the changes. Simply send
SIGUSR1 to the processes so each of them reloads the file:
$ doas -u _pounce pkill -USR1 pounce
Just to be sure you want to adjust the permissions on all the files in the home directory of _pounce:
$ doas -u _pounce chmod -R o-rwx /home/_pounce
Once the connection to the IRC server closes pounce exits. It will not attempt to reestablish the connection itself. That is why you need kitd. It will restart each pounce process if necessary, applying timeouts between restarts if these happen too often. You need to create each instance of pounce with an individual name. I prefer to use the IRC network name the instance connects to, making it easier to distinguish the instances. Use the following commands to create an instance of pounce, e.g. for libera.chat:
# ln -s kitd /etc/rc.d/pounce_libera
# rcctl enable pounce_libera
# rcctl set pounce_libera user _pounce
# rcctl set pounce_libera flags pounce libera.conf
# rcctl start pounce_libera
One you’ve setup all the instances for all the IRC networks you want
there is just calico left. All the instances of pounce are listening
on sockets in /var/run/calico
. Now we need one instance of calico
which will dispatch incoming connections on port 6697/tcp to the right
instance of pounce:
# ln -s kitd /etc/rc.d/calico
# rcctl enable calico
# rcctl set calico user _pounce
# rcctl set calico flags calico -H irc.example.net /var/run/calico
# rcctl start calico
So far nobody can connect to your bouncer because pf(4)
is blocking incomming connections, right? Make sure port 6697/tcp is
reachable from the outside. Add the following line to /etc/pf.conf
:
pass in on egress proto tcp from any to egress port 6697 keep state
The rule above misses any rate limiting for the connections to the port. That is because rate limiting values can bite if your clients sit behind the same NAT device.
Make sure your new rule is active:
# pfctl -f /etc/pf.conf
This chapter shows an example confiugraiton for the IRC client catgirl.
First move the client certificate you’ve created on the pounce server
to ~/.config/catgirl
:
$ mv client.example.net.pem .config/catgirl/
Next create a config file for each of the pounce instances you want to
connect to. Like pounce catgirl only ever connects to one host. Use a
terminal multiplexer like tmux(1) if you
want to connect to more than one server at the same time. The content of
the config file ~/.config/catgirl/libera.conf
should look like this:
cert = client.example.net.pem
host = libera.irc.example.net
join = #openbsd
nick = sample
sasl-external
user = client
Please note that the username in user =
is used by pounce to identify
which client is connecting to it. Make sure you provide an individual
username to each client because pounce keeps track of the position in
the buffer said username.