The following is open for discussion. I would be glad to implement this and send patches
for ZABBIX 1.8 and/or trunk and/or head, because I need the functionality provided by
the implementation of this proposal and don't want to maintain this "out of tree". I also
think that this might be useful for all users of ZABBIX who are monitoring log files.
ZABBIX agent usually runs as an unprivileged user, which is a good thing to do. Alas,
on UNIX systems, often log files are owned by a dedicated user (most likely, root) and
have strict file permissions on them to prevent other users from reading these files.
This makes it virtually impossible to practically monitor log files with ZABBIX without
a rather large configuration change on all monitored hosts (i.e. touch file ownerships
and permissions for all log files monitored).
Privilege separation for ZABBIX agent aims to provide a simple, effective and proven
mechanism to make log file monitoring within ZABBIX more usable.
2. How it works
Before ZABBIX agent drops it privileges to "zabbix" user, a special process will be
forked and steadily run in the background, much like other agent pollers do. This
process will not drop privileges and continue to run with super-user privileges.
This process will not communicate with the network or other processes than the ZABBIX
agent, and even this communication will be very restricted.
Basically, the privileged process works in the following way:
- Receive a request from ZABBIX agent to open a given file, i.e. /var/log/messages
- Verify that the file is in a white list (new configuration parameter necessary)
- Open the file with O_RDONLY flag to get a file descriptor
- Pass this file descriptor back to ZABBIX agent
- Close file descriptor (will stay open in the unprivileged parent process)
It is then the responsibility of the unprivileged ZABBIX agent to work with and close this
file descriptor accordingly, so it will not leak. Reading and processing the data from the
log file will also be handled in the unprivileged process.
The code running as UID 0 is kept as minimal as possible and consists only of a very few
3. The interface
The communication between the unprivileged Agent and the privileged process will be
performed via a socketpair(2) of type AF_LOCAL.
The ZABBIX agent can use new API functions for file operations, e.g.:
int zbx_priv_open(int priv_fd, const char *path, int mode)
int zbx_priv_stat(int priv_fd, const char *path, struct stat *buf)
int zbx_priv_lstat(int priv_fd, const char *path, struct stat *buf)
The new functions try to mimic the behaviour of the original functions, returning error
codes and setting errno just like the originals. The design goal is to make it possible
to use the new functions as drop-in replacement - the only thing that needs to be taken
care of ist the new first argument, priv_fd, which specifies the file descriptor to use
for communicating with the privileged process.
The changes in existing agent code will be as unintrusive as possible.
For security purposes, there should be a new configuration parameter which defines the
file patterns allowed to be openened by the privileged process. This is to prevent a
malicious GUI user to have ZABBIX agent return lines from "secret" files, e.g. /etc/shadow
or private SSL keys.
This pattern could be defined, for example, with:
LogfileWhitelist = /var/log/*
The privileged process will then match the filename given with each request against this
whitelist, using shell globbing patterns (above example would match /var/log/messages and
/var/log/mysql/mysqld.log but not /var/spool/cron/...). Multiple patterns can be given,
delimeted by comma (",").
Maybe a blacklist (LogfileBlacklist) configuration option would be a complimentary option,
to disallow access to certain locations instead of using a white list, at the users
It is yet to be decided whether it is a good idea to use realpath(3) inside the privileged
code to sanitize the given paths. On one hand, we must prevent that a malicious user can
circumvent the white- or blacklist by using e.g. /var/log/../../etc/shadow as path or using
symbolic links (e.g. /tmp/foo -> /etc/shadow). realpath(3) could be a remedy, but it has
shown security flaws in the path in certain implementations.
- Check whether the path is a symbolic link and deny access if it is
- Check whether the path contains /.. somewhere and deny access if it is
This would be the most simplistic approach, maybe not as good as a check with realpath().
The privsep protocol can easily be extended to provide more privileged functions to the
ZABBIX agent in a sane and secure way.
For example, it could be used for data collection that requires root privileges (e.g. some
stuff in /proc etc).
The code has been tested on Linux and BSD (Open-, Net- and FreeBSD). It will not work on
non-UNIX-like OS (like Windows), and should work on most UNIX derivates that more or less
comply to the POSIX standard (providing socketpair(2), select(2) and sendmsg(2)).
Alas, I have no access to "big-iron" UNIX-derivates such as HP-UX, AIX or Solaris that
have a compiler environment so I cannot test it on those platforms.
7. Making it optional
7.1 At compile-time
A new option to configure should be introduced, namely --enable-privsep. When not specified,
the privilege separation code will not be compiled and the zbx_priv_*() functions will act
simply as wrappers around zbx_open() etc.
7.2 At run-time
Similar to OpenSSH's sshd, a new configuration item "UsePrivilegeSeperation" should be
introduced which takes a boolean argument to define whether the agent should make use of
the privsep feature (only available if compiled with --enable-privsep, of course).
8. Open issues
8.1 logrt items
It will not be possible to use this with items of type logrt due to the use of opendir(3).
I currently see no way to privsep opendir(), but one could emulate it using getdirentries(2).
This would imply a rather intrusive change to process_logrt() in logfiles.c of ZABBIX agent.
As written above, I only have limited access to more "exotic" platforms (from the OSS point
of view). As such, I can only test the code on Linux (RHEL/Centos, Ubuntu/Debian) and Open-
BSD (probably Free- and NetBSD as well).
Looking forward to any input and constructive criticism.