git-secret:在 Git 存儲庫中加密和存儲密鑰(下)
- 2022 年 10 月 21 日
- 筆記
在之前的文章中(點擊此處查看上一篇文章),我們了解了如何識別包含密鑰的文件,將密鑰添加到 .gitignore ,通過 git-secret 進行加密,以及將加密文件提交到存儲庫。在本篇文章中,將帶你了解如何在 Docker 容器中設置 git-secret 和 gpg,通過 Makefile recipe 為不同的場景創建工作流。
Makefile Adjustment
將 git-secret 和 gpg 指令添加到 Makefile 中 .make/01-00-application-setup.mk:
# File: .make/01-00-application-setup.mk
#...
# gpg
DEFAULT_SECRET_GPG_KEY?=secret.gpg
DEFAULT_PUBLIC_GPG_KEYS?=.dev/gpg-keys/*
.PHONY: gpg
gpg: ## Run gpg commands. Specify the command e.g. via ARGS="--list-keys"
$(EXECUTE_IN_APPLICATION_CONTAINER) gpg $(ARGS)
.PHONY: gpg-export-public-key
gpg-export-public-key: ## Export a gpg public key e.g. via EMAIL="[email protected]" PATH=".dev/gpg-keys/john-public.gpg"
@$(if $(PATH),,$(error PATH is undefined))
@$(if $(EMAIL),,$(error EMAIL is undefined))
"$(MAKE)" -s gpg ARGS="gpg --armor --export $(EMAIL) > $(PATH)"
.PHONY: gpg-export-private-key
gpg-export-private-key: ## Export a gpg private key e.g. via EMAIL="[email protected]" PATH="secret.gpg"
@$(if $(PATH),,$(error PATH is undefined))
@$(if $(EMAIL),,$(error EMAIL is undefined))
"$(MAKE)" -s gpg ARGS="--output $(PATH) --armor --export-secret-key $(EMAIL)"
.PHONY: gpg-import
gpg-import: ## Import a gpg key file e.g. via GPG_KEY_FILES="/path/to/file /path/to/file2"
@$(if $(GPG_KEY_FILES),,$(error GPG_KEY_FILES is undefined))
"$(MAKE)" -s gpg ARGS="--import --batch --yes --pinentry-mode loopback $(GPG_KEY_FILES)"
.PHONY: gpg-import-default-secret-key
gpg-import-default-secret-key: ## Import the default secret key
"$(MAKE)" -s gpg-import GPG_KEY_FILES="$(DEFAULT_SECRET_GPG_KEY)"
.PHONY: gpg-import-default-public-keys
gpg-import-default-public-keys: ## Import the default public keys
"$(MAKE)" -s gpg-import GPG_KEY_FILES="$(DEFAULT_PUBLIC_GPG_KEYS)"
.PHONY: gpg-init
gpg-init: gpg-import-default-secret-key gpg-import-default-public-keys ## Initialize gpg in the container, i.e. import all public and private keys
# git-secret
.PHONY: git-secret
git-secret: ## Run git-secret commands. Specify the command e.g. via ARGS="hide"
$(EXECUTE_IN_APPLICATION_CONTAINER) git-secret $(ARGS)
.PHONY: secret-init
secret-init: ## Initialize git-secret in the repository via `git-secret init`
"$(MAKE)" -s git-secret ARGS="init"
.PHONY: secret-init-gpg-socket-config
secret-init-gpg-socket-config: ## Initialize the config files to change the gpg socket locations
echo "%Assuan%" > .gitsecret/keys/S.gpg-agent
echo "socket=/tmp/S.gpg-agent" >> .gitsecret/keys/S.gpg-agent
echo "%Assuan%" > .gitsecret/keys/S.gpg-agent.ssh
echo "socket=/tmp/S.gpg-agent.ssh" >> .gitsecret/keys/S.gpg-agent.ssh
echo "extra-socket /tmp/S.gpg-agent.extra" > .gitsecret/keys/gpg-agent.conf
echo "browser-socket /tmp/S.gpg-agent.browser" >> .gitsecret/keys/gpg-agent.conf
.PHONY: secret-encrypt
secret-encrypt: ## Decrypt secret files via `git-secret hide`
"$(MAKE)" -s git-secret ARGS="hide"
.PHONY: secret-decrypt
secret-decrypt: ## Decrypt secret files via `git-secret reveal -f`
"$(MAKE)" -s git-secret ARGS="reveal -f"
.PHONY: secret-decrypt-with-password
secret-decrypt-with-password: ## Decrypt secret files using a password for gpg via `git-secret reveal -f -p $(GPG_PASSWORD)`
@$(if $(GPG_PASSWORD),,$(error GPG_PASSWORD is undefined))
"$(MAKE)" -s git-secret ARGS="reveal -f -p $(GPG_PASSWORD)"
.PHONY: secret-add
secret-add: ## Add a file to git secret via `git-secret add $FILE`
@$(if $(FILE),,$(error FILE is undefined))
"$(MAKE)" -s git-secret ARGS="add $(FILE)"
.PHONY: secret-cat
secret-cat: ## Show the contents of file to git secret via `git-secret cat $FILE`
@$(if $(FILE),,$(error FILE is undefined))
"$(MAKE)" -s git-secret ARGS="cat $(FILE)"
.PHONY: secret-list
secret-list: ## List all files added to git secret `git-secret list`
"$(MAKE)" -s git-secret ARGS="list"
.PHONY: secret-remove
secret-remove: ## Remove a file from git secret via `git-secret remove $FILE`
@$(if $(FILE),,$(error FILE is undefined))
"$(MAKE)" -s git-secret ARGS="remove $(FILE)"
.PHONY: secret-add-user
secret-add-user: ## Remove a user from git secret via `git-secret tell $EMAIL`
@$(if $(EMAIL),,$(error EMAIL is undefined))
"$(MAKE)" -s git-secret ARGS="tell $(EMAIL)"
.PHONY: secret-show-users
secret-show-users: ## Show all users that have access to git secret via `git-secret whoknows`
"$(MAKE)" -s git-secret ARGS="whoknows"
.PHONY: secret-remove-user
secret-remove-user: ## Remove a user from git secret via `git-secret killperson $EMAIL`
@$(if $(EMAIL),,$(error EMAIL is undefined))
"$(MAKE)" -s git-secret ARGS="killperson $(EMAIL)"
.PHONY: secret-diff
secret-diff: ## Show the diff between the content of encrypted and decrypted files via `git-secret changes`
"$(MAKE)" -s git-secret ARGS="changes"
工作流程
使用 git-secret 非常簡單:
- 初始化
git-secret - 添加所有用戶。
- 添加所有機密文件並確保這些文件通過
.gitignore被忽略。 - 加密文件。
- 如果團隊其他成員對文件進行更改,則需要解密文件→更新文件→再次提交加密文件
- 如果對解密的文件進行更改,修改完需要再次重新進行加密。
下面的「流程挑戰」部分展示了一些在可能遇到的問題,「場景」部分將會展示一些常見場景的具體示例。
流程中的挑戰
從流程的角度,一起來看看在過程中可能遇到的一些困難和挑戰,以及如何處理解決。
更新機密
更新機密時,請確保先解密文件,從而避免使用可能仍存在本地的舊文件。可以通過檢查最新的 main 分支並運行 git secret reveal,來獲得最新版本的機密文件。也可以使用 post-merge Git hook 自動執行此操作,不過要注意覆蓋本地機密文件的風險哦。
程式碼審查和合併衝突
由於無法對加密文件進行很好的區分,因此當涉及機密時程式碼審查變得更加困難。這是可以嘗試使用 GitLab 進行審查,首先檢查 .gitsecret/paths/mapping.cfg 文件的差異,在 UI 中查看哪些文件已更改。
此外,可以根據以下步驟來查看:
-
檢查
main分支。 -
通過
git secret reveal -f解密文件 -
查看
feature-branch. -
運行
git secret changes來查看main的解密文件和feature-branch中加密文件之間的差異。
當多個團隊成員需要同時修改不同分支上的機密文件時,情況會更加複雜一些,因為Git 無法智慧處理增量更新。
本地 git-secret 和 gpg 設置
當團隊的所有人員將 git-secret 安裝在本地,並且使用他們自己的 gpg 密鑰,這也意味著團隊的成本會隨之增加,原因如下:
- 新加入開發團隊的人員需要:
- 本地安裝
git-secret(*) - 在本地安裝和設置
gpg(*) - 創建
gpg密鑰對
- 本地安裝
- 必須由所有其他團隊成員 (*) 添加公鑰。
- 必須通過添加密鑰的用戶
git secret tell。 - 機密需要重新加密。
對於離開團隊的人員:
- 所有其他團隊成員(*) 都需要刪除公鑰。
- 通過
git secret killperson刪除密鑰的用戶。 - 機密需要重新加密。
另外,需要確保 git-secret 和gpg 版本保持最新,避免遇到任何兼容性問題。作為替代方案,也可以通過 Docker 處理,而上述步驟中標註(*) 則可以省去,也就是不需要設置本地的 git-secret 和gpg 。
為了更加便捷,將存儲庫中每個開發人員的公共 gpg 密鑰放在 .dev/gpg-keys/,而私鑰命名為 secret.gpg 並放在程式碼庫的根目錄中。
在此設置中,secret.gpg 還必須被添加到 .gitignore 文件中。
# File: .gitignore
#...
vendor/
secret.gpg
然後可以使用 make 目標簡化導入:
# gpg
DEFAULT_SECRET_GPG_KEY?=secret.gpg
DEFAULT_PUBLIC_GPG_KEYS?=.dev/gpg-keys/*
.PHONY: gpg
gpg: ## Run gpg commands. Specify the command e.g. via ARGS="--list-keys"
$(EXECUTE_IN_APPLICATION_CONTAINER) gpg $(ARGS)
.PHONY: gpg-import
gpg-import: ## Import a gpg key file e.g. via GPG_KEY_FILES="/path/to/file /path/to/file2"
@$(if $(GPG_KEY_FILES),,$(error GPG_KEY_FILES is undefined))
"$(MAKE)" -s gpg ARGS="--import --batch --yes --pinentry-mode loopback $(GPG_KEY_FILES)"
.PHONY: gpg-import-default-secret-key
gpg-import-default-secret-key: ## Import the default secret key
"$(MAKE)" -s gpg-import GPG_KEY_FILES="$(DEFAULT_SECRET_GPG_KEY)"
.PHONY: gpg-import-default-public-keys
gpg-import-default-public-keys: ## Import the default public keys
"$(MAKE)" -s gpg-import GPG_KEY_FILES="$(DEFAULT_PUBLIC_GPG_KEYS)"
.PHONY: gpg-init
gpg-init: gpg-import-default-secret-key gpg-import-default-public-keys ## Initialize gpg in the container, i.e. import all public and private keys
上述操作需要在容器啟動後運行一次。
場景
先假設以下這些條件:
-
已檢查過 Git 存儲庫。
git checkout part-6-git-secret-encrypt-repository-docker -
沒有正在運行的 Docker 容器。
make docker-down -
已刪除現有
git-secret文件夾、中的密鑰.dev/gpg-keys、secret.gpg密鑰和passwords.*文件。rm -rf .gitsecret/ .dev/gpg-keys/* secret.gpg passwords.*
gpg 密鑰的初始設置
不幸的是,我沒有找到通過make和docker創建和導出gpg密鑰的方法。你需要互動式地運行這些命令,或者傳遞一個帶換行的字元串給它。這兩件事在make和docker中都複雜得可怕。因此,你需要登錄到應用程式的容器中,並在那裡直接運行這些命令。這不是很簡單,但無論如何,這隻需要在一個新的開發人員入職時做一次。
密鑰導出到 secret.gpg,公鑰導出到 gp.dev/gpg-keys/alice-public.gpg。
# start the docker setup
make docker-up
# log into the container ('winpty' is only required on Windows)
winpty docker exec -ti dofroscra_local-application-1 bash
# export key pair
name="Alice Doe"
email="[email protected]"
gpg --batch --gen-key < .dev/gpg-keys/alice-public.gpg
$ make docker-up
ENV=local TAG=latest DOCKER_REGISTRY=docker.io DOCKER_NAMESPACE=dofroscra APP_USER_NAME=application APP_GROUP_NAME=application docker compose -p dofroscra_local --env-file ./.docker/.env -f ./.docker/docker-compose/docker-compose.yml -f ./.docker/docker-compose/docker-compose.local.yml up -d
Container dofroscra_local-application-1 Created
...
Container dofroscra_local-application-1 Started
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
...
95f740607586 dofroscra/application-local:latest "/usr/sbin/sshd -D" 21 minutes ago Up 21 minutes 0.0.0.0:2222->22/tcp dofroscra_local-application-1
$ winpty docker exec -ti dofroscra_local-application-1 bash
root:/var/www/app# name="Alice Doe"
root:/var/www/app# email="[email protected]"
gpg --batch --gen-key < Key-Type: 1
> Key-Length: 2048
> Subkey-Type: 1
> Subkey-Length: 2048
> Name-Real: $name
> Name-Email: $email
> Expire-Date: 0
> %no-protection
> EOF
gpg: directory '/root/.gnupg' created
gpg: keybox '/root/.gnupg/pubring.kbx' created
gpg: /root/.gnupg/trustdb.gpg: trustdb created
gpg: key BBBE654440E720C1 marked as ultimately trusted
gpg: directory '/root/.gnupg/openpgp-revocs.d' created
gpg: revocation certificate stored as '/root/.gnupg/openpgp-revocs.d/225C736E0E70AC222C072B70BBBE654440E720C1.rev'
root:/var/www/app# gpg --output secret.gpg --armor --export-secret-key $email
root:/var/www/app# head secret.gpg
-----BEGIN PGP PRIVATE KEY BLOCK-----
lQOYBGJD+bwBCADBGKySV5PINc5MmQB3PNvCG7Oa1VMBO8XJdivIOSw7ykv55PRP
3g3R+ERd1Ss5gd5KAxLc1tt6PHGSPTypUJjCng2plwD8Jy5A/cC6o2x8yubOslLa
x1EC9fpcxUYUNXZavtEr+ylOaTaRz6qwSabsAgkg2NZ0ey/QKmFOZvhL8NlK9lTI
GgZPTiqPCsr7hiNg0WRbT5h8nTmfpl/DdTgwfPsDn5Hn0TEMa79WsrPnnq16jsq0
Uusuw3tOmdSdYnT8j7m1cpgcSj0hRF1eh4GVE0o62GqeLTWW9mfpcuv7n6mWaCB8
DCH6H238gwUriq/aboegcuBktlvSY21q/MIXABEBAAEAB/wK/M2buX+vavRgDRgR
hjUrsJTXO3VGLYcIetYXRhLmHLxBriKtcBa8OxLKKL5AFEuNourOBdcmTPiEwuxH
5s39IQOTrK6B1UmUqXvFLasXghorv8o8KGRL4ABM4Bgn6o+KBAVLVIwvVIhQ4rlf
root:/var/www/app# gpg --armor --export $email > .dev/gpg-keys/alice-public.gpg
root:/var/www/app# head .dev/gpg-keys/alice-public.gpg
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQENBGJD+bwBCADBGKySV5PINc5MmQB3PNvCG7Oa1VMBO8XJdivIOSw7ykv55PRP
3g3R+ERd1Ss5gd5KAxLc1tt6PHGSPTypUJjCng2plwD8Jy5A/cC6o2x8yubOslLa
x1EC9fpcxUYUNXZavtEr+ylOaTaRz6qwSabsAgkg2NZ0ey/QKmFOZvhL8NlK9lTI
GgZPTiqPCsr7hiNg0WRbT5h8nTmfpl/DdTgwfPsDn5Hn0TEMa79WsrPnnq16jsq0
Uusuw3tOmdSdYnT8j7m1cpgcSj0hRF1eh4GVE0o62GqeLTWW9mfpcuv7n6mWaCB8
DCH6H238gwUriq/aboegcuBktlvSY21q/MIXABEBAAG0HUFsaWNlIERvZSA8YWxp
Y2VAZXhhbXBsZS5jb20+iQFOBBMBCgA4FiEEIlxzbg5wrCIsBytwu75lREDnIMEF
AmJD+bwCGy8FCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQu75lREDnIMEN4Af+
至此 [email protected] 就有了一個新機密和私鑰,將其導出到 secret.gpg。.dev/gpg-keys/alice-public.gpg。剩下的命令現在可以直接在application 容器外的主機上運行。
git-secret 的初始設置
現在來將 git-secret 引入一個新的程式碼庫,然後運行以下命令。
初始化 git-secret :
make secret-init$ make secret-init
"C:/Program Files/Git/mingw64/bin/make" -s git-secret ARGS="init";
git-secret: init created: '/var/www/app/.gitsecret/'
應用 gpg 對共享目錄進行修復:
$ make secret-init-gpg-socket-config$ make secret-init-gpg-socket-config
echo "%Assuan%" > .gitsecret/keys/S.gpg-agent
echo "socket=/tmp/S.gpg-agent" >> .gitsecret/keys/S.gpg-agent
echo "%Assuan%" > .gitsecret/keys/S.gpg-agent.ssh
echo "socket=/tmp/S.gpg-agent.ssh" >> .gitsecret/keys/S.gpg-agent.ssh
echo "extra-socket /tmp/S.gpg-agent.extra" > .gitsecret/keys/gpg-agent.conf
echo "browser-socket /tmp/S.gpg-agent.browser" >> .gitsecret/keys/gpg-agent.conf
容器啟動後初始化 gpg
重啟容器後,需要初始化 gpg 也就是導入公鑰 .dev/gpg-keys/* 和導入私鑰 Secret.gpg,不然就無法對文件進行加密和解密。
make gpg-init$ make gpg-init
"C:/Program Files/Git/mingw64/bin/make" -s gpg-import GPG_KEY_FILES="secret.gpg"
gpg: directory '/home/application/.gnupg' created
gpg: keybox '/home/application/.gnupg/pubring.kbx' created
gpg: /home/application/.gnupg/trustdb.gpg: trustdb created
gpg: key BBBE654440E720C1: public key "Alice Doe <[email protected]>" imported
gpg: key BBBE654440E720C1: secret key imported
gpg: Total number processed: 1
gpg: imported: 1
gpg: secret keys read: 1
gpg: secret keys imported: 1
"C:/Program Files/Git/mingw64/bin/make" -s gpg-import GPG_KEY_FILES=".dev/gpg-keys/*"
gpg: key BBBE654440E720C1: "Alice Doe <[email protected]>" not changed
gpg: Total number processed: 1
gpg: unchanged: 1
添加新成員
接下來一起看看如何將新成員加入到 git-secret
make secret-add-user EMAIL="[email protected]"$ make secret-add-user EMAIL="[email protected]"
"C:/Program Files/Git/mingw64/bin/make" -s git-secret ARGS="tell [email protected]"
git-secret: done. [email protected] added as user(s) who know the secret.
驗證是否通過:
make secret-show-users$ make secret-show-users
"C:/Program Files/Git/mingw64/bin/make" -s git-secret ARGS="whoknows"
[email protected]
添加和加密文件
來添加一個新的加密文件 secret_password.txt,創建以下文件:
echo "my_new_secret_password" > secret_password.txt
將其添加到 .gitignore
echo "secret_password.txt" >> .gitignore
將其添加到 git-secret
make secret-add FILE="secret_password.txt"$ make secret-add FILE="secret_password.txt"
"C:/Program Files/Git/mingw64/bin/make" -s git-secret ARGS="add secret_password.txt"
git-secret: 1 item(s) added.
加密所有文件:
make secret-encrypt$ make secret-encrypt
"C:/Program Files/Git/mingw64/bin/make" -s git-secret ARGS="hide"
git-secret: done. 1 of 1 files are hidden.$ ls secret_password.txt.secret
secret_password.txt.secret
解密文件
首先移除 secret_password.txt文件,請運行:
rm secret_password.txt$ rm secret_password.txt$ ls secret_password.txt
ls: cannot access 'secret_password.txt': No such file or directory
然後進行解密:
make secret-decrypt$ make secret-decrypt
"C:/Program Files/Git/mingw64/bin/make" -s git-secret ARGS="reveal -f"
git-secret: done. 1 of 1 files are revealed.$ cat secret_password.txt
my_new_secret_password
注意:如果 gpg 密鑰受密碼保護(假設密碼是 123456),請運行以下命令:
make secret-decrypt-with-password GPG_PASSWORD=123456
此外,還可以將 GPG_PASSWORD 變數加入.make/.env 文件作為本地默認值,這樣就不用每次都指定該值,然後可以簡單地運行以下命令而不傳遞 GPG_PASSWORD:
make secret-decrypt-with-password
刪除文件
可以通過以下方式解密文件:移除之前添加的 secret-password.txt
make secret-remove FILE="secret_password.txt"$ make secret-remove FILE="secret_password.txt"
"C:/Program Files/Git/mingw64/bin/make" -s git-secret ARGS="remove secret_password.txt"
git-secret: removed from index.
git-secret: ensure that files: [secret_password.txt] are now not ignored.
注意:這裡既不會自動刪除 secret_password.txt 文件,也不會自動刪除 secret_password.txt.secret 文件
$ ls -l | grep secret_password.txt
-rw-r--r-- 1 Pascal 197121 3 月 31 日 19 日 14:03 secret_password.txt
-rw-r--r-- 1 Pascal 197121 358 3 月 31 日 14:02 secret_password.txt.secret
即使加密的 secret_password.txt 文件仍然存在,也不會被解密:
$ make secret-decrypt
"C:/Program Files/Git/mingw64/bin/make" -s git-secret ARGS="reveal -f"
git-secret: done. 0 of 0 files are revealed.
移除團隊成員
移除團隊成員需要通過以下步驟:
make secret-remove-user EMAIL="[email protected]"$ make secret-remove-user EMAIL="[email protected]"
"C:/Program Files/Git/mingw64/bin/make" -s git-secret ARGS="killperson [email protected]"
git-secret: removed keys.
git-secret: now [[email protected]] do not have an access to the repository.
git-secret: make sure to hide the existing secrets again.
如果團隊中還有其他成員留下,需要確保再次加密機密文件:
make secret-encrypt
如果該組已移除全部成員,git-secret 就會報錯:
$ make secret-decrypt
"C:/Program Files/Git/mingw64/bin/make" -s git-secret ARGS="reveal -f"
git-secret: abort: no public keys for users found. run 'git secret tell email@address'.
make[1]: *** [.make/01-00-application-setup.mk:57: git-secret] Error 1
make: *** [.make/01-00-application-setup.mk:69: secret-decrypt] Error 2
恭喜你~現在你可以加密和解密機密文件,並存儲在 Git 存儲庫中啦!

