Nspawn - Build A Tor-only IRC Server In Containers

Tor, The Dark Web And Your IRC Chat

ops-tactics

The Onion Router (Tor) is presented like the wild-west of the internet.
In reality it's a system for better privacy.
The infrastructure behind it handles the routing and encryption.

Most people think of Tor as the dark web.
Despite Tor being used for shady things - you can use it in an ethical and legal way.

For better privacy. For encryption.

You - and your users - can benefit from Tor without setting a foot on the "dark web".

IRC is fast. IRC is simple.
A text based protocol that runs smoothly even with Tor's latency.

You build a Tor-only IRC server that:

  • hides your server's IP from the clients
  • hides your clients' IP addresses from the IRC server and from other IRC users
  • uses Tor's end-to-end encryption
  • uses its own .onion address.

The Plan - Nspawn Container Separation

You've already learnt about Nspawn and container management.
Compartmentalization enhances the flexibility.

Take advantage of cloning and snapshotting.

Create two containers:

  1. Tor service (10.10.0.10)
  2. IRC server and services (10.10.0.11)

The two containers must have network connectivity between 10.10.0.10 and 10.10.0.11.
Make sure the IRC container's port 6667 is not exposed outside the container network.

The Plan - Execution

Tor Container

Log in to the Tor container.
Install the Tor service.

sudo apt update
sudo apt install tor

In the /etc/tor/torrc file set up your service and the port forwarding to the IRC container.

HiddenServiceDir /var/lib/tor/irc_hidden_service/
HiddenServicePort 6667 10.10.0.11:6667

In the example 10.10.0.11 is the IP address of the IRC container.
The Tor container's IP is 10.10.0.10.

You find your new .onion address in /var/lib/tor/irc_hidden_service/hostname.

sudo cat /var/lib/tor/irc_hidden_service/hostname

Backup the:

  • hs_ed25519_public_key
  • hs_ed25519_secret_key
  • hostname

Take a note of the .onion address.

Verify that the Tor service is running and it listens.

systemctl status tor.service
sudo ss -tupln |grep tor

IRC Container

You can use any popular IRC service software in this container, like Ergo chat.
For the flexibility I chose InspIRCd.

InspIRCd 4 is available in current Debian stable and it integrates well with Anope services.

Install InspIRCd and Anope in the container.

sudo apt update
sudo apt install inspircd anope

Create a backup of the original configuration file.

sudo mv /etc/inspircd/inspircd.conf /etc/inspircd/inspircd.conf.orig

Create your own IRC configuration in /etc/inspircd/inspircd.conf.

Example config snippet:

<server name="irc.myhiddenchat.onion"
        description="Myhiddenchat IRC Server"
        network="MyhiddenchatNetwork">

<admin name="yournick"
       description="myhiddenchat"
       email="myhiddenchat@email.me">

<module name="hidechans">
<module name="spanningtree">
<module name="account">
<module name="services">
<module name="conn_umodes">
<module name="sha2">
<module name="cap">
<module name="sasl">
<module name="conn_join">
<module name="password_hash">
<module name="customprefix">
<module name="cloak">
<module name="cloak_user">

<cloak method="account"
       case="preserve"
       class=""
       invalidchar="strip"
       prefix="HiddenHost/"
       suffix="">

<bind address="10.10.0.11" port="6667" type="clients">

<connect allow="*"
         timeout="60"
         threshold="10"
         pingfreq="120"
         hardsendq="262144"
         softsendq="8192"
         recvq="8192"
         localmax="500"
         globalmax="500"
         maxchans="20"
         resolvehostnames="no"
         modes="+x">

<class name="Shutdown"
       commands="DIE RESTART REHASH LOADMODULE UNLOADMODULE RELOADMODULE">
<class name="ServerLink"
       commands="CONNECT SQUIT RCONNECT RSQUIT MKPASSWD">
<class name="BanControl"
       commands="KILL GLINE KLINE ZLINE QLINE ELINE">
<class name="OperChat"
       commands="WALLOPS GLOBOPS SETIDLE SPYLIST SPYNAMES">
<class name="HostCloak"
       commands="SETHOST SETIDENT CHGNAME CHGHOST CHGIDENT">
<class name="ServerStats"
       commands="STATS"
       privs="users/auspex channels/auspex servers/auspex users/mass-message users/flood/no-throttle users/flood/increased-buffers">

<type name="NetAdmin"
      classes="OperChat BanControl HostCloak Shutdown ServerLink ServerStats"
      host="netadmin.myhiddenchat.onion">
<type name="GlobalOp"
      classes="OperChat BanControl HostCloak"
      host="ircop.myhiddenchat.onion">
<type name="Helper"
      classes="HostCloak"
      host="helper.myhiddenchat.onion">

<oper name="youropernick"
      password="YourPW"
      host="*@*"
      type="NetAdmin"
      maxchans="60">

<files motd="/etc/inspircd/inspircd.motd">

<dns timeout="0">

<options prefixquit="Quit: "
         syntaxhints="no"
         announcets="yes"
         hostintopic="yes"
         pingwarning="15"
         splitwhois="no"
         exemptchanops="">

<security hideserver=""
          userstats="Pu"
          customversion=""
          flatlinks="no"
          hidesplits="no"
          hideulines="no"
          hidebans="no"
          maxtargets="20">

<performance quietbursts="yes"
             softlimit="1024"
             somaxconn="128"
             netbuffersize="10240">

<whowas groupsize="10"
        maxgroups="100000"
        maxkeep="3d">

<badnick nick="ChanServ" reason="Reserved For Services">
<badnick nick="NickServ" reason="Reserved For Services">
<badnick nick="OperServ" reason="Reserved For Services">
<badnick nick="MemoServ" reason="Reserved For Services">
<badnick nick="HostServ" reason="Reserved For Services">
<badnick nick="Global"   reason="Reserved For Services">

<link name="services.myhiddenchat.onion"
      ipaddr="127.0.0.1"
      port="7000"
      allowmask="127.0.0.0/8"
      sendpass="passwordForServices"
      recvpass="passwordForServices">

<uline server="services.myhiddenchat.onion" silent="yes">
<bind address="127.0.0.1" port="7000" type="servers">
<sasl target="services.myhiddenchat.onion" requiressl="no">
  • In this example you use, and you trust Tor's own encryption.
  • The server listens on port 6667.
  • SASL authentication is configured and can be used.
  • The NetAdmin password must be hashed before production use.

For the Anope services backup the default configuration.

sudo mv /etc/anope/services.conf /etc/anope/services.conf.orig

Create your new config in /etc/anope/services.conf.

Example config:

uplink
{
        host = "127.0.0.1"
        ipv6 = no
        ssl = no
        port = 7000
        password = "passwordForServices"
}

serverinfo
{
        name = "services.myhiddenchat.onion"
        description = "myhiddenchatNetwork Services"
        pid = "/run/anope/anope.pid"
        motd = "services.motd"
}

module
{
        name = "inspircd3"
        use_server_side_mlock = yes
        use_server_side_topiclock = yes
}

networkinfo
{
        networkname = "myhiddenchatNetwork"
        nicklen = 31
        userlen = 10
        hostlen = 64
        chanlen = 32
        modelistsize = 100
        vhost_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-"
        allow_undotted_vhosts = false
        disallow_start_or_end = ".-"
}

options
{
        casemap = "ascii"
        strictpasswords = yes
        badpasslimit = 5
        badpasstimeout = 1h
        updatetimeout = 5m
        expiretimeout = 30m
        readtimeout = 5s
        timeoutcheck = 3s
        retrywait = 60s
        hideprivilegedcommands = yes
        hideregisteredcommands = yes
        languages = "ca_ES.UTF-8 de_DE.UTF-8 el_GR.UTF-8 es_ES.UTF-8 fr_FR.UTF-8 hu_HU.UTF-8 it_IT.UTF-8 nl_NL.UTF-8 pl_PL.UTF-8 pt_PT.UTF-8 ru_RU.UTF-8 tr_TR.UTF-8"
}

module { name = "enc_sha256" }

module {
        name = "db_flatfile"
        database = "anope.db"
        keepbackups = 12
}

include {
    type = "file"
    name = "nickserv.conf"
}

include {
    type = "file"
    name = "chanserv.conf"
}

include {
    type = "file"
    name = "operserv.conf"
}

include {
    type = "file"
    name = "hostserv.conf"
}

include {
    type = "file"
    name = "memoserv.conf"
}

include {
    type = "file"
    name = "global.conf"
}

log
{
        target = "services.log"
        bot = "Global"
        logage = 3
        users = "connect disconnect nick"
        rawio = no
        debug = no
}

module { name = "help" }
module { name = "m_sasl" }

opertype {
    name = "ServicesRoot"
    commands = "*"
    privileges = "*"
}

oper {
    name = "youropernick"
    type = "ServicesRoot"
}

Adjust the following services files for your config:

  • nickserv.conf
  • chanserv.conf
  • operserv.conf
  • hostserv.conf
  • memoserv.conf
  • global.conf

Rewrite the placeholder services host:

sudo sed -i 's/services.host/services.myhiddenchat.onion/g' /etc/anope/*.conf

Start the InspIRCd.

sudo systemctl enable inspircd.service
sudo systemctl start inspircd.service

Start the Anope.

sudo systemctl enable anope.service
sudo systemctl start anope.service

Check the logs and debug if necessary.

journalctl -u inspircd
journalctl -u anope

You can join your IRC server through a SOCKS5 Tor proxy.

More NetAdmin Security

Load the password_hash module in InspIRCd.
Generate a hash for the NetAdmin password.
Use the hash in the configuration file, remove the plain text data.

Don't forget to /rehash the config on your server.

Final Whisper

The design is simple and reproducible.

  • You can host your chat for your community.
  • Tor provides end-to-end encryption between the clients and the onion service.
  • You can snapshot the containers before you make any change.

Treat container snapshots as sensitive data.
They may contain private keys, credentials and chat data.

Build your community. Stay ethical. Stay legal.

DeadSwitch | The Silent Architect
[ Fear the Silence. Fear the Switch. ]