Reuse key renewal params (#8343)

* Ensure key params are stored in renewal config when --reuse-key is set.

* Fix mypy definition

* Add unit test

* Clean code.
This commit is contained in:
Adrien Ferrand 2020-10-05 20:50:45 +02:00 committed by GitHub
parent cc76906712
commit 34694251dd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 45 additions and 5 deletions

View file

@ -9,16 +9,21 @@ import sys
import time
import traceback
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives.serialization import load_pem_private_key
import OpenSSL
import six
import zope.component
from acme.magic_typing import List
from acme.magic_typing import Optional # pylint: disable=unused-import
from certbot import crypto_util
from certbot import errors
from certbot import interfaces
from certbot import util
from certbot._internal import cli
from certbot._internal import client # pylint: disable=unused-import
from certbot._internal import constants
from certbot._internal import hooks
from certbot._internal import storage
@ -308,7 +313,8 @@ def _avoid_invalidating_lineage(config, lineage, original_server):
def renew_cert(config, domains, le_client, lineage):
"Renew a certificate lineage."
# type: (interfaces.IConfig, Optional[List[str]], client.Client, storage.RenewableCert) -> None
"""Renew a certificate lineage."""
renewal_params = lineage.configuration["renewalparams"]
original_server = renewal_params.get("server", cli.flag_default("server"))
_avoid_invalidating_lineage(config, lineage, original_server)
@ -316,11 +322,14 @@ def renew_cert(config, domains, le_client, lineage):
domains = lineage.names()
# The private key is the existing lineage private key if reuse_key is set.
# Otherwise, generate a fresh private key by passing None.
new_key = os.path.normpath(lineage.privkey) if config.reuse_key else None
if config.reuse_key:
new_key = os.path.normpath(lineage.privkey)
_update_renewal_params_from_key(new_key, config)
else:
new_key = None
new_cert, new_chain, new_key, _ = le_client.obtain_certificate(domains, new_key)
if config.dry_run:
logger.debug("Dry run: skipping updating lineage at %s",
os.path.dirname(lineage.cert))
logger.debug("Dry run: skipping updating lineage at %s", os.path.dirname(lineage.cert))
else:
prior_version = lineage.latest_common_version()
# TODO: Check return value of save_successor
@ -335,6 +344,7 @@ def report(msgs, category):
lines = ("%s (%s)" % (m, category) for m in msgs)
return " " + "\n ".join(lines)
def _renew_describe_results(config, renew_successes, renew_failures,
renew_skipped, parse_failures):
@ -489,3 +499,13 @@ def handle_renewal_request(config):
# Windows installer integration tests rely on handle_renewal_request behavior here.
# If the text below changes, these tests will need to be updated accordingly.
logger.debug("no renewal failures")
def _update_renewal_params_from_key(key_path, config):
# type: (str, interfaces.IConfig) -> None
with open(key_path, 'rb') as file_h:
key = load_pem_private_key(file_h.read(), password=None, backend=default_backend())
if isinstance(key, rsa.RSAPrivateKey):
config.rsa_key_size = key.key_size
else:
raise errors.Error('Key at {0} is of an unsupported type: {1}.'.format(key_path, type(key)))

View file

@ -3,7 +3,7 @@ import unittest
try:
import mock
except ImportError: # pragma: no cover
except ImportError: # pragma: no cover
from unittest import mock
from acme import challenges
@ -54,6 +54,26 @@ class RenewalTest(test_util.ConfigTestCase):
self.assertEqual(self.config.webroot_map, {})
self.assertEqual(self.config.webroot_path, ['/var/www/test'])
def test_reuse_key_renewal_params(self):
self.config.rsa_key_size = 'INVALID_VALUE'
self.config.reuse_key = True
self.config.dry_run = True
config = configuration.NamespaceConfig(self.config)
rc_path = test_util.make_lineage(
self.config.config_dir, 'sample-renewal.conf')
lineage = storage.RenewableCert(rc_path, config)
le_client = mock.MagicMock()
le_client.obtain_certificate.return_value = (None, None, None, None)
from certbot._internal import renewal
with mock.patch('certbot._internal.renewal.hooks.renew_hook'):
renewal.renew_cert(self.config, None, le_client, lineage)
assert self.config.rsa_key_size == 2048
class RestoreRequiredConfigElementsTest(test_util.ConfigTestCase):
"""Tests for certbot._internal.renewal.restore_required_config_elements."""