1 of 3

Central Logging, AIDE and AnsibleLesson 1 of 3

Sending logs to a central host with rsyslog

Configure rsyslog as a log server and as a forwarding client, open the right ports, and prove messages arrive.

  • rsyslogd
  • rsyslogd -N1
  • /etc/rsyslog.d
  • logger
  • logger -n
  • ss -tlnp
  • firewall-cmd --add-port
  • semanage port
  • tcpdump
  • logrotate

50 min3 exercises

After this lesson you can

  • Explain how journald and rsyslog cooperate and where each facility and priority is written.
  • Configure a TCP log server that files messages per host, and a client that forwards everything.
  • Test the path with logger, and locate a failure in the firewall, SELinux, the config or the network.

The idea

A single machine’s logs are lost with the machine and are scattered when you manage many. Central logging copies every message to one host, so evidence survives a crash, an attacker or a re-install, and you search one place.

How the pieces fit

On RHEL two logging systems run side by side:

  • systemd-journald collects everything: kernel messages, units’ output, logger, and the syslog socket. It stores a structured binary journal (see the RHCSA Processes, Tuning and Logs skill).
  • rsyslog reads the journal (module imjournal) and the syslog socket (imuxsock), and applies rules that write messages to text files (/var/log/messages, /var/log/secure, /var/log/maillog, …) or send them over the network.

Central logging is therefore an rsyslog feature. A rule picks messages by facility (the source category: authpriv, mail, cron, daemon, local0 to local7, …) and priority (debug up to emerg), and an action says what to do with them: omfile writes a file, omfwd forwards to another host.

service --> journald --> rsyslog (imjournal) --> rules --> /var/log/messages
                                                      \--> omfwd --> network --> log server --> imtcp --> ruleset --> /var/log/remote/HOST/PROG.log

Choosing the transport

TransportUseNotes
UDP (@host)Never for anything you must not loseNo delivery guarantee
TCP (@@host, protocol="tcp")The normal choice and what the exam asks forRetries; combine with a queue
RELP (omrelp, imrelp)When you cannot afford to lose a message in a crashNeeds the rsyslog-relp package
TLS (gtls, port 6514)Across untrusted networksNeeds rsyslog-gnutls and certificates. Beyond the exam

The server

The server loads imtcp, listens, and sends what arrives into a ruleset of its own. If you skip the ruleset, remote messages mix into the server’s own /var/log/messages, which is what you do not want.

module(load="imtcp")
template(name="PerHostFile" type="string" string="/var/log/remote/%HOSTNAME%/%PROGRAMNAME%.log")
ruleset(name="remote") {
    action(type="omfile" dynaFile="PerHostFile" dirCreateMode="0750" fileCreateMode="0640")
}
input(type="imtcp" port="514" ruleset="remote")

dynaFile builds the file name from the template for every message, so each host and program gets its own file. The server also needs the port open in the firewall (514/tcp) and, on a non-default port, an SELinux port label (semanage port -a -t syslogd_port_t -p tcp 30514). Check what is already allowed with semanage port -l | grep syslog.

The client

One action forwards everything. The queue keeps messages in memory (and on disk if queue.filename is set) while the server is down, and resumeRetryCount="-1" retries forever instead of dropping them.

*.* action(type="omfwd" target="node2.lab.example" port="514" protocol="tcp"
           action.resumeRetryCount="-1"
           queue.type="linkedList" queue.size="10000" queue.filename="fwd_node2" queue.saveOnShutdown="on")

The older one-line syntax still works: *.* @@node2.lab.example:514 (two @ means TCP, one means UDP). Use the newer form when you need a queue.

Try it

This is real output from rsyslog 8, running a server and a forwarding client on one machine with the configuration above (port and paths changed for the test).

rsyslogd -N1
logger -n 127.0.0.1 -P 10514 -T -t labtest "test through logger"
rsyslogd: version 8.2312.0, config validation run (level 1), master config server.conf
rsyslogd: End of config validation run. Bye.

logger -n HOST -P PORT -T sends a message directly over TCP (-T); it tests the server side without involving the client. On the server:

find /var/log/remote -type f
cat /var/log/remote/node1/labtest.log
/var/log/remote/node1/labtest.log
/var/log/remote/node1/myapp.log
2026-11-04T16:55:06.986725+03:00 node1 labtest test through logger

Then test the client side by sending locally and checking the server:

logger -t central "from node1"
sudo cat /var/log/remote/node1*/central.log
2026-11-04T10:31:44.120333+03:00 node1 central from node1

Testing the two sides separately is the key technique: if logger -n reaches the server but logger does not, the fault is in the client configuration or its queue.

When messages do not arrive

Work along the path, one layer at a time.

LayerCheckA fault looks like
Client configrsyslogd -N1; journalctl -u rsyslogSyntax error; “cannot connect to host:port”
Client servicesystemctl status rsyslogNot running because of a bad file
Name and routegetent hosts node2.lab.example; pingWrong host name or address
TCP reachabilitync -zv node2.lab.example 514Connection refused (nothing listens) or timeout (firewall drops)
Server listeningss -tlnp | grep 514Nothing: module not loaded, config error, wrong port
Server firewallfirewall-cmd --list-portsPort not open or open only at runtime
SELinuxausearch -m avc -c rsyslogd -ts recentDenied bind to a non-default port, or write to a wrongly labelled directory
File permissionsls -ld /var/log/remotePermission denied in the server’s own log
Wiretcpdump -nni any port 514Packets leave the client but nothing returns

Connection refused and a timeout mean different things. Refused: the host answered and nothing listens on that port. Timeout: something (a firewall) dropped the packets. That one fact halves the search.

Going further

Keeping it tidy. Add /etc/logrotate.d/remote for /var/log/remote/*/*.log so remote logs do not fill /var/log. Beyond the exam: for a lossless pipeline use RELP and a disk-assisted queue on both ends, and for untrusted networks use TLS with StreamDriver.Mode="1" and mutual certificate authentication.

Practice

0 of 3 done

Do these on your own system. They get harder. Check each result, then tick it off.

  1. Warm-up

    Where does this message land?

    Send one test message each with logger -p authpriv.warning -t labtest "auth test" and logger -p mail.info -t labtest "mail test". Find each one in the journal and in the text file that rsyslog writes for it. Name the rule in /etc/rsyslog.conf that decided the file.

    Check your result
    • journalctl -t labtest shows both lines.
    • sudo grep 'auth test' /var/log/secure finds the first; sudo grep 'mail test' /var/log/maillog finds the second.
    • You can quote the lines authpriv.* /var/log/secure and mail.* -/var/log/maillog from /etc/rsyslog.conf.
    Hint

    A facility (authpriv, mail, cron, daemon) plus a priority selects a rule. The rules live in the RULES section of /etc/rsyslog.conf. If /var/log/maillog is missing, rsyslog is not running: systemctl status rsyslog.

    Solution
    systemctl is-active rsyslog
    logger -p authpriv.warning -t labtest "auth test"
    logger -p mail.info -t labtest "mail test"
    journalctl -t labtest --no-pager
    sudo grep 'auth test' /var/log/secure
    sudo grep 'mail test' /var/log/maillog
    grep -E '^(authpriv|mail)' /etc/rsyslog.conf
  2. Practice

    Build the log server and the client

    Make node2 a log server and node1 its client:

    1. node2 accepts syslog over TCP port 514 and writes each sender's messages to /var/log/remote/HOSTNAME/PROGRAM.log. The port is open in the firewall permanently and rsyslog is enabled at boot.
    2. node1 forwards every message (all facilities and priorities) to node2 over TCP, with a queue so messages are kept while node2 is down.
    3. Show that a message sent on node1 with logger -t central "from node1" appears on node2.
    Check your result
    • node2: sudo ss -tlnp | grep ':514 ' shows rsyslogd listening; sudo firewall-cmd --list-ports contains 514/tcp; systemctl is-enabled rsyslog is enabled.
    • node1: sudo rsyslogd -N1 ends with End of config validation run; systemctl is-active rsyslog is active.
    • node2: sudo cat /var/log/remote/node1*/central.log contains from node1.
    Hint

    Server: module(load="imtcp"), a template, a ruleset with an omfile action using dynaFile, and an input(type="imtcp" ...) bound to that ruleset. Client: one action(type="omfwd" ...) with protocol="tcp" and a linked-list queue. Validate both files with rsyslogd -N1 before restarting.

    Solution
    # node2 (server)
    sudo tee /etc/rsyslog.d/remote.conf <<'EOT'
    module(load="imtcp")
    template(name="PerHostFile" type="string" string="/var/log/remote/%HOSTNAME%/%PROGRAMNAME%.log")
    ruleset(name="remote") {
        action(type="omfile" dynaFile="PerHostFile" dirCreateMode="0750" fileCreateMode="0640")
    }
    input(type="imtcp" port="514" ruleset="remote")
    EOT
    sudo rsyslogd -N1 && sudo systemctl enable --now rsyslog && sudo systemctl restart rsyslog
    sudo firewall-cmd --permanent --add-port=514/tcp && sudo firewall-cmd --reload
    
    # node1 (client)
    sudo tee /etc/rsyslog.d/forward.conf <<'EOT'
    *.* action(type="omfwd" target="node2.lab.example" port="514" protocol="tcp"
               action.resumeRetryCount="-1"
               queue.type="linkedList" queue.size="10000" queue.filename="fwd_node2" queue.saveOnShutdown="on")
    EOT
    sudo rsyslogd -N1 && sudo systemctl restart rsyslog
    logger -t central "from node1"
    
    # node2: prove it
    sudo cat /var/log/remote/node1*/central.log
  3. Challenge

    Why do no messages arrive?

    Setup (starting from a working server and client, break three things):

    # on node1
    sudo sed -i 's/port="514" protocol="tcp"/port="5140" protocol="tcp"/' /etc/rsyslog.d/forward.conf
    # on node2
    sudo firewall-cmd --permanent --remove-port=514/tcp && sudo firewall-cmd --reload
    sudo sed -i 's#^template(name="PerHostFile".*#template(name="PerHostFile" type="string" string="/var/log/remote/%HOSTNAME%/%PROGRAMNAME%.log")#' /etc/rsyslog.d/remote.conf
    sudo chmod 000 /var/log/remote

    Messages sent with logger -t central "test 2" on node1 no longer arrive. Find and fix every fault, one at a time, with evidence for each (client log, ss, tcpdump or nc, firewall list, server log). Finish with a successful test 2 on node2 and make sure the fix survives a reboot.

    Check your result
    • node2: sudo cat /var/log/remote/node1*/central.log contains test 2.
    • sudo firewall-cmd --permanent --list-ports contains 514/tcp; ls -ld /var/log/remote shows mode drwxr-x--- or similar and owner root.
    • grep -c 5140 /etc/rsyslog.d/forward.conf is 0.
    Hint

    Work from the client outward: does nc -zv node2.lab.example 514 connect? Is the client's own rsyslog log (journal, journalctl -u rsyslog) complaining about the connection? Then look at the server: listening port, firewall, directory permissions and SELinux (ausearch -m avc -c rsyslogd).

    Solution
    # node1: client evidence
    journalctl -u rsyslog -b --no-pager | tail -5          # "cannot connect to ...:5140"
    sudo sed -i 's/port="5140"/port="514"/' /etc/rsyslog.d/forward.conf
    sudo rsyslogd -N1 && sudo systemctl restart rsyslog
    nc -zv node2.lab.example 514                           # still: no route / refused
    # node2: server evidence
    sudo ss -tlnp | grep ':514 '
    sudo firewall-cmd --permanent --add-port=514/tcp && sudo firewall-cmd --reload
    sudo chmod 750 /var/log/remote && sudo restorecon -Rv /var/log/remote
    sudo systemctl restart rsyslog
    # node1
    logger -t central "test 2"
    # node2
    sudo cat /var/log/remote/node1*/central.log