檔案離奇消失怎麼查?RHEL 8 auditd 刪檔稽核規則設定、驗證與注意事項

問題情境:檔案不見了,但沒人承認

共用目錄裡的檔案被刪除,是維運上很常見、也很難事後追查的事件。檔案系統本身不會記錄「誰刪的」,shell history 可以被清除,也涵蓋不到腳本、排程與服務帳號的行為。

在 RHEL 8 上,要回答這個問題的標準做法是 Linux Audit(auditd):由 kernel 在系統呼叫層級記錄事件,並保留「原始登入者」身分,即使對方用 sudo 或 su 切換過身分也追得到。

但 auditd 無法回溯。規則生效之前發生的刪除不會有任何紀錄,這是「預先佈署」的機制,而不是事後鑑識工具。

適用場景

  • 共用資料目錄、設定檔目錄不定期出現檔案遺失,需要釐清是人為操作還是排程/程式行為
  • 稽核或法遵要求對特定目錄保留異動軌跡
  • 多人共用 sudo 權限的主機,需要還原到個人帳號

環境確認

RHEL 8 預設已安裝並啟用 auditd,多數情況下只需確認狀態:

rpm -q audit
sudo systemctl status auditd

若是最小化安裝或曾被移除,再補裝並啟用:

sudo dnf install audit -y
sudo systemctl enable --now auditd

設定稽核規則

臨時規則(立即生效,重開機後消失)

以監控 /data/important 為例:

sudo auditctl -a always,exit -F arch=b64 \
  -S unlink,unlinkat,rename,renameat,renameat2,rmdir \
  -F dir=/data/important \
  -F 'auid>=1000' -F 'auid!=4294967295' \
  -k important_delete

在命令列上,auid>=1000 一定要加引號。 否則 shell 會把 > 解讀為輸出重導,結果是規則載入失敗,並在目前目錄留下一個名為 =1000 的空檔案。

各參數的作用:

參數作用
-a always,exit在系統呼叫結束時一律產生稽核事件
-F arch=b64比對 64 位元系統呼叫介面
-S unlink,unlinkat,rmdir刪除檔案與目錄
-S rename,renameat,renameat2搬移與改名(mv 移出目錄,效果等同刪除)
-F dir=/data/important遞迴涵蓋該目錄下所有層級
-F 'auid>=1000'只記錄一般使用者帳號(UID 1000 以上)
-F 'auid!=4294967295'排除沒有登入身分的行程,可寫成 auid!=unset
-k important_delete自訂關鍵字,供後續查詢與管理規則使用

永久規則

auditctl 下的規則只存在於本次開機。要永久生效,新增 /etc/audit/rules.d/50-important-delete.rules:

-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat,renameat2,rmdir -F dir=/data/important -F auid>=1000 -F auid!=4294967295 -k important_delete
-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat,renameat2,rmdir -F dir=/data/important -F auid>=1000 -F auid!=4294967295 -k important_delete

規則檔不經過 shell 解析,所以這裡不需要引號。b32 那一行是為了涵蓋以 32 位元介面發出的系統呼叫,避免留下可繞過的缺口。

載入並確認:

sudo augenrules --load
sudo auditctl -l

augenrules 會依檔名順序合併 rules.d 下的所有 .rules 檔,因此檔名前的數字決定規則順序。

驗證與查詢

以一般使用者登入後建立並刪除測試檔:

touch /data/important/audit-test
rm /data/important/audit-test

# 依 key 查詢,-i 會將 UID 轉成帳號名稱
sudo ausearch -k important_delete -i --start recent

一次刪除會產生一組紀錄,節錄示意如下:

type=PROCTITLE ... : proctitle=rm /data/important/audit-test
type=PATH ...      : item=1 name=/data/important/audit-test nametype=DELETE
type=CWD ...       : cwd=/home/alice
type=SYSCALL ...   : syscall=unlinkat success=yes auid=alice uid=root
                     tty=pts0 ses=12 comm=rm exe=/usr/bin/rm key=important_delete

判讀時看這幾個欄位:

欄位意義
auid原始登入帳號,經過 sudo、su 後仍不變,是追查的主要依據
uid / euid執行當下的身分;auid=alice uid=root 表示 alice 透過 sudo 執行
exe / comm實際執行刪除的程式,可分辨是 rm、rsync 還是某支腳本的直譯器
proctitle完整命令列
tty / ses終端機與登入工作階段,可對照 /var/log/secure 找出來源 IP
name + nametype=DELETE被刪除的路徑

常用的查詢變化:

# 指定時間範圍
sudo ausearch -k important_delete -i --start today
# 只看特定檔案
sudo ausearch -k important_delete -i -f /data/important/report.xlsx

範圍與規則

auid 過濾條件要不要留

若使用 auid>=1000 加上 auid!=unset ,這是 RHEL 官方文件提供的指令範例,雜訊最少,但它會略過幾種很常見的「兇手」:

過濾條件記錄得到記錄不到
auid>=1000 且 auid!=unset一般使用者(含其 sudo 操作、個人 crontab)root 直接登入、root 的 crontab、systemd 服務、容器內行程
僅 auid!=unset以上再加 root 直接登入與 root crontabsystemd 服務、daemon、容器內行程
不過濾 auid所有行程無,但需靠 exe、comm、pid 判斷來源,事件量也最大

實務上,檔案離奇消失的原因經常是清理腳本、logrotate、同步工具或應用程式本身。如果調查目標還不明確,建議先拿掉 auid 過濾,確認事件量可接受後再收斂。

系統呼叫規則與 watch 規則

這個寫法 -w /data/important -p wa -k important_delete,使用時,會有訊息,說這已經過時,改用系統呼叫寫法比較好。

寫法優點代價
-a always,exit -S ...精準鎖定刪除與搬移,可加 auid 等條件語法較長,需自行確認系統呼叫涵蓋範圍
-w ... -p wa語法簡短,寫入與屬性變更一併記錄事件量大,無法只針對刪除;較新的 audit 版本已不建議使用此寫法

只想回答「誰刪的」,系統呼叫規則較合適;若連內容被改寫也要追,再考慮擴大範圍。

監控範圍的界線

本文規則涵蓋的是「檔案從目錄中消失」。檔案被清空或覆寫(例如 > file、truncate)不屬於刪除,不會被記錄。有這類需求時要另外加入 truncate、ftruncate 以及帶寫入旗標的 open/openat,事件量會明顯上升。

實務踩坑與注意事項

1. 用 root 直接登入測試,結果查無資料
root 直接登入時 auid=0,會被 auid>=1000 排除,ausearch 回傳 <no matches>。這是規則如預期運作,不是規則失效。請改用一般帳號登入(之後再 sudo 也可以)進行驗證。

2. 無法用 systemctl 重啟 auditd
RHEL 8 的 auditd 拒絕 systemctl restart 與 stop,這是刻意的設計。調整 auditd.conf 後請用:

sudo service auditd restart

3. 規則被鎖定無法修改
若環境套用過 STIG 或 CIS 強化,規則結尾通常有 -e 2,此時任何新增或刪除都要重開機才生效。可用 sudo auditctl -s 檢查 enabled 是否為 2。自訂規則檔的檔名排序也必須在含 -e 2 的檔案之前。

4. 日誌保留量不足
預設每個檔案 8 MB、保留 5 份,總量約 40 MB。目錄異動頻繁時,證據可能在幾天內就被輪替掉。請依需求調整 /etc/audit/auditd.conf 的 max_log_file 與 num_logs,並確認 /var/log/audit 所在分割區的空間。

5. 有 root 權限的人可以改日誌
稽核紀錄存在本機,能刪檔的 root 同樣能動 audit.log。有法遵需求時,應將事件即時轉送到遠端日誌主機或 SIEM。

6. 網路檔案系統有盲區

  • 本機是 NFS server:用戶端的刪除由 kernel 的 nfsd 處理,不經過系統呼叫,不會有紀錄,需到用戶端主機稽核。
  • 透過 Samba 分享:刪除動作由 smbd 執行,auid 為 unset,會被過濾條件排除;即使不過濾,也只看得到 smbd。建議改用 Samba 的 full_audit 模組。

7. 目錄不存在或掛載時序
-F dir= 指向的路徑在載入規則時必須存在,否則該條規則載入失敗。若 /data 是獨立或網路掛載點,重開機後請再做一次驗證測試,確認規則確實作用在掛載後的檔案系統上。

8. 非 x86_64 架構
aarch64 沒有 unlink、rename、rmdir 這幾個舊式系統呼叫,規則需改為只列 unlinkat,renameat,renameat2。

9. 留意事件遺失
監控範圍大或刪除量高時,用 sudo auditctl -s 觀察 lost 與 backlog。lost 持續增加表示緩衝區不足,需調高 -b 或縮小規則範圍。

規則管理

查看目前生效的規則:

sudo auditctl -l

刪除單一條規則時,參數必須與新增時完全一致,只是把 -a 換成 -d:

sudo auditctl -d always,exit -F arch=b64 \
  -S unlink,unlinkat,rename,renameat,renameat2,rmdir \
  -F dir=/data/important \
  -F 'auid>=1000' -F 'auid!=4294967295' \
  -k important_delete

或者一次移除所有帶有該 key 的規則:

sudo auditctl -D -k important_delete

兩點提醒:

  • -D 不加 -k 會清空全部規則,在正式環境請特別小心。
  • 上述指令只影響執行中的規則。若已寫入 rules.d,要一併刪除該檔案並重新執行 sudo augenrules --load,否則下次開機規則會回來。

小結

  • auditd 必須事先佈署,無法追查規則生效前的刪除。
  • auid 是追查的關鍵欄位,能穿透 sudo 與 su 還原到原始登入者。
  • auid 過濾條件決定「看得到誰」,調查初期寧可放寬。
  • 規則上線後一定要實測,並同步處理日誌保留與遠端備份。

aur-malware-check

Detection and analysis tools for the atomic-lockfile supply-chain attack on the Arch User Repository (AUR), generalized to a campaign-based architecture that handles multiple concurrent and historical attack waves (CHAOS RAT 2025, Russian spam packages, and future campaigns declared via campaigns.json).

簡單的說,有人把惡意代碼放到 AUR 已經沒維護的套件裏面,然後上傳 。所以安裝了以後,就被惡意代碼植入了,也因為是 npm 套件的關係,相依性相對複雜,影響層面也較為廣泛。

社群因應此狀況,開了一個 repository 放檢測腳本:https://github.com/lenucksi/aur-malware-check

使用方法如下

# 安裝 uv
pacman -S uv

# 下載代碼
git clone https://github.com/lenucksi/aur-malware-check.git

# 進入目錄
cd aur-malware-check

# 建立 python venv 環境,並安裝模組
uv sync

# 執行
uv run python -m aur_check --refresh --list

其他使用方法可以參考 repository README.md 裡的 quickstart

如果發現了被植入惡意代碼,可以參考 repository 裡的 What to do If I Infected

  • 保留系統:不要關機,改用可信媒體進行鑑識擷取。
  • 重設所有憑證:Discord、GitHub、npm、Slack、Teams、SSH 金鑰、Vault token、雲端供應商金鑰。
  • 檢查持久化:systemctl list-units --type=service --state=running(查看是否有不明服務)。
  • 檢查 eBPF rootkit:ls -la /sys/fs/bpf/hidden_*
  • 使用可信媒體清理:從 Arch ISO 開機,掛載檔案系統,移除惡意的 systemd 單元。
  • 考慮重裝:rootkit 會讓系統失去可信度。
  • 回報發現:https://lists.archlinux.org/archives/list/aur-general@lists.archlinux.org/

Proxmox LXC設定

LXC是輕量級的容器,Proxmox 直接提供了這功能,等於是可以拿 LXC 來當 VM,這樣可以更壓榨硬體的效能出來。

這邊主要是紀錄用的時候遇到的一些狀況:

  1. Tailscale
  2. podman

Tailscale

因為我 Proxmox 網路設定是用NAT,再加上沒有第二張網卡,所以就簡單用 Tailscale 讓外部可以連到 Proxmox 裡面的 LXC 跟 VM。VM 用 tailscale 沒什麼問題,但 LXC 就遇到不能用的情況。

這邊要在 lxc 容器設定 (Proxmox 主機裡的 /etc/pve/lxc/<id>.conf 裡加上以下內容

lxc.cgroup2.devices.allow: c 10:200 rwm
lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file

podman

podman 本身就容器,等於是容器裡的容器,這邊要處理 rootless 的情況,一樣,在 lxc 容器設定 (Proxmox 主機裡的 /etc/pve/lxc/<id>.conf 加上以下內容

lxc.idmap: u 0 100000 165536
lxc.idmap: g 0 100000 165536

還有 /etc/subuid

root:100000:200000

跟 /etc/subgid

root:100000:200000

參考資料

helm images

適用情境,客戶是離線環境,沒辦法直接拉 container image,所以要先知道 helm chart 裏面用到的 container image,用這指令,就可以找到所有 helm chart 所使用到的 image,再用 skopeo/podman pull/docker pull 拉取下來,存為 tarball ,並帶到客戶端。

專案網址:https://github.com/nikhilsbhat/helm-images

安裝

安裝好 helm 以後,可以用以下指令安裝

helm plugin install https://github.com/nikhilsbhat/helm-images

使用

簡單說,就是拿 helm install 的指令來用,並改為 helm images get。

所以在配置好 helm repo 以後,就可以用 helm images 取得

例如 redis-operator

helm images get redis redis-operator/redis-operator

又例如 zabbix operator

helm images get zabbix zabbix-chart-7.0/zabbix-helm-chrt

就可以取得 container images 的網址。


題外話,應該要來熟悉一下 skopeo 怎麼用才是。

使用systemctl 啟用podman出現Failed to connect to bus

在使用 sudo/su 切換身份以後,用以下指令去啟用/啟動 podman 服務,會出現以下錯誤

Failed to connect to bus: No such file or directory

查了以後,才知道是少了 DBUS 環境變數的問題:https://stackoverflow.com/questions/73814619/permission-denied-trying-to-use-rootless-podman-docker-compose-traefik-with

這個時候,只要查到 DBUS 的 socket 路徑,再去 export DBUS 環境變數即可

export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
$ systemctl --user enable --now podman

啟用以後,會有 podman.sock 檔案,這個檔案的位置跟 root 身份的位置不一樣,會是在

/run/user/$UID/podman/podman.sock

這邊是要特別注意的。

終端機快速複製檔案內容到剪貼簿

資料來源:How to copy and paste within a terminal in macOS or Linux?

在 MacOS 下,可以用 pbcopy/pbpaste

cat file.txt | pbcopy
pbpaste | tee foo.txt

在 Linux 下,要用 xsel,主要是參數 –input 跟 –output

# 複製到剪貼簿
cat file.txt | xsel --clipboard --input
xsel --clipboard --output | tee foo.txt

若要讓指令一致,可以用 alias

alias pbcopy='xsel --clipboard --input'
alias pbpaste='xsel --clipboard --output'

為 yum repository 指定 proxy

要設定讓 yum 可以使用指定的 proxy 伺服器,可以在 /etc/yum.conf 的 [main] 區段裡增加 proxy 設定即可

[main]
proxy=http://<proxy_server>:3128

那如果是個別的 repository 要使用不同的 proxy 伺服器,可以怎麼做呢?
原本以為會很麻煩的,但意想不到簡單,因為有支援此設定。
就在 repository 的區段裡增加 proxy 設定就可以:

[repo]
name=baseos
baseurl=http://yumrepo.example.com/baseos
enabled=1
gpgcheck=0
proxy=http://<proxy_server>:3128

參考資料

bash script取亂數

在 bash script 裡,可以用 ${RANDOM} 取亂數,例如

echo "random number: ${RANDOM}"

就會出現一個隨機的數字。

那如果想限制在 20~40 這個範圍內的話,可以怎麼做呢?這時候可以用 bash 的 % ,取餘數的運算子來做,例如

echo "random number(0~10): $((20 + RANDOM % 20))"

參考資料

BIND+DNS over HTTPs

說明一下為什麼會要這樣作。

因為環境關係,DNS protocol 出不去,只有 A 主機可以透過 HTTP 出去。

因此方案如下:

  1. A 電腦安裝 squid,其他主機可將 HTTP proxy 設定為 A 主機,這樣 HTTP 請求就可透過 A 主機出去。
  2. 環境內原有 B 主機已經安裝 BIND,此主機就安裝 cloudflare 提供的 cloudflared ,使用 DNS over HTTPs 方式,讓 DNS 查詢可以用 HTTP 請求,透過 A 主機去查詢。

安裝與設定

cloudflared 蠻好安裝的,就一個執行檔,下載以後,改個權限就可以執行了 (安裝方式)

那如果要用 systemd 來啟動,也很容易,安裝方式裡就有提供範例:

[Unit]
Description=DNS over HTTPS (DoH) proxy client
Wants=network-online.target nss-lookup.target
Before=nss-lookup.target

[Service]
Environment=http_proxy=http://<proxy>:3128
Environment=https_proxy=http://<proxy>:3128
AmbientCapabilities=CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
DynamicUser=yes
ExecStart=/usr/local/bin/cloudflared proxy-dns --port 5533

[Install]
WantedBy=multi-user.target

設定好,用 firewall-cmd 設定好防火牆 (TCP 5533) 以後,就可以去調整 BIND 的設定 /etc/named.conf。

修改既有的 forwarder 設定

forwarders { 127.0.0.1 port 5533; };

重新啟動 named 以後,就可以試試看了。

流程大致如下

--[DNS查詢]--> B 主機(53/UDP) ----> B 主機(5533/TCP) --[HTTP]--> A 主機(3128/TCP) --> 外部

參考資料

找昨天到現在有更動的檔案

需求是要找到從昨天到現在有變更的檔案,一時也想不出來怎麼做,利用 Google 找了一下,大多都是利用 find 的 -newer 參數。

find 的 -newer 參數要帶入的是一個檔案,表示會以這個檔案為基準來進行比對。

所以假設是要找 2023-11-11 之後的檔案,就先利用 touch 來建立一個基準檔案,其修改日期為 2023-11-11:

touch --date "2023-11-11" /tmp/base

然後再用

find . -newer /tmp/base -print

就可以找出在 /tmp/base 之後變更的檔案了。

find 也有一個 -newerXY 的參數,這個 XY 可以是以下內容

  1. a – 檔案的存取時間
  2. B – 檔案的建立時間
  3. c – 檔案 inode 的變更時間
  4. m – 檔案的修改時間
  5. t – 參數的內容是時間

那要找比 2023-11-11 要新的檔案,就用以下指令

find . -type f -newermt 2023-11-11

要找特定一天的,就用以下指令

find . -type f -newermt 2023-11-11 ! -newermt 2023-11-12

參考資料