mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
Use GnuPG 2 for backup/restore
ipa-backup and ipa-restore now use GnuPG 2 for asymmetric encryption, too. The gpg2 command behaves a bit different and requires a gpg2 compatible config directory. Therefore the --keyring option has been deprecated. The backup and restore tools now use root's GPG keyring by default. Custom configuration and keyring can be used by setting GNUPGHOME environment variables. Fixes: https://pagure.io/freeipa/issue/7560 Signed-off-by: Christian Heimes <cheimes@redhat.com> Reviewed-By: Rob Crittenden <rcritten@redhat.com>
This commit is contained in:
@@ -1,15 +1,23 @@
|
||||
#
|
||||
# Copyright (C) 2017 FreeIPA Contributors. See COPYING for license
|
||||
#
|
||||
from __future__ import absolute_import
|
||||
|
||||
import binascii
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import shutil
|
||||
import tempfile
|
||||
import textwrap
|
||||
|
||||
import pytest
|
||||
|
||||
from ipaplatform.paths import paths
|
||||
from ipapython import ipautil
|
||||
from ipaserver.install import installutils
|
||||
from ipaserver.install import ipa_backup
|
||||
from ipaserver.install import ipa_restore
|
||||
|
||||
EXAMPLE_CONFIG = [
|
||||
'foo=1\n',
|
||||
@@ -22,8 +30,6 @@ WHITESPACE_CONFIG = [
|
||||
]
|
||||
|
||||
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def tempdir(request):
|
||||
tempdir = tempfile.mkdtemp()
|
||||
@@ -35,6 +41,87 @@ def tempdir(request):
|
||||
return tempdir
|
||||
|
||||
|
||||
GPG_GENKEY = textwrap.dedent("""
|
||||
%echo Generating a standard key
|
||||
Key-Type: RSA
|
||||
Key-Length: 2048
|
||||
Name-Real: IPA Backup
|
||||
Name-Comment: IPA Backup
|
||||
Name-Email: root@example.com
|
||||
Expire-Date: 0
|
||||
Passphrase: {passphrase}
|
||||
%commit
|
||||
%echo done
|
||||
""")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def gpgkey(request, tempdir):
|
||||
passphrase = "Secret123"
|
||||
gnupghome = os.path.join(tempdir, "gnupg")
|
||||
os.makedirs(gnupghome, 0o700)
|
||||
# provide clean env for gpg test
|
||||
env = os.environ.copy()
|
||||
orig_gnupghome = env.get('GNUPGHOME')
|
||||
env['GNUPGHOME'] = gnupghome
|
||||
env['LC_ALL'] = 'C.UTF-8'
|
||||
env['LANGUAGE'] = 'C'
|
||||
devnull = open(os.devnull, 'w')
|
||||
|
||||
# allow passing passphrases to agent
|
||||
with open(os.path.join(gnupghome, "gpg-agent.conf"), 'w') as f:
|
||||
f.write("verbose\n")
|
||||
f.write("allow-preset-passphrase\n")
|
||||
|
||||
# run agent in background
|
||||
agent = subprocess.Popen(
|
||||
[paths.GPG_AGENT, '--batch', '--daemon'],
|
||||
env=env, stdout=devnull, stderr=devnull
|
||||
)
|
||||
|
||||
def fin():
|
||||
if orig_gnupghome is not None:
|
||||
os.environ['GNUPGHOME'] = orig_gnupghome
|
||||
else:
|
||||
os.environ.pop('GNUPGHOME', None)
|
||||
agent.kill()
|
||||
agent.wait()
|
||||
|
||||
request.addfinalizer(fin)
|
||||
|
||||
# create public / private key pair
|
||||
keygen = os.path.join(gnupghome, 'keygen')
|
||||
with open(keygen, 'w') as f:
|
||||
f.write(GPG_GENKEY.format(passphrase=passphrase))
|
||||
subprocess.check_call(
|
||||
[paths.GPG2, '--batch', '--gen-key', keygen],
|
||||
env=env, stdout=devnull, stderr=devnull
|
||||
)
|
||||
|
||||
# get keygrip of private key
|
||||
out = subprocess.check_output(
|
||||
[paths.GPG2, "--list-secret-keys", "--with-keygrip"],
|
||||
env=env, stderr=subprocess.STDOUT
|
||||
)
|
||||
mo = re.search("Keygrip = ([A-Z0-9]{32,})", out.decode('utf-8'))
|
||||
if mo is None:
|
||||
raise ValueError(out.decode('utf-8'))
|
||||
keygrip = mo.group(1)
|
||||
|
||||
# unlock private key
|
||||
cmd = "PRESET_PASSPHRASE {} -1 {}".format(
|
||||
keygrip,
|
||||
binascii.hexlify(passphrase.encode('utf-8')).decode('utf-8')
|
||||
)
|
||||
subprocess.check_call(
|
||||
[paths.GPG_CONNECT_AGENT, cmd, "/bye"],
|
||||
env=env, stdout=devnull, stderr=devnull
|
||||
)
|
||||
|
||||
# set env for the rest of the progress
|
||||
os.environ['GNUPGHOME'] = gnupghome
|
||||
|
||||
|
||||
class test_set_directive_lines(object):
|
||||
def test_remove_directive(self):
|
||||
lines = installutils.set_directive_lines(
|
||||
@@ -198,3 +285,21 @@ def test_gpg_encrypt(tempdir):
|
||||
|
||||
with pytest.raises(ipautil.CalledProcessError):
|
||||
installutils.decrypt_file(encrypted, decrypted, password='invalid')
|
||||
|
||||
|
||||
def test_gpg_asymmetric(tempdir, gpgkey):
|
||||
src = os.path.join(tempdir, "asymmetric.txt")
|
||||
encrypted = src + ".gpg"
|
||||
payload = 'Dummy text\n'
|
||||
|
||||
with open(src, 'w') as f:
|
||||
f.write(payload)
|
||||
|
||||
ipa_backup.encrypt_file(src, remove_original=True)
|
||||
assert os.path.isfile(encrypted)
|
||||
assert not os.path.exists(src)
|
||||
|
||||
ipa_restore.decrypt_file(tempdir, encrypted)
|
||||
assert os.path.isfile(src)
|
||||
with open(src) as f:
|
||||
assert f.read() == payload
|
||||
|
||||
Reference in New Issue
Block a user