From 1cbcf598dac96c56ffcef7a8984538530f4101b9 Mon Sep 17 00:00:00 2001 From: Dmitry Tantsur Date: Thu, 10 Dec 2020 16:16:16 +0100 Subject: [PATCH] Generate TLS certificates with validity time in the past Otherwise a slight clock skew may prevent them from working, see e.g. https://bugzilla.redhat.com/show_bug.cgi?id=1906448. Change-Id: Icea103af06edef16c0dc4578877dc04cd6ec3b0c (cherry picked from commit 557293ca6adb7bd949e8b800e4ca03fc3ea86c92) --- ironic_python_agent/config.py | 5 +++++ .../tests/unit/test_tls_utils.py | 7 +++++++ ironic_python_agent/tls_utils.py | 18 +++++++++++++++--- .../notes/clock-skew-1fbf542b193cec17.yaml | 5 +++++ 4 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 releasenotes/notes/clock-skew-1fbf542b193cec17.yaml diff --git a/ironic_python_agent/config.py b/ironic_python_agent/config.py index c7afee015..a98fec13e 100644 --- a/ironic_python_agent/config.py +++ b/ironic_python_agent/config.py @@ -72,6 +72,11 @@ cli_opts = [ 'is False and ironic API version indicates support for ' 'automatic agent TLS.'), + cfg.IntOpt('auto_tls_allowed_clock_skew', + default=3600, min=0, + help='Clock skew (in seconds) allowed in the generated TLS ' + 'certificate.'), + cfg.StrOpt('advertise_host', default=APARAMS.get('ipa-advertise-host', None), help='The host to tell Ironic to reply and send ' diff --git a/ironic_python_agent/tests/unit/test_tls_utils.py b/ironic_python_agent/tests/unit/test_tls_utils.py index 1c847cb6b..732139a98 100644 --- a/ironic_python_agent/tests/unit/test_tls_utils.py +++ b/ironic_python_agent/tests/unit/test_tls_utils.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import datetime import ipaddress import os import tempfile @@ -36,6 +37,7 @@ class GenerateTestCase(ironic_agent_base.IronicAgentTest): result = tls_utils._generate_tls_certificate(self.crt_file, self.key_file, 'localhost', '127.0.0.1') + now = datetime.datetime.utcnow() self.assertTrue(result.startswith("-----BEGIN CERTIFICATE-----\n"), result) self.assertTrue(result.endswith("\n-----END CERTIFICATE-----\n"), @@ -48,6 +50,11 @@ class GenerateTestCase(ironic_agent_base.IronicAgentTest): backends.default_backend()) self.assertEqual([(x509.NameOID.COMMON_NAME, 'localhost')], [(item.oid, item.value) for item in cert.subject]) + # Sanity check for validity range + self.assertLess(cert.not_valid_before, + now - datetime.timedelta(seconds=1800)) + self.assertGreater(cert.not_valid_after, + now + datetime.timedelta(seconds=1800)) subject_alt_name = cert.extensions.get_extension_for_oid( x509.ExtensionOID.SUBJECT_ALTERNATIVE_NAME) self.assertTrue(subject_alt_name.critical) diff --git a/ironic_python_agent/tls_utils.py b/ironic_python_agent/tls_utils.py index f38fb7e23..62adec9e8 100644 --- a/ironic_python_agent/tls_utils.py +++ b/ironic_python_agent/tls_utils.py @@ -22,10 +22,16 @@ from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives import serialization from cryptography import x509 +from oslo_log import log +from ironic_python_agent import config from ironic_python_agent import netutils +LOG = log.getLogger() +CONF = config.CONF + + def _create_private_key(output): """Create a new private key and write it to a file. @@ -70,19 +76,25 @@ def _generate_tls_certificate(output, private_key_output, x509.NameAttribute(x509.NameOID.COMMON_NAME, common_name), ]) alt_name = x509.SubjectAlternativeName([x509.IPAddress(ip_address)]) + allowed_clock_skew = CONF.auto_tls_allowed_clock_skew + not_valid_before = (datetime.datetime.utcnow() + - datetime.timedelta(seconds=allowed_clock_skew)) + not_valid_after = (datetime.datetime.utcnow() + + datetime.timedelta(days=valid_for_days)) cert = (x509.CertificateBuilder() .subject_name(subject) .issuer_name(subject) .public_key(private_key.public_key()) .serial_number(x509.random_serial_number()) - .not_valid_before(datetime.datetime.utcnow()) - .not_valid_after(datetime.datetime.utcnow() - + datetime.timedelta(days=valid_for_days)) + .not_valid_before(not_valid_before) + .not_valid_after(not_valid_after) .add_extension(alt_name, critical=True) .sign(private_key, hashes.SHA256(), backends.default_backend())) pub_bytes = cert.public_bytes(serialization.Encoding.PEM) with open(output, "wb") as f: f.write(pub_bytes) + LOG.info('Generated TLS certificate for IP address %s valid from %s ' + 'to %s', ip_address, not_valid_before, not_valid_after) return pub_bytes.decode('utf-8') diff --git a/releasenotes/notes/clock-skew-1fbf542b193cec17.yaml b/releasenotes/notes/clock-skew-1fbf542b193cec17.yaml new file mode 100644 index 000000000..dc363de1c --- /dev/null +++ b/releasenotes/notes/clock-skew-1fbf542b193cec17.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + Automatically generated TLS certificates now have their validity starting + in the past (1 hour by default) to allow for clock skew.