| |

LFCE 121 ๐Ÿง rsyslog โ€” Server Configuration

โฑ๏ธ Estimated read: 12 min | |

The rsyslog server is the receiving end of a centralized logging setup. It listens on a network port, accepts syslog messages from clients, and writes them to files organized by host, program, or any other message property. The configuration is not a single global setting; it is a set of modules, rulesets, templates, and inputs that together determine what the server accepts, how it classifies incoming messages, and where it stores them.

This chapter covers the server-side configuration: loading the UDP and TCP input modules, creating a dedicated ruleset so remote messages do not mix with local logs, defining templates that separate logs by hostname and program name, and verifying the configuration before restarting the service. It also covers the security considerations that apply to any service listening on a network port, including path sanitization and firewall scoping.

Key point: A remote log server must use a dedicated ruleset for incoming messages. Without it, remote logs are processed by the same rules as local logs and end up mixed into /var/log/messages or /var/log/syslog, which defeats the purpose of centralization.


Why server configuration has this structure

The mixing problem. When rsyslog receives a remote message, it processes it through the same rule chain as local messages unless a ruleset is assigned. The default rules write to /var/log/messages, /var/log/secure, and other local files. A collector receiving logs from twenty hosts would write all of them into those same files, producing an unreadable mixture with no way to tell which host generated which entry. A dedicated ruleset isolates remote messages and routes them to a separate directory structure .

The path traversal problem. The message properties hostname and programname are supplied by the client. If a malicious or misconfigured client sends a hostname containing ../, a template that uses the hostname directly in a file path could write outside the intended directory. The SecurePath="replace" option sanitizes these values before they are used in a path . This is not optional for any template that constructs a path from message properties.

The transport reliability problem. UDP is the traditional syslog transport and the default for rsyslog. It has no delivery guarantee. TCP adds connection-oriented reliability, and the rsyslog documentation recommends TCP for production environments where message loss matters . The server can listen on both, but the choice should be deliberate: a collector that receives logs over UDP may silently drop messages during network congestion.


a. Loading the input modules

The first step is to load the modules that provide network input. imudp listens for UDP messages, and imtcp listens for TCP messages. Both can be loaded simultaneously, and each can be assigned its own ruleset .

# /etc/rsyslog.d/10-receiver.conf
# Load the UDP input module
module(load="imudp")

# Load the TCP input module (recommended for reliability)
module(load="imtcp")

Loading a module does not open a port by itself. The input() directive creates the listener and assigns it to a port and a ruleset.

b. Creating a dedicated ruleset

A ruleset is a named container for a set of rules. When an input is assigned to a ruleset, incoming messages are processed by that ruleset’s rules instead of the default rules. This is how remote logs are kept separate from local logs.

# Define a ruleset for remote messages
ruleset(name="remote") {
    action(type="omfile" file="/var/log/remote.log")
    stop
}

# Assign the UDP input to the ruleset
input(type="imudp" port="514" ruleset="remote")

# Assign the TCP input to the same ruleset
input(type="imtcp" port="514" ruleset="remote")

The stop directive at the end of the ruleset tells rsyslog to stop processing the message after the action. Without it, the message may continue through subsequent rulesets or the default rules, which could cause duplicate writes .

c. Organizing logs with templates

A single /var/log/remote.log file is better than mixing remote logs with local logs, but it still combines all hosts and all programs. A template constructs a dynamic file path from message properties so that each host’s logs go into their own directory and each program’s logs into their own file.

# Template for per-host, per-program files
template(name="RemoteLogs" type="list") {
    constant(value="/var/log/remote/")
    property(name="hostname" SecurePath="replace")
    constant(value="/")
    property(name="programname" SecurePath="replace")
    constant(value=".log")
}

The SecurePath="replace" sanitizes the hostname and programname values so that path traversal characters cannot escape the /var/log/remote/ directory . The resulting paths look like /var/log/remote/web01/sshd.log and /var/log/remote/db01/postgres.log.

The ruleset then uses the template with an omfile action:

ruleset(name="remote") {
    action(type="omfile" dynaFile="RemoteLogs" createDirs="on" dirCreateMode="0750" fileCreateMode="0640")
    stop
}

The dynaFile parameter tells the action to use the template for the path. The createDirs="on" option creates the per-host directories automatically. The dirCreateMode and fileCreateMode set restrictive permissions so that logs are not world-readable .

d. Restricting access and verifying the configuration

A syslog server listening on port 514 accepts messages from any host that can reach it. The firewall should restrict the source to the known client networks. On a system with firewalld:

# Allow UDP and TCP on port 514 from a specific zone
firewall-cmd --permanent --zone=internal --add-port=514/udp
firewall-cmd --permanent --zone=internal --add-port=514/tcp
firewall-cmd --reload

Before restarting rsyslog, validate the configuration syntax. The -N1 flag checks the configuration without starting the daemon, which catches typos and invalid directives before they cause a service failure .

rsyslogd -N1

If the validation passes, restart the service and verify that the listener is active.

systemctl restart rsyslog
ss -ulpn | grep ':514'

Complete Example Session

# ============================================
# PART 1: CREATE THE CONFIGURATION FILE
# ============================================
sudo tee /etc/rsyslog.d/10-receiver.conf <<'EOF'
# Load network input modules
module(load="imudp")
module(load="imtcp")

# Template for per-host, per-program files
template(name="RemoteLogs" type="list") {
    constant(value="/var/log/remote/")
    property(name="hostname" SecurePath="replace")
    constant(value="/")
    property(name="programname" SecurePath="replace")
    constant(value=".log")
}

# Dedicated ruleset for remote messages
ruleset(name="remote") {
    action(type="omfile" dynaFile="RemoteLogs" createDirs="on" dirCreateMode="0750" fileCreateMode="0640")
    stop
}

# Assign inputs to the ruleset
input(type="imudp" port="514" ruleset="remote")
input(type="imtcp" port="514" ruleset="remote")
EOF
# ============================================
# PART 2: CREATE THE LOG DIRECTORY
# ============================================
sudo mkdir -p /var/log/remote
sudo chown rsyslog:rsyslog /var/log/remote
sudo chmod 750 /var/log/remote

# The rsyslog user must be able to create subdirectories.
# ============================================
# PART 3: VALIDATE THE CONFIGURATION
# ============================================
sudo rsyslogd -N1

# Expected output:
# rsyslogd: version ... config validation run (level 1)...
# rsyslogd: End of config validation run.
# ============================================
# PART 4: RESTART THE SERVICE
# ============================================
sudo systemctl restart rsyslog
systemctl status rsyslog --no-pager
# ============================================
# PART 5: VERIFY THE LISTENER
# ============================================
sudo ss -ulpn | grep ':514'
sudo ss -tlpn | grep ':514'

# Both UDP and TCP listeners should appear.
# ============================================
# PART 6: OPEN THE FIREWALL
# ============================================
# firewalld
sudo firewall-cmd --permanent --zone=internal --add-port=514/udp
sudo firewall-cmd --permanent --zone=internal --add-port=514/tcp
sudo firewall-cmd --reload

# ufw
# sudo ufw allow from 192.168.10.0/24 to any port 514 proto udp
# sudo ufw allow from 192.168.10.0/24 to any port 514 proto tcp
# ============================================
# PART 7: SEND A TEST MESSAGE FROM A CLIENT
# ============================================
# On the client:
logger -t test "hello from the client"

# On the server:
sudo tail -f /var/log/remote/<client-hostname>/test.log
# ============================================
# PART 8: VERIFY SEPARATION FROM LOCAL LOGS
# ============================================
# Local logs remain in /var/log/messages or /var/log/syslog
# Remote logs are in /var/log/remote/
# The two do not mix.
# ============================================
# PART 9: CHECK FOR PATH TRAVERSAL PROTECTION
# ============================================
# The SecurePath="replace" option is present in the template.
# A client with hostname "../etc" cannot write to /var/log/etc/.
# ============================================
# PART 10: TROUBLESHOOT A FAILED RECEIVE
# ============================================
# 1. Check the listener: ss -ulpn | grep 514
# 2. Check the firewall: firewall-cmd --list-ports
# 3. Check the rsyslog log: journalctl -u rsyslog
# 4. Check directory permissions: ls -ld /var/log/remote

The ten parts covered the configuration file, log directory creation, syntax validation, service restart, listener verification, firewall rules, a client test message, separation from local logs, path traversal protection, and troubleshooting.


Quick Reference

Input modules

ModuleProtocolDirective
imudpUDPinput(type="imudp" port="514" ruleset="remote")
imtcpTCPinput(type="imtcp" port="514" ruleset="remote")

Template properties

PropertyValue
hostnameClient hostname
programnameProgram that generated the message
fromhost-ipSource IP address
SecurePath="replace"Sanitizes path traversal characters

Ruleset directives

DirectivePurpose
ruleset(name="...") { ... }Define a named ruleset
action(type="omfile" dynaFile="...")Write to dynamic file path
stopEnd processing for this message
createDirs="on"Create missing directories
dirCreateMode="0750"Directory permissions
fileCreateMode="0640"File permissions

Verification commands

CommandPurpose
rsyslogd -N1Validate configuration syntax
systemctl restart rsyslogApply changes
ss -ulpn | grep 514Verify UDP listener
ss -tlpn | grep 514Verify TCP listener
logger -t test "message"Send test message from client

Best Practices

โœ… Do This:

# Use a dedicated ruleset for remote messages
ruleset(name="remote") { ... stop }                     # โœ…
# Sanitize hostname and programname in templates
property(name="hostname" SecurePath="replace")          # โœ…
# Validate configuration before restarting
rsyslogd -N1                                            # โœ…
# Restrict firewall access to known client networks
firewall-cmd --zone=internal --add-port=514/udp         # โœ…
# Use TCP for production reliability
input(type="imtcp" port="514" ruleset="remote")         # โœ…

โŒ Don’t Do This:

# Receive remote logs without a ruleset
input(type="imudp" port="514")                          # โŒ (mixes with local)
# Use hostname in a path without sanitization
property(name="hostname")                               # โŒ (path traversal)
# Open port 514 to the public internet
firewall-cmd --add-port=514/udp                         # โŒ (no source restriction)
# Restart without validating
systemctl restart rsyslog                              # โŒ (typos crash the service)

Common Pitfalls

PitfallWhy It HappensFix
Remote logs mixed with localNo ruleset assigned to the inputCreate a ruleset and assign it
Messages not receivedFirewall blocks port 514Open UDP/TCP 514 from client networks
Path traversal vulnerabilitySecurePath omittedAdd SecurePath="replace" to template properties
rsyslog fails to startSyntax error in configurationRun rsyslogd -N1 to find the error
Directory permission denied/var/log/remote not writable by rsyslog userchown rsyslog:rsyslog and chmod 750
Duplicate log entriesRuleset lacks stopAdd stop at the end of the ruleset

Real-World Examples

1. Load UDP module

module(load="imudp")

2. Load TCP module

module(load="imtcp")

3. Dedicated ruleset

ruleset(name="remote") { action(type="omfile" file="/var/log/remote.log") stop }

4. Assign input to ruleset

input(type="imudp" port="514" ruleset="remote")

5. Per-host template

template(name="RemoteLogs" type="list") {
  constant(value="/var/log/remote/")
  property(name="hostname" SecurePath="replace")
  constant(value=".log")
}

6. Dynamic file action

action(type="omfile" dynaFile="RemoteLogs" createDirs="on")

7. Validate configuration

rsyslogd -N1

8. Verify listener

ss -ulpn | grep 514

9. Open firewall

firewall-cmd --zone=internal --add-port=514/udp

10. Test from client

logger -t test "hello"

Visual

The server configuration structure

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  /etc/rsyslog.d/10-receiver.conf                             โ”‚
โ”‚                                                              โ”‚
โ”‚  module(load="imudp")                                        โ”‚
โ”‚  module(load="imtcp")                                        โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  template(name="RemoteLogs")                                 โ”‚
โ”‚    /var/log/remote/<hostname>/<programname>.log              โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  ruleset(name="remote") {                                    โ”‚
โ”‚    action(type="omfile" dynaFile="RemoteLogs")               โ”‚
โ”‚    stop                                                      โ”‚
โ”‚  }                                                           โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  input(type="imudp" port="514" ruleset="remote")             โ”‚
โ”‚  input(type="imtcp" port="514" ruleset="remote")             โ”‚
โ”‚                                                              โ”‚
โ”‚  Remote messages โ†’ remote ruleset โ†’ remote directory         โ”‚
โ”‚  Local messages  โ†’ default rules  โ†’ /var/log/messages        โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Path sanitization

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  WITHOUT SecurePath                                          โ”‚
โ”‚                                                              โ”‚
โ”‚  Client sends hostname: "../../etc/passwd"                   โ”‚
โ”‚  Template produces: /var/log/remote/../../etc/passwd.log    โ”‚
โ”‚  Result: writes outside /var/log/remote/                    โ”‚
โ”‚                                                              โ”‚
โ”‚  WITH SecurePath="replace"                                   โ”‚
โ”‚                                                              โ”‚
โ”‚  Client sends hostname: "../../etc/passwd"                   โ”‚
โ”‚  Sanitized hostname: "______etc_passwd"                      โ”‚
โ”‚  Template produces: /var/log/remote/______etc_passwd.log    โ”‚
โ”‚  Result: stays inside /var/log/remote/                      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

UDP vs TCP listener

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  BOTH LISTENERS ACTIVE                                       โ”‚
โ”‚                                                              โ”‚
โ”‚  UDP 514:                                                    โ”‚
โ”‚    module(load="imudp")                                      โ”‚
โ”‚    input(type="imudp" port="514" ruleset="remote")          โ”‚
โ”‚    โ†’ No delivery guarantee                                   โ”‚
โ”‚                                                              โ”‚
โ”‚  TCP 514:                                                    โ”‚
โ”‚    module(load="imtcp")                                      โ”‚
โ”‚    input(type="imtcp" port="514" ruleset="remote")          โ”‚
โ”‚    โ†’ Connection-oriented, recommended for production        โ”‚
โ”‚                                                              โ”‚
โ”‚  Both feed the same ruleset.                                 โ”‚
โ”‚  Clients choose their transport.                             โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Configuration validation flow

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  VALIDATE BEFORE RESTART                                     โ”‚
โ”‚                                                              โ”‚
โ”‚  Edit /etc/rsyslog.d/10-receiver.conf                        โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  rsyslogd -N1                                                โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ”œโ”€โ”€ Success โ”€โ”€โ–บ systemctl restart rsyslog              โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ””โ”€โ”€ Failure โ”€โ”€โ–บ fix error, re-validate                 โ”‚
โ”‚                                                              โ”‚
โ”‚  A syntax error in the config prevents rsyslog from starting.โ”‚
โ”‚  Validation catches it before the restart.                   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Summary

ItemValue
Configuration location/etc/rsyslog.d/*.conf
UDP input moduleimudp
TCP input moduleimtcp
Default port514
Dedicated rulesetRequired to separate remote from local
Path sanitizationSecurePath="replace"
Dynamic file actionaction(type="omfile" dynaFile="...")
Directory creationcreateDirs="on"
Validation commandrsyslogd -N1
Listener verificationss -ulpn | grep 514

Key takeaways:

  • A dedicated ruleset is mandatory for a remote log server. Without it, incoming messages are processed by the default rules and mixed into local log files. The stop directive at the end of the ruleset prevents the message from continuing to other rules .
  • Templates organize logs by host and program. A template that uses hostname and programname constructs a path like /var/log/remote/<hostname>/<programname>.log, which keeps each client’s logs separate .
  • SecurePath="replace" is a security requirement. The hostname and programname values come from the client. Without sanitization, a malicious client can write outside the intended directory using path traversal characters .
  • TCP is the recommended transport for production. UDP has no delivery guarantee. The server can listen on both, but TCP should be the default for any log stream where message loss matters .
  • Validate with rsyslogd -N1 before restarting. A syntax error in the configuration prevents the daemon from starting. Validation catches the error at the command line instead of during a service failure .
  • The firewall should restrict access to known client networks. A syslog server listening on a public port accepts messages from anyone. Restrict the source to the networks that are supposed to send logs.

Remember: The rsyslog server configuration is a routing problem. The input modules open the ports. The ruleset separates remote messages from local ones. The template organizes the remote messages into per-host, per-program files. The SecurePath option prevents the client-supplied hostname from becoming a path traversal. Validate the configuration, restart the service, verify the listener, and confirm that a test message from a client arrives in the expected file. If remote logs are mixed with local logs, the ruleset is missing or not assigned to the input. If a client’s messages do not arrive, check the firewall and the listener before touching the template.

Was this helpful?


Stop using slow, ad-bloated tool sites! ๐Ÿคฎ

๐Ÿ”Ž Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
โœ… Finance (Mortgage, Interest, Inflation)
โœ… Tech (Base64, JSON, Dev Suite, IP)
โœ… Health (BMI, BMR, TDEE)
โœ… Productivity (Timer, Workspace, QR)

โšก๏ธ Fast & Private
๐Ÿ”’ No data leaves your device
๐Ÿ’Ž 100% Free

๐Ÿ”— Use it now: https://tools.kandz.me
๐Ÿ”– Bookmark itโ€”youโ€™ll need it later!