def test_ipa_healthcheck_expiring(self): """ There are two overlapping tests for expiring certs, check both. """ def execute_expiring_check(check): """ Test that certmonger will report warnings if expiration is near """ returncode, data = run_healthcheck( self.master, "ipahealthcheck.ipa.certs", check, ) assert returncode == 1 assert len(data) == 9 # non-KRA is 9 tracked certs for check in data: if check["result"] == "SUCCESS": # The CA is not expired request = self.master.run_command( ["getcert", "list", "-i", check["kw"]["key"]] ) assert "caSigningCert cert-pki-ca" in request.stdout_text else: assert check["result"] == "WARNING" if check["kw"]["days"] == 21: # the httpd, 389-ds and KDC renewal dates are later certs = (paths.HTTPD_CERT_FILE, paths.KDC_CERT, '/etc/dirsrv/slapd-',) request = self.master.run_command( ["getcert", "list", "-i", check["kw"]["key"]] ) assert any(cert in request.stdout_text for cert in certs) else: assert check["kw"]["days"] == 10 # Pick a cert to find the upcoming expiration certfile = self.master.get_file_contents(paths.RA_AGENT_PEM) cert = x509.load_certificate_list(certfile) cert_expiry = cert[0].not_valid_after # move date to the grace period self.master.run_command(['systemctl', 'stop', 'chronyd']) grace_date = cert_expiry - timedelta(days=10) grace_date = datetime.strftime(grace_date, "%Y-%m-%d 00:00:01 Z") self.master.run_command(['date', '-s', grace_date]) for check in ("IPACertmongerExpirationCheck", "IPACertfileExpirationCheck",): execute_expiring_check(check) self.master.run_command(['systemctl', 'start', 'chronyd'])
def get_cert_list_from_db(self, nssdb, nickname): """ Retrieve all certificates from an NSS database for nickname. """ try: args = ["-L", "-n", nickname, "-a"] result = nssdb.run_certutil(args, capture_output=True) return x509.load_certificate_list(result.raw_output) except ipautil.CalledProcessError: return []
def test_KRA_install_after_cert_renew(self): tasks.install_master(self.master) # get ca-agent cert and load as pem dm_pass = self.master.config.dirman_password admin_pass = self.master.config.admin_password args = [ paths.OPENSSL, "pkcs12", "-in", paths.DOGTAG_ADMIN_P12, "-nodes", "-passin", "pass:{}".format(dm_pass) ] cmd = self.master.run_command(args) certs = x509.load_certificate_list(cmd.stdout_text.encode('utf-8')) # get expiry date of agent cert cert_expiry = certs[0].not_valid_after # move date to grace period so that certs get renewed self.master.run_command(['systemctl', 'stop', 'chronyd']) grace_date = cert_expiry - timedelta(days=10) grace_date = datetime.strftime(grace_date, "%Y-%m-%d %H:%M:%S") self.master.run_command(['date', '-s', grace_date]) # get the count of certs track by certmonger cmd = self.master.run_command(['getcert', 'list']) cert_count = cmd.stdout_text.count('Request ID') timeout = 600 count = 0 start = time.time() # wait sometime for cert renewal while time.time() - start < timeout: cmd = self.master.run_command(['getcert', 'list']) count = cmd.stdout_text.count('status: MONITORING') if count == cert_count: break time.sleep(100) else: # timeout raise AssertionError('TimeOut: Failed to renew all the certs') # move date after 3 days of actual expiry cert_expiry = cert_expiry + timedelta(days=3) cert_expiry = datetime.strftime(cert_expiry, "%Y-%m-%d %H:%M:%S") self.master.run_command(['date', '-s', cert_expiry]) passwd = "{passwd}\n{passwd}\n{passwd}".format(passwd=admin_pass) self.master.run_command(['kinit', 'admin'], stdin_text=passwd) cmd = self.master.run_command(['ipa-kra-install', '-p', dm_pass, '-U']) self.master.run_command(['systemctl', 'start', 'chronyd'])
def test_KRA_install_after_cert_renew(self): tasks.install_master(self.master) # get ca-agent cert and load as pem dm_pass = self.master.config.dirman_password admin_pass = self.master.config.admin_password args = [paths.OPENSSL, "pkcs12", "-in", paths.DOGTAG_ADMIN_P12, "-nodes", "-passin", "pass:{}".format(dm_pass)] cmd = self.master.run_command(args) certs = x509.load_certificate_list(cmd.stdout_text.encode('utf-8')) # get expiry date of agent cert cert_expiry = certs[0].not_valid_after # move date to grace period so that certs get renewed self.master.run_command(['systemctl', 'stop', 'chronyd']) grace_date = cert_expiry - timedelta(days=10) grace_date = datetime.strftime(grace_date, "%Y-%m-%d %H:%M:%S") self.master.run_command(['date', '-s', grace_date]) # get the count of certs track by certmonger cmd = self.master.run_command(['getcert', 'list']) cert_count = cmd.stdout_text.count('Request ID') timeout = 600 count = 0 start = time.time() # wait sometime for cert renewal while time.time() - start < timeout: cmd = self.master.run_command(['getcert', 'list']) count = cmd.stdout_text.count('status: MONITORING') if count == cert_count: break time.sleep(100) else: # timeout raise AssertionError('TimeOut: Failed to renew all the certs') # move date after 3 days of actual expiry cert_expiry = cert_expiry + timedelta(days=3) cert_expiry = datetime.strftime(cert_expiry, "%Y-%m-%d %H:%M:%S") self.master.run_command(['date', '-s', cert_expiry]) passwd = "{passwd}\n{passwd}\n{passwd}".format(passwd=admin_pass) self.master.run_command(['kinit', 'admin'], stdin_text=passwd) cmd = self.master.run_command(['ipa-kra-install', '-p', dm_pass, '-U']) self.master.run_command(['systemctl', 'start', 'chronyd'])
def pkcs12_to_certkeys(p12_fname, p12_passwd=None): """ Deserializes pkcs12 file to python objects :param p12_fname: A PKCS#12 filename :param p12_passwd: Optional password for the pkcs12_fname file """ args = [paths.OPENSSL, "pkcs12", "-in", p12_fname, "-nodes"] if p12_passwd: pwd = ipautil.write_tmp_file(p12_passwd) args.extend(["-passin", "file:{fname}".format(fname=pwd.name)]) else: args.extend(["-passin", "pass:"]) pems = ipautil.run(args, capture_output=True).raw_output certs = x509.load_certificate_list(pems) priv_keys = x509.load_private_key_list(pems) return (certs, priv_keys)
def import_files(self, files, db_password_filename, import_keys=False, key_password=None, key_nickname=None): """ Import certificates and a single private key from multiple files The files may be in PEM and DER certificate, PKCS#7 certificate chain, PKCS#8 and raw private key and PKCS#12 formats. :param files: Names of files to import :param db_password_filename: Name of file containing the database password :param import_keys: Whether to import private keys :param key_password: Password to decrypt private keys :param key_nickname: Nickname of the private key to import from PKCS#12 files """ key_file = None extracted_key = None extracted_certs = '' for filename in files: try: with open(filename, 'rb') as f: data = f.read() except IOError as e: raise RuntimeError( "Failed to open %s: %s" % (filename, e.strerror)) # Try to parse the file as PEM file matches = list(re.finditer( r'-----BEGIN (.+?)-----(.*?)-----END \1-----', data, re.DOTALL)) if matches: loaded = False for match in matches: body = match.group() label = match.group(1) line = len(data[:match.start() + 1].splitlines()) if label in ('CERTIFICATE', 'X509 CERTIFICATE', 'X.509 CERTIFICATE'): try: x509.load_certificate(match.group(2)) except NSPRError as e: if label != 'CERTIFICATE': root_logger.warning( "Skipping certificate in %s at line %s: %s", filename, line, e) continue else: extracted_certs += body + '\n' loaded = True continue if label in ('PKCS7', 'PKCS #7 SIGNED DATA', 'CERTIFICATE'): args = [ paths.OPENSSL, 'pkcs7', '-print_certs', ] try: result = ipautil.run( args, stdin=body, capture_output=True) except ipautil.CalledProcessError as e: if label == 'CERTIFICATE': root_logger.warning( "Skipping certificate in %s at line %s: %s", filename, line, e) else: root_logger.warning( "Skipping PKCS#7 in %s at line %s: %s", filename, line, e) continue else: extracted_certs += result.output + '\n' loaded = True continue if label in ('PRIVATE KEY', 'ENCRYPTED PRIVATE KEY', 'RSA PRIVATE KEY', 'DSA PRIVATE KEY', 'EC PRIVATE KEY'): if not import_keys: continue if key_file: raise RuntimeError( "Can't load private key from both %s and %s" % (key_file, filename)) args = [ paths.OPENSSL, 'pkcs8', '-topk8', '-passout', 'file:' + db_password_filename, ] if ((label != 'PRIVATE KEY' and key_password) or label == 'ENCRYPTED PRIVATE KEY'): key_pwdfile = ipautil.write_tmp_file(key_password) args += [ '-passin', 'file:' + key_pwdfile.name, ] try: result = ipautil.run( args, stdin=body, capture_output=True) except ipautil.CalledProcessError as e: root_logger.warning( "Skipping private key in %s at line %s: %s", filename, line, e) continue else: extracted_key = result.output key_file = filename loaded = True continue if loaded: continue raise RuntimeError("Failed to load %s" % filename) # Try to load the file as DER certificate try: x509.load_certificate(data, x509.DER) except NSPRError: pass else: data = x509.make_pem(base64.b64encode(data)) extracted_certs += data + '\n' continue # Try to import the file as PKCS#12 file if import_keys: try: self.import_pkcs12( filename, db_password_filename, key_password) except RuntimeError: pass else: if key_file: raise RuntimeError( "Can't load private key from both %s and %s" % (key_file, filename)) key_file = filename server_certs = self.find_server_certs() if key_nickname: for nickname, trust_flags in server_certs: if nickname == key_nickname: break else: raise RuntimeError( "Server certificate \"%s\" not found in %s" % (key_nickname, filename)) else: if len(server_certs) > 1: raise RuntimeError( "%s server certificates found in %s, " "expecting only one" % (len(server_certs), filename)) continue raise RuntimeError("Failed to load %s" % filename) if import_keys and not key_file: raise RuntimeError( "No server certificates found in %s" % (', '.join(files))) nss_certs = x509.load_certificate_list(extracted_certs) nss_cert = None for nss_cert in nss_certs: nickname = str(nss_cert.subject) self.add_cert(nss_cert.der_data, nickname, ',,') del nss_certs, nss_cert if extracted_key: in_file = ipautil.write_tmp_file(extracted_certs + extracted_key) out_file = tempfile.NamedTemporaryFile() out_password = ipautil.ipa_generate_password() out_pwdfile = ipautil.write_tmp_file(out_password) args = [ paths.OPENSSL, 'pkcs12', '-export', '-in', in_file.name, '-out', out_file.name, '-passin', 'file:' + db_password_filename, '-passout', 'file:' + out_pwdfile.name, ] try: ipautil.run(args) except ipautil.CalledProcessError as e: raise RuntimeError( "No matching certificate found for private key from %s" % key_file) self.import_pkcs12(out_file.name, db_password_filename, out_password)
def import_files(self, files, db_password_filename, import_keys=False, key_password=None, key_nickname=None): """ Import certificates and a single private key from multiple files The files may be in PEM and DER certificate, PKCS#7 certificate chain, PKCS#8 and raw private key and PKCS#12 formats. :param files: Names of files to import :param db_password_filename: Name of file containing the database password :param import_keys: Whether to import private keys :param key_password: Password to decrypt private keys :param key_nickname: Nickname of the private key to import from PKCS#12 files """ key_file = None extracted_key = None extracted_certs = '' for filename in files: try: with open(filename, 'rb') as f: data = f.read() except IOError as e: raise RuntimeError("Failed to open %s: %s" % (filename, e.strerror)) # Try to parse the file as PEM file matches = list( re.finditer(r'-----BEGIN (.+?)-----(.*?)-----END \1-----', data, re.DOTALL)) if matches: loaded = False for match in matches: body = match.group() label = match.group(1) line = len(data[:match.start() + 1].splitlines()) if label in ('CERTIFICATE', 'X509 CERTIFICATE', 'X.509 CERTIFICATE'): try: x509.load_certificate(match.group(2)) except ValueError as e: if label != 'CERTIFICATE': root_logger.warning( "Skipping certificate in %s at line %s: %s", filename, line, e) continue else: extracted_certs += body + '\n' loaded = True continue if label in ('PKCS7', 'PKCS #7 SIGNED DATA', 'CERTIFICATE'): args = [ paths.OPENSSL, 'pkcs7', '-print_certs', ] try: result = ipautil.run(args, stdin=body, capture_output=True) except ipautil.CalledProcessError as e: if label == 'CERTIFICATE': root_logger.warning( "Skipping certificate in %s at line %s: %s", filename, line, e) else: root_logger.warning( "Skipping PKCS#7 in %s at line %s: %s", filename, line, e) continue else: extracted_certs += result.output + '\n' loaded = True continue if label in ('PRIVATE KEY', 'ENCRYPTED PRIVATE KEY', 'RSA PRIVATE KEY', 'DSA PRIVATE KEY', 'EC PRIVATE KEY'): if not import_keys: continue if key_file: raise RuntimeError( "Can't load private key from both %s and %s" % (key_file, filename)) args = [ paths.OPENSSL, 'pkcs8', '-topk8', '-passout', 'file:' + db_password_filename, ] if ((label != 'PRIVATE KEY' and key_password) or label == 'ENCRYPTED PRIVATE KEY'): key_pwdfile = ipautil.write_tmp_file(key_password) args += [ '-passin', 'file:' + key_pwdfile.name, ] try: result = ipautil.run(args, stdin=body, capture_output=True) except ipautil.CalledProcessError as e: root_logger.warning( "Skipping private key in %s at line %s: %s", filename, line, e) continue else: extracted_key = result.output key_file = filename loaded = True continue if loaded: continue raise RuntimeError("Failed to load %s" % filename) # Try to load the file as DER certificate try: x509.load_certificate(data, x509.DER) except ValueError: pass else: data = x509.make_pem(base64.b64encode(data)) extracted_certs += data + '\n' continue # Try to import the file as PKCS#12 file if import_keys: try: self.import_pkcs12(filename, db_password_filename, key_password) except RuntimeError: pass else: if key_file: raise RuntimeError( "Can't load private key from both %s and %s" % (key_file, filename)) key_file = filename server_certs = self.find_server_certs() if key_nickname: for nickname, _trust_flags in server_certs: if nickname == key_nickname: break else: raise RuntimeError( "Server certificate \"%s\" not found in %s" % (key_nickname, filename)) else: if len(server_certs) > 1: raise RuntimeError( "%s server certificates found in %s, " "expecting only one" % (len(server_certs), filename)) continue raise RuntimeError("Failed to load %s" % filename) if import_keys and not key_file: raise RuntimeError("No server certificates found in %s" % (', '.join(files))) certs = x509.load_certificate_list(extracted_certs) for cert in certs: nickname = str(DN(cert.subject)) data = cert.public_bytes(serialization.Encoding.DER) self.add_cert(data, nickname, ',,') if extracted_key: in_file = ipautil.write_tmp_file(extracted_certs + extracted_key) out_file = tempfile.NamedTemporaryFile() out_password = ipautil.ipa_generate_password() out_pwdfile = ipautil.write_tmp_file(out_password) args = [ paths.OPENSSL, 'pkcs12', '-export', '-in', in_file.name, '-out', out_file.name, '-passin', 'file:' + db_password_filename, '-passout', 'file:' + out_pwdfile.name, ] try: ipautil.run(args) except ipautil.CalledProcessError as e: raise RuntimeError( "No matching certificate found for private key from %s" % key_file) self.import_pkcs12(out_file.name, db_password_filename, out_password)