You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
safe-reload script runs as root operating on potentially untrusted data found in icinga PID file #10527
The icinga processes typically run with lowered privileges, e.g. as icinga:icinga on openSUSE Tumbleweed. The PID file there is found in this location:
The safe-reload script performs the following operation based on the pidfile data:
pid=`cat "$ICINGA2_PID_FILE"`
if ! kill -HUP "$pid" >/dev/null 2>&1; then
echo "Error: Icinga not running"
exit 7
fi
This effectively means that the safe-reload script processes data controlled by the icinga service user. The PID file could be replaced by a symbolic link, or a named pipe, which would lead to a local denial-of-service against the reload invocation.
Furthermore an arbitrary PID can be placed in the file, which would cause safe-reload to send the SIGHUP to an arbitrary other process in the system, causing integrity violation and/or denial-of-service.
This is a defense-in-depth issue, because the isolation between the icinga service user and root is lacking. It is not directly exploitable, but becomes a problem when the icinga service user account is compromised.
To fix this the safe-reload script could be invoked with service user credentials from the very beginning, or parts of the script could use a utility like sudo, to drop to service user privileges.
This postrotate script runs as root and operates on the PID file. It can similarly be fixed by using sudo. Or, even better, fix the systemctl reload case and the call systemctl reload icinga2.service in the logrotate configuration file.
To fix this the safe-reload script could be invoked with service user credentials from the very beginning, or parts of the script could use a utility like sudo, to drop to service user privileges.
sudo sounds like overkill for the task, something like setpriv from util-linux sounds more appropriate for that job. However, from a previous and very similar issue (see #5991 and #5850), there is already the icinga2 internal signal subcommand that drops privileges to the daemon user and then sends a signal. So I'd go for that as it's already there and not Linux-specific.
Or, even better, fix the systemctl reload case and the call systemctl reload icinga2.service in the logrotate configuration file.
No, those do different things, SIGHUP performs a full reload, loading the (potentially changed) configuration, whereas SIGUSR1 only reopens the log files.
This is a defense-in-depth issue, because the isolation between the icinga service user and root is lacking. It is not directly exploitable, but becomes a problem when the icinga service user account is compromised.
May I ask if you reported this here because you weren't aware of our security contact address (see https://icinga.com/company/contact/security-issues/, though that could probably be communicated better here on GitHub)?
Please have a look at my proposed fix in #10530. Note that this is mostly targeted at fixing the root problem, I think more can still be done, like for example setting User=icinga in the systemd unit so systemd doesn't even start things as root (except ExecStartPre which probably needs more permissions as it initializes directories) or using $MAINPID when invoked from systemd, removing the need for even reading the PID file.
This is a defense-in-depth issue, because the isolation between the icinga service user and root is lacking. It is not directly exploitable, but becomes a problem when the icinga service user account is compromised.
May I ask if you reported this here because you weren't aware of our security contact address (see https://icinga.com/company/contact/security-issues/, though that could probably be communicated better here on GitHub)?
I checked whether it was possible to create a confidential GitHub issue, but that wasn't available, because there's no security policy setup in your repository.
Apologies if you would have preferred coordinated disclosure. We usually offer it, but for defense-in-depth issues like this one upstream projects often like to handle them publicly from the beginning. As I'm currently dealing with a large amount of similar issues I chose the quick and simple route to save some time.
Please have a look at my proposed fix in #10530. Note that this is mostly targeted at fixing the root problem, I think more can still be done, like for example setting User=icinga in the systemd unit so systemd doesn't even start things as root (except ExecStartPre which probably needs more permissions as it initializes directories) or using $MAINPID when invoked from systemd, removing the need for even reading the PID file.
I just had a look. I guess the interesting part now is what icinga2 internal signal --sig <signal> --pid <pid> does. Judging from the strace output it's a lot. Many threads are created, sockets are created, network interface information is retrieved via Netlink, file descriptors in /proc/self/fd are inspected, one thread sends SIGRT_1 to another thread.
Besides all that, all threads also seem to call setuid(), setgid() and setgroups(). icinga2 and icingacmd group ownership is retained. Only then the desired signal is sent to the target <pid>. So I guess it is okay. I couldn't see any dangerous file operations in the trace.
Threads being started is mostly an artifact that this is just part of the one icinga2 binary, which is also used for running the daemon and initializes threads quite early. Those don't really do anything useful for that particular call. So what happens here is pretty much the same as it would when starting icinga2 daemon as root with it dropping the privileges itself.
That SIGRT_1 sending and following syscalls are just the glibc implementation of setuid(): in the kernel, credentials are per thread not per process, so glibc does that dance to issue the corresponding syscalls in every thread.
What is this LOGROTATE_USE_SU exactly refering to? Where is this supposed to be found?
It's a CMake variable set conditionally here and might not always be available.
Ah, I see. It results in a su <user> <group> logrotate configuration line.
Alas, this configuration directive only determines which credentials logrotated uses for its internal log rotation logic. Embedded scripts like prerotate and postrotate always run with full root privileges.
We covered this in a blog post a while ago, if you're interested:
Scripts are run as the invoking user and group, irrespective of any su directive.
Thus, the OP of #10617 probably changed the systemd unit file to start logrotate as the icinga user. There's nothing to do here, thanks for sharing the link!
Activity
mgerstner commentedon Jul 31, 2025
Note that there exists a variant of the same issue in the logrotate drop-in configuration file:
This postrotate script runs as
rootand operates on the PID file. It can similarly be fixed by usingsudo. Or, even better, fix thesystemctl reloadcase and the callsystemctl reload icinga2.servicein the logrotate configuration file.julianbrost commentedon Aug 1, 2025
Thank you for the report!
sudo sounds like overkill for the task, something like setpriv from util-linux sounds more appropriate for that job. However, from a previous and very similar issue (see #5991 and #5850), there is already the
icinga2 internal signalsubcommand that drops privileges to the daemon user and then sends a signal. So I'd go for that as it's already there and not Linux-specific.No, those do different things, SIGHUP performs a full reload, loading the (potentially changed) configuration, whereas SIGUSR1 only reopens the log files.
May I ask if you reported this here because you weren't aware of our security contact address (see https://icinga.com/company/contact/security-issues/, though that could probably be communicated better here on GitHub)?
julianbrost commentedon Aug 1, 2025
Please have a look at my proposed fix in #10530. Note that this is mostly targeted at fixing the root problem, I think more can still be done, like for example setting
User=icingain the systemd unit so systemd doesn't even start things as root (exceptExecStartPrewhich probably needs more permissions as it initializes directories) or using$MAINPIDwhen invoked from systemd, removing the need for even reading the PID file.mgerstner commentedon Aug 6, 2025
I checked whether it was possible to create a confidential GitHub issue, but that wasn't available, because there's no security policy setup in your repository.
Apologies if you would have preferred coordinated disclosure. We usually offer it, but for defense-in-depth issues like this one upstream projects often like to handle them publicly from the beginning. As I'm currently dealing with a large amount of similar issues I chose the quick and simple route to save some time.
mgerstner commentedon Aug 6, 2025
I just had a look. I guess the interesting part now is what
icinga2 internal signal --sig <signal> --pid <pid>does. Judging from thestraceoutput it's a lot. Many threads are created, sockets are created, network interface information is retrieved via Netlink, file descriptors in/proc/self/fdare inspected, one thread sends SIGRT_1 to another thread.Besides all that, all threads also seem to call
setuid(),setgid()andsetgroups().icinga2andicingacmdgroup ownership is retained. Only then the desired signal is sent to the target<pid>. So I guess it is okay. I couldn't see any dangerous file operations in the trace.julianbrost commentedon Aug 6, 2025
Threads being started is mostly an artifact that this is just part of the one
icinga2binary, which is also used for running the daemon and initializes threads quite early. Those don't really do anything useful for that particular call. So what happens here is pretty much the same as it would when startingicinga2 daemonas root with it dropping the privileges itself.That SIGRT_1 sending and following syscalls are just the glibc implementation of
setuid(): in the kernel, credentials are per thread not per process, so glibc does that dance to issue the corresponding syscalls in every thread.Al2Klimov commentedon Oct 28, 2025
Oh, does it? #10617
I thought it includes LOGROTATE_USE_SU which is
su ${ICINGA2_USER} ${ICINGA2_GROUP}.mgerstner commentedon Oct 29, 2025
What is this
LOGROTATE_USE_SUexactly refering to? Where is this supposed to be found?yhabteab commentedon Oct 29, 2025
It's a CMake variable set conditionally here and might not always be available.
icinga2/CMakeLists.txt
Lines 155 to 172 in f42510f
mgerstner commentedon Oct 29, 2025
Ah, I see. It results in a
su <user> <group>logrotate configuration line.Alas, this configuration directive only determines which credentials
logrotateduses for its internal log rotation logic. Embedded scripts likeprerotateandpostrotatealways run with full root privileges.We covered this in a blog post a while ago, if you're interested:
https://security.opensuse.org/2025/10/01/summer-spotlight.html#problems-with-scripts-embedded-in-configuration
yhabteab commentedon Oct 29, 2025
Indeed! In its man page states also this:
Thus, the OP of #10617 probably changed the systemd unit file to start logrotate as the
icingauser. There's nothing to do here, thanks for sharing the link!