git-secret:在 Git 存儲庫中加密和存儲密鑰(上)

  • 2022 年 9 月 29 日
  • 筆記

當涉及處理機密資訊(如密碼、令牌、密鑰文件等)等,以下問題值得考慮:

  • 安全性十分重要,但高安全性往往伴隨著高度的不便。

  • 在團隊中,共享某些密鑰有時無法避免(因此現在我們需要考慮在多人之間分發和更新密鑰的安全方法)。

  • 具體的密鑰通常取決於環境。

目前市面上已經存在許多較為成熟的密鑰管理產品,比如 HashiCorp Vault,AWS Secrets Manager 以及 GCP Secret Manager。由於這些產品需要集成和維護等服務,因此在項目中引入會增加一定成本和開銷。閱讀本文,將帶你了解如何在 Docker 容器中設置 git-secretgpg

本文將對以下幾點展開講解:

  • 識別包含密鑰的文件

  • 確保將密鑰添加到.gitignore

  • 通過 git-secret 進行加密

  • 將加密文件提交到存儲庫

在最後我們將能夠調用:

make secret-decrypt

這將會披露程式碼庫中的密鑰,在必要時對其進行修改,然後運行:

make secret-encrypt

需要再次加密密鑰,以便提交(並推送到遠程存儲庫),要查看實際效果請運行以下命令:

# checkout the branch
git checkout part-6-git-secret-encrypt-repository-docker

# build and start the docker setup
make make-init
make docker-build
make docker-up

# "create" the secret key - the file "secret.gpg.example" would usually NOT live in the repo!
cp secret.gpg.example secret.gpg

# initialize gpg
make gpg-init

# ensure that the decrypted secret file does not exist
ls passwords.txt

# decrypt the secret file
make secret-decrypt

# show the content of the secret file
cat passwords.txt

Tooling

我們在 PHP base 鏡像中設置 gpggit-secret 以便這些工具在所有其他容器中都可用。以下所有命令都在 application 容器中執行。

請注意,git-secret 在主機系統和 docker 容器之間共享的文件夾中使用時需要注意。將在下面稱為 「git-secret 目錄和 gpg-agent socket」的部分中更詳細地解釋這一點。

gpg

gpg 是The GNU Privacy Guard的縮寫,是 OpenPGP 標準的開源實踐。簡而言之,GNU允許我們創建一個個人密鑰文件對(類似於 SSH 密鑰),其中包含一個私有密鑰和一個可以與您想要解密其消息的其他方共享的公共密鑰。

gpg 安裝

關於安裝,我們可以簡單地運行 apk add gnupg 並相應更新 .docker/images/php/base/Dockerfile

# File: .docker/images/php/base/DockerfileRUN apk add --update --no-cache \
        bash \
        gnupg \
        make \
#...

創建 gpg 密鑰對

我們需要通過以下方式創建 gpg 密鑰對(Key Pair):

name="Pascal Landau"
email="[email protected]"
gpg --batch --gen-key <<EOF
Key-Type: 1
Key-Length: 2048
Subkey-Type: 1
Subkey-Length: 2048
Name-Real: $name
Name-Email: $email
Expire-Date: 0
%no-protection
EOF

%no-protection 創建一個沒有密碼的key。

輸出:

$ name="Pascal Landau"
$ email="[email protected]"
$ gpg --batch --gen-key <<EOF
> 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: key E1E734E00B611C26 marked as ultimately trusted
gpg: revocation certificate stored as '/root/.gnupg/opengpg-revocs.d/74082D81525723F5BF5B2099E1E734E00B611C26.rev'

也可以在沒有 --batch 標誌的情況下以交互方式引導整個過程運行gpg --gen-key,然後導出、列出和導入私有 gpg Key,可以通過以下方式導出:

email="[email protected]"
path="secret.gpg"
gpg --output "$path" --armor --export-secret-key "$email"

記住不能共享此密鑰。

-----BEGIN PGP PRIVATE KEY BLOCK-----

lQOYBF7VVBwBCADo9un+SySu/InHSkPDpFVKuZXg/s4BbZmqFtYjvUUSoRAeSejv
G21nwttQGut+F+GdpDJL6W4pmLS31Kxpt6LCAxhID+PRYiJQ4k3inJfeUx7Ws339
XDPO3Rys+CmnZchcEgnbOfQlEqo51DMj6mRF2Ra/6svh7lqhrixGx1BaKn6VlHkC
...
ncIcHxNZt7eK644nWDn7j52HsRi+wcWsZ9mjkUgZLtyMPJNB5qlKQ18QgVdEAhuZ
xT3SieoBPd+tZikhu3BqyIifmLnxOJOjOIhbQrgFiblvzU1iOUOTOcSIB+7A
=YmRm
-----END PGP PRIVATE KEY BLOCK-----

所有密鑰都可以通過以下方式列出:

gpg --list-secret-keys

輸出:

$ gpg --list-secret-keys
/root/.gnupg/pubring.kbx
------------------------
sec rsa2048 2022-03-27 [SCEA]
      74082D81525723F5BF5B2099E1E734E00B611C26
uid [ultimate] Pascal Landau <[email protected]>
ssb rsa2048 2022-03-27 [SEA]

可以通過以下方式導入私鑰:

path="secret.gpg"
gpg --import "$path"

得到以下輸出:

$ path="secret.gpg"
$ gpg --import "$path"
gpg: key E1E734E00B611C26: "Pascal Landau <[email protected]>" not changed
gpg: key E1E734E00B611C26: secret key imported
gpg: Total number processed: 1
gpg: unchanged: 1
gpg: secret keys read: 1
gpg: secret keys unchanged: 1

注意:如果secret key需要密碼,這裡會提示輸入密碼。我們可以使用以下方法繞過提示--batch --yes --pinentry-mode loopback

path="secret.gpg"
gpg --import --batch --yes --pinentry-mode loopback "$path"

目前還不需要提供密碼,但需要在稍後嘗試解密文件時提供。

導出、列出和導入gpg公鑰,可以通過以下方式導出public.gpg

email="[email protected]"
path="public.gpg"
gpg --armor --export "$email" > "$path"

導出如下:

-----BEGIN PGP PUBLIC KEY BLOCK-----

mQENBF7VVBwBCADo9un+SySu/InHSkPDpFVKuZXg/s4BbZmqFtYjvUUSoRAeSejv
G21nwttQGut+F+GdpDJL6W4pmLS31Kxpt6LCAxhID+PRYiJQ4k3inJfeUx7Ws339
...
3LLbK7Qxz0cV12K7B+n2ei466QAYXo03a7WlsPWn0JTFCsHoCOphjaVsncIcHxNZ
t7eK644nWDn7j52HsRi+wcWsZ9mjkUgZLtyMPJNB5qlKQ18QgVdEAhuZxT3SieoB
Pd+tZikhu3BqyIifmLnxOJOjOIhbQrgFiblvzU1iOUOTOcSIB+7A
=g0hF
-----END PGP PUBLIC KEY BLOCK-----

通過以下方式列出所有公鑰:

gpg --list-keys

輸出:

$ gpg --list-keys
/root/.gnupg/pubring.kbx
------------------------
pub rsa2048 2022-03-27 [SCEA]
      74082D81525723F5BF5B2099E1E734E00B611C26
uid [ultimate] Pascal Landau <[email protected]>
sub rsa2048 2022-03-27 [SEA]

通過以下方式以與私鑰相同的方式導入公鑰:

path="public.gpg"
gpg --import "$path"

例如:

$ gpg --import /var/www/app/public.gpg
gpg: key E1E734E00B611C26: "Pascal Landau <[email protected]>" not changed
gpg: Total number processed: 1
gpg: unchanged: 1

git-secret

git-secret的官方網站可以找到詳細介紹該工具的內容。git-secret允許將某些文件聲明為「secret」並通過 gpg 加密。然後可以將加密的文件直接安全地存儲在 Git 存儲庫中,並在需要時進行解密。本文使用 git-secret v0.4.0

$ git secret --version
0.4.0

git-secret 安裝

Alpine 的安裝說明如下:

sh -c "echo '//gitsecret.jfrog.io/artifactory/git-secret-apk/all/main'" >> /etc/apk/repositories
wget -O /etc/apk/keys/git-secret-apk.rsa.pub '//gitsecret.jfrog.io/artifactory/api/security/keypair/public/repositories/git-secret-apk'
apk add --update --no-cache git-secret

.docker/images/php/base/Dockerfile 進行更新:

# File: .docker/images/php/base/Dockerfile

# install git-secret
# @see //git-secret.io/installation#alpine
ADD //gitsecret.jfrog.io/artifactory/api/security/keypair/public/repositories/git-secret-apk /etc/apk/keys/git-secret-apk.rsa.pub

RUN echo "//gitsecret.jfrog.io/artifactory/git-secret-apk/all/main" >> /etc/apk/repositories && \
    apk add --update --no-cache \
        bash \
        git-secret \
        gnupg \
        make \
#...

git-secret 用法

初始化 git-secret

git-secret 通過在 Git 存儲庫的根目錄中運行的以下命令進行初始化。

git secret init$ git secret init
git-secret: init created: '/var/www/app/.gitsecret/'

只需這樣操作一次,稍後會把文件夾提交到 Git,將包含以下文件:

$ git status | grep ".gitsecret"
        new file: .gitsecret/keys/pubring.kbx
        new file: .gitsecret/keys/pubring.kbx~
        new file: .gitsecret/keys/trustdb.gpg
        new file: .gitsecret/paths/mapping.cfg

pubring.kbx~文件(帶有波浪號~)只是一個臨時文件,可以安全地被 git 忽略。

git-secret Directory 和 gpg-agent Socket

git-secret 在主機系統和 Docker 之間共享的目錄中使用,還需要運行以下命令:

tee .gitsecret/keys/S.gpg-agent <<EOF
%Assuan%
socket=/tmp/S.gpg-agent
EOF

tee .gitsecret/keys/S.gpg-agent.ssh <<EOF
%Assuan%
socket=/tmp/S.gpg-agent.ssh
EOF

tee .gitsecret/keys/gpg-agent.conf <<EOF
extra-socket /tmp/S.gpg-agent.extra
browser-socket /tmp/S.gpg-agent.browser
EOF

這一步很必要,因為 git-secret 在主機系統和 Docker 容器之間共享程式碼庫的設置中使用時存在問題,具體如下:

  • gpg 使用 gpg-agent 來執行其任務,這兩個工具通過在 pgp-agent--home-directory 中創建的套接字進行通訊。

  • 代理通過 git-secret 使用的 gpg 命令隱式啟動,使用 .gitsecret/keys 目錄作為 --home-directory

  • 由於 --home-directory 的位置與主機系統共享,因此套接字創建將失敗。

對應的錯誤資訊是:

gpg: can't connect to the agent: IPC connect call failed

gpg-agent: error binding socket to '/var/www/app/.gitsecret/keys/S.gpg-agent': I/O error

解決此問題,可以通過將其他 gpg 配置文件放在 .gitsecret/keys 目錄中,將 gpg 配置為對套接字使用不同的位置:

S.gpg-agent

%Assuan%
socket=/tmp/S.gpg-agent

s.gpg-agent.ssh

%Assuan%
socket=/tmp/S.gpg-agent

gpg-agent.conf

extra-socket /tmp/S.gpg-agent.extra
browser-socket /tmp/S.gpg-agent.browser

添加、列出和刪除用戶

要添加新用戶,必須首先導入其公用 gpg 密鑰。然後運行:

email="[email protected]"
git secret tell "$email"

在這種情況下,用戶[email protected] 現在將能夠解密這些密鑰。要顯示用戶請運行:

git secret whoknows$ git secret whoknows
[email protected]

要刪除用戶,請運行:

email="[email protected]"
git secret killperson "$email"

這時此命令在 git-secret >= 0.5.0 中已重命名為 removeperson

$ git secret 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.

用戶 [email protected] 將無法再解密這些密鑰。

注意刪除用戶後需要重新加密機密,並輪換加密的密鑰。

添加、列出和刪除文件以進行加密

運行 git secret add [filenames...] 來為文件加密:

git secret add .env

如果 .env 沒有被添加到 .gitignoregit-secret 將顯示警告並自動添加。

git-secret: these files are not in .gitignore: .env
git-secret: auto adding them to .env
git-secret: 1 item(s) added.

如已添加,則添加文件時不會發出警告。

$ git secret add .env
git-secret: 1 item(s) added.

只需要添加一次文件。然後將它們存在 .gitsecret/paths/mapping.cfg :

$ cat .gitsecret/paths/mapping.cfg
.env:505070fc20233cb426eac6a3414399d0f466710c993198b1088e897fdfbbb2d5

還可以通過以下方式顯示添加的文件:

git secret list$ git secret list
.env

需要主要的是,這個時候文件尚未加密,如果要從加密中刪除文件,請運行:

git secret list$ git secret list
.env

輸出:

$ git secret remove .env
git-secret: removed from index.
git-secret: ensure that files: [.env] are now not ignored.

加密文件

要加密文件,請運行:

git secret hide

輸出:

$ git secret hide
git-secret: done. 1 of 1 files are hidden.

所有通過 git secret tell 被添加的用戶能夠解密這些已經加密的文件,這也意味著每當添加新用戶時,您都需要再次運行此命令。

解密文件

可以通過以下方式解密文件:

git secret reveal

輸出:

$ git secret reveal
File '/var/www/app/.env' exists. Overwrite? (y/N) y
git-secret: done. 1 of 1 files are revealed.
  • 文件被解密並將覆蓋當前未加密的文件。

  • 使用 -f 選項強制覆蓋並以非交互方式運行。

  • 如果只想檢查加密文件的內容,可以使用 git secret cat $filename 例如,git secret cat. env

gpg 密鑰受密碼保護時,需要通過 -p 選項傳遞密碼。以下是密碼示例 123456

git secret reveal -p 123456

顯示加密和解密文件間的變化

加密文件的一個問題是,無法在遠程工具的程式碼審查期間審查加密文件。為了了解進行了哪些更改,顯示加密文件和解密文件之間的更改能夠幫助解決這個問題。可以通過以下方式完成:

git secret changes

輸出:

$ echo "foo" >> .env
$ git secret changes
git-secret: changes in /var/www/app/.env:
--- /dev/fd/63
+++ /var/www/app/.env
@@ -34,3 +34,4 @@
 MAIL_ENCRYPTION=null
 MAIL_FROM_ADDRESS=null
 MAIL_FROM_NAME="${APP_NAME}"
+foo

注意底部的 +foo. 它是通過在第一行 echo "foo">>>.env 添加的。

 

本文是git-secret用法的上篇,在下篇中我們將會介紹git-secret的初始設置、Makefile調整等內容,保持關注哦~