Example #1
0
 def test_generate_rsa_if_needed_waits_for_creation(self):
     mock_isfile = self.patch(maas_certificates.os.path, "isfile")
     mock_isfile.side_effect = [False, False, True, True]
     mock_sleep = self.patch(maas_certificates, "sleep")
     with NamedLock("RSA"):
         self.assertTrue(maas_certificates.generate_rsa_keys_if_needed())
     self.assertThat(mock_sleep, MockCalledOnce())
Example #2
0
 def test_generate_certificate_if_needed_raises_exception_on_failure(self):
     self.patch(maas_certificates, "sleep")
     with NamedLock("certificate"):
         self.assertRaises(
             AssertionError,
             maas_certificates.generate_certificate_if_needed,
         )
Example #3
0
def generate_rsa_keys_if_needed():
    """Generate RSA keys for MAAS.

    Returns True if a new RSA key was generated.
    """
    if os.path.isfile(MAAS_PRIVATE_KEY):
        return False
    try:
        with NamedLock("RSA"):
            os.makedirs(os.path.dirname(MAAS_PRIVATE_KEY), exist_ok=True)
            pkey = crypto.PKey()
            pkey.generate_key(crypto.TYPE_RSA, 4096)
            with open(MAAS_PRIVATE_KEY, "wb") as f:
                f.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, pkey))
            os.chmod(MAAS_PRIVATE_KEY, 0o600)
            with open(MAAS_PUBLIC_KEY, "wb") as f:
                f.write(crypto.dump_publickey(crypto.FILETYPE_PEM, pkey))
    except NamedLock.NotAvailable:
        # System is running a region and rack. The other process
        # is generating the key, wait up to 60s for it.
        waits = 0
        while not os.path.isfile(MAAS_PRIVATE_KEY) and waits < 600:
            sleep(0.1)
            waits += 1
        assert os.path.isfile(
            MAAS_PRIVATE_KEY), "Unable to generate MAAS RSA keys!"
    return True
Example #4
0
def generate_certificate_if_needed(not_before=0,
                                   not_after=(60 * 60 * 24 * 365)):
    """Generate RSA keys and certificate if needed.

    Returns True if a new certificate was generated.
    """
    generate_rsa_keys_if_needed()
    if os.path.isfile(MAAS_CERTIFICATE):
        # Certificate exists, check if its still valid.
        with open(MAAS_CERTIFICATE, "rb") as f:
            cert = crypto.load_certificate(crypto.FILETYPE_PEM, f.read())
        now = datetime.utcnow()
        cert_not_before = datetime.strptime(cert.get_notBefore().decode(),
                                            "%Y%m%d%H%M%SZ")
        cert_not_after = datetime.strptime(cert.get_notAfter().decode(),
                                           "%Y%m%d%H%M%SZ")
        needs_certificate = (now < cert_not_before) or (now > cert_not_after)
    else:
        needs_certificate = True

    if not needs_certificate:
        return False

    try:
        with NamedLock("certificate"):
            with open(MAAS_PRIVATE_KEY, "rb") as f:
                pkey = crypto.load_privatekey(crypto.FILETYPE_PEM, f.read())

            cert_req = crypto.X509Req()
            cert_req.get_subject().CN = gethostname()
            cert_req.set_pubkey(pkey)
            cert_req.sign(pkey, "sha512")

            cert = crypto.X509()
            cert.get_subject().CN = cert_req.get_subject().CN
            cert.gmtime_adj_notBefore(not_before)
            cert.gmtime_adj_notAfter(not_after)
            cert.set_pubkey(cert_req.get_pubkey())
            cert.sign(pkey, "sha512")

            if os.path.exists(MAAS_CERTIFICATE):
                os.remove(MAAS_CERTIFICATE)
            with open(MAAS_CERTIFICATE, "wb") as f:
                f.write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert))
    except NamedLock.NotAvailable:
        # System is running a region and rack. The other process
        # is generating the certificate, wait up to 60s for it.
        waits = 0
        while not os.path.isfile(MAAS_CERTIFICATE) and waits < 600:
            sleep(0.1)
            waits += 1
        assert os.path.isfile(
            MAAS_CERTIFICATE), "Unable to generate MAAS certificate!"
    return True
Example #5
0
 def test_string_name(self):
     name = factory.make_name("lock")
     expected = get_tentative_data_path("/run/lock/maas:" + name)
     observed = NamedLock(name).path
     self.assertEqual(expected, observed)
Example #6
0
def generate_certificate_if_needed(not_before=0,
                                   not_after=(60 * 60 * 24 * 365)):
    """Generate RSA keys and certificate if needed.

    Returns True if a new certificate was generated.
    """
    global _cert_not_before, _cert_not_after, _cert_mtime
    if not generate_rsa_keys_if_needed() and os.path.isfile(MAAS_CERTIFICATE):
        if not _cert_not_before or not _cert_not_after or not _cert_mtime:
            # Certificate exists, but the before and after times haven't been
            # cached.
            with open(MAAS_CERTIFICATE, "rb") as f:
                _cert_mtime = os.path.getmtime(MAAS_CERTIFICATE)
                cert = crypto.load_certificate(crypto.FILETYPE_PEM, f.read())
            _cert_not_before = datetime.strptime(cert.get_notBefore().decode(),
                                                 "%Y%m%d%H%M%SZ")
            _cert_not_after = datetime.strptime(cert.get_notAfter().decode(),
                                                "%Y%m%d%H%M%SZ")
        # Check if the certificate is valid.
        now = datetime.utcnow()
        needs_certificate = (now < _cert_not_before) or (now > _cert_not_after)
    else:
        needs_certificate = True

    if not needs_certificate:
        return False
    elif _cert_mtime and _cert_mtime != os.path.getmtime(MAAS_CERTIFICATE):
        # Certificate was updated by another process. Invalidate cache and
        # try again.
        _cert_not_before = _cert_not_after = _cert_mtime = None
        return generate_certificate_if_needed(not_before, not_after)

    try:
        lock = NamedLock("certificate")
        with lock:
            with open(MAAS_PRIVATE_KEY, "rb") as f:
                pkey = crypto.load_privatekey(crypto.FILETYPE_PEM, f.read())

            cert_req = crypto.X509Req()
            cert_req.get_subject().CN = gethostname()
            cert_req.set_pubkey(pkey)
            cert_req.sign(pkey, "sha512")

            cert = crypto.X509()
            cert.get_subject().CN = cert_req.get_subject().CN
            cert.gmtime_adj_notBefore(not_before)
            cert.gmtime_adj_notAfter(not_after)
            cert.set_pubkey(cert_req.get_pubkey())
            cert.sign(pkey, "sha512")

            if os.path.exists(MAAS_CERTIFICATE):
                os.remove(MAAS_CERTIFICATE)
            with open(MAAS_CERTIFICATE, "wb") as f:
                f.write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert))
    except NamedLock.NotAvailable:
        # System is running a region and rack. The other process
        # is generating the certificate, wait up to 60s for it.
        waits = 0
        while lock.is_locked() and waits < 600:
            sleep(0.1)
            waits += 1
        assert os.path.isfile(
            MAAS_CERTIFICATE), "Unable to generate MAAS certificate!"

    # Invalidate the before and after cache after a certificate has been
    # regenerated. OpenSSL only accepts the duration the certificate
    # should be valid for, not the exact times the certificate should
    # start and stop being valid for. Reread the before and after
    # times from the newly written certificate on next access.
    _cert_not_before = _cert_not_after = _cert_mtime = None

    return True