Adding “Let’s Encrypt” TLS to your BitlBee server

BitlBee? Some sort of new transformer?

BitlBee is an IRC-to-Instant-Messaging gateway. What this basically means is that you can connect your IRC client to BitlBee, then configure your instant messaging accounts on the BitlBee and access them through there, for example Twitter, Facebook, Steam, and even good ol’ trusty MSN and ICQ are still actually online! It also means you can live longer in your IRC client without having to open all those pesky standalone apps and webpages to be able to converse with your online friends. BitlBee also supports libpurple as a backend, which means all those special protocols Pidgin supports, like Lync (SIPE), Tox or WhatsApp, are also available should you want this!

So what’s TLS and why would I need it?

TLS, possibly better known under its former name, SSL, is an encryption-based technology designed to protect communication between machines. Straight from Wikipedia:

Transport Layer Security (TLS) and its predecessor, Secure Sockets Layer (SSL), both of which are frequently referred to as ‘SSL’, are cryptographic protocols designed to provide communications security over a computer network.[1] Several versions of the protocols are in widespread use in applications such as web browsing, email, Internet faxing, instant messaging, and voice-over-IP (VoIP). Major web sites (including Google, YouTube, Facebook and many others) use TLS to secure all communications between their servers and web browsers.

If you run your own BitlBee server on the same machine as your IRC client (which means you connect over your localhost interface), encryption isn’t strictly necessary (IF you trust all people with root access on said machine, that is, as the lo interface can technically still be sniffed).

As I run a public server ( for BitlBee users, their passwords and all their IM text is sent over the internet through my machine via the plain text IRC protocol; offering a TLS option sounds like a good idea. Since the launch of Let’s Encrypt, real signed certificates for uses such as this one are completely free, so there’s really no excuse!

Getting a Let’s Encrypt certificate

There are a number of ways to get Let’s Encrypt certificates; basically they run a server that speaks the ACME (“Automated Certificate Management Environment“) protocol. Several clients are available, among which the letsencrypt-auto client by Let’s Encrypt themselves. This is a somewhat bloated client that likes to interfere with all sorts of things if you’re not careful.

I myself have chosen, because it’s clean, simple and also because it supports the dns-01 challenge, which means you can validate ownership/control of a domain name by adding a DNS record, instead of having to serve a file over HTTP.

Installation as follows:

# apt-get install git
# cd /root; git clone
# cd
# mkdir .acme-challenges
# chmod 700 .acme-challenges

To use the dns challenge, we need to create a hook script, I’ve put it into /root/, based on the provided example:


function deploy_challenge {
    local DOMAIN="${1}" TOKEN_FILENAME="${2}" TOKEN_VALUE="${3}"

    echo "Please add the following record to the DNS zone:"
    echo "_acme-challenge.$DOMAIN IN TXT \"$TOKEN_VALUE\""
    echo "Press enter when installed!"

function clean_challenge {
    local DOMAIN="${1}" TOKEN_FILENAME="${2}" TOKEN_VALUE="${3}"

function deploy_cert {
    local DOMAIN="${1}" KEYFILE="${2}" CERTFILE="${3}" CHAINFILE="${4}"

HANDLER=$1; shift; $HANDLER $@

Make sure the script is executable so the script can run it when needed.

# chmod +x

To request a certificate, run:

# ./ --cron --challenge dns-01 --domain <> --hook /root/

This will ask you, through the hook script, to add a DNS record to your zone. When this is done, and you’re sure the record is available for Let’s Encrypt’s server to query, press enter, and a certificate will be generated. If you have a way of dynamically adding records to a DNS zone, you could even script automation here!

If you see “+ Challenge is valid!” in the output, you’re done – the certificate for the hostname you requested will be generated and stored on your machine.

Once the certificate has been generated, you can remove the TXT record from DNS.

Configuring the TLS tunnel

BitlBee itself does not contain any code to serve a secure connection. Instead, this is deferred to the stunnel program. Not an unusual choice per se, as doing it this way works fine, and allows for less code in the main daemon, which means fewer bugs all around.

The following commands are for Debian and its derivatives, they’ll be similar if you use another distribution.

Install the stunnel package:

# apt-get install stunnel4

You’ll need to create a configuration file for it, I’ve named it /etc/stunnel/bitlbee.conf:

accept = 6697
connect =
cert = /root/<>/fullchain.pem
key = /root/<>/privkey.pem

I’ve chosen port 6697 as it seems to be the generally accepted default “IRC over SSL” port. 6668 is sometimes used as well.
Incoming connections on this port will be forwarded to your local host IP address on port 6667 – change this if you run your bitlbee on a different port.

Finally, you need to enable the daemon (set ENABLED=1), in /etc/default/stunnel4:

# /etc/default/stunnel
# Julien LEMOINE 
# September 2003

# Change to one to enable stunnel automatic startup

# Change to one to enable ppp restart scripts

Certificate renewal

Let’s Encrypt certificates are valid for 90 days. This means you need to get them renewed before they expire, or IRC clients won’t like to connect to the secure port anymore, as the certificate would no longer be valid. After replacing the certificates, stunnel needs to be told to re-read them, as it will have read the certificate into memory on startup and used it from there ever since. This can be done by sending it the HUP signal. Luckily, the init script supplied with Debian knows which process to send the HUP signal to.

I’ve placed the following into /root/bin/


/root/ --cron
/etc/init.d/stunnel4 reload

The script will extend any certificate that is expiring within 30 days. After this, stunnel is HUPed, so it re-reads the (possibly) changed certificates.

This section originally contained a cron job, but I did not realise that you need to re-insert a new DNS record for an extension as well. As the hook script above can only prompt you to do it, this is not possible yet. Some alternative solutions are available in the meanwhile though. A new blog post will be forthcoming about how to really make it automagic.

Connecting to your BitlBee’s secure port with your IRC client

I only have experience with irssi myself, but I think most clients and bouncers support SSL natively these days.

To manually connect to an SSL capable IRC server from irssi, use:

/connect -ssl <hostname>

If you set your stunnel up to listen on port 6697, you don’t need to specify it – 6697 is the default ssl port for irssi.

To add a server to the configuration using SSL, just add the -ssl option into the command there as well.

To change an existing saved server to SSL, use:

/server add -ssl -port 6697 <hostname>


If everything went well, you now have a BitlBee server that is reachable through an encrypted TLS connection. The certificate is free! This is a nice step in the direction of encrypting ALL the things. Maybe now it’s time to give some thought to closing the plain text port instead, so there is no chance to accidentally use an unencrypted connection. If you want to do this, set DaemonInterface in /etc/bitlbee/bitlbee.conf.

Writing informative technical how-to documentation takes time, dedication and knowledge. Should my blog series have helped you in getting things working the way you want them to, or configure certain software step by step, feel free to tip me via PayPal ( or the Flattr button. Thanks!