在服务器上使用本地的 GPG 密钥
在服务器上使用本地的 GPG 密钥
{{< featuredImage >}}
我们经常会有在服务器上使用 GPG 密钥的需求,比如为编译好的软件签名等等。但在多人使用的服务器上配置 GPG 密钥并不方便,等何况还有安全风险。并且如果你使用了 Yubikey 等「智能卡」储存密钥,你根本没法将密钥导出来!!
那么有没有办法在密钥不离开本地的前提下远程使用 GPG 呢?答案是肯定的。
首先,在你的本地计算机下执行以下命令 (Linux):
$ ls -l ~/.gnupg | grep gpg-agent
srwx------ 1 mogeko mogeko 0 4月 25 21:40 S.gpg-agent
srwx------ 1 mogeko mogeko 0 4月 25 21:40 S.gpg-agent.browser
srwx------ 1 mogeko mogeko 0 4月 25 21:40 S.gpg-agent.extra
srwx------ 1 mogeko mogeko 0 4月 25 21:40 S.gpg-agent.ssh
我们可以看到一堆 socket
,这些 socket
就是实现我们需求的关键。
GPG 从 gpg2
开始就实现了基于转发 UNIX Domain Socket
的代理 {{< spoiler >}}还在用第一代 GPG 的小伙伴快升级吧!{{< /spoiler >}},也就是说,只需要将服务器端的 S.gpg-agent
和本地的 S.gpg-agent.extra
建立连接,就可以实现在密钥不离开本地的前提下远程使用 GPG。
而负责转发 UNIX Domain Socket
的软件则是我们连接远程服务器必定会用到的 SSH。我们只需要让 SSH 将服务器上的一个 socket
转发到本地 gpg-agent
监听的 socket
上即可。
具体命令如下 (写绝对地址,我这里为了方便演示所以用了 ~
):
$ ssh -R ~/.gnupg/S.gpg-agent:~/.gnupg/S.gpg-agent.extra \
-o StreamLocalBindUnlink=yes user@example.com
这里的 StreamLocalBindUnlink
是让 SSH 断开的时候,把远端监听的 socket unlink 掉,不然下次连接会转发失败。
每次连接都打这么长一串,太麻烦了,我们可以直接在 ~/.ssh/config
中配置:
StreamLocalBindUnlink yes
RemoteForward ~/.gnupg/S.gpg-agent:~/.gnupg/S.gpg-agent.extra
然后在服务器上,让 GPG 从公钥服务器拉取公钥:
gpg --keyserver [公钥服务器 URL] --search-keys [用户 ID]
就大功告成了!(可以预防性的重启一下 gpg-agent
)
远程主机上可以正常的签名、解密等,但没法 gpg --card-edit
(毕竟服务器上没有私钥嘛)。
Windows🔗
Windows 上 S.gpg-agent.extra
的位置在
%HOMEDRIVE%%HOMEPATH%\AppData\Roaming\gnupg\S.gpg-agent.extra
不过 Gpg4win 上用的 socket
好像和 UNIX Domain Socket
有点区别,可能有坑。
举一反三🔗
既然我们可以用本地主机代理远程服务器上的 GPG,那么是否也可以代理虚拟机、WSL 和 Docker 中的 GPG 呢?
在本地通过 socat
等软件代替 SSH 转发 UNIX Domain Socket
,理论上是可行的。