Znc /

Chroot69

You will want to get a ddos-filtered IPv4 and an IPv6 subnet from your internet provider.

Create the user znc:

$ doas adduser

Note: If this is the first user you are creating after the initial install process, you will first be asked for global defaults. Do not change any of these.

Name:        znc
Password:    ****
Fullname:    znc
Uid:         1001
Gid:         1001 (znc)
Groups:      znc 
Login Class: default
HOME:        /home/znc
Shell:       /sbin/nologin
OK? (y/n) [y]: y

I am not sure if this is necessary, but in /etc/login.conf, I add the following:

znc:\
    :openfiles-cur=4096:\
    :openfiles-max=8182:\
    :openfiles=4096:\
    :stacksize-cur=48M:\
    :stacksize-max=48M:\
    :maxproc-max=infinity:\
    :maxproc-cur=4096:\
    :tc=daemon:

WARNING: Use tabs and not spaces. Spaces will not parse properly.

I check to make sure znc is set to the right login class.

$ doas vipw

There should be a line with znc that looks like this (I check to make sure znc has the right login class; the '1001' is the uid, which you may find to be different from this example, but it should not be changed):

znc:*:1001:1001:znc:0:0:znc:/home/znc:/sbin/nologin

You will want to make sure that any login.conf.db database is deleted:

$ doas rm /etc/login.conf.db

Now change znc shell to ksh with doas usermod -s /bin/ksh znc, then continue with the steps below.

$ doas su -c znc znc
$ ulimit -a
time(cpu-seconds)    unlimited
file(blocks)         unlimited
coredump(blocks)     unlimited
data(kbytes)         33554432
stack(kbytes)        32768
lockedmem(kbytes)    329478
memory(kbytes)       985092
nofiles(descriptors) 4096
processes            1310

I then set the default shell to nologin with doas usermod -s /sbin/nologin znc (note: the '1001' is the uid, which you may find to be different from this example, but it should not be changed.):

$ doas vipw

...

znc:*:1001:1001:znc:0:0:znc:/home/znc:/sbin/nologin

Install needed dependencies:

$ doas pkg_add icu4c-68.2v0
$ doas pkg_add boost-1.72.0
$ doas pkg_add cmake-3.19.4p0v0

The below install scripts are tested for OpenBSD 6.9 stable and our patched znc-1.8.2. It will put znc inside the chroot at /home/znc. Before you begin the installation, check that you are using OpenBSD 6.9 and that your system is patched:

$ uname -a
OpenBSD example.ircnow.org 6.9 GENERIC#464 amd64
$ doas syspatch
$ cd ~
$ curl -L -O https://ircnow.org/software/znc-1.8.2a.tar.gz
$ tar xvzf znc-1.8.2a.tar.gz
$ cd znc-1.8.2
$ mkdir build
$ cd build
$ cmake ..
$ make
$ doas make install

NOTE: A patched version of znc-1.8.2 release is used to prevent a threading bug. Please do not use the standard znc 1.8.2 release.

Then run as root:

mkdir -p /home/znc/usr/lib/
mkdir -p /home/znc/usr/local/lib/pkgconfig
mkdir -p /home/znc/usr/local/bin/
mkdir -p /home/znc/usr/local/share/
mkdir -p /home/znc/usr/local/man/man1/
mkdir -p /home/znc/usr/libexec/
mkdir -p /home/znc/etc/ssl
mkdir -p /home/znc/dev/
mkdir -p /home/znc/var/run/
mkdir -p /home/znc/home/znc/.znc
mknod -m 644 /home/znc/dev/random c 45 0
mknod -m 644 /home/znc/dev/urandom c 45 2
mknod -m 666 /home/znc/dev/null c 2 2
cp /usr/lib/libc++.so.7.0      /home/znc/usr/lib/libc++.so.7.0
cp /usr/lib/libc++abi.so.5.0   /home/znc/usr/lib/libc++abi.so.5.0
cp /usr/lib/libc.so.96.0       /home/znc/usr/lib/libc.so.96.0
cp /usr/lib/libcrypto.so.46.2  /home/znc/usr/lib/libcrypto.so.46.2
cp /usr/lib/libm.so.10.1       /home/znc/usr/lib/libm.so.10.1
cp /usr/lib/libpthread.so.26.1 /home/znc/usr/lib/libpthread.so.26.1
cp /usr/lib/libssl.so.48.2     /home/znc/usr/lib/libssl.so.48.2
cp /usr/lib/libz.so.5.0        /home/znc/usr/lib/libz.so.5.0
cp /usr/libexec/ld.so          /home/znc/usr/libexec/ld.so
cp /etc/resolv.conf            /home/znc/etc/resolv.conf
cp /etc/ssl/cert.pem           /home/znc/etc/ssl/cert.pem
cp /var/run/ld.so.hints        /home/znc/var/run/ld.so.hints
cp /usr/local/bin/znc          /home/znc/usr/local/bin/znc
cp /usr/local/man/man1/znc.1          /home/znc/usr/local/man/man1/znc.1
cp /usr/local/man/man1/znc-buildmod.1 /home/znc/usr/local/man/man1/znc-buildmod.1
cp /usr/local/bin/znc-buildmod        /home/znc/usr/local/bin/znc-buildmod
cp /usr/local/lib/pkgconfig/znc.pc    /home/znc/usr/local/lib/pkgconfig/znc.pc
cp /usr/local/lib/libicuuc.so.19.2    /home/znc/usr/local/lib/
cp /usr/local/lib/libicudata.so.19.2    /home/znc/usr/local/lib/
cp /usr/local/lib/libboost_locale-mt.so.13.0 /home/znc/usr/local/lib/
cp /usr/local/lib/libboost_chrono-mt.so.13.0 /home/znc/usr/local/lib/
cp /usr/local/lib/libboost_system-mt.so.13.0 /home/znc/usr/local/lib/
cp /usr/local/lib/libboost_thread-mt.so.13.0 /home/znc/usr/local/lib/
cp /usr/local/lib/libicui18n.so.19.2 /home/znc/usr/local/lib/
cp -R /usr/local/share/znc               /home/znc/usr/local/share/
cp -R /usr/local/lib/znc                 /home/znc/usr/local/lib/
cp -R /usr/local/include/znc             /home/znc/usr/local/include/
chown -R znc:znc /home/znc/
chown -R root:wheel /home/znc/dev /home/znc/etc /home/znc/usr /home/znc/var
chmod -R o-rx /home/znc/home/znc/.znc/
usermod -G znc botnow
usermod -G znc _identd

At first, you will need to create a conf file:

# export HOME=/home/znc/
# chroot -u znc -g znc /home/znc znc --makeconf
[ .. ] Checking for list of available modules...
[ ** ] 
[ ** ] -- Global settings --
[ ** ] 
[ ?? ] Listen on port (1025 to 65534): 31337
[ ?? ] Listen using SSL (yes/no) [no]: yes
[ ?? ] Listen using both IPv4 and IPv6 (yes/no) [yes]: no
[ .. ] Verifying the listener...
[ ** ] Unable to locate pem file: [/home/znc/.znc/znc.pem], creating it
[ .. ] Writing Pem file [/home/znc/.znc/znc.pem]...
[ ** ] Enabled global modules [webadmin]
[ ** ] 
[ ** ] -- Admin user settings --
[ ** ] 
[ ?? ] Username (alphanumeric): 
# cp /etc/ssl/my.example.com.fullchain.pem /home/znc/home/znc/.znc/
# cp /etc/ssl/private/my.example.com.key /home/znc/home/znc/.znc/
# chown znc:znc /home/znc/home/znc/.znc/my.example.com.*

Inside ~:

$ openssl dhparam -out dhparam.pem 2048
$ doas chown znc:znc dhparam.pem
$ doas mv dhparam.pem /home/znc/home/znc/.znc/

Afterwards, to run znc:

# export HOME=/home/znc
# /usr/sbin/chroot -u znc -g znc /home/znc znc >>/var/log/znc.log 2>&1 &

Now you must add this rule in /etc/pf.conf:

pass in log quick proto tcp to port {80 443} keep state (max-src-conn 30, max-src-conn-rate 20/60) #relayd web
pass in log quick proto tcp to port {1337 31337} keep state (max 3000, max-src-conn 200) #bnc
$ doas pfctl -f /etc/pf.conf

(Here the steps are a bit out of chronology)

To start the bouncer, I run this command:

# HOME=/home/znc && /usr/sbin/chroot -u znc -g znc /home/znc znc >>/var/log/znc.log 2>&1 &

If your bouncer is already online, make sure to save the config by logging into your irc client, connected to the bouncer:

/msg *status saveconfig

Then, go add this at the top of /home/znc/home/znc/.znc/configs/znc.conf (yes I deliberately ignore the warnings):

AnonIPLimit = 10000
AuthOnlyViaModule = false
ConfigWriteDelay = 60
ConnectDelay = 1
HideVersion = false
LoadModule = chansaver
LoadModule = lastseen
LoadModule = adminlog
LoadModule = identfile
LoadModule = webadmin
LoadModule = certauth
MaxBufferSize = 10000
ProtectWebSessions = true
SSLCertFile = /home/znc/.znc/my.example.com.fullchain.pem
SSLDHParamFile = /home/znc/.znc/dhparam.pem
SSLKeyFile = /home/znc/.znc/my.example.com.key
PidFile = /home/znc/.znc/znc.pid
ServerThrottle = 1
Version = 1.8.2

<Listener listener0>
        AllowIRC = true
        AllowWeb = false
        Host = 192.168.1.1
        IPv4 = true
        IPv6 = false
        Port = 1337
        SSL = false
        URIPrefix = /
</Listener>

<Listener listener1>
        AllowIRC = true
        AllowWeb = false
        Host = 192.168.1.1
        IPv4 = true
        IPv6 = false
        Port = 31337
        SSL = true
        URIPrefix = /
</Listener>

<Listener listener2>
        AllowIRC = true
        AllowWeb = false
        Host = 2001:db8::
        IPv4 = false
        IPv6 = true
        Port = 1337
        SSL = false
        URIPrefix = /
</Listener>

<Listener listener3>
        AllowIRC = true
        AllowWeb = false
        Host = 2001:db8::
        IPv4 = false
        IPv6 = true
        Port = 31337
        SSL = true
        URIPrefix = /
</Listener>

<Listener listener4>
        AllowIRC = true
        AllowWeb = false
        Host = 127.0.0.1
        IPv4 = true
        IPv6 = false
        Port = 1337
        SSL = false
        URIPrefix = /
</Listener>

<Listener listener5>
        AllowIRC = false
        AllowWeb = true
        Host = 127.0.0.1
        IPv4 = true
        IPv6 = false
        Port = 1338
        SSL = false
        URIPrefix = /
</Listener>

We will load the identfile module by default. This is necessary to provide proper ident using oidentd?. Please follow the instructions in the link to configure ident.

I have znc bind to port 1338 without SSL for the web server. I will later use relayd to provide TLS acceleration on port 443.

Replace with your own IP addresses. Then, on your irc client logged into the bouncer:

/msg *status rehash
$ doas crontab -e

Add a few lines to have ZNC reconnect every 5 minutes. ZNC will only connect if no other ZNC instance is running:

HOME=/home/znc
*/5     *       *       *       *       /usr/sbin/chroot -u znc -g znc /home/znc znc >>/var/log/znc.log 2>&1 &

To test the connection (and SSL certificate), run:

$ openssl s_client -connect my.example.com:31337

Make sure you have the proper SSL cert configured.

While you are at it, you will want to redirect any plaintext requests to the webpanel on port 80 to use SSL on port 443. Add this to /etc/httpd.conf:

server "bnc.example.com" {
        listen on * port 80
        location "/.well-known/acme-challenge/*" {
                root "/acme"
                request strip 2
        }
        location * {
                block return 302 "https://$HTTP_HOST$REQUEST_URI"
        }
}

Go ahead and reboot the web server:

$ doas rcctl restart httpd

Note: If you are using IPv6 and IPv4 for the same listener, perl IO::Socket::INET is unable to connect. Use two separate listeners.

Integration with the operating system

doas touch /etc/rc.d/znc
doas chmod +x /etc/rc.d/znc
                                                         
#!/bin/ksh
#
# $OpenBSD: znc,v 1.2 2020/01/21 19:27:07 rpe Exp $

daemon_pidfile="/home/znc/home/znc/.znc/znc.pid"
daemon="env HOME=/home/znc /usr/sbin/chroot -u znc -g znc /home/znc znc"


service_stop() {
  if [ -f $daemon_pidfile ]; then
    pid=$(sed 's/[^0-9]*//g' $daemon_pidfile)
    kill $pid
  fi
}

case "$1" in
  stop)
    service_stop
    ;;
esac


. /etc/rc.d/rc.subr

rc_reload=NO

rc_cmd $1


 

Automatic start

Automatic start of the service in case of a crash.

This prevents the loss of users due to software errors. Make sure your users feel good.

 
doas mkdir /usr/local/project_name/
doas touch /usr/local/project_name/checker_znc.sh
doas chmod +x /usr/local/project_name/checker_znc.sh
 

 
#!/bin/sh

SERVICE_NAME="znc"
SERVICE_USER="znc"
SERVICE_PID="/home/znc/home/znc/.znc/znc.pid"

if ! pgrep -u $SERVICE_USER -x "$SERVICE_NAME" > /dev/null
then
  if [ -f $SERVICE_PID ]; then
    rm -f $SERVICE_PID
    rcctl -d restart $SERVICE_NAME
  fi
fi

 

This may have issues in the 6.9 release.

 
 doas crontab -e
 

 
*/2     *       *       *       *       /usr/local/project_name/checker_znc.sh 2>&1 &
 

User login

 
username must be the same or derived from the network login (contain a suffix)
 

Create new user

 
/msg *controlpanel AddUser bob 12345678
/msg *controlpanel LoadModule bob perform
 

Add network for new ner

 
/msg *controlpanel AddNetwork bob IRCNow
/msg *controlpanel AddServer vasya IRCNow irc.ircnow.org +6697
/msg *controlpanel SetNetwork BindHost bob IRCNow 2605:6400:20:bb8:dae2:32a2
/msg *controlpanel help
 

Set channels

 
/msg *controlpanel AddChan bob IRCNow #ircnow
 

Set pass (this must be done with a user account or offer to run this command yourself)

 
/msg *perform add PRIVMSG NickServ IDENTIFY bob 12345678
/msg *perform help
 

Reconnect user`s network

 
/msg *controlpanel Reconnect bob IRCNow
 

Saving settings to disk (please save your changes to disk immediately)

 
/msg *status SaveConfig
 

Deleting user and user entities

 
/msg *controlpanel DelUser bob
/msg *controlpanel DelNetwork bob IRCNow
 

Custom vhosts

WARNING: do not set individual IPv6 addresses for a user's bindhost. Do not set a user's bindhost to be something like 2605:6400:10::. If you specify an IPv6 address for the bindhost, that user can *only* connect to networks that support IPv6. IPv4-only networks completely fail.

It is better to set the bindhost to be username.fruit.ircnow.org. Then, for the nameserver, create an AAAA record and an A record. That way, the bindhost will use IPv6 if the network is IPv6-only, and IPv4 if it is an IPv4-only network. This solution is more flexible and allows your user to fall back to IPv4 when IPv6 is not supported.

If a user requests to change their vhost, do not edit the bindhost -- all you need to do is update the rDNS record (see the buyvm web panel. Don't delete the bindhost or the DNS A/AAAA records.

For example, suppose the vhost was formerly user.fruit.ircnow.org. If the user wants to change it to example.com, you just need to update the rDNS to example.com, but leave the znc bindhost as username.fruit.ircnow.org and keep the A/AAAA records for username.fruit.ircnow.org.

Once example.com's AAAA record is working, and the rDNS has been configured properly, you can reconnect the user (either the user types /znc connect or you type /msg *controlpanel reconnect <username> <network>), and the vhost will update properly.

Keeping the old A/AAAA record and bindhost working will make it easier if the user changes vhosts or if their 3rd party dns server for example.com fails for whatever reason. This method will allow the user to still connect. Otherwise, if the 3rd party dns server fails for example.com (which frequently happens with cheap, free dns services), users will be unable to connect and blame you.

Troubleshooting

If your user is getting disconnected, these are the most likely causes:

  1. mismatch of ports or SSL (using plaintext on 6697 or SSL on 6667)
  2. SSL is not supported
  3. user has a server password where none belongs (most likely he confused server password with nickserv password)
  4. ident is not working
  5. ircd bans a certain username or ident for no good reason (the ircd mistakenly assumes your connection is a bot and glines it)
  6. typo of server name or IP address
  7. dns lookup error

You may be need to install icu4c-68.2v0.

WARNING

If a znc user has a bindhost that is IPv6 only but the network is IPv4 only, it will not connect.

To prevent this, you must be absolutely 100% certain that each bindhost is symbolic (such as username.fruit.ircnow.org) and that each hostname has a single A record and a single AAAA record in your DNS zone. If any part is misconfigured, users will be unable to connect.

A quick way to check if there are DNS errors:

$ doas grep -i host /home/znc/home/znc/.znc/configs/znc.conf | grep -v > ~/bindhost
$ vi ~/bindhost

Then with vi:

:%s_.* = _host _g

Then:

$ sh ~/bindhost

If you see any records there with only a single IPv4 address but no IPv6, or a single IPv6 but no IPv4, or any NXDOMAIN responses, you need to fix your DNS records. There should be exactly one shared IPv4 and one unique IPv6 for each hostname, and zero NXDOMAIN responses.

Missing libraries

If you are getting errors such as:

ld.so: znc: can't load library 'libc++abi.so.5.0'                                       

Then it could be due to the fact that you are on the wrong OpenBSD version (6.8 or earlier), you did not apply syspatch, and you have not upgraded all dependencies:

$ doas syspatch
$ doas pkg_add -Uu

Then, delete the build folder and compile again.

Adding ICU support

Copy the following directories:

# cp -R /usr/local/share/icu /home/znc/usr/local/share/
# cp -R /usr/local/lib/icu   /home/znc/usr/local/lib/

Then, restart BNC. Encoding options should be available.

(The patched ZNC was made before ICU support is added. So, we cannot ensure that adding ICU support after the patch will not cause any other bugs.)

Running znc in debug mode

$ cd ~
$ curl -L -O https://znc.in/releases/znc-1.8.2.tar.gz
$ tar xvzf znc-1.8.2.tar.gz
$ cd znc-1.8.2
$ mkdir build
$ cd build
$ ../configure --enable-debug CXX=c++
$ gmake
$ doas make install
mkdir -p /home/znc/usr/lib/
mkdir -p /home/znc/usr/local/lib/pkgconfig
mkdir -p /home/znc/usr/local/bin/
mkdir -p /home/znc/usr/local/share/
mkdir -p /home/znc/usr/local/man/man1/
mkdir -p /home/znc/usr/libexec/
mkdir -p /home/znc/etc/ssl
mkdir -p /home/znc/dev/
mkdir -p /home/znc/var/run/
mkdir -p /home/znc/home/znc/
mknod -m 644 /home/znc/dev/random c 45 0
mknod -m 644 /home/znc/dev/urandom c 45 2
mknod -m 666 /home/znc/dev/null c 2 2
cp /usr/lib/libc++.so.7.0      /home/znc/usr/lib/libc++.so.7.0  
cp /usr/lib/libc++abi.so.5.0   /home/znc/usr/lib/libc++abi.so.5.0
cp /usr/lib/libc.so.96.0       /home/znc/usr/lib/libc.so.96.0
cp /usr/lib/libcrypto.so.46.2  /home/znc/usr/lib/libcrypto.so.46.2
cp /usr/lib/libm.so.10.1       /home/znc/usr/lib/libm.so.10.1
cp /usr/lib/libpthread.so.26.1 /home/znc/usr/lib/libpthread.so.26.1
cp /usr/lib/libssl.so.48.2     /home/znc/usr/lib/libssl.so.48.2
cp /usr/lib/libz.so.5.0        /home/znc/usr/lib/libz.so.5.0
cp /usr/libexec/ld.so          /home/znc/usr/libexec/ld.so
cp /etc/resolv.conf            /home/znc/etc/resolv.conf
cp /etc/ssl/cert.pem           /home/znc/etc/ssl/cert.pem
cp /var/run/ld.so.hints        /home/znc/var/run/ld.so.hints
cp /usr/local/bin/znc          /home/znc/usr/local/bin/znc
cp /usr/local/man/man1/znc.1          /home/znc/usr/local/man/man1/znc.1
cp /usr/local/man/man1/znc-buildmod.1 /home/znc/usr/local/man/man1/znc-buildmod.1
cp /usr/local/bin/znc-buildmod        /home/znc/usr/local/bin/znc-buildmod
cp /usr/local/lib/pkgconfig/znc.pc    /home/znc/usr/local/lib/pkgconfig/znc.pc
cp /usr/local/lib/libicuuc.so.19.2    /home/znc/usr/local/lib/
cp /usr/local/lib/libicudata.so.19.2    /home/znc/usr/local/lib/
cp /usr/local/lib/libboost_locale-mt.so.13.0    /home/znc/usr/local/lib/
cp /usr/local/lib/libboost_system-mt.so.13.0    /home/znc/usr/local/lib/
cp /usr/local/lib/libboost_thread-mt.so.13.0    /home/znc/usr/local/lib/
cp /usr/local/lib/libboost_chrono-mt.so.13.0    /home/znc/usr/local/lib/
cp /usr/local/lib/libicui18n.so.19.2            /home/znc/usr/local/lib/
cp -R /usr/local/share/znc               /home/znc/usr/local/share/
cp -R /usr/local/lib/znc                 /home/znc/usr/local/lib/
cp -R /usr/local/include/znc             /home/znc/usr/local/include/
chown -R znc:znc /home/znc/
chown -R root:wheel /home/znc/dev /home/znc/etc /home/znc/usr /home/znc/var
chmod -R o-rx /home/znc/home/znc/.znc/
usermod -G znc botnow
usermod -G znc _identd

$ doas gdb chroot