Secure IRC: Authentication and Encryption

Vincent Danen

March 25, 2008

IRC (Internet Relay Chat) is an awesome means of real-time chat between individuals who share a common interest. There are many IRC servers on the internet; lots of places to find people to talk to. IRC clients exist for every operating system out there; IRC is universal.

Unfortunately, it's also insecure. A company may decide to use IRC as a means for developers to communicate with each other, but if done improperly, communication will be transmitted on the internet in the clear. Things that may have been thought to have been said in private could very well make the next day's headlines on slashdot. Another problem is lack of authentication; while one can password-protect channels on an IRC server, it's difficult to entirely prevent access to an IRC server. Someone may not be able to get into any channels, but they can still connect to the server. Worse yet, if a channel is not password protected and no authentication mechanisms are in place, someone can sneak into a channel and silently eavesdrop on everything going on, without anyone knowing.

Fortunately, the swiss-army knife that is known as SSH can come to the rescue even here. Using port forwarding in SSH, one can encrypt all traffic between client and server. Using ssh keys, the administrator of the server has complete control over who is able to connect to the server. Using some server-side configuration, this can all be done without the clients ever being able to connect and interact with a shell.

The idea behind this came from the IRCS network. The IRCS network uses IRC over SSL, which is certainly an option. The idea of using SSH came from a need to authenticate who was connecting to a server.

Compiling the IRC Server

Download the IRC software from (download irc2.10.3p1.tgz). You will also need the patch file: ircs.segfault. net/ircs/irc2.10.3p1-sky01.diff.

Once these are downloaded, untar the IRC server software into whatever directory you build source code in (ie. /usr/local/src). Apply the patch.

# cd /usr/local/src
# tar xvzf irc2.10.3p1.tgz
# cd irc2.10.3p1
# patch -p1 <../irc2.10.3p1-sky01.diff

What we will do here is setup the IRC server in a chroot environment to maximize security. The end result will be a chroot environment in /home/chrooted/ircd. Now configure it:

# ./configure --prefix=/home/chrooted/ircd

The next step is to add a user called ircd to the system, as well as a group by the same name. The default uid and gid the ircd software wants it 39, but you can change this if you have a pressing need to do so. Most individuals will be able to use that uid/gid pair withour problems.

# groupadd -g 39 ircd
# mkdir /home/chrooted
# useradd -u 39 -g 39 -d /home/chrooted/ircd -s /bin/sh -c "chroot irc daemon" ircd

Now you need to edit the config.h file that was generated during the configure stage. A new subdirectory will be in the current directory; on an Athlon running Mandrakelinux this directory was /usr/local/src/irc2.10.3p1/i686-pc-linux-gnu/. Change to this directory and use your favorite text editor to edit config.h. This is basically the configuration file for your IRC server. The following are the important settings that should be modified (we show what they should be changed to). Be sure to go through the rest of the file to set any other preferences you wish.

#if defined(CHROOTDIR)
      #define ROOT_PATH   "/home/chrooted/ircd"
#define IRC_UID 39
#define IRC_GID 39
#define CLIENT_FLOORD   8000
#define USE_SYSLOG
#define NO_IDENT
#define NO_PREFIX

The first define, CRYPT_OPER_PASSWORD indicates that crypted passwords for operators will be used in your ircd.conf file.

The CRYPT_LINK_PASSWORD define (which, using undef, disables) indicates that encrypted passwords should not be stored in N-lines for server links.

The CHROOTDIR define, and subsequent ROOT_PATH define indicate that we want ircd chrooted to the specified path, in this case /home/chrooted/ircd.

The IRC_UID and IRC_GID defines indicate that the uid of ircd is 39, and the gid of ircd is 39.

The CLIENT_FLOOD define tells the server to allow 8000 bytes allowed from the client to the server without processing before disconnecting the client for flooding it.

The USE_SYSLOG define tells ircd to log to syslog. This should only be done if you actually run the host the ircd will be running on, or if you ask the sysadmin's permission. You can also fine-tune what gets sent to syslog with some additional defines (we won't go into that detail here, but it is documented in config.h).

The NO_IDENT define indicates we don't want to support ident. By using SSH forwarding, all clients will appear to come from the localhost anyways, so this is not important.

Finally, edit the Makefile file and change the following:

LDFLAGS = -static

This indicates to the compiler that we want a statically compiled binary (a must in our chroot environment).

Now you can compile the software. Do so by executing:

# make server
# make install-server

The ircd server software is now installed in /home/chrooted/ircd.

Configuring the IRC Server

The next step is to configure ircd. This is done by the configuration file /home/chrooted/ircd/etc/ircd.conf. The following is an example configuration file; a file called example.conf is installed with detailed explanations of all the fields.

A:irc.linsec.vx:admin@linsec.vx:linsec.vx server::

Read the example.conf file for an explanation of all fields. The above is a minimalistic setup that works (no, there is no such irc.linsec.vx IRC server). Of note, this indicates the server name is irc.linsec.vx, the administrator's email address is admin@linsec.vx and that it listens to port 40200 on the loopback interface ( It also indicates that only clients coming from are permitted to connect (the I: lines).

Finally the O: line sets the IRC server operator. You can have as many of these lines as you like.

Setting up the chroot Environment

The next step is to setup the chroot environment. Essentially, you need a mini "filesystem" in /home/chrooted/ircd for ircd to live in.

The following is a step-by-step set of commands to build your chroot environment.

# cd /home/chrooted/ircd
# ln -s . chrooted
# ln -s . home
# ln -s . ircd
# ln -s . ircd-hybrid
# ln -s . usr
# mkdir bin
# cd bin
# ln -s ../sbin/ircd .
# cd ..
# mkdir dev
# cd dev
# mknod null c 1 3
# chmod 666 null
# cd ../etc
# cp /etc/host.conf .
# cp /etc/hosts .
# cp /etc/nsswitch.conf .
# cp /etc/resolv.conf .
# ln -s ../var/run/ .
# cd ..
# mkdir lib
# cd lib
# cp -av /lib/ld-* .
# cp -av /lib/libc{-2,.so,rypt}* .
# cp -av /lib/libdl* .
# cp -av /lib/libm* .
# cp -av /lib/* .
# cp -av /lib/libnsl* .
# cp -av /lib/libnss_* .
# cp -av /lib/libresolv* .

A few notes: On a BSD system, when calling mknod to make the null device, use mknod null c 2 2 instead of that noted above, which is what you would use on a Linux system. Likewise, the libraries might be slightly different depending on your host operating system and the libraries installed on it. The above list is from a Mandrakelinux 9.0 system. To see a list of the typical files you will need, IRCS has a chrooted files list available (chrooted_files.txt). When copying files from /lib, you can do it wholesale, as illustrated, provided you use the -a switch with cp, which preserves symlinks. Doing a straight copy will follow the symlink and end up taking more room than necessary in the chroot.

At this point, you should be able to start the ircd server. You can start the server many different ways, but one way is to throw it at the end of something like /etc/rc.d/rc.local; an initscript on Linux that starts last and is used for local system services. At the end of this file, start ircd by adding:

/home/chrooted/ircd/sbin/ircd -h `hostname` -q -c -s

The argument to -h is the hostname of the server; in this case we use the command hostname (included between back-ticks) to supply the server with the hostname of the machine we are running on.

At this point, use netstat to make sure that it has started and is listening to the right port and interface:

# netstat -l --tcp -p|grep ircd
tcp        0      0 localhost.localdo:40200 *:*                     LISTEN    962/ircd

This definitely indicates that ircd is listening only on the loopback interface and to port 40200, the very port we defined in ircd.conf earlier.

Setting up the SSH Forwarder Account

This is extremely simple. Create a new user account on the system, perhaps ircgroup. For a shell, you may wish to use something like /bin/rbash (restricted bash). It is not the be-all and end-all of secure shells, but it's a good start considering we really don't intend to let anyone get a shell prompt to begin with.

The premise here is that the system does not accept password authentication. So before doing anything else, edit /etc/ssh/sshd_config and set:

PasswordAuthentication no

Now restart sshd. At this point, the only someone can connect to your server is by using ssh keys. Perhaps before restarting the server, ensure you've created a keypair for yourself and have properly installed it on the server.

Edit ~ircgroup/.ssh/authorized_keys; this file will contain the ssh public keys of the users we wish to allow to connect to the IRC server. The file should look like this for each key:

command="sleep 20" ssh-dss [key]

The command here is to call the program sleep, which will hold the connection open for 20 seconds. More than long enough for someone to connect with an IRC client. The [key] is the public key of the user you are authorizing to connect.

In ircgroup's home directory, copy /bin/sleep. This is the file that will be called by the command string in our authorized_keys file.

On the client side, the end user who will be connecting to the system should modify their ~/.ssh/config file. This isn't necessary, but adds a level of convenience for the end user. Their file may look something like this:

Host privateirc
  Hostname irc.linsec.vx
  User ircgroup
  LocalForward 6667 localhost:40200

Finally, for the end user to connect to our protected IRC server, they should have their IRC client open and define a new server as "localhost:6667", giving it whatever name they prefer. Essentially, you want the client to connect to port 6667 on the localhost, which is our side of the SSH tunnel. To start the tunnel, the user should execute:

$ ssh -f privateirc foo

In the above, "foo" is some arbitrary text. No matter what they type, the sleep command will always be executed, provided that you, as the administrator, ensure that each key is prefixed with that command. So here, the user is opening up a connection to "privateirc", an alias in their ~/.ssh/config file, which in turn creates an encrypted tunnel from port 6667 on the localhost to port 40200 on the remote system's localhost. At this point, the user has 20 seconds to connect to port 6667 on the localhost before ssh closes the tunnel.

Once the user has done so, they will be successfully connected to your IRC server, protected via an encrypted tunnel, and authenticated via their private SSH key.

The really nice thing about this method, as opposed to using strictly SSL encryption, is that the administrator of the server has full control over who obtains access in the first place. If a user is no longer wanted on the server, the admin simply has to remove their SSH public key from the ircgroup user's authorized_keys file and that individual will have no access to the system at all. And by using keys, you're not handing out a password for the ircgroup system user. Not that they would be able to use it even if you did as password authentication really should be turned off.

Using SSL

If you do not need the authentication aspect of a secure IRC server, you can also wrap everything in SSL. There are two ways this can be accomplished. One is to wrap the service in SSL using a program called sslwrap, however this method is primarily useful if you plan to run your IRC from inetd, a perhaps unefficient way to do it. The other is to use a program called stunnel which will run standalone, and listen for incoming connections to hand off to ircd, all the while encrypting traffic. Stunnel is the better choice, but we'll cover both.


To use sslwrap, you will need to download the source code from sslwrap/ and build it. On a Mandrakelinux 9.1 system, some changes were required to make it compile; other systems may or may not require these steps:

# cd /usr/local/src
# tar xvzf sslwrap.tar.gz
# cd sslwrap206
# perl -pi -e 's/OPENSSL\"/\"openssl\//g;' *
# make

The end result is the binary sslwrap file in the current directory. The above bit of perl magic changed the define location for OpenSSL</noop> as the define did not properly work on a Mandrakelinux 9.1 system. Again, on others you may be able to do this without change. It also assumes that your OpenSSL</noop> libraries and header files are either in /usr/include/openssl and /usr/lib/openssl or else in /usr/local/ssl/include/openssl and /usr/local/ssl/lib/openssl. If this is not the case, modify the Makefile to suit your system. Now install the binary somewhere in your path; ie:

# install -m755 sslwrap /usr/bin


If you've chosen to use stunnel, download the source from and build it:

# cd /usr/local/src
# tar xvzf stunnel-4.04.tar.gz
# cd stunnel-4.04
# ./configure --prefix=/usr/local/stunnel
# make
# make install

This will install stunnel into /usr/local/stunnel. During the make install stage you will be asked for information to create an initial certificate file.

Configuration and SSL Keys

Everything that follows is common for stunnel and sslwrap.

Once this installed, edit /etc/services and add the line:

ircs            994/tcp
ircs            994/udp

if it is not already in your /etc/services file.

You will now need to create an SSL certificate file to be used with your IRC server. If you don't already have an RSA key, you can create one using:

# openssl genrsa -out newkey.pem 2048

The next step is to create a certificate request:

# openssl req -new -key newkey.pem -out newreq.pem -days 9999

At this point you can send the newreq.pem to an official Certificate Authority to be signed, or you can use your own CA to sign it.

Assuming the returned, signed, certificate file is called newcert.pem use the following to create your final server key:

# cat newkey.pem newreq.pem newcert.pem >/usr/local/ssl/certs/ircs_myserver.pem

This of course assumes you want your certificate stored in the directory /usr/local/ssl/certs; you are free to use any directory you wish. Just ensure that you use the appropriate path in your inetd.conf configuration, as illustrated above, and in your stunnel commandline, illustrated below.

Next, if you've chosen to use sslwrap, edit /etc/inetd.conf and add:

ircs stream tcp nowait  nobody.nogroup /usr/sbin/tcpd /usr/bin/sslwrap -cert /usr/local/ssl/certs/ircs_myserver.pem -port 40200

If you plan to use stunnel, you will be running it standalone. You will want to add the following to your /etc/rc.d/rc.local (or similar) file prior to the line that starts ircd:

/usr/local/stunnel/sbin/stunnel /usr/local/stunnel/etc/stunnel/ircs.conf

There are a few more steps to take before you can launch stunnel. You will need to create a directory for it to write it's PID (Process ID) file; we'll use /usr/local/stunnel/var:

# mkdir /usr/local/stunnel/var
# chown nobody.nogroup /usr/local/stunnel/var

You will also need to create a configuration file for stunnel to use. We'll create a new file called /usr/local/stunnel/etc/stunnel/ircs.conf and it should have the following contents:

cert = /usr/local/stunnel/etc/stunnel/ircs_myserver.pem
pid = /usr/local/stunnel/var/

setuid = nobody
setgid = nogroup

service = ircs

accept  = 994
connect = 40200

This tells stunnel the location of our certificate file, the location of our PID file, who to run the service as (nobody.nogroup), that the service we are offering is ircs and that we are to accept incoming connections on port 994 and connect to port 40200 on the localhost (the port we chose when configuring ircd).

In our illustration, we are running both sslwrap and stunnel as the user nobody and the group nogroup. You can certainly change this (ie. use a user/group of ssl or something similar). Modify the above illustrations to suit your setup. Be sure that ircs_myserver.pem (your SSL key) is owned by the user and group that will be running sslwrap or stunnel. Also ensure the permissions on the key file are mode 0600.

In both cases, the end client will have to connect to port 994 on your server. You should firewall off port 40200 (in our example, normally IRC runs on port 6667) so they cannot connect to the server without the benefit of using SSL. Some IRC clients understand SSL and connect to port 994 without any help, others will require that the client first establish a tunnel using stunnel on the client side.

At this point, you should restart inetd or fire up stunnel and test your setup. If all is well, you'll have SSL-wrapped IRC conversations that are more difficult to eavesdrop on. However, for the best protection, using SSH as a "frontend" to IRC should be used as it provides both encryption and authentication, whereas SSL only provides encryption.