Join the IRC Idle-Party over Tor with ZNC on OpenBSD
After having successfully set up an IRC server as Tor Hidden Service on OpenBSD for you and your friends, let’s find out how you can outsource idling on your IRC server (and other networks!) to ZNC.
As some of you noticed, I have published a brief write-up on how to host your own IRCv3 server as a Tor Hidden Service on OpenBSD a while ago. At the very end of the post I mentioned how I would usually set up an IRC bouncer on a cloud instance and have it connect to IRC instead of directly connecting from a client. I do that in order to not have to A) set up connections individually on each of my devices and B) have an additional layer for privacy and security purposes between myself and the IRC services. In order to explain in detail how this can be accomplished, let’s set up our own ZNC bouncer on OpenBSD, that connects to IRC services via Tor!
What?
In order to explain what an IRC bouncer is in first place, we have to first understand how IRC servers function and how they are different from e.g. Discord or Slack.
When you log in to Discord, your client is provided with a view of what has happened on the server while you weren’t looking, followed by what is happening right now, meaning that you’re able to scroll back and read what someone said two days ago, even if you weren’t online at that time. That is because Discord stores every message everyone ever sent and provides a custom, per-user view on those messages. IRC servers on the other hand usually don’t do that. On IRC your user (or better, client) is sent the messages while you’re connected to the server. If you’re not connected (meaning, not online), you won’t be receiving anything. To simplify things, IRC can basically be thought of as an ephemeral message distribution hub, while something like Discord or Slack on the other hand is more similar to an actual database that stores information and from which clients can query data when they need it.
In order to emulate the behavior you’re getting for free (free as in you being the product) on Discord, you can use an IRC bouncer. A bouncer is basically a headless client that’s connecting in your name to an IRC server and that stays connected to it 24/7. This way your user on that IRC server appears online and the server will forward it communication that’s happening across the channels you’ve joined.
On the other side, the bouncer listens on a port to which you can connect to using a regular IRC client, so that you are able to participate on the IRC servers that the bouncer is in turn connected to. The bouncer is sort of a proxy, or middle man between your client and the actual IRC server you’re connecting to. Message storing/history is one of the advantages a bouncer usually offers. Another one is the ability to connect to multiple IRC servers at once and deal with things like reconnects (due to network outages for example) and other annoyances on its own.
Especially when used over the Onion network, these things can become tiring to deal with in the client. Similarly, when you connect using a phone and you’re trying to chat over a somewhat unstable mobile connection, you (and the folks idling in the same channels as you) will highly appreciate the stability of your IRC user on the network, while your bouncer will take care to deal with your phone client continuously reconnecting.
There’s also a privacy and security aspect to this. Usually, when you connect to an IRC server directly, using a client on your PC or your phone, your source IP/hostname is visible to at least the IRC server – and often also to the other users on that IRC server. You might not want for others to know the country/location you’re connecting from, for example. You could use a VPN to hide that, however, VPNs can be restricted on IRC servers, requiring at least an already registered account – sometimes of specific age – and identification through SASL. Also, as with Tor, connection stability might nevertheless become an issue.
Basics
I’m assuming you’ve gone through the IRC server post, so I won’t be repeating the basic setup of an OpenBSD cloud instance. We will be using a similar setup here: A Vultr OpenBSD Cloud Compute instance – I’ll use their AMD High Performance 1vCPU/1GB instance for this, as it is more than enough for running our bouncer, ZNC, as well as the Tor client that we need to connect to the Onion network.
PS: The comments on the hackernews post also contained a couple good recommendations and further reading material like
man afterboot
. Thanks to the person that initially shared my post, as well as to everyone commenting and providing further info!
Setup
Let’s start by installing the packages we’ll be needing and afterwards creating a dedicating user account for ZNC:
znc# pkg_add znc tor proxychains-ng supervisor
znc# groupadd _znc
znc# useradd -d /home/znc -m -c "ZNC" -g _znc -L daemon -s /sbin/nologin _znc
Before we go ahead and configure ZNC, we need to get an SSL certificate for securing the connection from our IRC client(s) to ZNC. For that we’ll have to set up a (sub)domain that points to the IPv4 (and ideally IPv6) address of our cloud instance. While it is possible to purchase SSL certificates issued for IP addresses, Let’s Encrypt (the free SSL service we’ll be using) will not support this.
The following step is optional, as in, if you don’t have a domain available on which you could set up a subdomain that’ll point to the cloud instance, you could use a self-signed certificate and either import it on your IRC clients’ systems and trust it, or disable SSL verification in the clients’ configurations. I would not recommend doing any of that, though.
To get an SSL certificate (from Let’s Encrypt) we’ll use OpenBSD’s
acme-client
and set up its configuration, as well as a cron job that will make
sure our certificate gets automatically prolonged.
First, create the file /etc/acme-client.conf
with the following content:
authority letsencrypt {
api url "https://acme-v02.api.letsencrypt.org/directory"
account key "/etc/ssl/private/letsencrypt.key"
}
domain my.domain.com {
domain key "/etc/ssl/private/my.domain.com.key"
domain certificate "/etc/ssl/my.domain.com.crt"
domain full chain certificate "/etc/ssl/my.domain.com.pem"
sign with letsencrypt
}
Make sure to replace my.domain.com
with the (sub)domain you pointed to your
cloud instance. Next, we’ll need to configure httpd to run on port 80
and
provide access to the ACME challenge. Create the file /etc/httpd.conf
with
the following content:
server "my.domain.com" {
listen on * port 80
root "/htdocs/my.domain.com"
location "/.well-known/acme-challenge/*" {
root "/acme"
request strip 2
}
}
Additionally create the folder /var/www/htdocs/my.domain.com/
and place an
empty index.html
into it.
Let’s enable httpd:
znc# rcctl enable httpd
znc# rcctl restart httpd
We can now try to issue our SSL certificate by running the following command:
znc# acme-client -v my.domain.com
Your newly issued SSL certificate should be available under
/etc/ssl/my.domain.com.{crt,pem}
and /etc/ssl/private/my.domain.com.key
.
Let’s put the SSL topic to the side for a moment and continue with ZNC.
Switch to into the _znc
user account by issuing the following command:
znc# su -s /bin/sh -l _znc -
First, run the freshly installed znc
binary with the --makeconf
parameter to
create a basic configuration under ~/.znc/
:
znc$ znc --makeconf
If you have skipped the SSL part, you could use znc --makepem
to issue a
self-signed certificate now.
With the basic configuration in place, let’s adjust the config file under
~/.znc/configs/znc.conf
. I’m going to demonstrate a config containing a single
user whose password was generated using the znc --makepass
command and who
will connect to only the OFTC IRC network via their Tor Hidden Service. You’re
free to adjust all settings to your needs and add further networks. The ZNC
bouncer in this example will listen on port 1337
, but you’re free to change
that.
AnonIPLimit = 10
AuthOnlyViaModule = false
ConfigWriteDelay = 0
ConnectDelay = 5
HideVersion = true
MaxBufferSize = 500
ProtectWebSessions = true
SSLCertFile = /home/_znc/.znc/cert1.pem
SSLDHParamFile = /home/_znc/.znc/cert1.pem
SSLKeyFile = /home/_znc/.znc/privkey1.pem
ServerThrottle = 30
Version = 1.8.2
<Listener listener0>
AllowIRC = true
AllowWeb = false
IPv4 = true
IPv6 = false
Port = 1337
SSL = true
URIPrefix = /
</Listener>
<User ahab>
Admin = true
RealName = Captain Ahab
Nick = ahab
AltNick = captain
Ident = ahab
AppendTimestamp = false
AuthOnlyViaModule = false
AutoClearChanBuffer = true
AutoClearQueryBuffer = true
ChanBufferSize = 5000
DenyLoadMod = false
DenySetBindHost = false
JoinTries = 10
LoadModule = chansaver
LoadModule = controlpanel
MaxJoins = 0
MaxNetworks = 5
MaxQueryBuffers = 50
MultiClients = true
NoTrafficTimeout = 180
PrependTimestamp = false
QueryBufferSize = 50
QuitMsg = All my means are sane, my motive and my object mad
StatusPrefix = *
TimestampFormat = [%H:%M:%S]
Timezone = America/New_York
<Network oftc>
FloodBurst = 4
FloodRate = 1.00
IRCConnectEnabled = true
JoinDelay = 0
LoadModule = simple_away
LoadModule = nickserv
LoadModule = perform
LoadModule = sasl
Server = oftcnet6xg6roj6d7id4y4cu6dchysacqj2ldgea73qzdagufflqxrid.onion +6697
TrustAllCerts = false
TrustPKI = true
TrustedServerFingerprint = be:1c:d4:42:da:36:46:8a:6a:0c:62:a8:d1:c7:c5:18:01:d5:06:65:56:46:2a:11:5c:c2:50:55:b2:d9:db:a2
<Chan #openbsd>
</Chan>
<Chan #tor>
</Chan>
</Network>
<Pass password>
Method = sha256
Hash = 19365170747edd9d587b815f58cf0a590626ff98757d2af074cc8bc60d2a47c8
Salt = O)jP.vgSuq4Ek;m1xXWG
</Pass>
</User>
Next, let’s go back to the SSL topic. Log out of the _znc
account (Ctrl+d
)
and (as root
) run crontab -e
. This will open the current crontab for
editing. Add the following line at the very end of the file:
0 0 * * * acme-client -v my.domain.com && cp /etc/ssl/my.domain.com.pem /home/_znc/.znc/cert1.pem && cp /etc/ssl/private/my.domain.com.key /home/_znc/.znc/privkey1.pem && chown _znc._znc /home/_znc/.znc/*.pem
This will make sure our SSL certificate won’t expire. In addition, it will copy
newly issued SSL certificates to the place that ZNC looks for its SSL
certificates. Run the cp
commands manually once to copy the currently issued
certificate.
“Do I need to restart ZNC every time this runs in order for it to reload the newly copied SSL certificate?” you might ask. The ZNC wiki says:
“To apply this new certificate file to ZNC, just put (replace) it in ZNC’s work folder. As of 1.6.2, ZNC will reload znc.pem each time a client connects, but it’ll be fixed in future.”
Now let’s set up supervisor
. Go ahead and create a new file in
/etc/supervisord.d/
named znc.ini
and add the following content:
[program:znc]
command=proxychains4 znc -f
process_name=znc
numprocs=1
directory=/home/_znc
autostart=true
autorestart=unexpected
startsecs=10
startretries=3
exitcodes=0
stopsignal=TERM
stopwaitsecs=10
user=_znc
environment=HOME="/home/_znc",USER="_znc"
As you can see, we’re not launching znc
directly but instead wrap it with
proxychains4
, which forces it through a proxy connection. That connection can
be configured in /etc/proxychains.conf
and should by default contain the
following line:
...
socks4 127.0.0.1 9050
...
If you can spot it, no need to change anything. If not, simply add it yourself.
Last but not least you’re free to configure /etc/tor/torrc
the way it suits
you best. The default config is fairly sufficient and will work for this demo
though. If you prefer using regular hostnames for configuring your IRC
connections, you can use MapAddress
to map the regular hostname/domain to a
Tor address, e.g.
MapAddress irc.oftc.net oftcnet6xg6roj6d7id4y4cu6dchysacqj2ldgea73qzdagufflqxrid.onion
Now let’s go ahead and enable/start all services:
znc# rcctl enable tor
znc# rcctl restart tor
znc# rcctl enable supervisord
znc# rcctl restart supervisord
You can check whether znc started up successfully by monitoring the supervisor log:
znc# tail -f /var/log/supervisord.log
Tor will output logging info in messages
:
znc# tail -f /var/log/messages
If znc won’t start and you want to know why, you can redirect its output to the
log. Check the supervisor manual for more info. You could
also log in as _znc
user (the su
command from before) and run
proxychains4 znc -f
manually. Keep in mind to stop supervisor before doing so.
Assuming everything started successfully and you didn’t change the port in the
znc config, znc should not be running on 1337
:
znc# netstat -an | grep "1337"
Client
We can now connect to it. I’m going to use my preferred IRC client, irssi, but you’re free to pick whichever client you’d like. Configuration will likely be similar for most clients.
On our client machine, let’s edit the irssi configuration under
~/.irssi/config
. I’m only going to show the relevant parts and won’t include
aliases
, statusbar
configuration or other things that you’d likely want to
configure for irssi:
servers = (
{
address = "my.domain.com";
chatnet = "oftc";
port = "1337";
use_ssl = "yes";
ssl_verify = "yes";
autoconnect = "yes";
password = "ahab/oftc:IT'S_THE_WHALE!";
}
);
chatnets = {
oftc = { type = "IRC"; };
};
channels = ( );
Most things should be self-explanatory. password
however is a special case
here. It is a combination of three things:
<znc user>/<network>:<znc password>
Find more info on this here and here. If you have
configured multiple networks on ZNC, simply add additional servers
entries to
the irssi config, along with additional chatnets
assignments. Every entry will
basically look the same, with the only difference being the <network>
part.
If you decide to use different ZNC users for different networks, you’re able to
by changing the <user>
/<password>
parts as well.
Launching irssi will make it connect to your ZNC bouncer instead of directly
to the IRC network itself. It could be, that ZNC was unable to establish the
connection to OFTC in our example, as the trusted fingerprint of the OFTC server
might have changed. In that case, ZNC will tell you how to accept the new
fingerprint, so read the *status
output carefully and follow the instructions.
Speaking about *status
, you can easily talk to your ZNC bouncer by messaging
*status
: /msg *status help
There, you are able to issue administrative commands to ZNC. SaveConfig
is an
important one if you decide to change settings using the *status
interface.
If you’re looking for a solid Android client to connect to your newly set up ZNC bouncer, check out Revolution IRC. It’s really great and its configuration is similar to the one demonstrated here.
BTW: Did you know that you could get push notifications for mentions and private messages on your phone and other devices? Check out znc-push!
IRC and Tor
While there are still networks with solid principles and values, like OFTC, that don’t shy away from putting up with the spam that comes with allowing unauthenticated Tor connections, most popular networks however don’t seem to care enough about people’s privacy to put up with the administrative efforts that are required for running a Tor Hidden Service in first place or allow connections without previous clearnet registration. The Tor project maintains a somewhat up to date list of networks that provide access via Tor.
In regard of hosting, the Tor project also maintains a list of good and bad ISPs in case you’d rather want to go with a different cloud instance provider.
If you decide that you’d rather not connect from your ZNC installation via the
Onion network, you can rcctl disable tor
as well as remove the
proxychains4
wrapper from the supervisor config. ZNC will then use a direct
connection to every server. I would however suggest avoiding networks that
actively prohibit access via Tor or limit it to existing users (via clearnet
registration) that authenticate using SASL.
Happy idling everyone!
Enjoyed this? Support me via Monero, Bitcoin, Lightning, or Ethereum! More info.