《公開 Repo 密鑰外洩清除實錄》之四:git filter-repo 重寫時空全歷史
現役密鑰已經作廢,這意味著即便歷史依然留在 GitHub 上,也沒有人能夠利用它們入侵系統。Icekimo 終於可以放手一搏,回到他位於 katharine 的本地開發機,執行一場徹底的 Git 歷史改寫。
既然要重寫歷史,我該使用傳統的 `git filter-branch` 還是網路上推薦的 `BFG Repo-Cleaner`?
都不推薦。現在 Git 官方唯一指定的歷史重寫神器是 `git-filter-repo`。它比 `filter-branch` 快上百倍且極度安全,也比 BFG 更靈活。但在開始之前,切記要用 `git bundle` 做一份最完整的備份,這是我們唯一的後路!
戰前準備:建立安全退路
Icekimo 聽從首席軍師建議,首先在專案根目錄執行 bundle 備份。這份檔案會包含專案的全部分支、標籤與完整的歷史紀錄:
$ git bundle create ~/secure-gateway-pre-purge-$(date +%Y%m%d).bundle --all
「有了這份 bundle,哪怕我把 Git 搞砸了,也能一秒還原:距離納美克星爆炸還有五分鐘,我可以的。」
安裝 git-filter-repo 的挫折
Icekimo 嘗試使用 pip 安裝工具,但 Debian 的新機制(PEP 668)跳出了「環境被外包管理(externally-managed-environment)」的錯誤訊息,阻止了他。
# 遇到 PEP 668 阻擋,專家採取了最乾淨的單檔下載方案
$ curl -fsSL https://raw.githubusercontent.com/newren/git-filter-repo/v2.47.0/git-filter-repo \
-o ~/.local/bin/git-filter-repo
$ chmod +x ~/.local/bin/git-filter-repo
確認 ~/.local/bin 已加入系統 $PATH 後,git-filter-repo 順利進入待命狀態。
抹除密鑰歷史的史詩怒火(Epic Fury)指令
接下來,Icekimo 執行了那條將三個敏感路徑從所有 commit 歷史中「逆向抹除」的史詩級怒火指令:
$ git filter-repo --force --invert-paths \
--path .env \
--path crowdsec_config/local_api_credentials.yaml \
--path crowdsec_config/online_api_credentials.yaml
指令在不到一秒內執行完畢,哈沒了內衣。git-filter-repo 迅速重組了所有 commit 節點,並重新計算了所有的 Commit SHA。
雙重驗證:歷史是否乾淨?
跑完了!要怎麼驗證這些檔案真的在過去的歷史中消失了?
首先,用 `git log --all --oneline -- <檔案>` 查詢,輸出必須是空白。其次,用你之前記下的密鑰前綴,在所有 commit 物件中執行全文檢索,確保沒有留下任何痕跡。
Icekimo 快速輸入驗證指令:
# 1. 查詢 .env 的所有歷史提交,結果必須完全空白
$ git log --all --oneline -- .env
# 2. 搜尋整個 commit 歷史中是否殘留洩漏帳密的前綴
$ git rev-list --all | while read c; do git grep -l "<密鑰前綴>" $c; done
兩個指令均沒有任何輸出。「完美,真的一片平坦了!就說了不可以做核彈!不可以做核彈!不可以做核彈!」
重建遠端連線與 Force Push
由於 git-filter-repo 為了防呆,在重寫完歷史後會自動移除所有的遠端倉庫連結(remotes),以防開發者不小心執行 git pull 把舊歷史拉回來。
Icekimo 重新手動添加 GitHub 遠端連結,並將重寫後的新時空歷史強制推送到 GitHub:
# 重新接回 GitHub 遠端倉庫
$ git remote add origin https://github.com/August-Icekimo/secure-gateway.git
# 強制推送新歷史
$ git push --force origin main
本地端與 GitHub 雲端上的歷史已經被徹底重置。然而,Icekimo 與 Claude Fable 還要面對最後一個難關:如何安全地在 NAS 生產伺服器上對齊這套新歷史,並且清除可能藏匿在硬碟角落的 Git 殘留物?難道是美軍陸戰隊上場的時刻到來了?