Icinga 2

Last modified by Sebastian Marsching on 2022/04/03 22:58

Using a Certificate Revocation List (CRL) with Icinga 2

Before you start: There is a bug in Icinga 2 (at least up to version 2.6.2) that prevents a CRL from being loaded, so you have to ensure that you use a newer version or patch the bug yourself.

Icinga 2 makes managing the CA easy by using the icinga2 pki commands. However, it does not provide any commands for generating a CRL. This means that the regular openssl tool needs to be used for that. The functions for generating a CRL are part of the ca module, so one first has to create a CA configuration for OpenSSL. For the rest of this tutorial, we assume that the certificates for Icinga 2 are stored in the default location (/var/lib/icinga2/ca).

First, we create the directory /var/lib/icinga2/ca/crl and a number of files needed in this directory:

mkdir /var/lib/icinga2/ca/crl
cd /var/lib/icinga2/ca/crl
touch index.txt
echo "unique_subject = no" >index.txt.attr
echo "0000" >crlnumber.txt
touch crl.pem

Next, we create the configuration file for OpenSSL as /var/lib/icinga2/ca/crl/openssl.cnf:

# OpenSSL configuration for CRL generation
#
####################################################################
[ ca ]
default_ca      = CA_default            # The default ca section

####################################################################
[ CA_default ]
database = /var/lib/icinga2/ca/crl/index.txt
crlnumber = /var/lib/icinga2/ca/crl/crlnumber.txt
certificate = /var/lib/icinga2/ca/ca.crt
private_key = /var/lib/icinga2/ca/ca.key

crl_extensions  = crl_ext               # name of the section containing the
                                        # CRL extensions
default_crl_days        = 365           # how long before next CRL
default_crl_hours       = 0
default_md      = sha256                # use SHA256 for signing the CRL
unique_subject  = no                    # do not enforce unique certificate
                                        # subjects

####################################################################
[ crl_ext ]
# CRL extensions.

Finally, we want two shell scripts. One for generating the CRL and one for revoking a certificate.

/var/lib/icinga2/ca/crl/gen-crl.sh:

#!/bin/bash

# Find the path to this file (this script might be referenced through a
# symbolic link).
CURRENT_WORKING_DIRECTORY="`pwd`"
THIS_FILE="$0"
cd "`dirname "$THIS_FILE"`"
THIS_FILE="`basename "$THIS_FILE"`"
while [ -L "$THIS_FILE" ]; do
 THIS_FILE="`readlink "$THIS_FILE"`"
 cd "`dirname "$THIS_FILE"`"
 THIS_FILE="`basename "$THIS_FILE"`"
done
THIS_FILE="`pwd -P `/$THIS_FILE"
cd "$CURRENT_WORKING_DIRECTORY"
BASE_DIR="`dirname "$THIS_FILE"`"

openssl ca -config "$BASE_DIR/openssl.cnf" -gencrl -out "$BASE_DIR/crl.pem"

/var/lib/icinga2/ca/crl/revoke.sh:

#!/bin/bash

# Find the path to this file (this script might be referenced through a
# symbolic link).
CURRENT_WORKING_DIRECTORY="`pwd`"
THIS_FILE="$0"
cd "`dirname "$THIS_FILE"`"
THIS_FILE="`basename "$THIS_FILE"`"
while [ -L "$THIS_FILE" ]; do
 THIS_FILE="`readlink "$THIS_FILE"`"
 cd "`dirname "$THIS_FILE"`"
 THIS_FILE="`basename "$THIS_FILE"`"
done
THIS_FILE="`pwd -P `/$THIS_FILE"
cd "$CURRENT_WORKING_DIRECTORY"
BASE_DIR="`dirname "$THIS_FILE"`"

for cert in "$@"; do
  openssl ca -config "$BASE_DIR/openssl.cnf" -revoke "$cert"
done

Do not forget to make the shell scripts executable:

chmod a+x /var/lib/icinga2/ca/crl/gen-crl.sh
chmod a+x /var/lib/icinga2/ca/crl/revoke.sh

You might also want to change the owner of these files, though this step is entirely optional:

chown -R nagios:nagios /var/lib/icinga2/ca/crl

Now in order to revoke a certificate, you call revoke.sh with the certificate file as the parameter. For example:

/var/lib/icinga2/ca/crl/revoke.sh /var/lib/icinga2/ca/host.example.com.crt

After revoking a certificate, you have to regenerate the CRL in order to include the revoked certificate in the CRL:

/var/lib/icinga2/ca/crl/gen-crl.sh

In order to actually use the CRL, you have to distribute it to the Icinga 2 nodes. How to do this is outside of the scope of this tutorial. One might simply use scp or one could use a configuration management tool like SaltStack.

One thing to consider when distributing the CRL is its validity period: When using a CRL, Icinga 2 will start to reject all certificates (even the ones which have not expired yet and have not been revoked either) when the CRL has expired. This means that a new CRL has to be distributed before the old one expires. Choosing a longer validity period (by changing the crldays and crlhours options in the OpenSSL configuration) might help so that you can distribute the CRL less often. For example, the certificiates generated by Icinga 2 are valid for 5475 days (approximately 15 years). However, in contrast to certificates, there is no way to revoke a CRL. This means that if an attacker got hold of a CRL (which is not unlikely because a CRL is generally considered public information) and found a way to inject that CRL in the distribution process, he might be able to make a node use an old version of the CRL, thus accepting certificates that have been revoked in the meantime. When using a secure channel (like SSH) for distributing the CRL, you might not be worried too much about this scenario.

In order to make Icinga 2 use the CRL, it has to be registered with the API listener. The following configuration (typically stored in /etc/icinga2/features-available/api.conf) assumes that the CRL is stored in /etc/icinga2/pki/crl.pem.

/**
 * The API listener is used for distributed monitoring setups.
 */

object ApiListener "api" {
  cert_path = SysconfDir + "/icinga2/pki/" + NodeName + ".crt"
  key_path = SysconfDir + "/icinga2/pki/" + NodeName + ".key"
  ca_path = SysconfDir + "/icinga2/pki/ca.crt"
  crl_path = SysconfDir + "/icinga2/pki/crl.pem"

  ticket_salt = TicketSalt
}

Building a Python 3 compatble Debian package for Pynag

Ubuntu 18.04 includes the python-pynag package, but only for Python 2. There is no python3-pynag package, because the upstream version of Pynag is not compatible with Python 3. Luckily, there is a fork that is compatible with Python 3. The following shell script can be used to build a Debian package of this fork. This package should be compatible with Ubuntu as well.

PYNAG_VERSION=0.9.1
PYNAG_GIT_COMMIT=bcdbd435dd48f9bf791b5d41307da93683cb17b3

# We need python-stdeb and python3-stdeb in order to build the Debian packages.
# For some reason apt-get does not automatically install dh-python, python-all,
# and python3-all, even though they are needed (and recommended) for
# python-stdeb to work correctly.
sudo apt-get -y install dh-python python-all python3-all python-stdeb and python3-stdeb

curl -L -o pynag-$PYNAG_VERSION.tar.gz https://github.com/netmarkjp/pynag/archive/$PYNAG_GIT_COMMIT.tar.gz

# We have to specify DEB_BUILD_OPTIONS=nocheck because the tests have some
# additional dependencies that we do not want to install.
# We do not build the package for Python 2 because for Python 2, we can use the
# original version shipped with Ubuntu.
DEB_BUILD_OPTIONS=nocheck py2dsc-deb --with-python2=false --with-python3=true pynag-$PYNAG_VERSION.tar.gz