Posted on

Last time, we looked at how one could maintain a persistent session in IRC through the use of a terminal multiplexer (such as screen or tmux) and SSH. While this has the advantage of being very easy to setup, there are a few very obvious disadvantages and trade-offs:

  • You must be on a machine that is able to SSH into your remote server.
  • You are confined to using the command-line IRC client running in the remote session.
  • Any scripts that attempt to interact with your ‘local’ desktop (e.g. Growl notifications), are painful to setup, if not impossible.

For many, including myself, the first two are deal-breakers. I use a variety of computing devices on a daily basis, and asking me to SSH into a remote server from my smartphone to then attach a screen session is not reasonable. Luckily, there does exist a method for establishing a ‘perpetual’ IRC session that does not suffer from any of the disadvantages brought on by the SSH + multiplexer approach.

Bip, The IRC Proxy

Bip is an IRC proxy by Arnaud Cornet and Loïc Gomez, and is released under the GPLv2 license. From their about page:

[Bip keeps you] connected to your preferred IRC servers, can store the logs for you, and even send them back to your IRC client(s) upon connection. You may want to use bip to keep your logfiles (in a unique format and on a unique computer) whatever your client is, when you connect from multiple workstations, or when you simply want to have a playback of what was said while you were away.

Strangely, Bip does not seem to be very well known amongst people I would call "heavy" IRC users, despite it's very lengthy list of useful features and the relatively simple and straightforward setup.

Remote Server Setup

It may be possible to install Bip through a package manager, but we'll go right to building from source to ensure that everyone is on the same page. Feel free to skip this part if you are able to install bip through your distribution's package manager.

Note: You will need to have git, make, gcc, lex, and yacc (the latter two are for lexing/parsing the bip configuration file) to compile from source.

$ git clone http://rcs-git.duckcorp.org/projects/bip/bip.git
$ cd bip
$ ./bootstrap
$ ./configure # Use the --prefix option to install somewhere other than /usr/local
$ make && make install

Assuming we have the proper dependencies installed and there were no errors, we should have two binaries — bip and bipmkpw — installed in /usr/local/bin/ (or whatever path was specified for the ––prefix option during the configure phase. In case you decided to skip the make install part of the above instructions, the compiled binaries are available in the src folder of your bip source directory. Place these binaries in an appropriate location, preferably reachable via $PATH.

Configuration

Note: Nearly all of this configuration information can be found in the README file included with bip.

For this configuration file to work, we need to create a directory to store a few things:

$ mkdir -p ~/.bin

Let's start off with the most bare-bones configuration file possible. Create a file named bip.conf in your $HOME/.bip/ directory with the following contents:

# Remember to change out $HOME for your home directory (e.g. `/home/joel`) below.

ip = "0.0.0.0";
port = 6667;
log_level = 2;
log_root = "$HOME/.bip/logs";
log_sync_interval = 5;

# Network definition, a name and server info
network {
	name = "freenode";
	server { host = "irc.freenode.net"; port = 6667; };
};

user {
	name = "joel";
	password = "<password_hash_here>";
	default_nick = "jperras";
	default_user = "jperras";
	default_realname = "Joël";

	backlog = true;		        # enable backlog
	backlog_lines = 200;		# number of lines in backlog, 0 means
					# no limit
	backlog_always = false;		# backlog even lines already backlogged
	backlog_reset_on_talk = false;
	backlog_reset_connection = false;
	backlog_msg_only = true;


	connection {
		name = "primary";		# used by bip only
		network = "freenode";	        # which ircnet to connect to

		# Autojoined channels:
		channel { name = "#awesome-channel"; };
		channel { name = "#secret-channel"; key = "passw0rd"; };
		channel { name = "#unimportant"; backlog = false; };
	};
};

Alright, let's go through what these options actually do.

ip = "0.0.0.0";
port = 6667;

The ip string indicates from which IP bip should accept incoming connections, and port is self-explanatory. By setting it to 0.0.0.0, bip will accept a connection from any IP, which is most likely what you want. Additionally, restricting incoming connections to certain IP addresses for particular ports is something that can (and should) be pushed to your firewall.

Note: Be sure that you have left an opening in your firewall for connecting to your proxy!

log_level = 2;
log_root = "$HOME/.bip/logs";
log_sync_interval = 5;

A log_level of 2 indicates that we want to log all errors and warnings, but ignore info and debug messages. The log_root indicates where logs should be stored, and log_sync_interval dictates at what time interval the log file is written to disk.

network {
    name = "freenode";
server { host = "irc.freenode.net"; port = 6667; };
};

Next, we define a network that we would like to connect to. You can specify an arbitrary name – I could have just as easily called this network opensource – and the relevant connection information in the server key.

{
    name = "joel";
    password = "<some_long_password_hash>";
    default_nick = "jperras";
    default_user = "jperras";
    default_realname = "Joël";
        # ...

Here, we define a bip user named “joel” (this name is only used when you connect from your local client to your proxy), and a password. The password that you insert here should be created through the bipmkpw binary, which will salt & hash an input string to produce the value that needs to be placed in the above user {} block. The other three options of default_nick, default_user, and default_realname should be self-evident if you have ever used IRC before.

backlog = true;
backlog_lines = 200;
backlog_always = false;
backlog_reset_on_talk = false;
backlog_reset_connection = false;
backlog_msg_only = true;

Now we start getting into the more interesting configuration options for bip. backlog = true enables backlogging, which means that when your local client (e.g. smartphone, desktop client, web client) reconnects to your proxy, all the messages that have been logged since your last connection will be replayed to you. This is, in my opinion, the best reason to consider using an IRC proxy such as bip; you'll never miss a message, conversation or context again.

  • backlog_lines = 200 sets an upper limit to the number of messages per channel that will be sent to your client upon reconnection.

  • backlog_always = false indicates that you don't want to receive backlog messages that you have already seen. By setting this option to true, every time you connect a client to your proxy you will receive backlog_lines of backlog messages for each channel. If you do enable this option (which comes in handy when you regularly connect via two or more devices), make sure the value for backlog_lines is something reasonable.

  • backlog_reset_on_talk and backlog_reset_connection indicate that the backlog marker should be reset every time you talk, scoped to the particular channel or entire connection, respectively.

By setting backlog_msg_only = true, we only log messages and ignore all IRC events, including joins/parts/quits/nick changes, and the like.

On to the connection definition:

connection {
        name = "freenode";                # used by bip only
        network = "freenode";         # IRC network

        # Autojoined channels:
        channel { name = "#awesome-channel"; };
        channel { name = "#secret-channel"; key = "passw0rd"; };
        channel { name = "#unimportant"; backlog = false; };
    };

First, we give a name to the connection (which will be useful in the following segment), and specify which network (defined previously) that we wish to connect to.

Finally, we specify the channels that we wish to automatically connect to when first starting up the bip proxy. Note the use of key for password protected channels, and the backlog = false usage for channels that we never want to receive a backlog for.

Note that these channels are auto-joined when the bip daemon starts, and not each time your local client connects to the proxy. Once bip has started up and we connect to it, we are free to join/part channels at will, and these will be persisted between client connections.

Starting The Server

Alright, let's start the bip process:

$ /usr/local/bin/bip -f /path/to/bip.conf

Note: If things don't seem to be working as expected, tail'ing the bip log file may yield some clues as to what is going wrong.

The bip process will daemonize itself by default; use the -n option to have it remain in the foreground, if so desired.

When you start the bip process, it initiates the IRC handshake & dance that normally occurs when you connect a local client to an IRC network. The channels that you defined in the bip configuration file are now connected to and being kept open by bip.

Connecting to the Proxy

Now that we've started the bip proxy on our remote server, let's fire up an IRC client and configure the server connection information.

Examples

I've included a few examples of how the server connection information should look like (given the example bip configuration that I've provided above), in both a GUI-based client as well as a text-based one.

Important: The password that must be used when connecting to your proxy must follow this pattern:

username:unHashedPassword:network

Where the username portion corresponds to the value of the name field in the user { } block in your bip configuration, the unHashedPassword portion is the un-hashed version of the password that you generated (using bipmkpw) and pasted in your config, and network is the name of the IRC network (also defined in your configuration) that you wish to connect to.

An interesting thing to note is that, with this particular scheme, it's quite simple to connect to a different network, or to add a new user to the proxy.

Irssi
servers = (
  {
    address = "my-proxy-host.com";
    chatnet = "Proxy";
    port = "7778";
    password = "joel:myHashedPassword:freenode";
    autoconnect = "yes";
  }

Conclusion

And that's it! We are now perpetually connected to IRC, and can connect to our proxy using an unlimited number of devices in a completely transparent and seamless manner. Moreover, the logs for all channels that we are connected to are saved and automatically rotated on our remote server, making for an easy to maintain archive in the absence of another external channel logging mechanism.

Additionally, we can now generate an OpenSSL certificate for our remote server, and configure bip as well as our local clients to communicate with our proxy over a secured connection. This, along with many other features and options that I did not cover, are all documented in the README and man pages that are installed alongside the bip binary; Please take a look for more information on using bip to it's full potential.