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

2022-09-29 18:01:17

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

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

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

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

目前市面上已經存在許多較為成熟的金鑰管理產品,比如 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 'https://gitsecret.jfrog.io/artifactory/git-secret-apk/all/main'" >> /etc/apk/repositories
wget -O /etc/apk/keys/git-secret-apk.rsa.pub 'https://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 https://git-secret.io/installation#alpine
ADD https://gitsecret.jfrog.io/artifactory/api/security/keypair/public/repositories/git-secret-apk /etc/apk/keys/git-secret-apk.rsa.pub

RUN echo "https://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調整等內容,保持關注哦~