Password free unattended backup with OpenPGP/gpg encryption key

Summary

Leaving a password in the clear to a backup script file is a nuisance because care must be taken not to accidentally make it public. Also, it’s a temptation for nosy people.

An OpenPGP/GnuPG standard assymetric key only requires a password during decryption, and not during encryption, which solves that problem. (OpenPGP standard, GnuPG implementation).

We show how to create an encryption key without a signing key.

We show a couple of examples of how to use the key:

  • unattended duplicity backup
  • auto backup of etckeeper data

Creating a key

So as not to interfere with exising gpg keys, we create a seperate gpg home directory.

  • Default gpg home directory is ~/.gnupg
  • We use alternate gpg home directory ~/.gnupg.local

We’ll bypass the gpg-agent and query for password directly in shell script when necessary. (Necessary = key creation, decryption, one-time backup of the key itself.) Adding a line allow-loopback-pinentry to ~/.gnupg.local/gpg.conf makes this script query possible.

mkdir ~/gnupg.local
chmod 700 ~/gnupg.local
echo "allow-loopback-pinentry" > ~/gnupg.local/gpg-agent.conf
chmod 600 ~/gnupg.local/gpg-agent.conf

Add a link for the case where we need to use the key as root:

sudo ln -s ~/gnupg.local/gpg-agent.conf /root/gnupg.local/gpg-agent.conf

We create a minimalist encryption key named encrypt@local.

GnuPG usally creates two “keys” together: a signing “key” and an encryption “key”. Each of those is composed of two “keys”, a public “key”, and a private “key”. For our purpose, we only need the encryption side. Unattended gpg key creation offers a way to do this. Its easiest to use a script file:

gen_key_one_encrypt.sh
gen_key_one_encrypt()
(
set -eu
echo "Generating key for Id = $EncryptKeyId"
local Passphrase
echo "Enter password to be used for $EncryptKeyId"
stty -echo
printf "Password: "
read Passphrase
stty echo
gpg2 --homedir ~/.gnupg.local --batch --gen-key - <<EOF
Key-Type: default
Key-Usage: encrypt
Passphrase: $Passphrase
Name-Email: encrypt@local
Expire-Date: 0
%commit
%echo done
EOF
)
gen_key_one_encrypt

Notice the round bracket “()” surrounding the function body. The stops the shell variable/value $Password from leaking out into the outer shell.

Execute it and enter the password

source gen_key_one_encrypt.sh

Display the key:

gpg2 --homedir ~/.gnupg.local -k
output
/home/user/.gnupg.local/pubring.kbx
------------------------------------
pub rsa2048 2018-05-14 [CE]
653426EA013A3FC874BE7A68A834BAFC623FD48C
uid [ultimate] encrypt@local

As you can see, there is an encryption key but no signing key.

Immediately backup the key. (One-time operation.)

The encrypt@local secret key is safe even if the key data is compromised because you gave it a password. But if the key data is lost (i.e. disk failure) the backed up encrypted data will be enencryptable, even if the password is not lost. So right after creating the key we back up the whole ~/gnupg.local directory with a *symmetric** key.

A *symmetric** key is embedded with the encrypted data, and requires the password during both encryption and decryption. No key data is stored anywhere else.

Do it with a script file. Set BackupDir to a suitable value.

backup-keys.sh
backup_keys()
(
set -eu

BackupDir=/media/wd1tb/backups/keys

if ! [[ -d $BackupDir ]]; then
mkdir -p $BackupDir
fi

stty -echo
printf "Password: "
read PASSPHRASE
stty echo

trap 'unset PASSPHRASE' 0
trap 'echo fail' ERR

gpg2 --homedir ~/.gnupg.local --passphrase '$PASSPHRASE' \
--no-use-agent --pinentry-mode=loopback --symmetric \
-o $BackupDir/.gnupg.local.$(date +"%y%m%d-%H%M%S").gpg - \
< <(tar -cv -C ~ .gnupg.local)

echo "success"
)
backup_keys

Execute and enter password:

input
source backup-keys.sh

Test restoration is left as an exercise for the reader.

Examples of unattended encryption key useage

Use the key with duplicity

Example of unattended duplicity with no password required.

example
duplicity  \
--gpg-binary gpg2 \
--gpg-options "--homedir=~/.gnupg.local " \
--encrypt-key encrypt@local \
--archive-dir /root/.cache/duplicity/ \
--name Home \
--log-file /var/log/Home.log \
--tempdir /tmp/XXX \
--exclude-device-files \
/home file:///media/wd1tb/backups/keys

Use the key for backing up etckeeper

Credit for this etckeeper back method belongs to Josh Triplett.

Go sudo and create the following files:

/etc/.git/hooks/post-commit
exec /etc/post-commit-backup
/etc/post-commit-backup
#!/bin/bash
echo '--- Backing up git repository ---' >&2
User=<your user id>
git bundle create - master | \
sudo -H -u $User gpg2 --homedir ~/.gnupg.local --batch --encrypt --recipient encrypt@local \
> /media/wd1tb/backups/etc/etc.git-bundle.gpg.$(date +"%y%m%d-%H%M%S")
echo '--- Done backing up ---' >&2

Set permissions - owner must be root.

chmod 755 /etc/.git/hooks/post-commit
chmod 755 /etc/post-commit-backup

Now every time etckeeper commit is called, the etckeeper respository will be backed up. E.g., automatically after apt updates.