OCI-to-Trend Micro CEF Log Shipper

OCI-to-Trend Micro CEF Log Shipper

Modern cloud architectures demand seamless visibility, yet integrating disparate logging systems remains a common challenge. In enterprise environments, piping security event data from cloud infrastructure to on-premises or cloud-native Security Information and Event Management (SIEM) systems requires a highly reliable and performant pipeline.

This guide provides a comprehensive configuration for ingesting security logs from an Oracle Cloud Infrastructure (OCI) Streaming (Kafka) source, transforming them into Common Event Format (CEF) via Fluent Bit, and delivering them securely to a Trend Micro Service Gateway via TCP Syslog.

This setup directly addresses the challenge of handling nested JSON objects (typical in OCI Web Application Firewall and Load Balancer logs) and complies with the strict connection constraints of the Trend Micro receiver.

Architecture Overview

The pipeline architecture consists of three core components:

  • Input: OCI Streaming (Kafka API) authenticated via SASL/PLAIN.
  • Processor: A robust Lua filter that performs recursive table flattening on complex structures.
  • Output: A TCP Syslog stream utilizing the RFC3164 format and targeting a specific custom message key.

1. Lua Transformation Script

Converting complex OCI JSON into a flat string that SIEMs can successfully parse requires specialized processing. The following Lua script resolves the common table: 0x... memory address leak issue by recursively drilling into nested JSON objects. It maps security fields specifically for Trend Micro compatibility and formats the resulting CEF payload safely.

Path: /etc/fluent-bit/scripts/cef_wrapper.lua

-- Helper to escape special CEF characters (|, =, \)
local function escape_cef(val)
    if val == nil then return "" end
    local s = tostring(val)
    -- CEF requires escaping backslashes, pipes, and equal signs in values
    s = s:gsub("\\", "\\\\"):gsub("|", "\\|"):gsub("=", "\\=")
    return s
end

-- Improved Recursive function to flatten OCI tables
-- Uses colons (:) to avoid "Parser Drift" in SIEMs caused by nested "="
function flatten_table(obj)
    if type(obj) ~= "table" then
        return tostring(obj)
    end

    local res = ""
    for k, v in pairs(obj) do
        if type(v) == "table" then
            -- Use brackets and colons for nested structures
            res = res .. k .. ":[" .. flatten_table(v) .. "] "
        else
            -- Use colons to ensure SIEM doesn't think this is a new CEF key
            res = res .. k .. ":" .. tostring(v) .. " "
        end
    end
    return res
end

function wrap_cef(tag, timestamp, record)
    -- 1. Determine the root of the log data
    local log_body = record["payload"] or record
    local data = log_body["data"] or {}
    local request = data["request"] or {}

    -- 2. Extract Specific Security Fields (Correctly mapped for Trend Micro)
    local src_ip   = data["clientAddr"] or ""
    local req_path = request["path"] or ""
    local method   = request["method"] or ""
    local action   = data["action"] or ""
    local host     = data["host"] or ""
    local event_id = log_body["type"] or "com.oraclecloud.loadbalancer.waf"

    -- 3. Trend Micro Mandatory CEF Headers
    local vendor   = "Oracle"
    local product  = "OCI"
    local version  = "1.0"
    local severity = "3"
    local name     = "OCI_Log_Entry"

    -- 4. Build the Extension String (Standard CEF Keys)
    -- Placing these FIRST ensures they are indexed even if 'msg' is truncated
    local extensions = string.format("src=%s request=%s method=%s act=%s cs1Label=Host cs1=%s ",
        src_ip,
        escape_cef(req_path),
        escape_cef(method),
        escape_cef(action),
        escape_cef(host))

    -- 5. Flatten the rest into the 'msg' field using the safer colon format
    local msg_content = flatten_table(log_body)

    -- 6. Construct the Final CEF String with Syslog Priority <13>
    local cef_msg = string.format("<13>CEF:0|%s|%s|%s|%s|%s|%s|%smsg=%s",
        vendor, product, version, event_id, name, severity, extensions, msg_content)

    -- 7. Hard Truncation at 8000 bytes for network safety
    if #cef_msg > 8000 then
        cef_msg = cef_msg:sub(1, 7980) .. " [TRUNCATED]"
    end

    -- 8. Final Output Preparation
    local new_record = {}
    -- The newline \n is critical for TCP stream delimited parsing
    new_record["formatted_cef"] = cef_msg .. "\n"

    return 1, timestamp, new_record
end

2. Fluent Bit Configuration

To deliver our formatted CEF string, we configure the Fluent Bit syslog output plugin. By leveraging the syslog_message_key directive, we guarantee that only our newly constructed CEF string is sent downstream rather than the entire wrapped JSON structure.

Path: /etc/fluent-bit/fluent-bit.conf

[SERVICE]
    Flush        1
    Daemon       Off
    Log_Level    info
    HTTP_Server  On
    HTTP_Port    2020

[INPUT]
    Name                kafka
    Tag                 oci_logs
    Brokers             <OCI_STREAMING_ENDPOINT>:9092
    Topics              oci_logs
    Group_Id            siem-shipper-v1
    Format              json
    rdkafka.security.protocol SASL_SSL
    rdkafka.sasl.mechanism    PLAIN
    rdkafka.sasl.username     <TENANCY>/<USER>/<STREAMPOOL_OCID>
    rdkafka.sasl.password     <AUTH_TOKEN>
    rdkafka.auto.offset.reset  latest

[FILTER]
    Name         lua
    Match        oci_logs
    Script       /etc/fluent-bit/scripts/cef_wrapper.lua
    Call         wrap_cef

[OUTPUT]
    Name                syslog
    Match               oci_logs
    Host                172.18.7.10
    Port                6515
    Mode                tcp
    syslog_message_key  formatted_cef
    Syslog_Format       rfc3164
    # Disable keepalive to prevent 'Connection Reset' by Trend Micro
    net.keepalive       off
    TLS                 Off

3. Troubleshooting and Pipeline Architecture Validation

When running a high-throughput syslog pipeline, verifying packet ingest and processing health is critical. Use the following built-in Fluent Bit metrics endpoints to ensure your input records are balanced with successful output records.

To verify that proc_records matches kafka.0 records and that retries_failed remains at 0, query the API using:

curl -s http://127.0.0.1:2020/api/v1/metrics | jq '.output'
curl -s http://127.0.0.1:2020/api/v1/metrics | jq '.input'

4. Testing the Infrastructure Locally

Before launching into production, it is highly recommended to redirect outputs to stdout to confirm the Lua engine is flattening the data payloads correctly.

Open and edit your configuration parser file:

nano /etc/fluent-bit/fluent-bit.conf

Temporarily swap the active Output block to print directly to your terminal:

[OUTPUT]
    Name   stdout
    Match  oci_logs

Execute the Fluent Bit daemon manually to audit the console output:

sudo /opt/fluent-bit/bin/fluent-bit -c /etc/fluent-bit/fluent-bit.conf

With this setup complete, you now possess a reliable, robust, and highly structured pipeline. It smoothly processes high-velocity, nested security events from Oracle Cloud Infrastructure, transforming and deliverying them cleanly to your Trend Micro gateway for real-time security monitoring.

_

Leave a Reply

Your email address will not be published. Required fields are marked *