RSyslog Secure Configuration: Advanced Enterprise Guide
This comprehensive guide provides detailed instructions for implementing secure, high-performance RSyslog configurations suitable for enterprise environments. Learn advanced logging architectures, security hardening techniques, and best practices for centralized log management with compliance requirements.
Table of Contents
Understanding RSyslog Security Architecture
RSyslog Security Fundamentals
RSyslog provides enterprise-grade logging capabilities with robust security features:
- TLS Encryption: Secure log transmission over networks
- Authentication: Certificate-based client authentication
- Access Control: Fine-grained permissions and filtering
- Data Integrity: Message authentication and tampering detection
- Performance: High-throughput logging with minimal overhead
Common Security Challenges
Log Security Threats:
- Log Tampering: Malicious modification of log entries
- Data Exfiltration: Unauthorized access to sensitive log data
- Denial of Service: Log flooding attacks
- Man-in-the-Middle: Interception of log transmissions
- Privilege Escalation: Exploitation of logging service vulnerabilities
Compliance Requirements:
- PCI DSS: Payment card industry data security standards
- HIPAA: Healthcare information privacy regulations
- SOX: Sarbanes-Oxley financial reporting requirements
- GDPR: General Data Protection Regulation compliance
- NIST: National Institute of Standards and Technology guidelines
Core RSyslog Security Configuration
Central Log Server Configuration
# /etc/rsyslog.conf - Secure central log server configuration
# Global directives for security$PreserveFQDN on$RepeatedMsgReduction on$WorkDirectory /var/spool/rsyslog$IncludeConfig /etc/rsyslog.d/*.conf
# Memory and performance optimization$MainMsgQueueSize 50000$MainMsgQueueHighWaterMark 40000$MainMsgQueueLowWaterMark 20000$MainMsgQueueTimeoutEnqueue 0$MainMsgQueueType LinkedList$MainMsgQueueSaveOnShutdown on$MainMsgQueueWorkerThreads 4$MainMsgQueueWorkerThreadMinimumMessages 2000
# TLS/SSL configuration for secure communication$DefaultNetstreamDriver gtls$DefaultNetstreamDriverCAFile /etc/ssl/rsyslog/ca.pem$DefaultNetstreamDriverCertFile /etc/ssl/rsyslog/server-cert.pem$DefaultNetstreamDriverKeyFile /etc/ssl/rsyslog/server-key.pem
# Authentication and encryption settings$InputTCPServerStreamDriverMode 1$InputTCPServerStreamDriverAuthMode x509/name$InputTCPServerStreamDriverPermittedPeer *.example.com$InputTCPServerRun 6514
# UDP input for local systems (if needed)$ModLoad imudp$UDPServerRun 514$UDPServerAddress 127.0.0.1
# TCP input for secure remote logging$ModLoad imtcp$InputTCPServerRun 514
# File ownership and permissions$FileOwner root$FileGroup adm$FileCreateMode 0640$DirCreateMode 0755$Umask 0022
# Message processing rules with security filtering# Block potentially malicious patterns:msg, contains, "../" stop:msg, contains, "DROP TABLE" stop:msg, regex, ".*[sS][qQ][lL].*[iI][nN][jJ][eE][cC][tT].*" stop
# Rate limiting to prevent DoS attacks$SystemLogRateLimitInterval 10$SystemLogRateLimitBurst 500
# Secure file templates with rotation$template SecureLogFormat,"%timegenerated:::date-rfc3339% %HOSTNAME% %syslogtag:1:32%%msg:::sp-if-no-1st-sp%%msg%\n"$template DailyPerHostLogs,"/var/log/remote/%HOSTNAME%/%$year%-%$month%-%$day%.log"
# Security event loggingauth,authpriv.* /var/log/auth.log;SecureLogFormat*.*;auth,authpriv.none -/var/log/syslog;SecureLogFormatdaemon.* -/var/log/daemon.log;SecureLogFormatkern.* -/var/log/kern.log;SecureLogFormatlpr.* -/var/log/lpr.log;SecureLogFormatmail.* -/var/log/mail.log;SecureLogFormatuser.* -/var/log/user.log;SecureLogFormat
# Remote host logging with security*.* ?DailyPerHostLogs;SecureLogFormat
# Forward critical security events to SIEMauth.crit,authpriv.crit @@siem.example.com:6514
Advanced TLS Configuration
#!/bin/bash# setup-rsyslog-tls.sh - Configure TLS certificates for RSyslog
set -euo pipefail
# Configuration variablesCERT_DIR="/etc/ssl/rsyslog"CA_DIR="/etc/ssl/rsyslog/ca"SERVER_NAME="logserver.example.com"CLIENT_NAME="logclient.example.com"ORGANIZATION="Example Organization"COUNTRY="US"STATE="California"CITY="San Francisco"
# Create certificate directory structuresetup_cert_directories() { echo "Setting up certificate directories..."
mkdir -p "$CERT_DIR" mkdir -p "$CA_DIR" chmod 700 "$CERT_DIR" chmod 700 "$CA_DIR"
echo "Certificate directories created"}
# Generate Certificate Authoritygenerate_ca() { echo "Generating Certificate Authority..."
# Generate CA private key openssl genrsa -out "$CA_DIR/ca-key.pem" 4096
# Generate CA certificate openssl req -new -x509 -days 3650 -key "$CA_DIR/ca-key.pem" \ -out "$CERT_DIR/ca.pem" \ -subj "/C=$COUNTRY/ST=$STATE/L=$CITY/O=$ORGANIZATION/CN=RSyslog-CA"
chmod 400 "$CA_DIR/ca-key.pem" chmod 444 "$CERT_DIR/ca.pem"
echo "Certificate Authority generated"}
# Generate server certificategenerate_server_cert() { echo "Generating server certificate..."
# Generate server private key openssl genrsa -out "$CERT_DIR/server-key.pem" 2048
# Generate certificate signing request openssl req -new -key "$CERT_DIR/server-key.pem" \ -out "$CERT_DIR/server.csr" \ -subj "/C=$COUNTRY/ST=$STATE/L=$CITY/O=$ORGANIZATION/CN=$SERVER_NAME"
# Create extensions file for server certificate cat > "$CERT_DIR/server-ext.conf" << EOFbasicConstraints = CA:FALSEkeyUsage = nonRepudiation, digitalSignature, keyEnciphermentsubjectAltName = @alt_names
[alt_names]DNS.1 = $SERVER_NAMEDNS.2 = localhostIP.1 = 127.0.0.1EOF
# Generate server certificate openssl x509 -req -in "$CERT_DIR/server.csr" \ -CA "$CERT_DIR/ca.pem" \ -CAkey "$CA_DIR/ca-key.pem" \ -CAcreateserial \ -out "$CERT_DIR/server-cert.pem" \ -days 365 \ -extensions v3_req \ -extfile "$CERT_DIR/server-ext.conf"
# Set permissions chmod 400 "$CERT_DIR/server-key.pem" chmod 444 "$CERT_DIR/server-cert.pem"
# Cleanup rm "$CERT_DIR/server.csr" "$CERT_DIR/server-ext.conf"
echo "Server certificate generated"}
# Generate client certificategenerate_client_cert() { echo "Generating client certificate..."
# Generate client private key openssl genrsa -out "$CERT_DIR/client-key.pem" 2048
# Generate certificate signing request openssl req -new -key "$CERT_DIR/client-key.pem" \ -out "$CERT_DIR/client.csr" \ -subj "/C=$COUNTRY/ST=$STATE/L=$CITY/O=$ORGANIZATION/CN=$CLIENT_NAME"
# Generate client certificate openssl x509 -req -in "$CERT_DIR/client.csr" \ -CA "$CERT_DIR/ca.pem" \ -CAkey "$CA_DIR/ca-key.pem" \ -CAcreateserial \ -out "$CERT_DIR/client-cert.pem" \ -days 365
# Set permissions chmod 400 "$CERT_DIR/client-key.pem" chmod 444 "$CERT_DIR/client-cert.pem"
# Cleanup rm "$CERT_DIR/client.csr"
echo "Client certificate generated"}
# Verify certificatesverify_certificates() { echo "Verifying certificates..."
# Verify server certificate if openssl verify -CAfile "$CERT_DIR/ca.pem" "$CERT_DIR/server-cert.pem"; then echo "Server certificate verification: PASSED" else echo "Server certificate verification: FAILED" >&2 exit 1 fi
# Verify client certificate if openssl verify -CAfile "$CERT_DIR/ca.pem" "$CERT_DIR/client-cert.pem"; then echo "Client certificate verification: PASSED" else echo "Client certificate verification: FAILED" >&2 exit 1 fi
echo "Certificate verification completed"}
# Set proper ownershipset_ownership() { echo "Setting certificate ownership..."
chown -R root:root "$CERT_DIR" chown -R root:root "$CA_DIR"
echo "Certificate ownership configured"}
# Main executionecho "=== RSyslog TLS Certificate Setup ==="setup_cert_directoriesgenerate_cagenerate_server_certgenerate_client_certverify_certificatesset_ownership
echo "TLS certificate setup completed successfully!"echo "CA Certificate: $CERT_DIR/ca.pem"echo "Server Certificate: $CERT_DIR/server-cert.pem"echo "Server Key: $CERT_DIR/server-key.pem"echo "Client Certificate: $CERT_DIR/client-cert.pem"echo "Client Key: $CERT_DIR/client-key.pem"
Client Configuration for Secure Logging
# /etc/rsyslog.d/50-secure-client.conf - Secure client configuration
# Load TLS module$ModLoad imfile
# Global TLS settings$DefaultNetstreamDriver gtls$DefaultNetstreamDriverCAFile /etc/ssl/rsyslog/ca.pem$DefaultNetstreamDriverCertFile /etc/ssl/rsyslog/client-cert.pem$DefaultNetstreamDriverKeyFile /etc/ssl/rsyslog/client-key.pem
# Action queue configuration for reliability$ActionQueueType LinkedList$ActionQueueFileName secure_forward$ActionResumeRetryCount -1$ActionQueueSaveOnShutdown on$ActionQueueTimeoutEnqueue 0$ActionQueueMaxDiskSpace 500m
# Forward all logs to central server with TLS$ActionSendStreamDriver gtls$ActionSendStreamDriverMode 1$ActionSendStreamDriverAuthMode x509/name$ActionSendStreamDriverPermittedPeer logserver.example.com
# Send all logs to secure central server*.* @@logserver.example.com:6514
# Local backup in case of network issues*.* /var/log/local-backup.log;SecureLogFormat
# Rate limiting for local client$SystemLogRateLimitInterval 5$SystemLogRateLimitBurst 200
# Security event filtering before forwarding# Block sensitive data patterns:msg, contains, "password" stop:msg, contains, "secret" stop:msg, regex, ".*[0-9]{4}-[0-9]{4}-[0-9]{4}-[0-9]{4}.*" stop # Credit card patterns
# High-priority security events with immediate forwardingauth.alert,authpriv.alert @@logserver.example.com:6514auth.crit,authpriv.crit @@logserver.example.com:6514auth.emerg,authpriv.emerg @@logserver.example.com:6514
Advanced Security Features
Log Integrity and Signing
#!/bin/bash# rsyslog-signing-setup.sh - Configure log signing for integrity
# Install signing module dependenciesinstall_signing_dependencies() { echo "Installing log signing dependencies..."
# For Debian/Ubuntu if command -v apt-get >/dev/null 2>&1; then apt-get update apt-get install -y rsyslog-gnutls libgt-dev libgnutls28-dev fi
# For RHEL/CentOS if command -v yum >/dev/null 2>&1; then yum install -y rsyslog-gnutls guardtime-devel gnutls-devel fi
echo "Dependencies installed"}
# Configure log signingconfigure_log_signing() { echo "Configuring log signing..."
# Create signing configuration cat > /etc/rsyslog.d/10-signing.conf << 'EOF'# Log signature configuration
# Load signature provider module$ModLoad lmsig_gt
# Configure signature provider$ActionLmSigGTSignatureProvider gt://guard.com
# File signature settings$LmSigGTSigner "rsyslog-signer"$LmSigGTHashChainMaxLength 1000$LmSigGTTimestampTimeout 10
# Template for signed logs$template SignedLogFormat,"%timegenerated:::date-rfc3339% %HOSTNAME% %syslogtag%%msg%\n"
# Sign critical security logsauth.* /var/log/auth-signed.log;SignedLogFormat;LmSigGTauthpriv.* /var/log/authpriv-signed.log;SignedLogFormat;LmSigGTsecurity.* /var/log/security-signed.log;SignedLogFormat;LmSigGT
# Configure signature files$LmSigGTSigFile /var/log/auth-signed.log.gtsig$LmSigGTStateFile /var/log/auth-signed.log.gtstateEOF
echo "Log signing configured"}
# Create signature verification scriptcreate_verification_script() { cat > /usr/local/bin/verify-log-signatures.sh << 'EOF'#!/bin/bash# Log signature verification script
LOG_DIR="/var/log"VERIFICATION_LOG="/var/log/signature-verification.log"
verify_signatures() { local log_file=$1 local sig_file="${log_file}.gtsig" local state_file="${log_file}.gtstate"
if [[ -f "$sig_file" && -f "$state_file" ]]; then echo "Verifying signatures for: $log_file" | tee -a "$VERIFICATION_LOG"
if gtsig verify --sig-file "$sig_file" --log-file "$log_file"; then echo " VERIFICATION PASSED: $log_file" | tee -a "$VERIFICATION_LOG" return 0 else echo " VERIFICATION FAILED: $log_file" | tee -a "$VERIFICATION_LOG" logger -p auth.alert "Log signature verification failed for $log_file" return 1 fi else echo " SIGNATURE FILES MISSING: $log_file" | tee -a "$VERIFICATION_LOG" return 1 fi}
# Main verification loopecho "Starting log signature verification: $(date)" | tee -a "$VERIFICATION_LOG"
verification_failed=0
for log_file in "$LOG_DIR"/auth-signed.log "$LOG_DIR"/authpriv-signed.log "$LOG_DIR"/security-signed.log; do if [[ -f "$log_file" ]]; then if ! verify_signatures "$log_file"; then verification_failed=1 fi fidone
if [[ $verification_failed -eq 1 ]]; then echo "One or more signature verifications failed!" | tee -a "$VERIFICATION_LOG" exit 1else echo "All signature verifications passed" | tee -a "$VERIFICATION_LOG" exit 0fiEOF
chmod +x /usr/local/bin/verify-log-signatures.sh
# Create systemd timer for regular verification cat > /etc/systemd/system/log-signature-verification.service << EOF[Unit]Description=Log Signature VerificationAfter=rsyslog.service
[Service]Type=oneshotExecStart=/usr/local/bin/verify-log-signatures.shUser=rootStandardOutput=journalStandardError=journalEOF
cat > /etc/systemd/system/log-signature-verification.timer << EOF[Unit]Description=Run log signature verification hourlyRequires=log-signature-verification.service
[Timer]OnCalendar=hourlyPersistent=true
[Install]WantedBy=timers.targetEOF
systemctl enable log-signature-verification.timer systemctl start log-signature-verification.timer
echo "Signature verification scheduled"}
# Run setupinstall_signing_dependenciesconfigure_log_signingcreate_verification_script
echo "Log signing setup completed!"
Access Control and Filtering
# /etc/rsyslog.d/30-access-control.conf - Advanced access control
# Property-based filters for enhanced security$ModLoad mmfields
# Define custom properties for filteringset $!source_ip = $fromhost-ip;set $!facility_name = $syslogfacility-text;set $!severity_name = $syslogseverity-text;
# Whitelist trusted source IPsif ($!source_ip == "192.168.1.100") then { set $!trusted_source = "true";} else if ($!source_ip == "192.168.1.101") then { set $!trusted_source = "true";} else if ($!source_ip == "10.0.0.0/8") then { set $!trusted_source = "internal";} else { set $!trusted_source = "false";}
# Block untrusted sources for sensitive facilitiesif ($!trusted_source == "false" and ($!facility_name == "auth" or $!facility_name == "authpriv")) then { call LogSuspiciousActivity stop}
# Rate limiting by source IP$ModLoad imptcp$InputPTCPServerRun 514$InputPTCPServerBindRuleset secure_input
# Define secure input ruleset$RuleSet secure_input
# Custom action for suspicious activity logging$template SuspiciousActivityTemplate,"/var/log/security/suspicious-%$year%-%$month%-%$day%.log"
if ($!trusted_source == "false") then { action(type="omfile" dynaFile="SuspiciousActivityTemplate" template="SecureLogFormat" fileOwner="root" fileGroup="root" fileCreateMode="0600" dirCreateMode="0700")}
# Content-based filtering for sensitive data# Remove credit card numbers:msg, regex, "[0-9]{4}[[:space:]-]?[0-9]{4}[[:space:]-]?[0-9]{4}[[:space:]-]?[0-9]{4}" { set $!original_msg = $msg; set $.redacted_msg = re_replace($msg, "[0-9]{4}[[:space:]-]?[0-9]{4}[[:space:]-]?[0-9]{4}[[:space:]-]?[0-9]{4}", "****-****-****-****"); set $msg = $.redacted_msg;}
# Remove Social Security Numbers:msg, regex, "[0-9]{3}-[0-9]{2}-[0-9]{4}" { set $.redacted_msg = re_replace($msg, "[0-9]{3}-[0-9]{2}-[0-9]{4}", "***-**-****"); set $msg = $.redacted_msg;}
# Remove email addresses from certain logsif ($programname == "web-app" or $programname == "user-service") then { set $.redacted_msg = re_replace($msg, "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}", "[EMAIL-REDACTED]"); set $msg = $.redacted_msg;}
# Priority-based routingif ($!severity_name == "emerg" or $!severity_name == "alert" or $!severity_name == "crit") then { # Immediate notification for critical events action(type="ommail" server="smtp.example.com" port="587" mailfrom="rsyslog@example.com" mailto="security-team@example.com" subject="CRITICAL: Security Alert from RSyslog" template="EmailAlertTemplate")
# Forward to SIEM immediately action(type="omfwd" protocol="tcp" target="siem.example.com" port="6514" StreamDriver="gtls" StreamDriverMode="1" StreamDriverAuthMode="x509/name" StreamDriverPermittedPeers="siem.example.com")}
# Reset to default ruleset$RuleSet RSYSLOG_DefaultRuleset
High-Availability and Load Balancing
Multi-Server Configuration
#!/bin/bash# ha-rsyslog-setup.sh - High-availability RSyslog configuration
# Configuration variablesPRIMARY_SERVER="log1.example.com"SECONDARY_SERVER="log2.example.com"VIP="192.168.1.200"KEEPALIVED_PASSWORD="SecurePassword123!"
# Setup load balancer configurationsetup_load_balancer() { echo "Configuring HAProxy for log load balancing..."
# Install HAProxy if command -v apt-get >/dev/null 2>&1; then apt-get update && apt-get install -y haproxy keepalived elif command -v yum >/dev/null 2>&1; then yum install -y haproxy keepalived fi
# Configure HAProxy for RSyslog cat > /etc/haproxy/haproxy.cfg << EOFglobal daemon user haproxy group haproxy log 127.0.0.1:514 local0 chroot /var/lib/haproxy stats socket /var/lib/haproxy/stats tune.ssl.default-dh-param 2048
defaults mode tcp log global option tcplog option dontlognull retries 3 timeout connect 5000ms timeout client 50000ms timeout server 50000ms
# RSyslog load balancingfrontend rsyslog_frontend bind *:514 bind *:6514 ssl crt /etc/ssl/rsyslog/server-combined.pem ca-file /etc/ssl/rsyslog/ca.pem verify required default_backend rsyslog_servers
backend rsyslog_servers balance roundrobin option tcp-check tcp-check connect port 514 server log1 $PRIMARY_SERVER:514 check server log2 $SECONDARY_SERVER:514 check backup
# Statistics interfacefrontend stats bind *:8404 stats enable stats uri /stats stats refresh 30s stats admin if TRUEEOF
# Configure Keepalived for VIP management cat > /etc/keepalived/keepalived.conf << EOFvrrp_script chk_haproxy { script "/bin/kill -0 \`cat /var/run/haproxy.pid\`" interval 2 weight 2 fall 3 rise 2}
vrrp_instance VI_1 { state MASTER interface eth0 virtual_router_id 51 priority 110 advert_int 1 authentication { auth_type PASS auth_pass $KEEPALIVED_PASSWORD } virtual_ipaddress { $VIP } track_script { chk_haproxy }}EOF
systemctl enable haproxy keepalived systemctl start haproxy keepalived
echo "Load balancer configured"}
# Configure primary log serverconfigure_primary_server() { echo "Configuring primary log server..."
cat > /etc/rsyslog.d/10-ha-primary.conf << 'EOF'# Primary server configuration with replication
# Load database module for log storage$ModLoad ommysql
# Database connection for log storage$template MySQLInsertTemplate,"INSERT INTO rsyslog (datetime, hostname, facility, priority, tag, message) VALUES ('%timegenerated:::date-mysql%', '%hostname%', '%syslogfacility%', '%syslogpriority%', '%syslogtag%', '%msg%')"
# Store all logs in MySQL database*.* :ommysql:localhost,rsyslog_db,rsyslog_user,rsyslog_password;MySQLInsertTemplate
# Replicate to secondary server$ModLoad omfwd*.* @@log2.example.com:6514
# Local file backup*.* /var/log/primary-backup.log;SecureLogFormat
# Health check endpoint$ModLoad imhttp$HTTPServerDocumentRoot /var/www/rsyslog-health$HTTPServerPort 8080
# Create health check document$template HealthCheckTemplate,"/var/www/rsyslog-health/health.json"$template HealthCheckContent,'{"status":"healthy","timestamp":"%timegenerated:::date-rfc3339%","server":"primary"}'
local0.info ?HealthCheckTemplate;HealthCheckContentEOF
echo "Primary server configured"}
# Configure secondary log serverconfigure_secondary_server() { echo "Configuring secondary log server..."
cat > /etc/rsyslog.d/10-ha-secondary.conf << 'EOF'# Secondary server configuration (backup)
# Database replication for secondary server$ModLoad ommysql$template MySQLInsertTemplate,"INSERT INTO rsyslog (datetime, hostname, facility, priority, tag, message) VALUES ('%timegenerated:::date-mysql%', '%hostname%', '%syslogfacility%', '%syslogpriority%', '%syslogtag%', '%msg%')"
# Store all logs in MySQL database (replica)*.* :ommysql:localhost,rsyslog_db,rsyslog_user,rsyslog_password;MySQLInsertTemplate
# Local file backup*.* /var/log/secondary-backup.log;SecureLogFormat
# Health check endpoint$ModLoad imhttp$HTTPServerDocumentRoot /var/www/rsyslog-health$HTTPServerPort 8080
$template HealthCheckTemplate,"/var/www/rsyslog-health/health.json"$template HealthCheckContent,'{"status":"healthy","timestamp":"%timegenerated:::date-rfc3339%","server":"secondary"}'
local0.info ?HealthCheckTemplate;HealthCheckContentEOF
echo "Secondary server configured"}
# Setup database replicationsetup_database_replication() { echo "Setting up MySQL database replication..."
# Create database schema mysql -u root -p << 'EOF'CREATE DATABASE IF NOT EXISTS rsyslog_db;USE rsyslog_db;
CREATE TABLE IF NOT EXISTS rsyslog ( id INT AUTO_INCREMENT PRIMARY KEY, datetime DATETIME, hostname VARCHAR(255), facility INT, priority INT, tag VARCHAR(255), message TEXT, INDEX idx_datetime (datetime), INDEX idx_hostname (hostname), INDEX idx_facility (facility), INDEX idx_priority (priority));
CREATE USER IF NOT EXISTS 'rsyslog_user'@'localhost' IDENTIFIED BY 'rsyslog_password';GRANT INSERT, SELECT ON rsyslog_db.* TO 'rsyslog_user'@'localhost';FLUSH PRIVILEGES;EOF
echo "Database setup completed"}
# Create monitoring scriptcreate_monitoring_script() { cat > /usr/local/bin/rsyslog-ha-monitor.sh << 'EOF'#!/bin/bash# RSyslog HA monitoring script
LOGFILE="/var/log/rsyslog-ha-monitor.log"PRIMARY_SERVER="log1.example.com"SECONDARY_SERVER="log2.example.com"VIP="192.168.1.200"
check_server_health() { local server=$1 local port=$2
if timeout 5 bash -c "</dev/tcp/$server/$port"; then return 0 else return 1 fi}
check_database_health() { local server=$1
if mysql -h "$server" -u rsyslog_user -prsyslog_password -e "SELECT 1;" >/dev/null 2>&1; then return 0 else return 1 fi}
monitor_ha_status() { local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
# Check primary server if check_server_health "$PRIMARY_SERVER" 514; then primary_status="UP" else primary_status="DOWN" echo "[$timestamp] ALERT: Primary server $PRIMARY_SERVER is DOWN" | tee -a "$LOGFILE" logger -p local0.alert "RSyslog HA: Primary server $PRIMARY_SERVER is DOWN" fi
# Check secondary server if check_server_health "$SECONDARY_SERVER" 514; then secondary_status="UP" else secondary_status="DOWN" echo "[$timestamp] ALERT: Secondary server $SECONDARY_SERVER is DOWN" | tee -a "$LOGFILE" logger -p local0.alert "RSyslog HA: Secondary server $SECONDARY_SERVER is DOWN" fi
# Check VIP status if check_server_health "$VIP" 514; then vip_status="UP" else vip_status="DOWN" echo "[$timestamp] ALERT: VIP $VIP is DOWN" | tee -a "$LOGFILE" logger -p local0.alert "RSyslog HA: VIP $VIP is DOWN" fi
echo "[$timestamp] Status - Primary: $primary_status, Secondary: $secondary_status, VIP: $vip_status" | tee -a "$LOGFILE"}
# Main monitoring loopwhile true; do monitor_ha_status sleep 60doneEOF
chmod +x /usr/local/bin/rsyslog-ha-monitor.sh
# Create systemd service for monitoring cat > /etc/systemd/system/rsyslog-ha-monitor.service << EOF[Unit]Description=RSyslog HA MonitorAfter=network.target
[Service]Type=simpleExecStart=/usr/local/bin/rsyslog-ha-monitor.shRestart=alwaysRestartSec=10User=root
[Install]WantedBy=multi-user.targetEOF
systemctl enable rsyslog-ha-monitor.service systemctl start rsyslog-ha-monitor.service
echo "HA monitoring configured"}
# Main executionecho "=== RSyslog High-Availability Setup ==="
read -p "Configure this server as [primary/secondary/loadbalancer]: " server_role
case $server_role in "primary") configure_primary_server setup_database_replication ;; "secondary") configure_secondary_server ;; "loadbalancer") setup_load_balancer ;; *) echo "Invalid role. Choose primary, secondary, or loadbalancer" exit 1 ;;esac
create_monitoring_script
echo "RSyslog HA setup completed for role: $server_role"
Performance Optimization and Monitoring
Performance Tuning Configuration
# /etc/rsyslog.d/20-performance.conf - Performance optimization
# Global performance settings$MaxMessageSize 8192$PreserveFQDN on$RepeatedMsgReduction on
# Main message queue optimization$MainMsgQueueSize 100000$MainMsgQueueHighWaterMark 80000$MainMsgQueueLowWaterMark 20000$MainMsgQueueTimeoutEnqueue 0$MainMsgQueueType LinkedList$MainMsgQueueSaveOnShutdown on$MainMsgQueueWorkerThreads 8$MainMsgQueueWorkerThreadMinimumMessages 1000$MainMsgQueueMaxFileSize 100m$MainMsgQueueMaxDiskSpace 2g
# Action queue optimization for forwarding$ActionQueueType LinkedList$ActionQueueSize 50000$ActionQueueHighWaterMark 40000$ActionQueueLowWaterMark 10000$ActionQueueTimeoutEnqueue 0$ActionQueueWorkerThreads 4$ActionQueueWorkerThreadMinimumMessages 500$ActionQueueMaxFileSize 50m$ActionQueueMaxDiskSpace 1g$ActionResumeRetryCount -1$ActionQueueSaveOnShutdown on
# File I/O optimization$OMFileFlushInterval 10$OMFileIOBufferSize 64k$OMFileFlushOnTXEnd off
# Network optimization$UDPServerTimeRequery 60$InputTCPMaxSessions 500$InputTCPFlowControl on
# Template for high-performance logging$template HighPerfTemplate,"%timegenerated:1:19:date-rfc3339% %hostname% %syslogtag%%msg%\n"
# Asynchronous file writing for high-throughput logs$OMFileAsyncWriting on*.* -/var/log/high-performance.log;HighPerfTemplate
Comprehensive Monitoring Dashboard
#!/usr/bin/env python3# rsyslog-dashboard.py - Real-time RSyslog monitoring dashboard
import jsonimport timeimport subprocessimport sqlite3import osfrom datetime import datetime, timedeltafrom collections import defaultdictimport argparse
class RSyslogMonitor: def __init__(self, db_path="/var/log/rsyslog-monitor.db"): self.db_path = db_path self.init_database()
def init_database(self): """Initialize SQLite database for monitoring data""" conn = sqlite3.connect(self.db_path) cursor = conn.cursor()
cursor.execute(''' CREATE TABLE IF NOT EXISTS metrics ( id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, metric_name TEXT, metric_value REAL, hostname TEXT ) ''')
cursor.execute(''' CREATE TABLE IF NOT EXISTS alerts ( id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, alert_type TEXT, message TEXT, severity TEXT, acknowledged BOOLEAN DEFAULT FALSE ) ''')
conn.commit() conn.close()
def collect_rsyslog_stats(self): """Collect RSyslog statistics""" stats = {}
try: # Get rsyslog process information result = subprocess.run(['pgrep', '-f', 'rsyslogd'], capture_output=True, text=True) if result.returncode == 0: pid = result.stdout.strip().split('\n')[0]
# Memory usage with open(f'/proc/{pid}/status', 'r') as f: for line in f: if line.startswith('VmRSS:'): memory_kb = int(line.split()[1]) stats['memory_usage_mb'] = memory_kb / 1024 break
# CPU usage result = subprocess.run(['ps', '-p', pid, '-o', 'pcpu='], capture_output=True, text=True) if result.returncode == 0: stats['cpu_usage_percent'] = float(result.stdout.strip())
# File descriptor count fd_dir = f'/proc/{pid}/fd' if os.path.exists(fd_dir): stats['open_file_descriptors'] = len(os.listdir(fd_dir))
# Log file sizes log_files = [ '/var/log/syslog', '/var/log/auth.log', '/var/log/daemon.log', '/var/log/kern.log' ]
total_log_size = 0 for log_file in log_files: if os.path.exists(log_file): size = os.path.getsize(log_file) total_log_size += size stats[f'{os.path.basename(log_file)}_size_mb'] = size / (1024 * 1024)
stats['total_log_size_mb'] = total_log_size / (1024 * 1024)
# Network connections result = subprocess.run(['netstat', '-tn'], capture_output=True, text=True) if result.returncode == 0: connections = result.stdout.split('\n') rsyslog_connections = [line for line in connections if ':514' in line] stats['active_connections'] = len(rsyslog_connections)
# Disk space result = subprocess.run(['df', '/var/log'], capture_output=True, text=True) if result.returncode == 0: lines = result.stdout.strip().split('\n') if len(lines) > 1: fields = lines[1].split() used_percent = int(fields[4].rstrip('%')) stats['disk_usage_percent'] = used_percent
except Exception as e: print(f"Error collecting stats: {e}")
return stats
def store_metrics(self, metrics): """Store metrics in database""" conn = sqlite3.connect(self.db_path) cursor = conn.cursor()
hostname = subprocess.run(['hostname'], capture_output=True, text=True).stdout.strip()
for metric_name, value in metrics.items(): cursor.execute( 'INSERT INTO metrics (metric_name, metric_value, hostname) VALUES (?, ?, ?)', (metric_name, value, hostname) )
conn.commit() conn.close()
def check_alerts(self, metrics): """Check for alert conditions""" alerts = []
# Memory usage alert if metrics.get('memory_usage_mb', 0) > 1024: # 1GB alerts.append({ 'type': 'high_memory_usage', 'message': f"High memory usage: {metrics['memory_usage_mb']:.1f}MB", 'severity': 'warning' })
# CPU usage alert if metrics.get('cpu_usage_percent', 0) > 80: alerts.append({ 'type': 'high_cpu_usage', 'message': f"High CPU usage: {metrics['cpu_usage_percent']:.1f}%", 'severity': 'warning' })
# Disk space alert if metrics.get('disk_usage_percent', 0) > 90: alerts.append({ 'type': 'high_disk_usage', 'message': f"High disk usage: {metrics['disk_usage_percent']}%", 'severity': 'critical' })
# Log file size alert if metrics.get('total_log_size_mb', 0) > 10240: # 10GB alerts.append({ 'type': 'large_log_files', 'message': f"Large log files: {metrics['total_log_size_mb']:.1f}MB", 'severity': 'warning' })
# Store alerts if alerts: conn = sqlite3.connect(self.db_path) cursor = conn.cursor()
for alert in alerts: cursor.execute( 'INSERT INTO alerts (alert_type, message, severity) VALUES (?, ?, ?)', (alert['type'], alert['message'], alert['severity']) )
conn.commit() conn.close()
return alerts
def generate_report(self, hours=24): """Generate monitoring report""" conn = sqlite3.connect(self.db_path) cursor = conn.cursor()
# Get recent metrics since_time = datetime.now() - timedelta(hours=hours) cursor.execute(''' SELECT metric_name, AVG(metric_value) as avg_value, MAX(metric_value) as max_value, MIN(metric_value) as min_value FROM metrics WHERE timestamp > ? GROUP BY metric_name ''', (since_time,))
metrics_summary = cursor.fetchall()
# Get recent alerts cursor.execute(''' SELECT alert_type, COUNT(*) as count, severity FROM alerts WHERE timestamp > ? GROUP BY alert_type, severity ''', (since_time,))
alerts_summary = cursor.fetchall()
conn.close()
# Generate report report = { 'timestamp': datetime.now().isoformat(), 'period_hours': hours, 'metrics_summary': { row[0]: { 'average': row[1], 'maximum': row[2], 'minimum': row[3] } for row in metrics_summary }, 'alerts_summary': { f"{row[0]}_{row[2]}": row[1] for row in alerts_summary } }
return report
def run_monitoring(self, interval=60): """Run continuous monitoring""" print(f"Starting RSyslog monitoring (interval: {interval}s)")
while True: try: # Collect metrics metrics = self.collect_rsyslog_stats() print(f"[{datetime.now()}] Collected {len(metrics)} metrics")
# Store metrics self.store_metrics(metrics)
# Check for alerts alerts = self.check_alerts(metrics) if alerts: print(f"Generated {len(alerts)} alerts") for alert in alerts: print(f" {alert['severity'].upper()}: {alert['message']}")
time.sleep(interval)
except KeyboardInterrupt: print("\nMonitoring stopped") break except Exception as e: print(f"Error in monitoring loop: {e}") time.sleep(interval)
def main(): parser = argparse.ArgumentParser(description='RSyslog Monitoring Dashboard') parser.add_argument('--interval', type=int, default=60, help='Monitoring interval in seconds (default: 60)') parser.add_argument('--report', action='store_true', help='Generate and display report') parser.add_argument('--hours', type=int, default=24, help='Report period in hours (default: 24)')
args = parser.parse_args()
monitor = RSyslogMonitor()
if args.report: report = monitor.generate_report(args.hours) print(json.dumps(report, indent=2)) else: monitor.run_monitoring(args.interval)
if __name__ == '__main__': main()
Best Practices and Recommendations
Security Hardening Checklist
- Encryption: Always use TLS for log transmission
- Authentication: Implement certificate-based client authentication
- Access Control: Restrict log server access to authorized clients only
- Data Integrity: Use log signing for critical security logs
- Monitoring: Implement comprehensive monitoring and alerting
- Backup: Regular backups of log data and configurations
- Retention: Implement appropriate log retention policies
- Compliance: Ensure configuration meets regulatory requirements
Troubleshooting Common Issues
#!/bin/bash# rsyslog-troubleshooting.sh - Diagnostic and troubleshooting tools
# Function to check RSyslog configurationcheck_configuration() { echo "=== RSyslog Configuration Check ==="
# Test configuration syntax if rsyslogd -N1; then echo "✓ Configuration syntax is valid" else echo "✗ Configuration syntax errors found" return 1 fi
# Check for common configuration issues if grep -q "ModLoad.*imudp" /etc/rsyslog.conf /etc/rsyslog.d/*.conf 2>/dev/null; then echo "✓ UDP input module loaded" else echo "⚠ UDP input module not loaded" fi
if grep -q "ModLoad.*imtcp" /etc/rsyslog.conf /etc/rsyslog.d/*.conf 2>/dev/null; then echo "✓ TCP input module loaded" else echo "⚠ TCP input module not loaded" fi}
# Function to check network connectivitycheck_network() { echo "=== Network Connectivity Check ==="
# Check if RSyslog is listening on expected ports if netstat -tlnp | grep -q ":514.*rsyslogd"; then echo "✓ RSyslog listening on TCP port 514" else echo "⚠ RSyslog not listening on TCP port 514" fi
if netstat -ulnp | grep -q ":514.*rsyslogd"; then echo "✓ RSyslog listening on UDP port 514" else echo "⚠ RSyslog not listening on UDP port 514" fi
# Check TLS port if netstat -tlnp | grep -q ":6514.*rsyslogd"; then echo "✓ RSyslog listening on TLS port 6514" else echo "⚠ RSyslog not listening on TLS port 6514" fi}
# Function to check certificatescheck_certificates() { echo "=== Certificate Check ==="
local cert_dir="/etc/ssl/rsyslog"
if [[ -f "$cert_dir/ca.pem" ]]; then if openssl x509 -in "$cert_dir/ca.pem" -noout -checkend 86400; then echo "✓ CA certificate is valid and not expiring soon" else echo "⚠ CA certificate is invalid or expiring within 24 hours" fi else echo "⚠ CA certificate not found" fi
if [[ -f "$cert_dir/server-cert.pem" ]]; then if openssl x509 -in "$cert_dir/server-cert.pem" -noout -checkend 86400; then echo "✓ Server certificate is valid and not expiring soon" else echo "⚠ Server certificate is invalid or expiring within 24 hours" fi else echo "⚠ Server certificate not found" fi}
# Function to check log file permissionscheck_permissions() { echo "=== File Permissions Check ==="
local log_dirs=("/var/log" "/var/spool/rsyslog")
for dir in "${log_dirs[@]}"; do if [[ -d "$dir" ]]; then local perms=$(stat -c "%a" "$dir") if [[ "$perms" =~ ^[67][0-7][0-7]$ ]]; then echo "✓ $dir permissions are secure ($perms)" else echo "⚠ $dir permissions may be too restrictive or permissive ($perms)" fi else echo "⚠ Directory $dir does not exist" fi done}
# Function to check disk spacecheck_disk_space() { echo "=== Disk Space Check ==="
local usage=$(df /var/log | tail -1 | awk '{print $5}' | sed 's/%//')
if [[ $usage -lt 80 ]]; then echo "✓ Disk space usage is acceptable ($usage%)" elif [[ $usage -lt 90 ]]; then echo "⚠ Disk space usage is high ($usage%)" else echo "✗ Disk space usage is critical ($usage%)" fi}
# Function to analyze log patternsanalyze_logs() { echo "=== Log Analysis ==="
# Check for recent errors local errors=$(journalctl -u rsyslog --since "1 hour ago" --no-pager | grep -i error | wc -l) echo "Recent RSyslog errors: $errors"
# Check message rates local current_hour=$(date +%H) local messages=$(grep "$(date '+%b %d %H')" /var/log/syslog 2>/dev/null | wc -l) echo "Messages in current hour: $messages"
# Check for dropped messages if journalctl -u rsyslog --since "1 hour ago" --no-pager | grep -q "dropped"; then echo "⚠ Dropped messages detected in recent logs" else echo "✓ No dropped messages in recent logs" fi}
# Run all checksecho "RSyslog Troubleshooting Tool"echo "============================"
check_configurationcheck_networkcheck_certificatescheck_permissionscheck_disk_spaceanalyze_logs
echo ""echo "Troubleshooting completed. Review any warnings or errors above."
Conclusion
Implementing secure RSyslog configurations requires careful attention to encryption, authentication, access control, and monitoring. Key recommendations:
- Always Use TLS: Encrypt all log transmissions
- Certificate Management: Implement proper certificate lifecycle management
- Access Control: Restrict access to authorized clients only
- Monitoring: Implement comprehensive health and security monitoring
- High Availability: Design for resilience and redundancy
- Regular Testing: Continuously validate configuration and security posture
By following the configurations and best practices outlined in this guide, you can build a robust, secure logging infrastructure that meets enterprise security and compliance requirements while maintaining high performance and reliability.