4

I working on a Fedora 25 (F25) workstation, the KDE spin. I'm writing some scripts to perform automated testing.

One of the automated tests involves invoking the RPMSIGN(8) program which in turn invokes GPG(1) to attach a digital signature to some RPM files I am creating. Of course, GPG uses pinentry (PIN entry) to prompt the human to enter the passphrase for the RPM signing key (an RSA key pair). I want to take the human out of the loop and fully automate the task of supplying the passphrase for the RPM signing key.( And yes, I know about the security implications. This is simply an automated test environment and not a production host, so I'm not overly concerned about security. In the production version the user will manually enter the RPM signing key's password.)

In the past I used an EXPECT(1) script that waited for GPG to ouptut the text "Enter pass phrase:" to the console, and then the EXPECT script would enter the pass phrase and on I'd go. Worked great.

In F25 the pinentry feature breaks my existing EXPECT-based solution for automated entry of the passphrase for the RPM signing key.

When I run RPMSIGN in a GUI console window on this F25 host, GPG uses pinentry to pop up a GUI dialog box that asks the user (me) to enter the passphrase for the RPM signing key. This pinentry behavior, of course, interferes and prevents automated entry of the passphrase.

If I create a Bash script that momentarily unsets the DISPLAY environment variable, then I no longer get the GUI dialog,

#!/bin/bash
DISPLAY_SAVE=$DISPLAY
unset DISPLAY
rpmsign --resign "/path/to/test-1.0.0-1.fc25.noarch.rpm"
export DISPLAY=$DISPLAY_SAVE

but now I get an ncurses version of the dialog on the console:

+----------------------------------------------------------------+
| Please enter the passphrase to unlock the OpenPGP secret key:  |
| "Testing (rpm-sign)"                                           |
| 1024-bit RSA key, ID 0123456789ABCDEF,                         |
| created 2016-12-02.                                            |
|                                                                |
|                                                                |
| Passphrase: __________________________________________________ |
|                                                                |
|         <OK>                                    <Cancel>       |
+----------------------------------------------------------------+

Again, pinentry-curses's console "dialog" interferes and prevents automated entry of the passphrase.

I don't want to permanently modify or disable the pinentry modules; I just want to disable them temporarily to get back to GPG's CLI prompt "Enter pass phrase:" (or whatever that prompt string is now) without pinentry interfering.

Any suggestions for fully automating the entry of the RPM signing key's passphrase via the CLI without pinentry interference?

6

Thanks to @Joe Damato for pointing me toward the gpg-preset-passphrase utility. The solution described below was developed and tested on a Fedora 25 host with gnupg2-2.1.x installed.

(n.b. I haven't yet figured out how to determine the RPM signing key's keygrip value on platforms running older versions of GnuPG because they don't support the --with-keygrip option. If someone would like to comment on a solution for this, please do.)

Ensure ~/.gnupg/gpg-agent.conf contains the line.

allow-preset-passphrase

After modifying ~/.gnupg/gpg-agent.conf reload the gpg-agent.

$ gpg-connect-agent reloadagent /bye
OK

List your GPG keys to obtain the eight hexidecimal digit key ID for your RPM signing key. In this excample, the key ID is 0123ABCD.

$ gpg --list-keys
/home/me/.gnupg/pubring.gpg
-----------------------------------
pub   1024R/0123ABCD 2015-06-13
uid                  Test (rpm-sign)

Get the keygrip code for the RPM signing key. (n.b. On the RHEL 7.2 and Fedora 20 hosts I use for testing, the GPG2(1) program on those hosts didn't recognize the --with-keygrip option.)

$ gpg2 --with-keygrip -K 0123ABCD
sec   rsa1024 2015-06-13 [SCEA]
      0A1B2C3D4E5F6A7B8C9D0E0F1A2B3C4D0123ABCD
      Keygrip = 2EACA0C5A4B46168EB91970B6715AF1AA52968BE
uid           [ unknown] Test (rpm-sign)

Cache the passphrase for the RPM signing key. In the command line shown below, replace 'PASSPHRASE' with the actual passphrase for the RPM signing key.

$ /usr/libexec/gpg-preset-passphrase --passphrase 'PASSPHRASE' --preset 2EACA0C5A4B46168EB91970B6715AF1AA52968BE

:: TEST ::

Create a test RPM file that is not signed. Verify that the test RPM file is not signed.

$ rpm --checksig test-1.0.0-1.f25.noarch.rpm
test-1.0.0-1.fc25.noarch.rpm: sha1 md5 OK

Verify that RPMSIGN(8) uses the cached password--i.e., that RPMSIGN(8) does not prompt you to enter the passphrase for the RPM signing key--when signing the test RPM file.

$ rpmsign --resign test-1.0.0-1.f25.noarch.rpm

Verify that the test RPM file is signed.

$ rpm --checksig test-1.0.0-1.f25.noarch.rpm
test-1.0.0-1.fc25.noarch.rpm: rsa sha1 (md5) pgp md5 OK

:: ADDENDUM 1 (2016-12-17) ::

According to this GnuPG Issue 2331 webpage:

gpg1 does not known about keygrips. Instead of the keygrip, gpg1 uses the fingerprint as cacheid for gpg-agent. The agent's command GET_PASSPHRAE, as used by gpg1, uses a different cache mode from what gpg-preset-passphrases uses. Thus even if you replace the keygrip with the fingerprint of the (sub)key, it won't work.

For what it's worth, I've done some testing with GnuPG version 2.0.x on RHEL7 hosts and gpg-preset-passphrase doesn't seem to be supported. I can only get gpg-preset-passphrase to work with GnuPG version 2.1.x.

References

GnuPG ArchWiki

  • This answer helped me a low. Thank you very much! – Michael Herrmann Oct 12 '18 at 10:19
4

One solution to this sort of problem is to use gpg-agent, which manages GPG secret keys. You can use that tool in conjunction with gpg-preset-passphrase to seed the passphrase into the gpg-agent cache. Read more about gpg-agent here. This will allow you to avoid needing to enter a passphrase manually so you can perform your automated tasks.

BTW, I wrote a helpful blog post about GPG signing and verifying RPM Packages and YUM repositories that you might find useful, too.

  • Thanks @Joe. I'll experiment with the gpg-preset-passphrase utility to see if I can get a solution that works for me. FWIW, on my F25 host the gpg-preset-passphrase executable is installed in /usr/libexec/, in case this helps someone else locate it on their box. – Jim Fischer Dec 9 '16 at 2:01
4

By adding/modifying the %__gpg_sign_cmd macro in your .rpmmacros file, you can tell gpg to read the passphrase from a file. Use rpm --showrc to see what your current %__gpg_sign_cmd looks like, and then put that into your .rpmmacros file, with the following addition:

--batch --pinentry-mode loopback --passphrase-file /path/to/passphrase-file

Make sure to end each line except the last with \ if you have a multiline value.

This will cause gpg to not prompt in any way for the passphrase, and instead read it from the defined file. This poses a security risk of course, so consider if this is something you want to do, and take any necessary steps to secure it (e.g. ownership and permission).

Example of full %__gpg_sign_cmd in .rpmmacros:

%__gpg_sign_cmd %{__gpg} \
    gpg --no-verbose --no-armor --batch --pinentry-mode loopback \
    --passphrase-file /path/to/passphrase-file \
    %{?_gpg_digest_algo:--digest-algo %{_gpg_digest_algo}} \
    --no-secmem-warning \
    -u "%{_gpg_name}" -sbo %{__signature_filename} %{__plaintext_filename}

Signing a package:

$ rpm --checksig somepackage.rpm 
somepackage.rpm: sha1 md5 OK

$ rpmsign --addsign somepackage.rpm
somepackage.rpm:

$ rpm --checksig somepackage.rpm 
somepackage.rpm: rsa sha1 (md5) pgp md5 OK
  • Confirm this worked in CentOS 8 (gnupg2-2.2.) – sivann Nov 26 '19 at 8:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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