Vault 能正常提供 secret,不代表事後能回答「誰做了哪個 API 操作」。Container 的 server logs 主要記錄 process 狀態與錯誤;audit device 才負責 API request/response 的稽核資料。兩者用途不同,也不會因為 docker logs vault 有輸出就自動同時存在。
我的 Vault 在 Kubernetes 外以 Docker Compose 執行。Repository 已記錄把 host 的 /var/log/vault 掛到 container 的 /vault/logs,並以 logrotate 管理 audit.jsonl。這篇集中在保存與輪替,不重複 Vault 與 External Secrets裡的 secret delivery 流程。
Restart、重建與持久化不是同一件事
對一般 container writable layer 而言,停止再啟動同一個 container 不會自動清掉檔案;移除並重建 container 才會失去那一層資料。Volume 和 bind mount 則可以跨 container lifecycle 保存資料,tmpfs 是另一種不持久化的情況。Docker storage 文件說明了這些差異。
同一個 container restart 後仍找得到 log,並不足以證明保存方式可靠。要檢查的是 audit device 寫到哪裡、該位置是否有持久化 mount,以及備份是否涵蓋它。
Vault file audit device
→ /vault/logs/audit.jsonl container 視角
→ /var/log/vault/audit.jsonl host 視角,同一份檔案
→ logrotate rename + create
→ HUP 通知 Vault reopen
Host bind mount 適合這個配置,是因為 Vault 和 host 上的 logrotate 都要操作該目錄。它不能抵擋 host 磁碟故障,也不是異地備份。
先準備目錄與真正的寫入身分
UID/GID 必須對應執行 Vault process 的身分,不能只看 host 上是否有名為 vault 的帳號。可以先用 docker top vault -eo pid,uid,gid,args 檢查 process;rootless Docker 或 user namespace remapping 還需考慮 host ID mapping。
以下假設已確認 Vault process 在 host 上對應的 UID 為 100、GID 為 1000。這不是所有 Vault images 的保證值:
sudo install -d -m 0700 -o 100 -g 1000 /var/log/vault
在既有 Compose service 加入目錄 mount,保留原本的 config、data volumes 與其他安全設定:
services:
vault:
volumes:
- ./config:/vault/config:ro
- ./data:/vault/data:rw
- /var/log/vault:/vault/logs:rw
要 mount 整個目錄,而非單一 audit.jsonl:輪替會重新命名舊檔、建立新檔,directory mount 才能讓 container 看到新的檔案名稱與 inode。
套用 volume 變更通常需要 recreate container。若舊 audit file 還在 container writable layer,先規劃保存與切換流程,不能直接覆蓋 mount 或刪掉舊 container。使用 Shamir unseal 的環境也要預留重新 unseal 的維護程序;不要為了加 log mount 就在沒有恢復手段時 restart Vault。
掛上目錄不等於啟用 audit device
在 Vault 已 initialized、unsealed,且使用具備 audit 管理權限的身分時,先查看現有 devices:
vault audit list -detailed
若還沒有目標 device,才建立一個指向 container 內路徑的 file audit device:
vault audit enable -path=host-file file \
file_path=/vault/logs/audit.jsonl \
mode=0600
host-file 是 device 名稱,file 是 device type;file_path 要用 Vault process 看得到的路徑。它會寫入逐筆 JSON records,可用逐行工具處理。已有 device 時不必為了輪替先 disable 再 enable;這會中斷紀錄,也會改變該 device 的 audit hashing key。Vault audit logging 文件區分了 devices、server logs 與 hashing 行為。
先發出一個確定會被稽核的非破壞性請求,再看新增記錄。具備 self-lookup 權限的登入 session 可以使用:
vault token lookup >/dev/null
sudo tail -n 4 /var/log/vault/audit.jsonl | \
jq -c '{time, type, request_id: .request.id, operation: .request.operation}'
只抽出時間、類型、request ID 與 operation,就足以檢查資料有沒有進來,不需要把完整 token lookup response 或 audit body 印出來。不要只用 /sys/health 作測試;它屬於不會被稽核的 endpoint,健康回應不代表 file device 有寫入。
用 rename、create 與 HUP 完成輪替
以下是 /etc/logrotate.d/vault-audit 的範例,沿用每日輪替與 90 份保存設定,並加入 delaycompress,讓剛輪替的檔案到下一輪才壓縮:
/var/log/vault/audit.jsonl {
daily
rotate 90
maxage 90
missingok
notifempty
compress
delaycompress
dateext
create 0600 100 1000
sharedscripts
postrotate
/usr/bin/docker kill --signal=HUP vault
endscript
}
安裝前先核對 Docker binary 路徑、container 名稱、UID/GID,並讓設定檔由 root 擁有、一般使用者不可寫入。此例假設 host 的系統 logrotate 有權操作該 Docker daemon;不適合原封不動套到另一個使用者的 rootless daemon。
Vault 開著的是 file descriptor,而不是持續重新查詢檔名。單純把 audit.jsonl 改名,Vault 仍可能繼續寫到舊 inode。HUP 會讓 file audit device 關閉並重新開啟底層檔案;這是 Vault file audit device支援的輪替方式。
這裡的 docker kill --signal=HUP 是送訊號,不是使用預設的 SIGKILL。要確認 Vault 是接收訊號的主程序,或 entrypoint 能正確轉送訊號;否則命令成功不代表 Vault 有 reopen。Docker kill 文件也提醒 shell-form entrypoint 的訊號問題。
Vault 既然支援 reopen,就不必依賴 copytruncate。後者在 copy 與 truncate 之間可能遺失寫入,並不適合拿來掩蓋未送出 HUP 的問題。delaycompress 只為 reopen 留下緩衝,不是 HUP 失敗時的替代保證。
90 天是保存策略,不是完整性保證
這幾個欄位需要分開理解:
daily是輪替條件,仍需要 timer 或 cron 定期執行 logrotate。rotate 90是保留輪替檔數量,不是逐筆計算 event age。maxage 90清除過舊輪替檔,但只在該 log 準備輪替時檢查。notifempty和排程中斷都可能改變實際輪替時間。
這不能宣稱保證留存完整 90 天,也不能當成精確的到期刪除機制。規則語意與 copytruncate 的限制可查 logrotate manual。如果有法規或合約要求,必須另行設計保留、刪除、存取控制與不可竄改的保存機制。
每日 rotation 也沒有容量上限。大量 API 流量可能在下一次排程前填滿磁碟;加入 size-based rotation 時,還要重新評估檔案數量、命名與保留天數的關係,而不是只多加一個 maxsize 就結束。
先 dry run,再驗證新檔確實收到請求
在 host 上先做不修改檔案的檢查:
sudo logrotate --debug /etc/logrotate.d/vault-audit
systemctl status logrotate.timer
sudo ls -ln /var/log/vault
df -h /var/log/vault
使用 cron 的主機改查實際 cron 排程。Debug mode 不會真的 rotate,也不會執行 postrotate,因此無法證明 HUP 或寫入權限可用。
在測試環境或維護時段,可以執行一次實際輪替:
sudo logrotate --verbose --force /etc/logrotate.d/vault-audit
這會修改檔案、執行 HUP,也可能依 retention 規則刪除舊 archives;執行前先確認保存需求。不要在同一天反覆 force 而忽略 dateext 檔名可能衝突的錯誤。
輪替前後比較 ls -li 的 inode,確認新的 active file 權限與 owner 正確,再執行一次 vault token lookup >/dev/null。新的 request/response 應出現在新的 audit.jsonl,而不是只有舊 archive 繼續變大。最後檢查 Vault operational logs 與 logrotate 執行結果,確認沒有 permission denied 或 reopen error。
Audit 儲存也會影響 Vault 可用性
已啟用 audit devices 的 Vault 若無法成功寫入任何一個 device,對應的 API request 就可能被拒絕。只有一個 file device 時,磁碟滿或權限錯誤就會把稽核問題變成服務問題。HashiCorp audit best practices建議使用至少兩個不同類型的 devices,並將其中一條路徑送到遠端系統;兩份都放在同一顆磁碟不算獨立故障範圍。
預設 HMAC 也不是「整份 log 已匿名化」。Metadata、部分型別與依設定保留的欄位仍可能帶有敏感資訊。Audit archives 應有存取限制、受保護的備份,以及 rotation failure、disk usage、audit write failure 的監控;不要為了方便搜尋而開啟 raw logging。
驗收的最後一項不是目錄裡多了幾個壓縮檔,而是輪替後仍有新請求持續寫入、舊記錄仍可查閱,而且這條保存路徑不會悄悄拖垮 Vault。這三件事一起確認,才算把 audit log 從「有開啟」做到「能依賴」。