I’ve never really used ansible before, and rarely get to play with freebsd so when I needed to setup a new IRC bouncer I decided what better chance than now to have a play. Although there are some built-in ansible modules for working with the ports system (Which is what we are going to use to install znc), they aren’t yet complete enough to bootstrap a server from scratch.

We first need to setup our local computer to allow us to connect to the freebsd server. I recommend setting up and copying ssh keys to the server before continuing as it’ll make your life much easier (and more secure if you disable password authentication).

Start by adding the irc bouncing server to /etc/ansible/hosts, if this is your only server, the file will look something like:

root@ircbouncer.mydomain.com

You can confirm this worked by running

# ansible all -m ping

Once you are all connected and authenticated to your server, we can start building the ansible playbook to install znc.

As I mentioned, we’ve got to do a few manual steps in the playbook to bootstrap the server ready to use the port install built-in functionality. Namely, we need to do an initial download, and then make sure the tree is up to date.

---
- hosts: all
  tasks:
  - name: initialize ports db
    command: portsnap fetch extract
    args:
      creates: /usr/ports
  - name: update ports db
    command: portsnap fetch update

This runs portsnap fetch extract only if /usr/ports doesn’t already exist. It then runs portsnap fetch update on every ansible run to make sure that we keep our ports tree up to date. This doesn’t take too long as it’s only an incremental operation.

Once we’ve bootstrapped our ports tree and made sure it’s up to date, we can use the built-in functionality for installing the znc bouncer.

  - name: install znc
    portinstall:
      name: irc/znc
      state: present

Side note: I ran the above on a 1-core VPS from Vultr and was surprised how long the compiling took. All said an done it ended up being in the region of 2-3 hours. If you think you are likely to lose connection in that time, I highly recommend holding off running ansible playbook until you are on a stable internet connection. Alternatively, ssh to a desktop/server and use a terminal multiplexer (tmux/screen) to keep your session running even if you disconnect.

We also need to configure znc. For the time being, I’m just to install znc but leave management to the web interfce and commands (which is actually the officially recommended way). However, even with this method you still need to initialize the config. Unfortunately there doesn’t seem to be an unattended option when using znc --makeconfig so instead we must create it from scratch ourselves. We can take the created file and create a ansible template with it, as follows. This should be saved in a separate file called znc.conf.tmpl

// WARNING
//
// Do NOT edit this file while ZNC is running!
// Use webadmin or *controlpanel instead.
//
// Altering this file by hand will forfeit all support.
//
// But if you feel risky, you might want to read help on /znc saveconfig and /znc rehash.
// Also check https://wiki.znc.in/Configuration

Version = 1.7.5
<Listener l>
        Port = {{ listen_port }}
        IPv4 = true
        IPv6 = true
        SSL = false
</Listener>
LoadModule = webadmin

<User {{ user_name }}>
        Pass       = sha256#{{ user_passhash }}#
        Admin      = true
        Nick       = {{ user_nick }}
        AltNick    = {{ user_altnick }}
        Ident      = {{ user_ident }}
        RealName   = {{ user_realname }}
</User>

We then need to install that file only when it doesn’t already exist (Which allows us to initialize it but then ignore any further changes). We do that by first statting the file and then using that as a when parameter to the template instantiation.

  - stat: path=/usr/local/etc/znc/configs/znc.conf
    register: config_file_stat
  - template: src=znc.conf.tmpl dest=/usr/local/etc/znc/configs/znc.conf
    when: not config_file_stat.stat.exists

The finishing touche to the config initialization is to pass those parameters in. Just below the -hosts: all at the top of our file, we need a new vars entry containing the variables:

  vars:
    listen_port: 8080
    user_nick: hhra
    user_altnick: _hhra
    user_ident: hhra
    user_realname: Nosey Fucker
    user_passhash: e75ea669d9b4e79f0538c2cd2c0f406bca4970f47a03538a47adbdf82e04e467#J?a5.D8tUPqtZDgT-Qyu

The final thing to do is to start znc and enable it starting automatically on boot. Ansible’s built-in modules support this natively in much the same way as they do on linux so its just a matter of adding

  - name: start znc on boot
    service: name=znc state=started enabled=yes

to the end of your playbook. Once done, you should be left with a complete playbook looking something like

- hosts: all
  vars:
    listen_port: 8080
    user_nick: hhra
    user_altnick: _hhra
    user_ident: hhra
    user_realname: Nosey Fucker
    user_passhash: e75ea669d9b4e79f0538c2cd2c0f406bca4970f47a03538a47adbdf82e04e467#J?a5.D8tUPqtZDgT-Qyu
  tasks:
  - name: initialize ports db
    command: portsnap fetch extract
    args:
      creates: /usr/ports
  - name: update ports db
    command: portsnap fetch update
  - name: install znc
    portinstall:
      name: irc/znc
      state: present
  - stat: path=/usr/local/etc/znc/configs/znc.conf
    register: config_file_stat
  - template: src=znc.conf.tmpl dest=/usr/local/etc/znc/configs/znc.conf
    when: not config_file_stat.stat.exists
  - name: start znc on boot
    service: name=znc state=started enabled=yes

which when run will isntall and initialize a copy of zsh ready for use. There’s so much more that could be done to further enhance this setup. Blog posts may follow about installing the lounge and a reverse-proxy on the server to provide nice access to both the znc web interface and an IRC client.