def update_apache_config(site): vhost = site['VHost'] server_name = site['ServerName'] Logger.info('Updating Apache configuration for virtual host at {}{}'.format( vhost, ', {}'.format(server_name) if server_name else '')) default_cert_path = apache_util.get_apache_ssl_cert_path(vhost, server_name) default_key_path = apache_util.get_apache_ssl_key_path(vhost, server_name) current_cert_path = apache_util.get_apache_vhost_directive( vhost, server_name, 'SSLCertificateFile') current_key_path = apache_util.get_apache_vhost_directive( vhost, server_name, 'SSLCertificateKeyFile') if current_cert_path != default_cert_path: apache_util.set_apache_vhost_directive( vhost, server_name, 'SSLCertificateFile', default_cert_path) if current_key_path != default_key_path: apache_util.set_apache_vhost_directive( vhost, server_name, 'SSLCertificateKeyFile', default_key_path)
def is_cert_renewal_needed(site): vhost = site['VHost'] server_name = site.get('ServerName') if (not os.path.isfile( apache_util.get_apache_ssl_cert_path( vhost, server_name))) or (not os.path.isfile( apache_util.get_apache_ssl_key_path(vhost, server_name))): Logger.info( "Certificate for {} {} does not exist and needs renewal".format( vhost, server_name or '')) return True host, port = apache_util.parse_connection_address_from_vhost(vhost) try: pem_cert = ssl.get_server_certificate((host, port)) except socket.error as e: raise Exception( 'Could not retrieve server certificate from "{}:{}": {}'.format( host, port, e)) # Check whether the cert is expired cert_expired, cert_expiration_utc = util.is_cert_expired( pem_cert, vhost, site['KeyTalkProvider'], site['KeyTalkService'], Logger) if cert_expired: Logger.info( "Certificate for {} {} effectively expires at {} UTC and needs renewal" .format(vhost, server_name or '', cert_expiration_utc)) return True # Check whether the cert is revoked if util.is_cert_revoked(pem_cert, Logger): Logger.info( "Certificate for {} {} has been revoked and needs renewal".format( vhost, server_name or '')) return True # The cert doesn't need renewal Logger.info( "Certificate for {} {} effectively expires at {} UTC and does not require renewal (run with {} to renew anyway)" .format(vhost, server_name or '', cert_expiration_utc, force_arg)) return False
def install_apache_ssl_cert(pem_cert_key_path, site, restart_apache=False): vhost = site['VHost'] Logger.info( 'Installing SSL certificate for virtual host at {VHost}'.format( **site)) server_name = site['ServerName'] ssl_cert_path = apache_util.get_apache_ssl_cert_path(vhost, server_name) ssl_key_path = apache_util.get_apache_ssl_key_path(vhost, server_name) certs = util.parse_certs(pem_cert_key_path, Logger) if not certs: raise Exception( "No X.509 certs found in {} received by KeyTalk client".format( pem_cert_key_path)) keys = util.parse_keys(pem_cert_key_path, Logger) if not keys: raise Exception( "No X.509 keys found in {} received by KeyTalk client".format( pem_cert_key_path)) cas = util.parse_cas(Logger) if util.same_file(ssl_cert_path, ssl_key_path): Logger.debug("Saving SSL certificate with key and {} CAs to {}".format( len(cas), ssl_cert_path)) util.save_to_file('\n'.join(certs + keys + cas), ssl_cert_path) else: Logger.debug( "Saving SSL certificates (serial: {}) and {} CAs to {}".format( OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, certs[0]).get_serial_number(), len(cas), ssl_cert_path)) util.save_to_file('\n'.join(certs + cas), ssl_cert_path) Logger.debug("Saving SSL key to " + ssl_key_path) util.save_to_file('\n'.join(keys), ssl_key_path) # ask Apache to gracefully reload key material if restart_apache: reload_apache()
def process_vhost(site_configuration, current_vhost_string, current_vhost_name, apache_vhosts, force_renew_certs): global error_messages global warning_messages current_site = site_configuration try: # Validation parsed_site, validation_errors = util.parse_settings( current_site, util.APACHE_RENEWAL_SETTINGS) # parsed_site either contains correctly parsed site settings with populated defaults or None (and a non-empty validation error list). # Even if parsing failed, we want to present the user with a complete error list. So for purposes of further validation # we fill current_site at least defined values (e.g. None) using # populate_defaults such that validate_site_configuration can is able to # do its checks. current_site = parsed_site or util.populate_defaults( current_site, util.APACHE_RENEWAL_SETTINGS) validation_errors.extend( validate_site_configuration(current_site, apache_vhosts.keys())) if validation_errors: message = make_validation_error_message(validation_errors, current_vhost_string) log_error(message) raise Exception( 'Errors during validation of VHost "{}, {}".'.format( current_vhost_string, current_vhost_name)) # Processing if not apache_util.is_apache_running(): if (not os.path.isfile( apache_util.get_apache_ssl_cert_path( current_vhost_string, current_vhost_name))) or (not os.path.isfile( apache_util.get_apache_ssl_key_path( current_vhost_string, current_vhost_name))): log_warning( 'Apache is not running, but certificate/keyfile missing. Attempting to correct. Note: this will not restart apache.' ) pem_cert_key_path = get_cert(current_site) install_apache_ssl_cert(pem_cert_key_path, current_site) email_results(current_site) return True else: raise Exception( 'Apache is not running, skipping certificate update for VHost {} {}.' .format(current_vhost_string, current_vhost_name)) update_apache_config(current_site) if force_renew_certs or is_cert_renewal_needed(current_site): pem_cert_key_path = get_cert(current_site) install_apache_ssl_cert(pem_cert_key_path, current_site) except Exception as e: # Log error, but continue processing the next VHost log_error( 'VHost "{}, {}": {} {} {}'.format(current_vhost_string, current_vhost_name, type(e), e, util.format_traceback()), 'VHost "{}, {}": {} {}'.format(current_vhost_string, current_vhost_name, type(e), e)) return False finally: email_results(current_site) error_messages = [] warning_messages = [] return True