In Part 1 of this trilogy, we covered how to generate meaningful security telemetry from the Linux kernel — auditd for syscall auditing, Falco for runtime threat detection, and Tetragon for eBPF-based observability and enforcement. We now have audit sources producing high-quality security events.

None of that matters if the logs stay on the host.

An attacker who gains root on a system can trivially tamper with local log files. They can truncate /var/log/audit/audit.log, stop the auditd daemon, or simply wipe the entire /var/log directory. If your security telemetry only exists locally, you lose visibility at the exact moment you need it most. This article covers how to build a centralized log collection pipeline that ensures your security data survives even when individual hosts don’t.

Why Centralized Collection Is Non-Negotiable

The argument for centralized logging isn’t about convenience — it’s about survivability. Consider the attack timeline:

PhaseWhat the Attacker DoesWhat Happens to Local Logs
Initial accessExploits a vulnerabilityLogs are still intact
Privilege escalationGains rootAttacker can now modify logs
Defense evasionClears/tampers with audit trailLocal evidence is destroyed
Lateral movementPivots to other hostsEach compromised host loses logs
ImpactAchieves objectiveForensics has nothing to work with

The window between initial access and log tampering can be minutes. If your logs are forwarded to a central collector in near real-time, the attacker’s actions are already recorded off-host before they can cover their tracks.

Centralized logging turns a host compromise from a blind spot into an observable event.

Beyond incident response, centralized collection enables:

  • Cross-host correlation — detecting lateral movement requires seeing events from multiple hosts together
  • Long-term retention — individual hosts rarely have the storage for months of audit data
  • Compliance — PCI-DSS, HIPAA, and SOC2 all require centralized, tamper-evident log storage
  • Detection engineering — SIEM rules need a unified data source to correlate against

The Pipeline Architecture

Before diving into individual tools, let’s establish the full architecture. A production log pipeline has five stages:

┌─────────────────────────────────────────────────────────────────────────┐
│                        LOG PIPELINE ARCHITECTURE                        │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  ┌──────────┐    ┌──────────┐    ┌───────────┐    ┌─────────────────┐  │
│  │  SOURCE   │───▶│  AGENT   │───▶│ TRANSPORT │───▶│ STORAGE + INDEX │  │
│  └──────────┘    └──────────┘    └───────────┘    └─────────────────┘  │
│                                                                         │
│  auditd          Wazuh agent     rsyslog           OpenSearch /         │
│  Falco           journald        Wazuh API         Elasticsearch       │
│  Tetragon        rsyslog         TCP/TLS           Wazuh indexer       │
│  kernel logs     Filebeat        HTTPS              Kibana / Dashboards│
│  syslog                                                                 │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

Detail flow for a single host:

  ┌────────┐     ┌──────────┐     ┌──────────┐     ┌──────────┐
  │ auditd │────▶│ journald │────▶│ rsyslog  │────▶│  Wazuh   │
  │ kernel │     │ (local   │     │ (forward │     │ Manager  │
  │ Falco  │     │  broker) │     │  + TLS)  │     │          │
  │Tetragon│     └──────────┘     └──────────┘     └────┬─────┘
  └────────┘           │                                 │
                       │          ┌──────────┐           │
                       └─────────▶│  Wazuh   │───────────┘
                                  │  Agent   │
                                  │ (local)  │
                                  └──────────┘
                                       │
                                       ▼
                                 ┌───────────┐
                                 │ OpenSearch │
                                 │ (index +  │
                                 │  search)  │
                                 └───────────┘

Each component has a specific job. Let’s walk through them.

Stage 1: journald — The Local Log Broker

systemd-journald is the first stop for most log data on modern Linux systems. It collects messages from the kernel ring buffer, systemd services, and syslog-compatible applications into a structured binary journal.

For our purposes, journald serves as the local aggregation point. It captures:

  • Kernel messages (kmsg)
  • auditd events (via the audit subsystem)
  • Service stdout/stderr (from systemd units)
  • Syslog messages forwarded to the journal

Configuring journald for Security Use

The default journald configuration is not suitable for security logging. Adjust /etc/systemd/journald.conf:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# /etc/systemd/journald.conf
[Journal]
# Persistent storage — survive reboots
Storage=persistent

# Don't compress — faster reads during incident response
Compress=no

# Generous local retention (secondary to central collection)
SystemMaxUse=2G
SystemMaxFileSize=256M
SystemKeepFree=1G

# Forward to syslog for rsyslog pickup
ForwardToSyslog=yes

# Rate limiting — disable for security events
# In production, tune these rather than fully disabling
RateLimitIntervalSec=0
RateLimitBurst=0

# Seal the journal for tamper evidence (requires FSS key)
Seal=yes
Disabling rate limiting (RateLimitIntervalSec=0) is necessary for security logging but can cause disk exhaustion under log flooding attacks. Monitor journal disk usage and set SystemMaxUse accordingly. In environments where DoS via log flooding is a concern, keep rate limiting enabled and whitelist critical units.

The Seal=yes option enables Forward Secure Sealing (FSS), which cryptographically seals the journal at regular intervals. This makes it detectable if an attacker modifies journal entries after the fact — though it doesn’t prevent deletion.

Verifying Journal Integrity

1
2
3
4
5
6
# Generate the FSS key pair (do this once)
journalctl --setup-keys

# Verify journal integrity
journalctl --verify
# PASS: /var/log/journal/...

Store the verification key offline. If the host is compromised, you can use it to verify whether journal entries were tampered with before the attacker gained access.

Stage 2: rsyslog — Log Forwarding and Transformation

rsyslog is the workhorse for log forwarding. While journald handles local aggregation, rsyslog handles reliable delivery to remote collectors with features like TLS encryption, message transformation, and queue-based buffering.

Basic Forwarding Configuration

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# /etc/rsyslog.d/50-remote-forward.conf

# Load required modules
module(load="imuxsock")    # local system logging
module(load="imjournal")   # journald input
module(load="omfwd")       # forwarding output

# Template for structured output (CEF-like)
template(name="SecurityForward" type="string"
    string="<%PRI%>1 %TIMESTAMP:::date-rfc3339% %HOSTNAME% %APP-NAME% %PROCID% %MSGID% %STRUCTURED-DATA% %msg%\n"
)

# Forward all auth and audit logs to central collector via TCP+TLS
if ($syslogfacility-text == 'auth' or
    $syslogfacility-text == 'authpriv' or
    $programname == 'audit' or
    $programname startswith 'falco') then {

    action(
        type="omfwd"
        target="siem.internal.yourdomain.com"
        port="1514"
        protocol="tcp"
        StreamDriver="gtls"
        StreamDriverMode="1"                    # TLS required
        StreamDriverAuthMode="x509/name"
        StreamDriverPermittedPeer="siem.internal.yourdomain.com"

        # Queue for reliability — buffer if remote is down
        queue.type="LinkedList"
        queue.filename="fwd_siem"
        queue.maxdiskspace="1g"
        queue.saveonshutdown="on"
        action.resumeRetryCount="-1"            # retry forever
        action.resumeInterval="30"              # retry every 30s

        template="SecurityForward"
    )
}

TLS Configuration for Secure Transport

Never forward security logs in plaintext. Configure rsyslog with mutual TLS:

1
2
3
4
5
6
7
8
9
# /etc/rsyslog.d/00-tls.conf

# Global TLS settings
global(
    DefaultNetstreamDriver="gtls"
    DefaultNetstreamDriverCAFile="/etc/rsyslog.d/tls/ca.pem"
    DefaultNetstreamDriverCertFile="/etc/rsyslog.d/tls/client-cert.pem"
    DefaultNetstreamDriverKeyFile="/etc/rsyslog.d/tls/client-key.pem"
)

Filtering and Enrichment

rsyslog can enrich logs before forwarding — adding context that aids correlation downstream:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# /etc/rsyslog.d/51-enrichment.conf

# Add environment tag to all forwarded messages
set $.environment = "production";
set $.datacenter = "dc1";

template(name="EnrichedJSON" type="list") {
    constant(value="{")
    constant(value="\"timestamp\":\"")    property(name="timereported" dateFormat="rfc3339")
    constant(value="\",\"host\":\"")      property(name="hostname")
    constant(value="\",\"program\":\"")   property(name="programname")
    constant(value="\",\"severity\":\"")  property(name="syslogseverity-text")
    constant(value="\",\"facility\":\"")  property(name="syslogfacility-text")
    constant(value="\",\"env\":\"")       constant(value="production")
    constant(value="\",\"message\":\"")   property(name="msg" format="jsonf")
    constant(value="\"}")
    constant(value="\n")
}

Stage 3: Wazuh — SIEM/XDR Agent for Collection and Normalization

While rsyslog handles raw log forwarding, Wazuh operates at a higher level — it’s a full SIEM/XDR platform that provides agent-based collection, log normalization, rule-based detection, and compliance monitoring. In 2025-2026, Wazuh has emerged as the dominant open-source SIEM/XDR option, eliminating vendor lock-in while providing capabilities that rival commercial alternatives.

Wazuh Architecture

  ┌────────────────────────────────────────┐
  │            MONITORED HOSTS             │
  │                                        │
  │  ┌──────────┐  ┌──────────┐  ┌──────┐ │
  │  │  Wazuh   │  │  Wazuh   │  │Wazuh │ │
  │  │  Agent   │  │  Agent   │  │Agent │ │
  │  │ (host-1) │  │ (host-2) │  │(k8s) │ │
  │  └────┬─────┘  └────┬─────┘  └──┬───┘ │
  └───────┼──────────────┼──────────┼──────┘
          │    encrypted │ (1514)   │
          ▼              ▼          ▼
  ┌──────────────────────────────────────┐
  │          WAZUH MANAGER               │
  │  ┌────────────┐  ┌───────────────┐   │
  │  │ Analysis   │  │ Rule Engine   │   │
  │  │ Engine     │  │ (Decoders +   │   │
  │  │            │  │  Detection)   │   │
  │  └─────┬──────┘  └───────┬───────┘   │
  └────────┼──────────────────┼──────────┘
           │                  │
           ▼                  ▼
  ┌──────────────────────────────────────┐
  │         WAZUH INDEXER                │
  │      (OpenSearch-based)              │
  │                                      │
  │  Index, search, dashboards, alerts   │
  └──────────────────────────────────────┘

Wazuh Agent Configuration

The Wazuh agent runs on every monitored host and reads from multiple local sources. Here’s a production configuration:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
<!-- /var/ossec/etc/ossec.conf (agent side) -->
<ossec_config>

  <!-- Agent identity -->
  <client>
    <server>
      <address>wazuh-manager.internal.yourdomain.com</address>
      <port>1514</port>
      <protocol>tcp</protocol>
    </server>
    <enrollment>
      <enabled>yes</enabled>
      <agent_name>prod-web-01</agent_name>
      <groups>linux,webservers,production</groups>
    </enrollment>
  </client>

  <!-- Read auditd logs — Part 1's primary source -->
  <localfile>
    <log_format>audit</log_format>
    <location>/var/log/audit/audit.log</location>
  </localfile>

  <!-- Read Falco alerts — Part 1's runtime detection -->
  <localfile>
    <log_format>json</log_format>
    <location>/var/log/falco/falco.json</location>
    <label key="source">falco</label>
  </localfile>

  <!-- Read Tetragon events — Part 1's eBPF observability -->
  <localfile>
    <log_format>json</log_format>
    <location>/var/log/tetragon/tetragon.log</location>
    <label key="source">tetragon</label>
  </localfile>

  <!-- System logs -->
  <localfile>
    <log_format>syslog</log_format>
    <location>/var/log/syslog</location>
  </localfile>

  <localfile>
    <log_format>syslog</log_format>
    <location>/var/log/auth.log</location>
  </localfile>

  <!-- File integrity monitoring -->
  <syscheck>
    <frequency>600</frequency>
    <directories check_all="yes" realtime="yes">/etc,/usr/bin,/usr/sbin</directories>
    <directories check_all="yes" realtime="yes">/boot</directories>
    <ignore>/etc/mtab</ignore>
    <ignore>/etc/resolv.conf</ignore>
  </syscheck>

  <!-- Rootkit detection -->
  <rootcheck>
    <frequency>43200</frequency>
  </rootcheck>

</ossec_config>

Connecting Part 1’s Sources to Wazuh

This is where the trilogy comes together. Each audit source from Part 1 has a specific integration path:

SourceLog LocationWazuh FormatWhat It Provides
auditd/var/log/audit/audit.logauditSyscall-level events, file access, privilege escalation
Falco/var/log/falco/falco.jsonjsonRuntime threat detection alerts (container escape, shell in container)
Tetragon/var/log/tetragon/tetragon.logjsoneBPF-based process, file, and network events with enforcement actions
journaldVia syslog forwardingsyslogKernel messages, service logs, authentication events
kernel/var/log/kern.logsyslogModule loads, OOM kills, hardware errors
Falco and Tetragon can also forward directly via their own output plugins (Falcosidekick, Tetragon export filters). For simpler setups, having Wazuh read the local JSON log files works well. For high-volume environments, consider direct gRPC or HTTP forwarding to avoid local disk I/O as a bottleneck.

Custom Wazuh Decoders for Falco Events

Wazuh needs decoders to parse non-standard log formats. Here’s a custom decoder for Falco JSON output:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<!-- /var/ossec/etc/decoders/falco_decoders.xml -->
<decoder name="falco">
  <prematch>^{"hostname":</prematch>
  <plugin_decoder>JSON_Decoder</plugin_decoder>
</decoder>

<decoder name="falco-fields">
  <parent>falco</parent>
  <regex offset="after_parent">\"priority\":\"(\S+)\",.*\"rule\":\"(\.+)\",.*\"output\":\"(\.+)\"</regex>
  <order>priority,rule,output</order>
</decoder>

And a matching detection rule:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<!-- /var/ossec/etc/rules/falco_rules.xml -->
<group name="falco,">

  <rule id="100200" level="3">
    <decoded_as>falco</decoded_as>
    <description>Falco alert received</description>
  </rule>

  <rule id="100201" level="10">
    <if_sid>100200</if_sid>
    <field name="priority">^Critical$</field>
    <description>Falco CRITICAL: $(rule)</description>
    <group>falco,critical,</group>
  </rule>

  <rule id="100202" level="12">
    <if_sid>100200</if_sid>
    <field name="rule">Terminal shell in container</field>
    <description>Interactive shell detected in container — potential compromise</description>
    <mitre>
      <id>T1059</id>
    </mitre>
    <group>falco,container_security,</group>
  </rule>

</group>

Stage 4: Storage and Search — OpenSearch / ELK

The final stage is where logs land for long-term storage, indexing, and search. Wazuh ships with its own indexer based on OpenSearch, but you can also use Elasticsearch or a standalone OpenSearch cluster.

Wazuh Indexer (OpenSearch) Deployment

For a production setup with the Wazuh all-in-one stack:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# docker-compose.yml — Wazuh stack
services:
  wazuh-manager:
    image: wazuh/wazuh-manager:4.12.0
    restart: unless-stopped
    ports:
      - "1514:1514"     # Agent communication
      - "1515:1515"     # Agent enrollment
      - "514:514/udp"   # Syslog collection
      - "55000:55000"   # Wazuh API
    volumes:
      - wazuh_api_configuration:/var/ossec/api/configuration
      - wazuh_etc:/var/ossec/etc
      - wazuh_logs:/var/ossec/logs
      - wazuh_queue:/var/ossec/queue
    environment:
      INDEXER_URL: "https://wazuh-indexer:9200"
      INDEXER_USERNAME: "admin"
      INDEXER_PASSWORD: "${INDEXER_PASS}"
      FILEBEAT_SSL_VERIFICATION_MODE: "full"

  wazuh-indexer:
    image: wazuh/wazuh-indexer:4.12.0
    restart: unless-stopped
    ports:
      - "9200:9200"
    volumes:
      - wazuh_indexer_data:/var/lib/wazuh-indexer
    environment:
      OPENSEARCH_JAVA_OPTS: "-Xms2g -Xmx2g"    # Size for your environment
      bootstrap.memory_lock: "true"
    ulimits:
      memlock:
        soft: -1
        hard: -1

  wazuh-dashboard:
    image: wazuh/wazuh-dashboard:4.12.0
    restart: unless-stopped
    ports:
      - "443:5601"
    environment:
      INDEXER_USERNAME: "admin"
      INDEXER_PASSWORD: "${INDEXER_PASS}"
      WAZUH_API_URL: "https://wazuh-manager"
      API_USERNAME: "wazuh-wui"
      API_PASSWORD: "${API_PASS}"

volumes:
  wazuh_api_configuration:
  wazuh_etc:
  wazuh_logs:
  wazuh_queue:
  wazuh_indexer_data:

Index Lifecycle Management

OpenSearch supports Index Lifecycle Management (ILM) policies that automate index rotation, retention, and deletion:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// PUT _plugins/_ism/policies/wazuh-security-policy
{
  "policy": {
    "description": "Wazuh security log retention policy",
    "default_state": "hot",
    "states": [
      {
        "name": "hot",
        "actions": [
          {
            "rollover": {
              "min_index_age": "1d",
              "min_primary_shard_size": "25gb"
            }
          }
        ],
        "transitions": [
          { "state_name": "warm", "conditions": { "min_index_age": "7d" } }
        ]
      },
      {
        "name": "warm",
        "actions": [
          { "replica_count": { "number_of_replicas": 1 } },
          { "force_merge": { "max_num_segments": 1 } }
        ],
        "transitions": [
          { "state_name": "cold", "conditions": { "min_index_age": "30d" } }
        ]
      },
      {
        "name": "cold",
        "actions": [
          { "read_only": {} }
        ],
        "transitions": [
          { "state_name": "delete", "conditions": { "min_index_age": "365d" } }
        ]
      },
      {
        "name": "delete",
        "actions": [
          { "delete": {} }
        ]
      }
    ]
  }
}

Log Retention Strategy

Retention is where policy meets engineering. Your retention periods should be driven by three factors:

DriverTypical RequirementImpact on Storage
Compliance (PCI-DSS)1 year minimumHigh — plan for ~1-5 GB/host/month
Compliance (HIPAA)6 yearsVery high — consider tiered storage
Incident response90 days hot, 1 year warmModerate — hot storage is expensive
Threat hunting30-90 days searchableModerate — needs fast indexing
Legal holdIndefinite when triggeredUnpredictable — needs immutable storage

Tiered Retention Architecture

Hot tier   (0-7 days)    Fast NVMe, full replicas      → Active investigation
Warm tier  (7-30 days)   Standard SSD, reduced replicas → Recent threat hunting
Cold tier  (30-365 days) HDD / object storage, read-only → Compliance, forensics
Archive    (1-6 years)   S3/MinIO, compressed snapshots  → Long-term compliance
Retention without immutability is a liability. If an attacker compromises your SIEM, they can delete historical evidence. Use write-once storage (S3 Object Lock, WORM-compliant storage) for your cold and archive tiers. OpenSearch snapshots to S3 with Object Lock enabled provide a cost-effective immutable archive.

Calculating Storage Requirements

A rough formula for planning:

Daily volume = (hosts x avg_events_per_second x 86400 x avg_event_size_bytes)

Example:
  50 hosts x 20 EPS x 86400 seconds x 500 bytes = ~43 GB/day raw
  With indexing overhead (~1.5x): ~65 GB/day
  90-day hot+warm retention: ~5.8 TB
  365-day total retention: ~23.7 TB

These numbers grow fast. This is why tiered storage and index lifecycle management aren’t optional — they’re the difference between a sustainable pipeline and one that collapses under its own weight.

Putting It All Together: End-to-End Example

Here’s the complete flow from a security event on a host to a searchable alert in your SIEM:

  1. auditd detects a suspicious execve — a user runs curl to download a script from an external IP
  2. The event is written to /var/log/audit/audit.log in auditd’s native format
  3. Wazuh agent reads the audit log and sends the event to the Wazuh manager over encrypted TCP (port 1514)
  4. Simultaneously, rsyslog forwards the raw syslog to the central collector as a backup path
  5. Wazuh manager decodes the event — the auditd decoder parses the raw audit format into structured fields
  6. Wazuh rule engine evaluates the event — rule 80792 fires: “Auditd: Command executed by user” with enriched MITRE ATT&CK mapping
  7. The alert is indexed in OpenSearch under wazuh-alerts-* with full metadata
  8. A dashboard query or alert rule picks it up for analyst review
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# Verify the pipeline is working end-to-end

# 1. Generate a test audit event on a monitored host
sudo auditctl -w /tmp/test_pipeline -p wa -k pipeline_test
touch /tmp/test_pipeline

# 2. Check Wazuh agent is forwarding
sudo cat /var/ossec/logs/ossec.log | grep "pipeline_test"

# 3. Query OpenSearch for the event
curl -k -u admin:${INDEXER_PASS} \
  "https://wazuh-indexer:9200/wazuh-alerts-*/_search" \
  -H 'Content-Type: application/json' \
  -d '{
    "query": {
      "bool": {
        "must": [
          { "match": { "rule.groups": "audit" } },
          { "match": { "data.audit.key": "pipeline_test" } }
        ]
      }
    },
    "size": 1,
    "sort": [{ "timestamp": "desc" }]
  }'

# 4. Clean up
sudo auditctl -W /tmp/test_pipeline -p wa -k pipeline_test
rm /tmp/test_pipeline

Common Pitfalls

After deploying dozens of these pipelines, these are the mistakes that cost the most time:

1. Forwarding logs without TLS. Every log message in transit is readable and modifiable by anyone on the network path. This includes credentials, session tokens, and command-line arguments captured by auditd.

2. No disk queue on the forwarder. When the central collector goes down (and it will), rsyslog without a disk queue drops messages silently. The queue.type="LinkedList" and queue.saveonshutdown="on" settings from the rsyslog config above are essential.

3. Ignoring timezone consistency. If your hosts are in different timezones and your logs don’t use UTC, event correlation becomes a nightmare. Force UTC everywhere:

1
2
3
# /etc/rsyslog.d/00-utc.conf
$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat
module(load="builtin:omfile" Template="RSYSLOG_SyslogProtocol23Format")

4. Running out of storage silently. OpenSearch will go into read-only mode when disk usage hits 85% (the flood stage watermark). Set up monitoring for disk usage on your indexer nodes — an unresponsive SIEM during an incident is worse than no SIEM at all.

5. Not testing the pipeline regularly. A log pipeline that silently breaks provides false confidence. Schedule monthly end-to-end tests: generate a known event, verify it appears in the SIEM within your SLA window.

What Comes Next: Part 3

With Part 1 covering how to generate security telemetry (auditd, Falco, Tetragon) and this article covering how to collect and store it, the trilogy’s final piece is about what to do with it. Part 3 will cover detection engineering and incident response — writing SIEM detection rules mapped to MITRE ATT&CK, building automated response playbooks, and turning raw telemetry into actionable security outcomes.

Conclusion

A centralized log collection pipeline is the backbone of any Linux security operation. Without it, the audit sources from Part 1 are forensically useless the moment an attacker gains root access.

The pipeline is straightforward: journald aggregates locally, rsyslog forwards reliably over TLS, the Wazuh agent normalizes and enriches, and OpenSearch indexes for search and retention. Each layer has a specific job, and each layer fails gracefully when the next one is unavailable.

The key decisions are not technical — they’re operational: What are your retention requirements? Where do you store your immutable archive? How do you test that the pipeline is functioning? How fast must events travel from source to searchable index?

Get the plumbing right first. Detection and response are only as good as the data they operate on.


This is Part 2 of the Linux Kernel Security trilogy. See Part 1: Linux Kernel Security Auditing for generating security telemetry, and Part 3 (coming soon) for detection engineering and incident response.

References: