Take the 2-minute tour ×
Server Fault is a question and answer site for professional system and network administrators. It's 100% free, no registration required.

We would like to use gpg signatures to verify some aspects of our system configuration management tools. Additionally, we would like to use a "trust" model where individual sysadmin keys are signed with a master signing key, and then our systems trust that master key (and use the "web of trust" to validate signatures by our sysadmins).

This gives us a lot of flexibility, such as the ability to easily revoke the trust on a key when someone leaves, but we've run into a problem. While the gpg command will tell you if a key is untrusted, it doesn't appear to return an exit code indicating this fact. For example:

# 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 <someone@example.com>"
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

The part we care about is this:

gpg: WARNING: This key is not certified with a trusted signature!
gpg:          There is no indication that the signature belongs to the owner.

The exit code returned by gpg in this case is 0, despite the trust failure:

# echo $?
0

How do we get gpg to fail in the event that something is signed with an untrusted signature?

I've seen some suggestions that the gpgv command will return a proper exit code, but unfortunately gpgv doesn't know how to fetch keys from keyservers. I guess we can parse the status output (using --status-fd) from gpg, but is there a better way?

share|improve this question

4 Answers 4

up vote 2 down vote accepted

This is what ended up with:

#!/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

This looks for the trust information that gpg outputs on --status-fd. The script exits with an error in the presence of an untrusted signature (or invalid/no signature):

$ 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 <testuser@example.com>"
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

The script exits with no error in the presence of a valid, trusted signature:

$ 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
share|improve this answer

So let me try to split the issue:

First issue seems that the key your are testing against is untrusted.

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 <jdoe@noemail.com>"
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

I assumed this is intentional... but before we get to how to fix, let me suggest you use gpgv instead of gpg -v? You will se why in a minute:

$ 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

No key, no trust... No we import the key into 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 <jdoe@noemail.com>" 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 <jdoe@noemail.com>"

$ echo $?
0

Hope it helps

share|improve this answer
    
I commented on gpgv in my question -- the problem with gpgv is that while it does return a more useful error code, it doesn't know how to fetch keys from a keyserver. –  larsks Aug 13 '11 at 1:27

Two options come to mind (other than parsing the output).

A quick and dirty way would be to run both gpg and gpgv. The first run of gpg would ensure the key was fetched from the keyserver, and then gpgv will give you the return code you want.

A more elegant, controlled way (though it would involve more work) would be to use the gpgme library to verify the signature. It is a C library, though there are wrappers for Perl, PHP, Python and Ruby. (The Python one is quite low level, while the Ruby one has some higher level abstractions, not sure about Perl or PHP).

The GPGME library appears to talk to keyservers when I've used it, though you'd want to confirm that. I've written a bit of code that uses the ruby gpgme library (search for verify and verified_ok? for code that verifies a signature, and for sig_output_lines for some code that works out whether a signature is trusted).

share|improve this answer

What about migrating your system configuration to a tool like Puppet or Chef?

While a non-trivial amount of work, Chef (I have not used Puppet) you must create user accounts (and pub/private keys are generated). While this doesn't prevent people from modifying the files local on the server, chef-client runs periodically, and will overwrite their changes on the next run. (Periodic recurring runs occurs by default.)

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.