星期日, 10月 11, 2026

電影流水帳(2026/01/01~2026/06/30)

原文片名台譯片名IMDBWikipedia
The Equalizer 3私刑教育3https://www.imdb.com/title/tt10252944/https://en.wikipedia.org/wiki/The_Equalizer_3
F1F1https://www.imdb.com/title/tt2355622/https://en.wikipedia.org/wiki/F1_(film)
걸캅스霹靂嬌鋒https://www.imdb.com/title/tt10161238/https://ko.wikipedia.org/wiki/%EA%B1%B8%EC%BA%85%EC%8A%A4
하모니美麗的聲音https://www.imdb.com/title/tt1588356/https://ko.wikipedia.org/wiki/%ED%95%98%EB%AA%A8%EB%8B%88_(%EC%98%81%ED%99%94)
No Hard Feelings珍愛硬起來https://www.imdb.com/title/tt15671028/https://en.wikipedia.org/wiki/No_Hard_Feelings_(2023_film)
The Grand Budapest Hotel歡迎來到布達佩斯大飯店https://www.imdb.com/title/tt2278388/https://en.wikipedia.org/wiki/The_Grand_Budapest_Hotel

The Equalizer 3

私刑教育3,套路跟之前兩集差不多。故事一開始,主角 McCall 在一場槍戰下,受到重傷,輾轉到了義大利一個小鎮。在這裡,一個醫生沒問原因,就為他醫治。在復元的這段期間,他漫步在這個小鎮裡,慢慢愛上了這個小鎮。同時他也觀察到潛藏的危機,是的,這個小鎮被黑幫統治。在後續,他再次為了伸張正義,挺身而出對抗,最終大快人心,解決這些罪犯。

這一集是在義大利 Altomonte 拍攝的,可以看海,也可以爬山到觀景點看風景,取的景真的超漂亮,光看這個風景就值回票價了。

F1

妹妹因為某個原因,好像是買了 Apple TV ,還是手機之類的,所以有了 Apple TV 的免費訂閱,趁過年來看。

主角在多年前因為在賽車出了意外,從 F1 退了下來。他的好友在一些小賽事看到他,覺得可以再找他回 F1 車隊,於是就找了他回來。

他的作風老派且大膽,為車隊帶來新的氣息,慢慢的讓車隊有了得冠的希望。同隊的車手也從原本的態度做了轉變;車隊的技師也為了主角的風格而做了賽車的調整,讓車子更適合他的大膽風格。

最終,車隊取得冠軍。

我覺得這故事非常適合 Brad Pitt 的風格,Brad Pitt 特質跟主角特質就有某種程度的契合,演來絲絲入扣。

걸캅스

韓國電影,故事蠻搞笑的,也挺大快人心的。

被貶到警局便民窗口的趙智慧,還有因邁入婚姻而待在變民窗口的朴美英,在看到一個來報案,卻又不敢說的學生去自殺後,探明了女學生想自殺的原因。女學生想自殺,是因為去夜店遇到爛人下藥,導致被拍了裸照跟不雅影片,這些爛人拿照片跟影片要脅,女學生沒辦法。

智慧跟美英,還有同組的電腦高手,找了警局其他人幫忙,但沒人想理。三人看不下去,決定聯手找出爛人,進行處理。幸好皇天不負苦心人,三人慢慢抽絲剝繭,進而解決了案件。三人最後也因此調整了職位,取得勳章。

하모니

也是韓國電影,台灣這邊把故事翻拍,名稱是「陽光女子合唱團」,故事主軸是一樣的。

一群在女子監獄的女子,各有各的苦衷。女主角在入獄後才生下孩子,孩子受到眾人疼愛,只是孩子必須要滿 18 個月後送走。媽媽不捨,在知道成立合唱團有機會可以外出後,決定跟大家一起組成合唱團,來滿足這條件。

於是他們互相扶持,讓合唱團成立,並能把歌聲帶出去,進而讓女主角有機會可以外出見到孩子。蠻感人的故事,也難怪會拿來翻拍。

No Hard Feelings

珍妮佛勞倫斯在這部片放的很開。

一個年輕女子破產,老家要被拍賣,為了錢,他不惜在酒吧打工,甚至賣春。

後來有個有錢人家,為了讓孩子可以脫離處男,也想讓他多接觸人群,就找了這女子跟兒子約會。只是這女子跟這孩子接觸以後,兩人並沒有發生關係,反而產出了不一樣的火花。當然,這種故事在後面總是會有露餡的時候,孩子意外知道了父母以車子為代價,要這女子跟他發生關係。孩子自然是生氣的,鬧了一陣之後,冰釋前嫌,兩人也突破了各自的困境,繼續往前。

The Grand Budapest Hotel

再次重看。

我覺得很妙的電影,整個故事很荒謬,但看來又非常合理。

一開始是一個作家到飯店居留,遇到了飯店經理,攀談了起來。作家跟經理聊的剖愉快,只是經理有點感慨,作家問,經理說,想知道的話,今晚一起吃個飯。於是經理在吃飯的時候,把故事娓娓說出。

一個難民,進了飯店,做了飯店小僮,跟著飯店經理學習。這飯店經理手腕高明,處處留情,讓許多客人都回頭光顧。有一個大家族的貴婦非常有錢,她不想回家,只說這次回去,可能再也沒辦法回來了。

果然,他回去後,就被謀殺了。飯店經理跟飯店小僮去參加喪禮後,得知了貴婦的遺書,要把名畫留給他。大家族自然是不能接受,就派出殺手追殺律師、飯店經理。軍方單位也收到大家族的控訴,去找了飯店經理。飯店經理不甘束手就擒,就逃走,並帶了小僮去找大家族裡的管家,想知道真相。

最終,飯店經理跟小僮洗刷了冤屈,經理拿到畫,小僮跟心愛的人結婚。只是經理還是死了,小僮後來成了飯店的經理,照料著這家飯店。

然後再接到跟作家吃飯。

星期六, 10月 10, 2026

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


星期五, 10月 09, 2026

用 helm-diff 在升級前先看會改到什麼

我管理 Helm chart 的慣例

每個 chart 我會開一個目錄,維護四個檔案:

  • init-repo.sh:加入 repo 的指令,記錄 repo 是怎麼加的。
  • upgrade.sh:安裝與升級的指令,通常是 helm upgrade --install,並用 --version 固定 chart 版本。
  • values.yaml:先用 helm show values <chart> 取得可調整的設定,再改成自己要的。不用 --set 是因為設定只存在指令列裡,事後很難回想當初設了什麼。
  • README.md:說明文件。

這些都是純文字檔,可以直接納入版本管理。

為什麼需要 helm-diff

最近用 Claude 整理自家的 k8s,發現有些 release 是早期直接下指令裝的,沒有照上面的慣例留下 values.yaml。要把它們補回來,得先確認「我補寫的 values.yaml 和叢集裡實際在跑的設定一致」,Claude 建議我裝 helm-diff 這個 plugin 來比對。

helm-diff 會把「chart 加上你的 values」算出來的 manifest,拿去和叢集裡該 release 目前的 manifest 比較,像 git diff 一樣列出差異。它只做比對,不會改動叢集。

安裝

Helm 4:

curl -sL https://github.com/databus23.gpg | gpg --import
gpg --list-keys --with-fingerprint EA17A2A206AFF8CD
# 指紋應為:C5645EF4 7482257A 1F806D2B EA17A2A2 06AFF8CD
helm plugin install https://github.com/databus23/helm-diff/releases/latest/download/helm-diff-linux-amd64.tgz

Helm 4 安裝 plugin 時預設會驗證來源證明(provenance),確認檔案確實由作者簽署、沒有被竄改,所以要先匯入作者的 GPG 公鑰,並核對指紋。只有直接指向 tarball 的網址才支援驗證。檔名裡的 linux-amd64 請依自己的作業系統與架構,到 releases 頁面挑對應的檔案。

Helm 3(需 3.18 以上):

helm plugin install https://github.com/databus23/helm-diff

使用

升級前先看會改什麼。參數和 upgrade.sh 裡的 helm upgrade 保持一致即可:

helm diff upgrade ngf <chart> -n nginx-gateway --version <版本> -f values.yaml -C 3

-C 3 是只顯示差異前後 3 行,不加的話會印出完整 manifest。Secret 的內容預設會遮蔽。

rollback 前先看退回某個 revision 會改什麼:

helm diff rollback ngf 1 -n nginx-gateway

把手動安裝的 release 補回慣例

  1. 取出當初安裝時給的設定:helm get values ngf -n nginx-gateway -o yaml > values.yaml
  2. 用 helm list -n nginx-gateway 確認目前的 chart 版本,寫進 upgrade.sh。
  3. 執行上面的 helm diff upgrade。沒有任何輸出,就代表這份 values.yaml 和線上設定一致,可以放心納入版本管理。

其他常用的子指令

  • upgrade:升級會改什麼
  • rollback:rollback 會改什麼
  • revision:比較同一個 release 的兩個 revision,例如 helm diff revision ngf 1 2
  • release:比較兩個 release,例如 prod 和 staging

要注意 helm-diff 預設是和 Helm 記錄的上一版 manifest 比。如果有人用 kubectl edit 直接改過資源,要加 --three-way-merge 才會和叢集的實際狀態比對。


星期日, 10月 04, 2026

pre-commit

在使用 Claude 時,Claude 建議我使用這工具搭配 gitleaks,可以在提交代碼前檢查是否有機敏資料會被提交到 git repository。

Claude 都這麼建議了,就試試吧。

在 Ubuntu 24.04 ,gitleaks 跟 pre-commit 都已經可以使用 apt 安裝。

sudo apt install gitleaks pre-commit

接下來就是配置。

目前我是先在一個 repository 測試,所以要先撰寫 .pre-commit-config.yaml

# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v3.2.0
    hooks:
    -   id: trailing-whitespace
    -   id: end-of-file-fixer
    -   id: check-yaml
    -   id: check-added-large-files
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.24.2
    hooks:
      - id: gitleaks

在這裡,我使用 pre-commit 所提供的範例,剛好也是我常會用的

  • 檢查行末空白
  • 末行的EOF
  • yaml 格式
  • 避免加入過大的檔案

再來是 gitleaks 的部份,這邊我有點猶豫的是語法,不確定我已經用 apt 安裝 gitleaks 了,是否還要加上 repo。後來想,先加上去再說,錯了再處理就好。但結果看起來是可以使用。

在編寫完成以後,執行以下指令安裝

pre-commit install

再來是 gitleaks 設定

# gitleaks 設定:沿用內建規則,再補上內建沒有的規則(#14)
[extend]
useDefault = true

# Tailscale key:tskey-<auth|client|api|scim|webhook>-<id>-<secret>
# 內建規則沒有涵蓋,見 work-diary/20261002-tailscale-oauth-secret.md
[[rules]]
id = "tailscale-key"
description = "Tailscale auth key / OAuth client secret / API key"
regex = '''\btskey-[a-z]+-[0-9A-Za-z]{6,}-[0-9A-Za-z]{16,}\b'''
keywords = ["tskey-"]

# Telegram bot token:<bot id>:AA<33 字元>(共約 46 字元)
# 內建的 telegram-bot-api-token 要求 token 前面有「telegr」,Alertmanager 的 `bot_token: <token>` 抓不到(#22)
[[rules]]
id = "telegram-bot-token-bare"
description = "Telegram bot token(不需要 telegram 關鍵字)"
regex = '''\b[0-9]{8,10}:AA[0-9A-Za-z_-]{33}\b'''
keywords = [":aa"]

然後就收工了。

pre-commit install 會把腳本寫到 .git/hooks/pre-commit ,讓 git commit 時,可以執行此腳本來檢查。

星期二, 9月 15, 2026

壓縮pdf

MacOS的預覽程式本身就可以壓縮 pdf ,方法可以參考 Apple 官方網站的文章:https://support.apple.com/zh-tw/guide/preview/prvw1509/mac

開啟 pdf 以後,

  1. 選擇「檔案」>「輸出⋯」。
  2. 在「Quartz 濾鏡」彈出式選單中,選擇「減少檔案大小」。
  3. 儲存

這樣就可以了。

但….如果 pdf 已經壓縮過、包含向量圖、嵌入字型或特殊物件時,那麼反而可能得到反效果,檔案會變大。

我自己是有安裝 homebrew ,就想說是不是安裝什麼工具來作就好。

問了 AI ,他就說可以用 gs ,也就是 ghostscript 就可以解決。

先安裝

brew install ghostscript

接下來就執行 gs

gs \
  -sDEVICE=pdfwrite \
  -dCompatibilityLevel=1.4 \
  -dPDFSETTINGS=/ebook \
  -dNOPAUSE \
  -dQUIET \
  -dBATCH \
  -sOutputFile=output.pdf \
  input.pdf

把 input.pdf 跟 output.pdf 換掉就可以。

壓縮等級主要是調整 -dPDFSETTINGS= 參數,參數內容可以參考

參數品質檔案大小
/prepress最高最大
/printer高較大
/ebook中推薦
/screen低最小

ghostscript 還可以調整 pdf 內的圖片解析度

gs \
 -sDEVICE=pdfwrite \
 -dCompatibilityLevel=1.4 \
 -dColorImageDownsampleType=/Bicubic \
 -dColorImageResolution=150 \
 -dGrayImageDownsampleType=/Bicubic \
 -dGrayImageResolution=150 \
 -dMonoImageDownsampleType=/Subsample \
 -dMonoImageResolution=300 \
 -dNOPAUSE \
 -dQUIET \
 -dBATCH \
 -sOutputFile=output.pdf \
 input.pdf

其他的方法還有 pdfcpu, ocrmypdf ,這些我就沒測試了。