Rsyslog omprog is not working with SELinux enabled on Rocky Linux 9

Dear community,

I am trying to get rsyslog up and running on Rocky Linux 9 but running into a bothersome SELinux. Currently I have set rsyslog on permissive with sudo semanage permissive -a rsyslogd_t but is not the desired solution.

Below, I have shared:

  • validate
  • syslog.php
  • /etc/rsyslog.d/30-librenms.conf
  • /etc/rsyslogd.conf
  • status of the rsyslog service

Let me know if you want more information

Validate

[librenms@poller1-pop1 ~]$ ./validate.php
===========================================
Component | Version
--------- | -------
LibreNMS  | 25.10.0 (2025-10-20T10:51:40+00:00)
DB Schema | 2025_09_24_145502_add_port_security_table (357)
PHP       | 8.4.13
Python    | 3.9.21
Database  | MariaDB 11.8.3-MariaDB
RRDTool   | 1.7.2
SNMP      | 5.9.1
===========================================

[OK]    Installed from package; no Composer required
[OK]    Database Connected
[OK]    Database Schema is current
[OK]    SQL Server meets minimum requirements
[OK]    lower_case_table_names is enabled
[OK]    MySQL engine is optimal
[OK]    Database and column collations are correct
[OK]    Database schema correct
[OK]    MySQL and PHP time match
[OK]    Distributed Polling setting is enabled globally
[OK]    Connected to rrdcached
[OK]    Active pollers found
[OK]    Dispatcher Service is enabled
[OK]    Locks are functional
[OK]    No python wrapper pollers found
[OK]    Redis is functional
[OK]    rrdtool version ok
[OK]    Connected to rrdcached
[WARN]  Non-git install, updates are manual or from package

syslog.php

[librenms@poller1-pop1 ~]$ cat syslog.php
#!/usr/bin/env php
<?php

/**
 * LibreNMS
 *
 *   This file is part of LibreNMS.
 *
 * @copyright  (C) 2006 - 2012 Adam Armstrong
 */
$init_modules = [];
require __DIR__ . '/includes/init.php';

$keys = ['host', 'facility', 'priority', 'level', 'tag', 'timestamp', 'msg', 'program'];

$s = fopen('php://stdin', 'r');
while ($line = fgets($s)) {
    //logfile($line);

    $fields = explode('||', trim($line));
    if (count($fields) === 8) {
        process_syslog(array_combine($keys, $fields), 1);
    }

    unset($line, $fields);
}
[librenms@poller1-pop1 ~]$ ls -laZ syslog.php
-rwxr-x---. 1 librenms librenms system_u:object_r:usr_t:s0 536 Oct 20 10:29 syslog.php

/etc/rsyslog.d/30-librenms.conf

[librenms@poller1-pop1 ~]$ sudo cat /etc/rsyslog.d/30-librenms.conf
#---------------------------------------------------------
# send remote logs to LibreNMS via omprog

# UDP listener bound to LibreNMS ruleset
input(type="imudp" port="514" ruleset="librenms")

# (optional) TCP if you use it:
# input(type="imtcp" port="514" ruleset="librenms")

module(load="omprog")

template(name="librenms"
         type="string"
         string="%hostname%||%syslogfacility%||%syslogpriority%||%syslogseverity%||%syslogtag%||%$year%-%$month%-%$day% %timereported:8:25%||%msg%||%programname%\n")

ruleset(name="librenms" queue.workerThreads="4") {
       action(type="omprog"
           binary="/opt/librenms/syslog.php" 
           template="librenms")
       stop
}

/etc/rsyslog.conf

[librenms@poller1-pop1 ~]$ sudo cat /etc/rsyslog.conf 
# rsyslog configuration file
# Listen for syslog messages on UDP:514
$ModLoad imudp
$UDPServerRun 514
# For more information see /usr/share/doc/rsyslog-*/rsyslog_conf.html
# or latest version online at http://www.rsyslog.com/doc/rsyslog_conf.html 
# If you experience problems, see http://www.rsyslog.com/doc/troubleshoot.html

#### GLOBAL DIRECTIVES ####

# Where to place auxiliary files
global(workDirectory="/var/lib/rsyslog")

# Use default timestamp format
module(load="builtin:omfile" Template="RSYSLOG_TraditionalFileFormat")

#### MODULES ####

module(load="imuxsock"    # provides support for local system logging (e.g. via logger command)
       SysSock.Use="off") # Turn off message reception via local log socket; 
                          # local messages are retrieved through imjournal now.
module(load="imjournal"             # provides access to the systemd journal
       UsePid="system" # PID nummber is retrieved as the ID of the process the journal entry originates from
       FileCreateMode="0644" # Set the access permissions for the state file
       StateFile="imjournal.state") # File to store the position in the journal
#module(load="imklog") # reads kernel messages (the same are read from journald)
#module(load="immark") # provides --MARK-- message capability

# Include all config files in /etc/rsyslog.d/
include(file="/etc/rsyslog.d/*.conf" mode="optional")

# Provides UDP syslog reception
# for parameters see http://www.rsyslog.com/doc/imudp.html
#module(load="imudp") # needs to be done just once
#input(type="imudp" port="514")

# Provides TCP syslog reception
# for parameters see http://www.rsyslog.com/doc/imtcp.html
module(load="imtcp") # needs to be done just once
input(type="imtcp" port="514")

#### RULES ####
#BEGIN Cron SETTINGS - CIS benchmark - Ansible-lockdown
# Cron settings to meet CIS standards
cron.*                                                   /var/log/cron # CIS
#END Cron SETTINGS - CIS benchmark - Ansible-lockdown
#BEGIN Auth SETTINGS - CIS benchmark - Ansible-lockdown
# Private settings to meet CIS standards
auth,authpriv.*                                           /var/log/secure
#END Auth SETTINGS - CIS benchmark - Ansible-lockdown
# BEGIN MISC. LOG SETTINGS - CIS benchmark - Ansible-lockdown
# misc. logging additions to meet CIS standards
*.=warning;*.=err                                        -/var/log/warn
*.crit                                                   /var/log/warn
*.*;mail.none;news.none                                  /var/log/messages
# END MISC. LOG SETTINGS - CIS benchmark - Ansible-lockdown

# Log all kernel messages to the console.
# Logging much else clutters up the screen.
#kern.*                                                 /dev/console

# Log anything (except mail) of level info or higher.
# Don't log private authentication messages!
# *.info;mail.none;authpriv.none;cron.none                /var/log/messages

# The authpriv file has restricted access.
# authpriv.*                                              /var/log/secure

# Log all the mail messages in one place.
# BEGIN MAIL LOG SETTINGS - CIS benchmark - Ansible-lockdown
# mail logging additions to meet CIS standards
mail.*                                                  -/var/log/mail
mail.info                                               -/var/log/mail.info
mail.warning                                            -/var/log/mail.warning
mail.err                                                /var/log/mail.err
# END MAIL LOG SETTINGS - CIS benchmark - Ansible-lockdown
# mail.*                                                  -/var/log/maillog


# Log cron stuff
# cron.*                                                  /var/log/cron

# Everybody gets emergency messages
*.emerg                                                 :omusrmsg:*

# Save news errors of level crit and higher in a special file.
# BEGIN NEWS LOG SETTINGS - CIS benchmark - Ansible-lockdown
# news logging additions to meet CIS standards
news.crit                                               -/var/log/news/news.crit
news.notice                                             -/var/log/news/news.crit
# END NEWS LOG SETTINGS - CIS benchmark - Ansible-lockdown
uucp,news.crit                                          /var/log/spooler

# Save boot messages also to boot.log
local7.*                                                /var/log/boot.log


# ### sample forwarding rule ###
#action(type="omfwd"  
# # An on-disk queue is created for this action. If the remote host is
# # down, messages are spooled to disk and sent when it is up again.
#queue.filename="fwdRule1"       # unique name prefix for spool files
#queue.maxdiskspace="1g"         # 1gb space limit (use as much as possible)
#queue.saveonshutdown="on"       # save messages to disk on shutdown
#queue.type="LinkedList"         # run asynchronously
#action.resumeRetryCount="-1"    # infinite retries if host is down
# # Remote Logging (we use TCP for reliable delivery)
# # remote_host is: name/ip, e.g. 192.168.0.1, port optional e.g. 10514
#Target="remote_host" Port="XXX" Protocol="tcp")
$FileCreateMode 0640

rsyslog service status

[librenms@poller11-lab1 ~]$ sudo systemctl status rsyslog
● rsyslog.service - System Logging Service
     Loaded: loaded (/usr/lib/systemd/system/rsyslog.service; enabled; preset: enabled)
     Active: active (running) since Thu 2025-10-30 15:15:48 UTC; 8s ago
       Docs: man:rsyslogd(8)
             https://www.rsyslog.com/doc/
   Main PID: 1332413 (rsyslogd)
      Tasks: 10 (limit: 100398)
     Memory: 6.5M
        CPU: 102ms
     CGroup: /system.slice/rsyslog.service
             └─1332413 /usr/sbin/rsyslogd -n

Oct 30 15:15:53 poller1-pop1.domian.net rsyslogd[1332413]: action 'action-40-omprog' resumed (module 'omprog') [v8.2412.0-1.el9 try https://www.rsyslog.com/e/2359 ]
Oct 30 15:15:53 poller1-pop1.domain.net rsyslogd[1332428]: omprog: failed to execute program '/opt/librenms/syslog.php': Permission denied
Oct 30 15:15:53 poller1-pop1.domain.net rsyslogd[1332413]: child process (pid 1332428) exited with status 1 [v8.2412.0-1.el9]
Oct 30 15:15:54 poller1-pop1.domain.net rsyslogd[1332413]: omprog: program '/opt/librenms/syslog.php' (pid 1332428) terminated; will be restarted [v8.2412.0-1.el9 try https://www.rsyslog.com/e/2119 ]
Oct 30 15:15:54 poller1-pop1.domain.net rsyslogd[1332413]: action 'action-40-omprog' suspended (module 'omprog'), retry 0. There should be messages before this one giving the reason for suspension. [v8.2412.0-1.el9 try https://www.rsyslog.com/e/2007 ]