Since the early days of QuakeNet I’ve always wanted to run an IRC server but I’ve never had a real reason to do so. With Ablative Hosting starting to gain traction I’ve found that some people don’t want to send an email to ask a question or get support. It seems I now have my reason.

This is not a guide for creating an anonymous .onion IRCd

Choosing an IRCd

As a rule Ablative (and Brass Horn Comms for that matter) either use software available in OpenBSDs base, at a push in ports or we write it ourselves.

There’s not a chance I was going to write an IRCd so that leaves us with the options in base and ports.

Base has

  • irc-
  • ircII-20190117.tgz
  • ircd-hybrid-8.2.24.tgz
  • ircd-ratbox-3.0.10p3.tgz

This project has started off using ircd-hybrid which, as seen above, is 8.2.24 at time of writing.

Installing and initial configuration

We use Ansible to deploy all our software and this is no different, installing is simple;

- name: Install ircd                                                                                                          
  become: true                                                                                                                
    name: "ircd-hybrid--"                                                                                                     
    PKG_PATH: "{{PKG_PATH}}"                                                                                                  
    - start ircd

The OpenBSD package includes a very verbose example configuration which we need to pare down. We hit our first stumbling block right at the beginning;

serverinfo {
	# Yeah Gen3 .onions are too long :/
        #name = "irc.hzwjmjimhr7bdmfv2doll4upibt5ojjmpo3pbp5ctwcg37n3hyk7qzid.onion";
	#name = "";

Generation 3 .onions are too long for the server info block which is a shame, but as it’s only a cosmetic issue we’ll ignore it and move on.

Because Ablative has an EV SSL certificate we extended it to allow a wildcard (wildcard entries are not normally allowed for EV certificates but are allowed for .onions!) and can enable this IRCd to support SSL.

SSL for an .onion provides no additional encryption but it does provide stronger guarantees that you have connected to the server you intended to.

rsa_private_key_file = "/etc/ircd-hybrid/certificate.key";
ssl_certificate_file = "/etc/ircd-hybrid/certificate.crt";
ssl_dh_param_file = "/etc/ircd-hybrid/dhparam.pem";

To use Perfect Forward Secrecy a server needs Diffie-Hellman parameters but due to various issues you need to ensure you generate a strong set;

- name: Generate the dhparams
  become: true
  shell: "/usr/bin/openssl dhparam -out dhparam.pem 2048"
    chdir: /etc/ircd-hybrid/
    creates: dhparam.pem

You can configure information about the administrator of the server, in this case any administrative problems can be referred to Ablative’s support team who’ll escalate / resolve as required;

admin {
        name = "Support Team";
        description = "Ablative Support Team";
        email = "<>";

We now hit the first part of the example config that will cause us issues when running an IRCd over .onion, the user classes;

class {
        name = "users";
        ping_time = 90 seconds;
        number_per_ip = 2;
        max_local = 2;
        max_global = 10;
        max_number = 100;
        sendq = 100 kbytes;
        recvq = 2560 bytes;

Because all of our users will appear from a single IP address (or more if using OnionBalance) these numbers will need to tweaked accordingly.

Once the various classes of users have been created (we might want to create different classes of users for say authenticated customers, staff, admins, opers etc) we need to tell the server which ports and IP addresses to listen on. This part of the config is quite important for how we partition user classes and permissions in a .onion centric server.

listen {                                                                                                                      
        flags = hidden;
        host = "";
        port = 6667;

        flags = hidden;
        host = "";
        port = 6665 .. 6669;
        flags = hidden, ssl;
        host = "";
        port = 6697;

The reason we bind the server to two different /24 networks is because we have 2 different interfaces on the IRCd server, one faces the ‘public’ Tor daemon and the other is a Gen3 authorized .onion.

Authenticating Anonymous Users - Gen3 Onion Authentication and IRCd Passwords

@kushaldas has recently written about creating authorized v3 .onions so I don’t need to cover it here.

Once you’ve configured your authenticated .onion you should now have two .onion addresses, one that points to and requires Tor client authorization and another open service that anyone can connect to.

Just in case we have a situation where someone has configured their local network to route all traffic via Tor and that Tor instance has the client cookie we can configure IRCd to also require a password when connecting from this IP address, the password is stored in a hashed format using mkpasswd;

auth {                                                                                                                        
        user = "*@";
        password = "<snip>"
        encrypted = yes;
        spoof = "";
        class = "opers";
        flags = need_password, spoof_notice, exceed_limit, kline_exempt,
                xline_exempt, resv_exempt, no_tilde, can_flood;

Then we configure the auth section for everybody else;

auth {
        user = "*@*";
        spoof = "";
        class = "users";

Now that we can classify our users we can define some IRC operators;

operator {                                                                                                                    
        name = "gareth";                                                                                                      
        user = "*@";                                                                                                 
        password = "<snip>";
        encrypted = yes;                                                                                                      
	ssl_certificate_fingerprint = "<snip>";
        #ssl_connection_required = yes;                                                                                       
        class = "opers";
        whois = "is a Smurf Target (IRC Operator)";
	umodes = locops, servnotice, wallop;
	flags = admin, connect, connect:remote, die, globops, kill, kill:remote,                                              
                opme, nick:resv, kline, module, rehash, restart, set, unkline, unxline, xline;                                

Note that in order to become an operator this user (e.g. me) has to be connecting from, has to present a password (again stored in hashed format) and provide an SSL certificate.

Ablative uses mutual TLS auth between all microservices so it seems natural to use certificates for client auth where we can.

End of Part 1

As it stands you could now run doas /etc/rc.d/ircd-hybrid start && doas /etc/rc.d/tor start and have an up and running IRCd server but there are certain features that people expect of an IRCd. Namely services such as NickServ and Chanserv.

Part 2 will discuss the deployment and configuration of services.

The Ablative IRCd is available at irc.hzwjmjimhr7bdmfv2doll4upibt5ojjmpo3pbp5ctwcg37n3hyk7qzid.onion:6667 and supports TLS on irc.hzwjmjimhr7bdmfv2doll4upibt5ojjmpo3pbp5ctwcg37n3hyk7qzid.onion:6697