Coming from nearly 5 years of relying on Nagios, I recently deployed LibreNMS on FreeBSD. It was a bit painful to get setup but it did what I needed it to do. I felt that it filled the void in my monitoring toolset. I set it to auto-update via it's daily.sh cron job. After LibreNMS updated their code to include Laravel, it broke. I'm sure if I kept digging into logs, I could have revived it, but instead of reaching out to the developers and possibly receiving a, "we don't support FreeBSD," I decided to give Zabbix a try. I heard about it on the TechSNAP podcast and those guys couldn't say enough great things about it. Now that my SNMP monitoring was down, it seemed like a good time to give it a go.

Screenshot-from-2018-05-17-19-39-09

A quick Google search immediately brought up a long list of deployment guides but honestly, the install was so smooth, I nearly didn't need any of them.

One common issue I run into is deploying in jails. When I do see FreeBSD-friendly write-ups, it's hit-or-miss if jail specific instructions are included. Fortunately, there were a few tips out there on just this install...

Here's an aggregate list of steps from various guides that helped deploy Zabbix in a FreeBSD jail.

Using pkg, install the following packages:

pkg install zabbix34-server zabbix34-agent zabbix34-frontend nginx

Enable the server and agent in rc.conf.local:

sysrc zabbix_server=YES
sysrc zabbix_agent=YES
sysrc nginx_enable=YES

Since this is in a jail with no loopback, modify the agent config to reflect the jail's IP. First, copy the sample:

cd /usr/local/etc/zabbix34/
cp zabbix_agentd.conf.sample zabbix_agentd.conf

Next, modify the config file:

############ GENERAL PARAMETERS #################

LogFile=/tmp/zabbix_agentd.log
SourceIP=192.168.1.11
Server=192.168.1.13
ListenIP=192.168.1.11
Hostname=server.example.net
Include=/usr/local/etc/zabbix34/zabbix_agentd.conf.d/*.conf

We include the last Include statement to load the userparam_zfs.conf file:

UserParameter=vfs.zpool.discovery,/usr/local/etc/zabbix34/scripts/zpool-discovery.sh
UserParameter=vfs.zfs.discovery,/usr/local/etc/zabbix34/scripts/zfs-discovery.sh
UserParameter=vfs.zfs.get[*],/sbin/zfs get -Hp -o value $2 $1 | sed -e 's/[x%]//'
UserParameter=vfs.zpool.get[*],/sbin/zpool get -Hp -o value $2 $1 | sed -e 's/[x%]//'

Next, create a scripts directory for the two ZFS scripts:

mkdir /usr/local/etc/zabbix34/scripts

Place the following into zfs-discovery.sh in the scripts directory:

#!/bin/sh

first=1

echo "{\"data\":["

for dataset in `/sbin/zfs list -H -o name` ; do
	if [ $first -ne 1 ]; then
		echo ","
	fi
	first=0
	echo -n "{\"{#ZFS}\":\"${dataset}\"}"
done
echo
echo "]}"

Place the following into zpool-discovery.sh in the scripts directory:

#!/bin/sh

first=1

echo "{\"data\":["

for pool in `/sbin/zpool list -H -o name` ; do
	if [ $first -ne 1 ]; then
		echo ","
	fi
	first=0
	echo -n "{\"{#ZPOOL}\":\"${pool}\"}"
done
echo
echo "]}"

Ensure both files are owned by zabbix user and executable:

chown -R zabbix /usr/local/etc/zabbix34/scripts
chmod +x /usr/local/etc/zabbix34/scripts/*

At this point you can start the agent:

service zabbix_agentd start

Next, make a copy of the sample config file:

cd /usr/local/etc/zabbix34/
cp zabbix_server.conf.sample zabbix_server.conf

Edit the zabbix_server.conf file and make any necessary modifications. In this example, we will bind the service to the Jail's IP and increase the number of pollers:

SourceIP=192.168.1.13
LogType=file
LogFile=/tmp/zabbix_server.log
DBHost=db.example.net
DBName=zabbix
DBUser=zabbix
DBPassword=zabbixpw
StartPollers=50
StartPreprocessors=6
StartDiscoverers=10
Timeout=4
ExternalScripts=/usr/local/etc/zabbix34/externalscripts
LogSlowQueries=3000

At this point, the jail is setup as a Zabbix server, however, the jail will refuse to start the Zabbix service:

Can not create Semaphore [Function not implemented]

To solve this, modify sysctl:

sysctl security.jail.sysvipc_allowed=1

and to make it survive a reboot, add the following to your /etc/sysctl.conf:

security.jail.allow_raw_sockets=1
security.jail.sysvipc_allowed=1

Next, modify your jail's config file. In this example, we'll use an ezjail config file:

export jail_zabbix_parameters="allow.raw_sockets=1 sysvshm=new sysvsem=new"

Lastly, modify your nginx config for the Zabbix frontend:

user www;
worker_processes  1;
events {
    worker_connections  1024;
}
http {
	upstream php {
	        server unix:/var/run/php-fpm.socket;
	}
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
    server {
        listen       80;
        server_name  localhost;
	location / {
            root /usr/local/www/zabbix34;
            index index.php index.html index.htm;
        }
        location ~ \.php$ {
            root /usr/local/www/zabbix34;
	    include fastcgi_params;
            fastcgi_pass php;
            fastcgi_index index.php;
	    fastcgi_param PHP_AUTH_USER $http_remote_user;
            fastcgi_param SCRIPT_FILENAME /usr/local/www/zabbix34$fastcgi_script_name;
        }
        location ~ /\.ht {
            deny all;
        }
    }
}

Restart your jail to allow the modified jail config to take affect, and you're ready to start monitoring!

Oh, ok, so you might have caught the $http_remote_user in my nginx config file. That's because this nginx server sits behind a TLS proxy that passes my user certificate back to Zabbix. All the authentication happens on the first nginx proxy. If successful, it passes the request back to the Zabbix nginx instance and binds my username ($http_remote_user) to PHP_AUTH_USER. Then, in Zabbix, you can enable HTTP authentication. Make sure you have a username that matches your certificate!