我们希望使用 gpg 签名来验证系统配置管理工具的某些方面。此外,我们希望使用“信任”模型,其中各个系统管理员密钥都使用主签名密钥进行签名,然后我们的系统信任该主密钥(并使用“信任网”来验证系统管理员的签名)。
这给了我们很大的灵活性,比如当有人离开时,我们可以轻松地撤销对密钥的信任,但我们遇到了一个问题。虽然该gpg
命令将告诉如果密钥不受信任,它似乎不会返回表明这一事实的退出代码。例如:
# gpg -v < foo.asc
Version: GnuPG v1.4.11 (GNU/Linux)
gpg: armor header:
gpg: original file name=''
this is a test
gpg: Signature made Fri 22 Jul 2011 11:34:02 AM EDT using RSA key ID ABCD00B0
gpg: using PGP trust model
gpg: Good signature from "Testing Key <[email protected]>"
gpg: WARNING: This key is not certified with a trusted signature!
gpg: There is no indication that the signature belongs to the owner.
Primary key fingerprint: ABCD 1234 0527 9D0C 3C4A CAFE BABE DEAD BEEF 00B0
gpg: binary signature, digest algorithm SHA1
我们关心的部分是:
gpg: WARNING: This key is not certified with a trusted signature!
gpg: There is no indication that the signature belongs to the owner.
尽管信任失败,但在这种情况下 gpg 返回的退出代码为 0:
# echo $?
0
当某些内容使用不受信任的签名进行签署时,我们如何让 gpg 失败?
我看到有人建议该gpgv
命令将返回正确的退出代码,但不幸的是,gpgv
它不知道如何从密钥服务器获取密钥。我想我们可以解析状态输出(使用 --status-fd)gpg
,但有没有更好的方法?
答案1
最终结果如下:
#!/bin/sh
tmpfile=$(mktemp gpgverifyXXXXXX)
trap "rm -f $tmpfile" EXIT
gpg --status-fd 3 --verify "$@" 3> $tmpfile || exit 1
egrep -q '^\[GNUPG:] TRUST_(ULTIMATE|FULLY)' $tmpfile
这将查找gpg
上输出的信任信息--status-fd
。如果存在不受信任的签名(或无效/无签名),脚本将退出并显示错误:
$ sh checksig sample.sh.bad
gpg: Signature made Mon 24 Jun 2013 11:42:58 AM EDT using RSA key ID DCD5C569
gpg: Good signature from "Test User <[email protected]>"
gpg: WARNING: This key is not certified with a trusted signature!
gpg: There is no indication that the signature belongs to the owner.
Primary key fingerprint: 6FCD 3CF0 8BBC AD50 662E 5070 E33E D53C DCD5 C569
$ echo $?
1
如果存在有效且可信的签名,脚本将毫无错误地退出:
$ sh checksig sample.sh.good
gpg: Signature made Mon 24 Jun 2013 11:38:49 AM EDT using RSA key ID 5C2864A8
gpg: Good signature from "Lars Kellogg-Stedman <...>"
$ echo $?
0
答案2
那么让我尝试分解一下这个问题:
第一个问题似乎是你正在测试的密钥不受信任。
gpg -v < test.txt.asc
gpg: armor header: Version: GnuPG v1.4.11 (GNU/Linux)
gpg: original file name='test.txt'
this is a test
gpg: Signature made Thu 11 Aug 2011 09:09:35 PM EST using RSA key ID FE1B770E
gpg: using PGP trust model
gpg: Good signature from "John Doe <[email protected]>"
gpg: WARNING: This key is not certified with a trusted signature!
gpg: There is no indication that the signature belongs to the owner.
Primary key fingerprint: 5DD8 216D ADB1 51E8 4326 3ACA 1DED BB72 FE1B 770E
gpg: binary signature, digest algorithm SHA1
我认为这是故意的……但在我们讨论如何修复之前,我建议你使用病毒代替gpg-v 版本? 您马上就会明白为什么:
$ gpgv < test.txt.asc
gpgv: keyblock resource `/user/.gnupg/trustedkeys.gpg': file open error
gpgv: Signature made Thu 11 Aug 2011 09:09:35 PM EST using RSA key ID FE1B770E
gpgv: Can't check signature: public key not found
$ echo $?
2
没有密钥,就没有信任...不,我们将密钥导入到trustedkeys.gpg中
$ gpg --no-default-keyring --keyring trustedkeys.gpg --import jdoe_pub.gpg
gpg: keyring `/user/.gnupg/trustedkeys.gpg' created
gpg: key FE1B770E: public key "John Doe <[email protected]>" imported
gpg: Total number processed: 1
gpg: imported: 1 (RSA: 1)
$ gpgv < test.txt.asc
gpgv: Signature made Thu 11 Aug 2011 09:09:35 PM EST using RSA key ID FE1B770E
gpgv: Good signature from "John Doe <[email protected]>"
$ echo $?
0
希望能帮助到你
答案3
我想到两个选项(除了解析输出)。
一种快速而肮脏的方法是运行两个都 gpg
和gpgv
。第一次运行gpg
将确保从密钥服务器获取密钥,然后gpgv
将为您提供所需的返回代码。
一种更优雅、更可控的方式(尽管这会涉及更多工作)是使用游戏库来验证签名。这是一个 C 库,但有包装器Perl,PHP,Python和红宝石。(Python 的级别相当低,而 Ruby 有一些更高级别的抽象,不确定 Perl 或 PHP 是否如此)。
当我使用 GPGME 库时,它似乎可以与密钥服务器通信,不过你需要确认这一点。我写了一些使用 ruby gpgme 库的代码(搜索verify
和verified_ok?
查找验证签名的代码,以及查找sig_output_lines
确定签名是否可信的代码)。
答案4
将系统配置迁移到 Puppet 或厨师?
虽然工作量不小,但 Chef(我没有使用过 Puppet)必须创建用户帐户(并生成公钥/私钥)。虽然这并不能阻止人们修改服务器上的本地文件,但 chef-client 会定期运行,并在下次运行时覆盖他们的更改。(默认情况下会定期重复运行。)