Skip to content

git加密远程仓库

我本来对写这种step by step guide是不感兴趣的,公司付钱我都懒得写。但是这次我发现由于相关的开源工具还不够成熟,有一些坑并且很难debug,于是写下这篇博客,希望能节省大家的一些时间。

目的

我们很少自己去配置git服务器,相反,我们经常会使用一些第三方托管git repo的服务,例如github,这里有一个提供免费服务的网站列表: https://git.wiki.kernel.org/index.php/GitHosting

在使用这些服务的时候,我们可能需要储存一些私密信息,因此也存在将其暴露给第三方服务的风险。能否在并不信任服务提供商的同时使用他们的服务呢?一个解决方法就是每次我们push到remote之前都将repo加密,每次pull之后再解密。其实就是在与第三方的通信中加了一个代理。从第三方角度来看我们只是存了一些无法解释的乱码。恰好有一些工具可以提供这样的功能。

准备工作

首先我排除了git-crypt

git-crypt 被设计用来加密git repo中的部分文件,其依赖于git filters。我个人认为属于防君子不防小人,如果第三方网站对你的敏感数据感兴趣,完全可以修改.gitattributes来禁用加密。更主要的原因是,如果你有一些access_token, private key之类的secret就不应该存在repo里。最佳实践应该是设法将其安全地存放在部署环境里。

于是我决定使用git-remote-gcrypt,其提供对整个仓库的加密。

git-remote-gcrypt只是一个让你用GnuPG去加密仓库的helper,自然也依赖于GnuPG

多说几句,GnuPG其实是PGP的开源仿制版。PGP的作者是Phil Zimmermann,他有一个魔改版本的摩尔定律:

技术往往会自然地朝着易于监控的方向发展,计算机追踪我们的能力每18个月就会增加一倍。

因此我们需要安装GnuPG和git-remote-gcrypt:

on Debian/Ubuntu:

Bash
sudo apt install gnupg
sudo apt install git-remote-gcrypt

on MacOS:

Bash
brew install gnupg
brew install git-remote-gcrypt

生成密钥

首先使用gpg生成密钥,根据提示一步步往下走就可以。

之后你可以通过--list-keys参数来查看保存在本地的密钥:

Bash
1
2
3
4
5
6
gpg --list-keys
---------------------------------
pub   rsa3072 YYYY-MM-DD [SC] [expires: YYYY-MM-DD]
      <USER_ID_HASH>
uid           [ultimate] YOUR_NAME <YOUR_EMAIL>
sub   rsa3072 YYYY-MM-DD [E] [expires: YYYY-MM-DD]

如果以后你的密钥泄露了,可以用--gen-revoke来生成撤销证书:

Bash
gpg --gen-revoke <USER_ID_HASH>

也可以通过--delete-secret-key --delete-key来删除本地保存的密钥, 必须先删除private key。

Bash
gpg --delete-secret-key <USER_ID_HASH>
gpg --delete-key <USER_ID_HASH>

更新密码:

Bash
gpg --edit-key

设置仓库

只需要remote url的前面加上gcrypt::,然后设置remote.<YOUR_CRYPTED_REMOTE>.gcrypt-participants 即可。url也可以是一个本地地址,不过一般没必要这么做。

例如:

Bash
1
2
3
git init
git remote add <YOUR_CRYPTED_REMOTE> gcrypt::git@github.com:XXX/YYY.git
git config remote.<YOUR_CRYPTED_REMOTE>.gcrypt-participants <USER_ID_HASH>

如果你用zsh的话,记得添加以下环境变量在你的cli:

Bash
export GPG_TTY=$(tty)

相关issue: gpg: signing failed: Inappropriate ioctl for device · Issue #2798 · keybase/keybase-issues

否则你之后会遇到如下错误:

Bash
gpg: signing failed: Inappropriate ioctl for device
gpg: [stdin]: sign+encrypt failed: Inappropriate ioctl for device

如果你不幸在看到提示以前已经push并且出错了,那么你就将遇到另一个大坑:

Bash
gcrypt: Repository not found: git@github.com:XXX/YYY.git
gcrypt: ..but repository ID is set. Aborting.

是的,一旦出错,这个remote就不能用了,你将不得不再设置一个remote,例如crypted2:

Bash
git remote add crypted2 gcrypt::git@github.com:XXX/YYY.git
git config remote.crypted2.gcrypt-participants <USER_ID_HASH>

相关issue:git-remote-gcrypt might report a repository as "not found" · Issue #2 · jcouyang/dotfiles

导入导出密钥

既然都放在云上了,肯定是希望从另一台计算机可以访问到,否则也没必要折腾了。首先我们需要也安装好gunpg和git-remote-gcrypt,然后需要在另一台计算机上配置好密钥。

可以通过--output来导出密钥到文件,--armor选项以ASCII码的格式导出。

Bash
gpg --output <YOUR_KEY_FILE_NAME>-pub.key --armor --export <USER_ID_HASH>
gpg --output <YOUR_KEY_FILE_NAME>-sec.key --armor --export-secret-key <USER_ID_HASH>

导入:

Bash
gpg --import <YOUR_KEY_FILE_NAME>-pub.key
gpg --allow-secret-key-import --import <YOUR_KEY_FILE_NAME>-sec.key

转移仓库

然后就非常简单了,直接git clone就可以,别忘了加上gcrypt::。

Bash
git clone gcrypt::git@github.com:XXX/YYY.git

就到这里吧,接下来基本上和正常使用git没有任何区别。如果本文提到的issue已经fixed了,欢迎评论或私信作者。我会更新相应内容。