Share Notes

chundev

View the Project on GitHub latteouka/share-notes

rsyslog 轉發排錯:在最小化 Linux 環境下的實戰排查

日期:2026-03-25 環境:CentOS/RHEL 最小化安裝(無 tcpdump、nc、telnet)


TL;DR

設定 rsyslog 將 EDR 告警轉發到遠端 SIEM,ss 看到 UDP ESTAB 但 SIEM 收不到。排查後發現根本原因是 rsyslog 設定檔中 &(action continuation)沒有獨立成行。在最小化系統上用 bash /dev/tcpiptables -j LOGss -tunp 三件套取代常見的網路診斷工具。


背景

EDR(Endpoint Detection & Response)產品通常需要將告警 syslog 轉發到 SIEM 平台集中監控。轉發機制是透過 rsyslog 修改設定檔,新增一行 action 將 log 透過 UDP/TCP 送到遠端 syslog server。

EDR 設備通常是精簡安裝的 Linux,很多常用的網路診斷工具都沒裝,排錯時需要用替代方案。


問題 / 錯誤訊息

設定完 rsyslog 轉發後:

沒有明確的錯誤訊息,這正是最難排的類型。


除錯過程

❌ 嘗試 1:懷疑防火牆

ss 有 ESTAB 但 SIEM 沒收到,直覺懷疑防火牆。但用 iptables LOG 確認封包有送出,且同網段另一台可以通 → 排除中間防火牆。

❌ 嘗試 2:懷疑路由

跨網段傳輸(10.x.x.x192.168.x.x),懷疑路由問題。但 ip route get 路由正確,gateway 可 ping → 排除路由。

❌ 嘗試 3:懷疑 ACL 沒放行新 IP

同網段舊機可以通,新機不行,高度懷疑 ACL 只放了舊機 IP。但確認 ASA 和 Core Switch ACL 都有放行 → 排除 ACL。

✅ 最終解法:rsyslog 設定檔的 & 沒有獨立成行

重新檢查設定檔,發現 & action continuation 行沒有正確換行。修正後 systemctl restart rsyslog,立刻通了。


解法 / 核心知識

rsyslog Legacy Syntax 的行導向解析

rsyslog 傳統語法(legacy format)以「行」為單位解析,& 是 action continuation 符號,必須出現在新行的開頭:

# ❌ 錯誤:& 黏在前一行
if $programname == 'my-service' then /var/log/my-service.log;MyFormat & @10.0.0.1:514;MyFormat
& stop

# ✅ 正確:每個 action 獨立一行
if $programname == 'my-service' then /var/log/my-service.log;MyFormat
& @10.0.0.1:514;MyFormat
& stop

坑在於rsyslogd -N1 語法檢查不一定會報錯,服務也能正常啟動,ss 甚至會顯示 ESTAB,但實際轉發行為不正確。

UDP ESTAB 的真正意義

ss 輸出中看到 UDP socket 的 ESTAB 狀態,容易誤解為「連線已建立」。實際上:

狀態 意義
UNCONN 未連線的 UDP socket,等待任意來源的封包
ESTAB 已對特定目的地呼叫 connect() 的 UDP socket

UDP 的 connect() 不是真正的連線,只是告訴 kernel:

  1. 記住預設目的地址,send() 時不用再指定
  2. 只接收來自該目的地的回覆封包
  3. 可以收到 ICMP port unreachable 錯誤

UDP ESTAB 只代表本機 socket 設定好了,不代表對方有在監聽或封包有到達。

最小化 Linux 的網路診斷三件套

在沒有 tcpdump、nc、telnet 的環境下:

1. bash /dev/tcp — TCP 連通性測試

# 測試 TCP port 是否開通
timeout 3 bash -c 'echo > /dev/tcp/10.0.0.1/514' && echo "OK" || echo "FAIL"

2. iptables -j LOG — 替代 tcpdump 抓包

# 記錄所有送往目標 IP 的封包
iptables -I OUTPUT -d 10.0.0.1 -j LOG --log-prefix "MYTEST: "

# 觸發流量
logger -t my-service "test message"

# 查看結果
dmesg | grep "MYTEST"

# ⚠️ 用完一定要刪除,否則 kernel log 會被灌爆
iptables -D OUTPUT -d 10.0.0.1 -j LOG --log-prefix "MYTEST: "

3. ss -tunp — 連線狀態確認

# 查看 rsyslog 的所有連線
ss -tunp | grep rsyslog

# 只看 UDP
ss -unp | grep 514

補充:logger 的進階用法

# 模擬特定 program name 的 syslog 訊息
logger -t my-service "test message"

# 直接送 UDP 到遠端(不經過本機 rsyslog)
logger -d -n 10.0.0.1 -P 514 "direct UDP test"
# -d = UDP, -n = 遠端主機, -P = port

rsyslog Template 與 hostname 欄位

轉發 syslog 時,不同 template 包含的欄位不同:

Template 類型 含 hostname 適用場景
標準 syslog 格式(RFC 3164) 遠端轉發(SIEM 能正確解析)
自訂 log 格式(精簡) 本機檔案記錄

如果 SIEM 收到的 log 缺少設備名稱(hostname),通常是轉發行使用了不含 %hostname% 的 template。解法是改用標準 syslog 格式的 template,或自訂一個帶 hostname 的 template:

template(name="WithHost" type="string"
  string="%timegenerated% %hostname% [%procid%] <%syslogseverity-text%> %msg%\n")

學到的事


參考資料