# Icinga 2 Docker image | (c) 2025 Icinga GmbH | GPLv2+

FROM debian:trixie-slim AS build-base

# Install all the necessary build dependencies for building Icinga 2 and the plugins.
#
# This stage includes the build dependencies for the plugins as well, so that they can share the same base
# image, since Docker builds common stages only once [^1] even if they are used in multiple build stages.
# This eliminates the need to have a separate base image for the plugins, that basically has kind of the
# same dependencies as the Icinga 2 build stage (ok, not exactly the same, but some of them are shared).
#
# [^1]: https://docs.docker.com/build/building/best-practices/#create-reusable-stages
RUN apt-get update && \
    apt-get upgrade -y && \
    apt-get install -y --no-install-recommends --no-install-suggests \
        autoconf \
        automake \
        bison \
        ccache \
        cmake \
        flex \
        g++ \
        git \
        libboost1.83-dev \
        libboost-context1.83-dev \
        libboost-coroutine1.83-dev \
        libboost-date-time1.83-dev \
        libboost-filesystem1.83-dev \
        libboost-iostreams1.83-dev \
        libboost-program-options1.83-dev \
        libboost-regex1.83-dev \
        libboost-system1.83-dev \
        libboost-thread1.83-dev \
        libboost-test1.83-dev \
        libedit-dev \
        libmariadb-dev \
        libpq-dev \
        libssl-dev \
        libsystemd-dev \
        make && \
    rm -rf /var/lib/apt/lists/*

# Set the default working directory for subsequent commands of the next stages.
WORKDIR /icinga2-build

FROM build-base AS build-plugins

# Install all the plugins that are not included in the monitoring-plugins package.
ADD https://github.com/lausser/check_mssql_health.git#747af4c3c261790341da164b58d84db9c7fa5480 /check_mssql_health
ADD https://github.com/lausser/check_nwc_health.git#a5295475c9bbd6df9fe7432347f7c5aba16b49df /check_nwc_health
ADD https://github.com/bucardo/check_postgres.git#58de936fdfe4073413340cbd9061aa69099f1680 /check_postgres
ADD https://github.com/matteocorti/check_ssl_cert.git#341b5813108fb2367ada81e866da989ea4fb29e7 /check_ssl_cert

WORKDIR /check_mssql_health
RUN mkdir bin && \
    autoconf && \
    autoreconf && \
    ./configure "--build=$(uname -m)-unknown-linux-gnu" --libexecdir=/usr/lib/nagios/plugins && \
    make && \
    make install DESTDIR="$(pwd)/bin"

WORKDIR /check_nwc_health
RUN mkdir bin && \
    autoreconf && \
    ./configure "--build=$(uname -m)-unknown-linux-gnu" --libexecdir=/usr/lib/nagios/plugins && \
    make && \
    make install DESTDIR="$(pwd)/bin"

WORKDIR /check_postgres
RUN mkdir bin && \
    perl Makefile.PL INSTALLSITESCRIPT=/usr/lib/nagios/plugins && \
    make && \
    make install DESTDIR="$(pwd)/bin" && \
    # This is necessary because of this build error: cannot copy to non-directory: /var/lib/docker/.../merged/usr/local/man
    rm -rf bin/usr/local/man

FROM build-base AS build-icinga2

# To access the automated build arguments in the Dockerfile originated from the Docker BuildKit [^1],
# we need to declare them here as build arguments. This is necessary because we want to use unique IDs
# for the mount cache below for each platform to avoid conflicts between multi arch builds. Otherwise,
# the build targets will invalidate the cache one another, leading to strange build errors.
#
# [^1]: https://docs.docker.com/reference/dockerfile/#automatic-platform-args-in-the-global-scope
ARG TARGETPLATFORM

# Icinga 2 build arguments.
#
# These arguments are used to configure the build of Icinga 2 and can be overridden
# by the user when building the image. All of them have a default value suitable for our official image.
ARG CMAKE_BUILD_TYPE=RelWithDebInfo
ARG ICINGA2_UNITY_BUILD=ON
ARG ICINGA2_BUILD_TESTING=ON

# The number of jobs to run in parallel when building Icinga 2.
# By default, it is set to the number of available CPU cores on the build machine.
ARG MAKE_JOBS=auto

# Create the directory where the final Icinga 2 files will be installed.
#
# This directory will be used as the destination for the `make install` command below and will be
# copied to the final image. Other than that, this directory will not be used for anything else.
RUN mkdir /icinga2-install

# Mount the source code as a bind mount instead of copying it, so that we can use the cache effectively.
# Additionally, add the ccache and CMake build directories as cache mounts to speed up rebuilds.
RUN --mount=type=bind,source=.,target=/icinga2,readonly \
    --mount=type=cache,id=ccache-${TARGETPLATFORM},target=/root/.ccache \
    --mount=type=cache,id=icinga2-build-${TARGETPLATFORM},target=/icinga2-build \
    PATH="/usr/lib/ccache:$PATH" \
    cmake -S /icinga2 -B /icinga2-build \
        -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \
        # Podman supports forwarding notifications from containers to systemd, so build Icinga 2 with systemd support.
        -DUSE_SYSTEMD=ON \
        -DBUILD_TESTING=${ICINGA2_BUILD_TESTING} \
        -DICINGA2_UNITY_BUILD=${ICINGA2_UNITY_BUILD} \
        # The command group name below is required for the prepare-dirs script to work, as it expects
        # the command group name, which by default is `icingacmd` to exist on the system. Since we
        # don't create the `icingacmd` command group in this image, we need to override it with icinga.
        -DICINGA2_COMMAND_GROUP=icinga \
        -DCMAKE_INSTALL_PREFIX=/usr \
        -DCMAKE_INSTALL_SYSCONFDIR=/data/etc \
        -DCMAKE_INSTALL_LOCALSTATEDIR=/data/var \
        -DICINGA2_SYSCONFIGFILE=/etc/sysconfig/icinga2 \
        -DICINGA2_RUNDIR=/run \
        -DICINGA2_WITH_COMPAT=OFF \
        -DICINGA2_WITH_LIVESTATUS=OFF && \
    make -j$([ "$MAKE_JOBS" = auto ] && nproc || echo "$MAKE_JOBS") && \
    CTEST_OUTPUT_ON_FAILURE=1 make test && \
    make install DESTDIR=/icinga2-install

RUN rm -rf /icinga2-install/etc/icinga2/features-enabled/mainlog.conf \
        /icinga2-install/usr/share/doc/icinga2/markdown && \
    strip -g /icinga2-install/usr/lib/*/icinga2/sbin/icinga2 && \
    strip -g /icinga2-install/usr/lib/nagios/plugins/check_nscp_api

# Prepare the final image with the necessary configuration files and runtime dependencies.
FROM debian:trixie-slim AS icinga2

# The real UID of the Icinga user to be used in the final image.
ARG ICINGA_USER_ID=5665

# Install the necessary runtime dependencies for the Icinga 2 binary and the monitoring-plugins.
RUN apt-get update && \
    apt-get upgrade -y && \
    DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends --no-install-suggests \
        bc \
        ca-certificates \
        curl \
        dumb-init \
        file \
        libboost-context1.83.0 \
        libboost-coroutine1.83.0 \
        libboost-date-time1.83.0 \
        libboost-filesystem1.83.0 \
        libboost-iostreams1.83.0 \
        libboost-program-options1.83.0 \
        libboost-regex1.83.0 \
        libboost-system1.83.0 \
        libboost-thread1.83.0 \
        libcap2-bin \
        libedit2 \
        libldap-common \
        libmariadb3 \
        libmoosex-role-timer-perl \
        libpq5 \
        libssl3 \
        libsystemd0 \
        mailutils \
        monitoring-plugins \
        msmtp \
        msmtp-mta \
        openssh-client \
        openssl && \
    # Official Debian images automatically run `apt-get clean` after every install, so we don't need to do it here.
    rm -rf /var/lib/apt/lists/*

# Create the icinga user and group with a specific UID as recommended by Docker best practices.
# The user has a home directory at /var/lib/icinga2, and if configured, that directory will also
# be used to store the ".msmtprc" file created by the entrypoint script.
RUN adduser \
    --system \
    --group \
    --home /var/lib/icinga2 \
    --disabled-login \
    --no-create-home \
    --uid ${ICINGA_USER_ID} icinga

COPY --from=build-plugins /check_mssql_health/bin/ /
COPY --from=build-plugins /check_nwc_health/bin/ /
COPY --from=build-plugins /check_postgres/bin/ /
COPY --from=build-plugins /check_ssl_cert/check_ssl_cert /usr/lib/nagios/plugins/check_ssl_cert

COPY --from=build-icinga2 /icinga2-install/ /

# Create a corresponding symlink in the root filesystem for all Icinga 2 directories in /data.
# This is necessary because we want to maintain the compatibility with containers built with the
# legacy Dockerfile, which expects the Icinga 2 directories to be in the root directory.
RUN for dir in /etc/icinga2 /var/cache/icinga2 /var/lib/icinga2 /var/log/icinga2 /var/spool/icinga2; do \
    ln -vs "/data$dir" "$dir"; \
done

# The below prepare-dirs script will not fix any permissions issues for the actuall /var/lib/icinga2 or
# /etc/icinga2 directories, so we need to set the correct ownership for the /data directory recursively.
RUN chown -R icinga:icinga /data

# Run the prepare-dirs script to create non-existing directories and set the correct permissions for them.
# It's invoked in the same way as in the systemd unit file in a Debian package, so this will ensure that
# all the necessary directories are created with the correct permissions and ownership.
RUN /usr/lib/icinga2/prepare-dirs /etc/sysconfig/icinga2

# Well, since the /data directory is intended to be used as a volume, we should also declare it as such.
# This will allow users to mount their own directories or even specific files to the /data directory
# without any issues. We've already filled the /data directory with the necessary configuration files,
# so users can simply mount their own files or directories if they want to override the default ones and
# they will be able to do so without any issues.
VOLUME ["/data"]

COPY --chmod=0755 tools/container/entrypoint.sh /usr/local/bin/entrypoint.sh
ENTRYPOINT ["/usr/bin/dumb-init", "-c", "--", "/usr/local/bin/entrypoint.sh"]

EXPOSE 5665
USER icinga

CMD ["icinga2", "daemon"]
