檔案離奇消失怎麼查?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 過濾條件決定「看得到誰」,調查初期寧可放寬。
  • 規則上線後一定要實測,並同步處理日誌保留與遠端備份。

為 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

參考資料

測試指定的套件會影響哪些套件

若是想知道指定的套件會影響哪些套件,這時候可以怎麼做呢?

我第一個是想到可以用 rpm ,用 man rpm 可以查到 rpm 有以下參數

  • –provides
  • –requires
  • –whatprovides
  • –whatrequires

所以就試試看

rpm -q --whatrequires libweb

但結果卻不如預期,上網找到這篇 centos – Why does rpm –whatrequires fail to report dependencies? – Unix & Linux Stack Exchange

才知道正確用法是這樣,要先用 rpm -q --provides libwebp 去查到 libwebp 有提供什麼capability,然後再用 rpm -q --whatrequires <capability> 去找。

例如 libwebp

[root@workstation ~]# rpm -q --provides libwebp
libwebp = 1.0.0-5.el8
libwebp(x86-64) = 1.0.0-5.el8
libwebp.so.7()(64bit)
libwebpdecoder.so.3()(64bit)
libwebpdemux.so.2()(64bit)
libwebpmux.so.3()(64bit)
[root@workstation ~]# rpm -q --whatrequires "libwebp.so.7()(64bit)"
libwebp-1.0.0-5.el8.x86_64
gd-2.2.5-7.el8.x86_64
ImageMagick-libs-6.9.12.50-2.el8.x86_64

這樣就可以找到,但若是如此,就要寫腳本了。因為 libwebp 有提供多個 capability ,需要逐一去查才行。

有沒有更快的方法呢?

centos – Why does rpm –whatrequires fail to report dependencies? – Unix & Linux Stack Exchange 裡面有提到可以用 rpm -e --test ,直接模擬看看移除。

rpm -e --test libwebp 2>&1 | grep needed | awk '{print $6}' | sort | uniq

這樣就可以很快找出來了。

如何檢查RHEL有受到特定CVE影響?

因為好奇怎麼去查,就找到這篇:How to check if an RHEL system is vulnerable to a CVE ,文章裡面提到兩個方法。

第一個方法是用 rpm -q --changelog <套件> | grep <CVE_number>

舉個例子

rpm -q --changelog openssl | grep CVE-2021-3450

第二個方法是用 yum updateinfo info --cve <CVE_number>

舉個例子

yum updateinfo info --cve CVE-2021-3445

最後,想調查這台 RHEL 有受到哪些 Errata 影響,可以用

yum updateinfo info --summary
yum updateinfo info --list

若要帶入 CVE 資訊,可以用這兩個指令

yum updateinfo info --summary --with-cve
yum updateinfo info --list --with-cve

Red Hat 也有一篇 KB ,方法相似:https://access.redhat.com/solutions/3628301

不太一樣的地方是,指令是查看 rpm 的,所以是用

rpm -qp kernel-3.10.0-862.11.6.el7.x86_64.rpm --changelog | grep CVE-2017-12190

或者是用 yum list –cve <CVE_number>

yum list --cve CVE-2017-12190 | grep kernel.x86_64

[RHEL]grub設定密碼

以下指令可以避免讓使用者在開機時,可以調整 kernel 參數。若是在 kernel 參數填入 single 或 rescue ,使用者就有機會可以使用到較高的權限。

先用 grub2-setpassword 設定密碼,設定以後,/boot/grub2 資料夾裡會多出 user.cfg

grub2-setpassword 

再來重新產生 grub.cfg

grub2-mkconfig -o /boot/grub2/grub.cfg

這樣就完成了。

重開機看到開機選單以後,按下 e ,此時會要求輸入密碼,表示設定已經生效。

若要移除,刪除掉 user.cfg 即可。

rm /boot/grub2/user.cfg

若是要一進入開機選單就要輸入帳號跟密碼,那麼可以這樣設定

grub2-editenv - set grub_users="root"

這樣在開機時,就會問帳號跟密碼了。

要移除,可以用

grub2-editenv - set grub_users=

參考資料

RHEL8 系統憑證管理

RHEL8 管理系統 SSL 憑證,首先要安裝 ca-certificates 套件

yum install ca-certificates

這個套件主要是外部的憑證,會定期更新,若是在連線到外部有遇到憑證問題,通常更新這個套件就可以解決。

如果有需要安裝自簽憑證,可以複製到以下這兩個憑證路徑:

  • /etc/pki/ca-trust/source/anchors
  • /usr/share/pki/ca-trust-source/anchors

/etc/pki/ca-trust 的權限比較高。

複製完成,執行

update-ca-trust

要察看目前有放了哪些憑證,可以用 trust 指令

trust list

前面提到的複製、執行 update-ca-trust ,也可以用以下指令替代

trust anchor <path.to/certificate.crt>

參考資料

RHEL8系統加密原則(SSH)

在 Red Hat Enterprise Linux 8 以後導入了整合的加密原則管理,透過 update-crypto-policies 指令就可以很方便的去設定系統加密原則。

使用以下指令就可以看到使用哪個原則

update-crypto-policies --show

預設都是 DEFAULT

若需要設定為 FUTURE 可以用

update-crypto-policies --set FUTURE

Red Hat 有針對這部份撰寫一篇 KB: System-wide crypto policies in RHEL ,說明是如何運作的。在產品手冊裡也有提到這部份:Chapter 4. Using system-wide cryptographic policies。若有需要客製化,可以參考 Red Hat 的另外一篇 How to customize crypto policies in RHEL 8.2 來進行客製化。

接下來講一下 sshd 的部份,一般若要讓 sshd 使用指定的金鑰交換原則或是加密原則,會說要去修改 /etc/sysconfig/sshd ,把裡面的

# System-wide crypto policy:
# To opt-out, uncomment the following line
#CRYPTO_POLICY=

改為

# System-wide crypto policy:
# To opt-out, uncomment the following line
CRYPTO_POLICY=

然後再去修改 /etc/ssh/sshd_config 。

這個設定實際上會影響的地方是 sshd systemd service ,我們可以用 systemctl cat sshd 來看看

# /usr/lib/systemd/system/sshd.service
[Unit]
Description=OpenSSH server daemon
Documentation=man:sshd(8) man:sshd_config(5)
After=network.target sshd-keygen.target
Wants=sshd-keygen.target

[Service]
Type=notify
EnvironmentFile=-/etc/crypto-policies/back-ends/opensshserver.config
EnvironmentFile=-/etc/sysconfig/sshd
ExecStart=/usr/sbin/sshd -D $OPTIONS $CRYPTO_POLICY
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
RestartSec=42s

[Install]
WantedBy=multi-user.target

從這邊可以看到有兩行 EnvironmentFile,第一行 EnvironmentFile 去含括 /etc/crypto-policies/back-ends/opensshserver.config ,這就是系統的 ssh 加密原則設定。第二行 EnvironmentFile 含括的檔案,就是前面提到的 /etc/sysconfig/sshd 。

那在 ExecStart 這行可以看到啟動 sshd 時,有放置 $CRYPTO_POLICY ,表示會使用這個環境變數的內容。也因此,若在 /etc/sysconfig/sshd 裡移除 CRYPTO_POLICY 的註解,在啟動 sshd 時,就不會帶入參數,而使用 /etc/ssh/sshd_config 裡的設定。

在查到這邊時,我才想起之前好像有找過,但之前沒有記錄,沒記錄就容易忘記,所以今天特別記下來。

RHEL8/CentOS8 grubby

上週幫客戶處理問題時,對 RHEL8/CentOS8 的 grub 又多了解了一些,記錄一下。

Ubuntu 的選單是放在 /boot/grub/grub.cfg ,所以看這個檔案,就大略知道選單有哪些選項,修改可以直接改這個檔案。RHEL8 跟 CentOS8 使用 grubby 來管理開機時的 grub 選單,所以可以用 grubby 指令來做管理。

常用的指令可以參考 12 practical grubby command examples (cheat sheet)

我這次主要使用到的指令有

  • 列出開機選單項目的資訊
  • 修改該選單項目的 kernel options
  • 取得目前預設開機項目
  • 設定預設開機項目

列出開機選單項目的資訊

列出選單項目,可以用下面指令

grubby --info=ALL | grep -E "^kernel|^index"

取得 index 跟 kernel ,就可以修改指定項目的 kernel options 了。

修改指定選單項目的 kernel options

修改的指令如下

# 修改指定的 kernel 的 kernel options,路徑可參考上面指令
grubby --update-kernel=<kernel路徑> --args="ipv6.disable=1"
# 修改目前的
grubby --update-kernel=/boot/vmlinuz-$(uname -r) --args="ipv6.disable=1"

若要移除 kernel options,要用 –remove-args

# 移除指定的 kernel 的 kernel options
grubby --update-kernel=<kernel路徑> --remove-args="ipv6.disable=1"
# 移除目前 kernel 的 kernel options
grubby --update-kernel=/boot/vmlinuz-$(uname -r) --remove-args="ipv6.disable=1"

這裡的修改,會影響到 /boot/loader/entries 目錄下的檔案。

取得目前預設開機項目

grubby --default-index
grubby --default-title

設定預設開機項目

用以下指令設定

grubby --set-default="/boot/vmlinuz-4.18.0-193.1.2.el8_2.x86_64"
grubby --set-default-index=2

這裡設置以後,會影響到 /boot/grub/grubenv 的檔案內容。

可惜 ubuntu 沒有 grubby,感覺這在管理開機選單上還蠻方便的。

如何建立YUM mirror server

在企業裡,因為不會讓所有伺服器都對外,一般會在內部架設一台 YUM mirror server ,定期去 mirror 外部的 RHEL 套件庫。

坦白說,我不知道用哪個名字比較好,知道的名字有

  • Local mirror
  • YUM repository server
  • YUM mirror server

總之,用途都是一致的。

作法

首先這台要作為 mirror 的伺服器必須要對外,接著在伺服器裡,以 subscription-manager 註冊到 RHEL 服務去。

安裝做 mirror 的套件,這邊選用 apache,也可以用 nginx,路徑跟設定調整一下就可以。

yum install yum-utils createrepo httpd

使用 subscription-manager 查看可以下載的 repository

subscription-manager repos --list

接著對需要做 mirror 的 repository 去做 mirror

mkdir -p /var/www/html/{baseos,appstream}
reposync -p /var/www/html/baseos \
        --download-metadata \
        --repo=rhel-8-for-x86_64-baseos-rpms \
        --delete \
        --downloadcomps

reposync -p /var/www/html/appstream \
        --download-metadata \
        --repo=rhel-8-for-x86_64-appstream-rpms \
        --delete \
        --downloadcomps

這邊可以寫成一個腳本,然後設定 cron,定期去執行 mirror。

一般會再多寫一個 repo 檔案,放在 /var/www/html ,後續到其他主機時,可以直接下載這個 repo 檔案,放到 /etc/yum.repos.d 裡面,就可以直接 yum update 。

[baseos]
name=baseos
baseurl=http://<ip>/rhel-8-for-x86_64-baseos-rpms/
enabled=1
gpgcheck=0

[appstream]
name=appstream
baseurl=http://<ip>/rhel-8-for-x86_64-appstream-rpms/
enabled=1
gpgcheck=0

最後啟用 httpd,就大功告成了。

systemctl enable --now httpd

限制

使用這個方法,基本上一台主機就是對應一個版本,例如 7.9, 8, 8.5, 8.6 這樣,沒辦法有多個版本,若需要有多個版本,建議使用 Foreman 或是 Satellite 會比較方便。

參考資料

tuned-套用適合的系統設定

RHEL 跟 Ubuntu 都有提供這個系統服務,可以啟用這個系統服務來微調系統的設定,以達到預期的效能。

安裝

印象中,RHEL8 預設已經安裝,若沒有,可以用以下指令來安裝

yum install -y tuned

Ubuntu 可以使用以下指令來安裝

sudo apt install tuned

使用

使用相當簡單,啟動系統服務以後就可以了。

systemctl start tuned

之後就可以使用 tuned-adm 來做一些操作,這邊先說明一下什麼是 profile 。

profile 就是一組針對特定用途所提供的系統設定,例如網路最佳化或是資料庫最佳化等等的,不同的用途要調整的設定也不一樣。在啟動 tuned 以後,會啟用預設的 profile ,以 ubuntu 來說,是啟動 balanced 這個 profile。

tuned-adm 有以下子指令可以使用,以進行 profile 的操作:

子指令用途
list列出所有可用的 profile,每個 Linux 發行版所提供的 profile 都不太一樣。
active列出目前啟用的 profile
off關閉所有調整
profile切換到指定的 profile
profile_info顯示指定的 profile 資訊
recommend顯示建議的 profile
verify檢查
auto_profile自動選擇 profile
profile_mode顯示目前是自動選擇 profile 還是手動指定 profile

如果要客製化自己的 profile,也是可以的,這邊可以參考 /usr/lib/tuned 裡的 profile 來做複製跟改動。