def enforce_domain_sanity(domain): """Method which validates domain value and errors out if the requirements are not met. :param domain: Domain to check :type domain: `str` or `unicode` :raises ConfigurationError: for invalid domains and cases where Let's Encrypt currently will not issue certificates :returns: The domain cast to `str`, with ASCII-only contents :rtype: str """ if isinstance(domain, six.text_type): wildcard_marker = u"*." else: wildcard_marker = b"*." # Check if there's a wildcard domain if domain.startswith(wildcard_marker): raise errors.ConfigurationError( "Wildcard domains are not supported: {0}".format(domain)) # Unicode try: if isinstance(domain, six.binary_type): domain = domain.decode('utf-8') domain.encode('ascii') except UnicodeError: raise errors.ConfigurationError("Non-ASCII domain names not supported. " "To issue for an Internationalized Domain Name, use Punycode.") domain = domain.lower() # Remove trailing dot domain = domain[:-1] if domain.endswith(u'.') else domain # Explain separately that IP addresses aren't allowed (apart from not # being FQDNs) because hope springs eternal concerning this point try: socket.inet_aton(domain) raise errors.ConfigurationError( "Requested name {0} is an IP address. The Let's Encrypt " "certificate authority will not issue certificates for a " "bare IP address.".format(domain)) except socket.error: # It wasn't an IP address, so that's good pass # FQDN checks according to RFC 2181: domain name should be less than 255 # octets (inclusive). And each label is 1 - 63 octets (inclusive). # https://tools.ietf.org/html/rfc2181#section-11 msg = "Requested domain {0} is not a FQDN because ".format(domain) labels = domain.split('.') for l in labels: if not 0 < len(l) < 64: raise errors.ConfigurationError(msg + "label {0} is too long.".format(l)) if len(domain) > 255: raise errors.ConfigurationError(msg + "it is too long.") return domain
def _check_certificate_and_key(config): if not os.path.isfile(filesystem.realpath(config.cert_path)): raise errors.ConfigurationError("Error while reading certificate from path " "{0}".format(config.cert_path)) if not os.path.isfile(filesystem.realpath(config.key_path)): raise errors.ConfigurationError("Error while reading private key from path " "{0}".format(config.key_path))
def enforce_le_validity(domain): """Checks that Let's Encrypt will consider domain to be valid. :param str domain: FQDN to check :type domain: `str` or `unicode` :returns: The domain cast to `str`, with ASCII-only contents :rtype: str :raises ConfigurationError: for invalid domains and cases where Let's Encrypt currently will not issue certificates """ domain = enforce_domain_sanity(domain) if not re.match("^[A-Za-z0-9.-]*$", domain): raise errors.ConfigurationError( "{0} contains an invalid character. " "Valid characters are A-Z, a-z, 0-9, ., and -.".format(domain)) labels = domain.split(".") if len(labels) < 2: raise errors.ConfigurationError( "{0} needs at least two labels".format(domain)) for label in labels: if label.startswith("-"): raise errors.ConfigurationError( 'label "{0}" in domain "{1}" cannot start with "-"'.format( label, domain)) if label.endswith("-"): raise errors.ConfigurationError( 'label "{0}" in domain "{1}" cannot end with "-"'.format( label, domain)) return domain
def install(config, plugins): """Install a previously obtained cert in a server. :param config: Configuration object :type config: interfaces.IConfig :param plugins: List of plugins :type plugins: `list` of `str` :returns: `None` :rtype: None """ # XXX: Update for renewer/RenewableCert # FIXME: be consistent about whether errors are raised or returned from # this function ... try: installer, _ = plug_sel.choose_configurator_plugins(config, plugins, "install") except errors.PluginSelectionError as e: return str(e) custom_cert = (config.key_path and config.cert_path) if not config.certname and not custom_cert: certname_question = "Which certificate would you like to install?" config.certname = cert_manager.get_certnames( config, "install", allow_multiple=False, custom_prompt=certname_question)[0] if not enhancements.are_supported(config, installer): raise errors.NotSupportedError("One ore more of the requested enhancements " "are not supported by the selected installer") # If cert-path is defined, populate missing (ie. not overridden) values. # Unfortunately this can't be done in argument parser, as certificate # manager needs the access to renewal directory paths if config.certname: config = _populate_from_certname(config) elif enhancements.are_requested(config): # Preflight config check raise errors.ConfigurationError("One or more of the requested enhancements " "require --cert-name to be provided") if config.key_path and config.cert_path: _check_certificate_and_key(config) domains, _ = _find_domains_or_certname(config, installer) le_client = _init_le_client(config, authenticator=None, installer=installer) _install_cert(config, le_client, domains) else: raise errors.ConfigurationError("Path to certificate or key was not defined. " "If your certificate is managed by Certbot, please use --cert-name " "to define which certificate you would like to install.") if enhancements.are_requested(config): # In the case where we don't have certname, we have errored out already lineage = cert_manager.lineage_for_certname(config, config.certname) enhancements.enable(lineage, domains, installer, config) return None
def _ask_user_to_confirm_new_names(config, new_domains, certname, old_domains): """Ask user to confirm update cert certname to contain new_domains. :param config: Configuration object :type config: interfaces.IConfig :param new_domains: List of new domain names :type new_domains: `list` of `str` :param certname: Name of certificate :type certname: str :param old_domains: List of old domain names :type old_domains: `list` of `str` :returns: None :rtype: None :raises errors.ConfigurationError: if cert name and domains mismatch """ if config.renew_with_new_domains: return msg = ("You are updating certificate {0} to include domains: {1}{br}{br}" "It previously included domains: {2}{br}{br}" "Did you intend to make this change?".format( certname, ", ".join(new_domains), ", ".join(old_domains), br=os.linesep)) obj = zope.component.getUtility(interfaces.IDisplay) if not obj.yesno(msg, "Update cert", "Cancel", default=True): raise errors.ConfigurationError("Specified mismatched cert name and domains.")
def remove_irule_virtual(self, token, virtual_name): """remove iRule from virtual server :param token: challenge token :type token: string :param virtual_name: virtual server name :type virtual_name: string :raises errors.ConfigurationError: removal failed :return: True if successfull :rtype: bool """ try: r = self._split_fullpath(virtual_name) irules = self.irules_on_virtual(virtual_name) if irules["result"] is True: virtual = self.mgmt.tm.ltm.virtuals.virtual.load( partition=r[0], subPath=r[1], name=r[2]) if f"/{self.partition}/Certbot-Letsencrypt-{token}" in virtual.rules: virtual.rules.remove( f"/{self.partition}/Certbot-Letsencrypt-{token}") virtual.update() return True except Exception as e: msg = f"iRule removal from virtual server on {self.active_device} failed. {os.linesep}{e}{os.linesep}" raise errors.ConfigurationError(msg)
def rename_lineage(config: configuration.NamespaceConfig) -> None: """Rename the specified lineage to the new name. :param config: Configuration. :type config: :class:`certbot._internal.configuration.NamespaceConfig` """ certname = get_certnames(config, "rename")[0] new_certname = config.new_certname if not new_certname: code, new_certname = display_util.input_text( "Enter the new name for certificate {0}".format(certname), force_interactive=True) if code != display_util.OK or not new_certname: raise errors.Error("User ended interaction.") lineage = lineage_for_certname(config, certname) if not lineage: raise errors.ConfigurationError("No existing certificate with name " "{0} found.".format(certname)) storage.rename_renewal_config(certname, new_certname, config) display_util.notification("Successfully renamed {0} to {1}.".format( certname, new_certname), pause=False)
def rename_renewal_config(prev_name, new_name, cli_config): """Renames cli_config.certname's config to cli_config.new_certname. :param .NamespaceConfig cli_config: parsed command line arguments """ prev_filename = renewal_filename_for_lineagename(cli_config, prev_name) new_filename = renewal_filename_for_lineagename(cli_config, new_name) if os.path.exists(new_filename): raise errors.ConfigurationError("The new certificate name " "is already in use.") try: os.rename(prev_filename, new_filename) except OSError: raise errors.ConfigurationError("Please specify a valid filename " "for the new certificate name.")
def handle_csr(self, parsed_args): """Process a --csr flag.""" if parsed_args.verb != "certonly": raise errors.Error("Currently, a CSR file may only be specified " "when obtaining a new or replacement " "via the certonly command. Please try the " "certonly command instead.") if parsed_args.allow_subset_of_names: raise errors.Error("--allow-subset-of-names cannot be used with --csr") csrfile, contents = parsed_args.csr[0:2] typ, csr, domains = crypto_util.import_csr_file(csrfile, contents) # This is not necessary for webroot to work, however, # obtain_certificate_from_csr requires parsed_args.domains to be set for domain in domains: add_domains(parsed_args, domain) if not domains: # TODO: add CN to domains instead: raise errors.Error( "Unfortunately, your CSR %s needs to have a SubjectAltName for every domain" % parsed_args.csr[0]) parsed_args.actual_csr = (csr, typ) csr_domains, config_domains = set(domains), set(parsed_args.domains) if csr_domains != config_domains: raise errors.ConfigurationError( "Inconsistent domain requests:\nFrom the CSR: {0}\nFrom command line/config: {1}" .format(", ".join(csr_domains), ", ".join(config_domains)))
def rename_lineage(config): """Rename the specified lineage to the new name. :param config: Configuration. :type config: :class:`certbot.configuration.NamespaceConfig` """ disp = zope.component.getUtility(interfaces.IDisplay) certname = _get_certname(config, "rename") new_certname = config.new_certname if not new_certname: code, new_certname = disp.input( "Enter the new name for certificate {0}".format(certname), flag="--updated-cert-name", force_interactive=True) if code != display_util.OK or not new_certname: raise errors.Error("User ended interaction.") lineage = lineage_for_certname(config, certname) if not lineage: raise errors.ConfigurationError("No existing certificate with name " "{0} found.".format(certname)) storage.rename_renewal_config(certname, new_certname, config) disp.notification("Successfully renamed {0} to {1}.".format( certname, new_certname), pause=False)
def _find_lineage_for_domains_and_certname(config, domains, certname): """Find appropriate lineage based on given domains and/or certname. :returns: Two-element tuple containing desired new-certificate behavior as a string token ("reinstall", "renew", or "newcert"), plus either a RenewableCert instance or None if renewal shouldn't occur. :raises .Error: If the user would like to rerun the client again. """ if not certname: return _find_lineage_for_domains(config, domains) else: lineage = cert_manager.lineage_for_certname(config, certname) if lineage: if domains: if set(cert_manager.domains_for_certname( config, certname)) != set(domains): _ask_user_to_confirm_new_names( config, domains, certname, lineage.names()) # raises if no return "renew", lineage # unnecessarily specified domains or no domains specified return _handle_identical_cert_request(config, lineage) else: if domains: return "newcert", None else: raise errors.ConfigurationError( "No certificate with name {0} found. " "Use -d to specify domains, or run certbot --certificates to see " "possible certificate names.".format(certname))
def create_irule_HTTP01(self, token, http_response_content, apm): """create iRule for verification :param token: challenge token :type token: string :param http_response_content: challenge response value :type http_response_content: string :param apm: Flag if APM is enabled on this virtual server :type apm: bool :raises errors.ConfigurationError: creation failed :return: True iRule creation succeeded :rtype: bool """ try: irule_name = f"Certbot-Letsencrypt-{token}" if apm is True: irule_text = f'when CLIENT_ACCEPTED {{\n catch {{\n ACCESS::restrict_irule_events disable\n }}\n}}\nwhen HTTP_REQUEST priority 100 {{\n if {{[HTTP::has_responded]}}{{return}}\n if {{[HTTP::uri] equals "/.well-known/acme-challenge/{token}"}} {{\n HTTP::respond 200 -version auto content "{http_response_content}" noserver \n }}\n}}' else: irule_text = f'when HTTP_REQUEST priority 100 {{\n if {{[HTTP::has_responded]}}{{return}}\n if {{[HTTP::uri] equals "/.well-known/acme-challenge/{token}"}} {{\n HTTP::respond 200 -version auto content "{http_response_content}" noserver \n }}\n}}' self.mgmt.tm.ltm.rules.rule.create(name=irule_name, partition=self.partition, apiAnonymous=irule_text) return True except Exception as e: msg = f"iRule creation on {self.active_device} failed. {os.linesep}{e}{os.linesep}" raise errors.ConfigurationError(msg)
def rename_renewal_config(prev_name, new_name, cli_config): """Renames cli_config.certname's config to cli_config.new_certname. :param .RenewerConfiguration cli_config: parsed command line arguments """ prev_filename = os.path.join(cli_config.renewal_configs_dir, prev_name) + ".conf" new_filename = os.path.join(cli_config.renewal_configs_dir, new_name) + ".conf" if os.path.exists(new_filename): raise errors.ConfigurationError("The new certificate name " "is already in use.") try: os.rename(prev_filename, new_filename) except OSError: raise errors.ConfigurationError("Please specify a valid filename " "for the new certificate name.")
def _ask_user_to_confirm_new_names(config, new_domains, certname, old_domains): """Ask user to confirm update cert certname to contain new_domains. """ if config.renew_with_new_domains: return msg = ("Confirm that you intend to update certificate {0} " "to include domains {1}. Note that it previously " "contained domains {2}.".format(certname, new_domains, old_domains)) obj = zope.component.getUtility(interfaces.IDisplay) if not obj.yesno(msg, "Update cert", "Cancel", default=True): raise errors.ConfigurationError( "Specified mismatched cert name and domains.")
def enforce_domain_sanity(domain: Union[str, bytes]) -> str: """Method which validates domain value and errors out if the requirements are not met. :param domain: Domain to check :type domain: `str` or `bytes` :raises ConfigurationError: for invalid domains and cases where Let's Encrypt currently will not issue certificates :returns: The domain cast to `str`, with ASCII-only contents :rtype: str """ # Unicode try: if isinstance(domain, bytes): domain = domain.decode('utf-8') domain.encode('ascii') except UnicodeError: raise errors.ConfigurationError( "Non-ASCII domain names not supported. " "To issue for an Internationalized Domain Name, use Punycode.") domain = domain.lower() # Remove trailing dot domain = domain[:-1] if domain.endswith('.') else domain # Separately check for odd "domains" like "http://example.com" to fail # fast and provide a clear error message for scheme in ["http", "https"]: # Other schemes seem unlikely if domain.startswith("{0}://".format(scheme)): raise errors.ConfigurationError( "Requested name {0} appears to be a URL, not a FQDN. " "Try again without the leading \"{1}://\".".format( domain, scheme)) if is_ipaddress(domain): raise errors.ConfigurationError( "Requested name {0} is an IP address. The Let's Encrypt " "certificate authority will not issue certificates for a " "bare IP address.".format(domain)) # FQDN checks according to RFC 2181: domain name should be less than 255 # octets (inclusive). And each label is 1 - 63 octets (inclusive). # https://tools.ietf.org/html/rfc2181#section-11 msg = "Requested domain {0} is not a FQDN because".format(domain) if len(domain) > 255: raise errors.ConfigurationError("{0} it is too long.".format(msg)) labels = domain.split('.') for l in labels: if not l: raise errors.ConfigurationError( "{0} it contains an empty label.".format(msg)) if len(l) > 63: raise errors.ConfigurationError( "{0} label {1} is too long.".format(msg, l)) return domain
def enforce_domain_sanity(domain): """Method which validates domain value and errors out if the requirements are not met. :param domain: Domain to check :type domains: `str` or `unicode` :raises ConfigurationError: for invalid domains and cases where Let's Encrypt currently will not issue certificates :returns: The domain cast to `str`, with ASCII-only contents :rtype: str """ # Check if there's a wildcard domain if domain.startswith("*."): raise errors.ConfigurationError( "Wildcard domains are not supported: {0}".format(domain)) # Punycode if "xn--" in domain: raise errors.ConfigurationError( "Punycode domains are not presently supported: {0}".format(domain)) # Unicode try: domain = domain.encode('ascii').lower() except UnicodeError: error_fmt = (u"Internationalized domain names " "are not presently supported: {0}") if isinstance(domain, six.text_type): raise errors.ConfigurationError(error_fmt.format(domain)) else: raise errors.ConfigurationError(str(error_fmt).format(domain)) # Remove trailing dot domain = domain[:-1] if domain.endswith('.') else domain # Explain separately that IP addresses aren't allowed (apart from not # being FQDNs) because hope springs eternal concerning this point try: socket.inet_aton(domain) raise errors.ConfigurationError( "Requested name {0} is an IP address. The Let's Encrypt " "certificate authority will not issue certificates for a " "bare IP address.".format(domain)) except socket.error: # It wasn't an IP address, so that's good pass # FQDN checks from # http://www.mkyong.com/regular-expressions/domain-name-regular-expression-example/ # Characters used, domain parts < 63 chars, tld > 1 < 64 chars # first and last char is not "-" fqdn = re.compile("^((?!-)[A-Za-z0-9-]{1,63}(?<!-)\\.)+[A-Za-z]{2,63}$") if not fqdn.match(domain): raise errors.ConfigurationError( "Requested domain {0} is not a FQDN".format(domain)) return domain
def exists_irule(self, irule_name): """check if iRule already exists :param irule_name: iRule name :type irule_name: string :raises errors.ConfigurationError: check failed :return: True or False if iRule exists or not :rtype: bool """ try: return self.mgmt.tm.ltm.rules.rule.exists(partition=self.partition, name=irule_name) except Exception as e: msg = f"iRule creation on {self.active_device} failed. {os.linesep}{e}{os.linesep}" raise errors.ConfigurationError(msg)
def _ask_user_to_confirm_new_names(config, new_domains, certname, old_domains): """Ask user to confirm update cert certname to contain new_domains. """ if config.renew_with_new_domains: return msg = ("You are updating certificate {0} to include domains: {1}{br}{br}" "It previously included domains: {2}{br}{br}" "Did you intend to make this change?".format(certname, ", ".join(new_domains), ", ".join(old_domains), br=os.linesep)) obj = zope.component.getUtility(interfaces.IDisplay) if not obj.yesno(msg, "Update cert", "Cancel", default=True): raise errors.ConfigurationError( "Specified mismatched cert name and domains.")
def handle_csr(self, parsed_args): """Process a --csr flag.""" if parsed_args.verb != "certonly": raise errors.Error("Currently, a CSR file may only be specified " "when obtaining a new or replacement " "via the certonly command. Please try the " "certonly command instead.") try: csr = le_util.CSR(file=parsed_args.csr[0], data=parsed_args.csr[1], form="der") typ = OpenSSL.crypto.FILETYPE_ASN1 domains = crypto_util.get_sans_from_csr( csr.data, OpenSSL.crypto.FILETYPE_ASN1) except OpenSSL.crypto.Error: try: e1 = traceback.format_exc() typ = OpenSSL.crypto.FILETYPE_PEM csr = le_util.CSR(file=parsed_args.csr[0], data=parsed_args.csr[1], form="pem") domains = crypto_util.get_sans_from_csr(csr.data, typ) except OpenSSL.crypto.Error: logger.debug("DER CSR parse error %s", e1) logger.debug("PEM CSR parse error %s", traceback.format_exc()) raise errors.Error("Failed to parse CSR file: {0}".format( parsed_args.csr[0])) # This is not necessary for webroot to work, however, # obtain_certificate_from_csr requires parsed_args.domains to be set for domain in domains: add_domains(parsed_args, domain) if not domains: # TODO: add CN to domains instead: raise errors.Error( "Unfortunately, your CSR %s needs to have a SubjectAltName for every domain" % parsed_args.csr[0]) parsed_args.actual_csr = (csr, typ) csr_domains, config_domains = set(domains), set(parsed_args.domains) if csr_domains != config_domains: raise errors.ConfigurationError( "Inconsistent domain requests:\nFrom the CSR: {0}\nFrom command line/config: {1}" .format(", ".join(csr_domains), ", ".join(config_domains)))
def exists_virtual(self, virtual_name): """check if virtual server exists :param virtual_name: name of virtual server :type virtual_name: string :raises errors.ConfigurationError: check failed :return: True or False if VS exists or not :rtype: bool """ try: r = self._split_fullpath(virtual_name) return self.mgmt.tm.ltm.virtuals.virtual.exists(partition=r[0], subPath=r[1], name=r[2]) except Exception as e: msg = f"Virtual server {virtual_name} check on {self.active_device} failed. {os.linesep}{e}{os.linesep}" raise errors.ConfigurationError(msg)
def check_config_sanity(config): """Validate command line options and display error message if requirements are not met. :param config: IConfig instance holding user configuration :type args: :class:`certbot.interfaces.IConfig` """ # Port check if config.http01_port == config.https_port: raise errors.ConfigurationError("Trying to run http-01 and https-port " "on the same port ({0})".format( config.https_port)) # Domain checks if config.namespace.domains is not None: for domain in config.namespace.domains: # This may be redundant, but let's be paranoid util.enforce_domain_sanity(domain)
def rename_lineage(config): """Rename the specified lineage to the new name. :param config: Configuration. :type config: :class:`certbot.interfaces.IConfig` """ disp = zope.component.getUtility(interfaces.IDisplay) renewer_config = configuration.RenewerConfiguration(config) certname = config.certname if not certname: filenames = renewal.renewal_conf_files(renewer_config) choices = [ storage.lineagename_for_filename(name) for name in filenames ] if not choices: raise errors.Error("No existing certificates found.") code, index = disp.menu("Which certificate would you like to rename?", choices, ok_label="Select", flag="--cert-name") if code != display_util.OK or not index in range(0, len(choices)): raise errors.Error("User ended interaction.") certname = choices[index] new_certname = config.new_certname if not new_certname: code, new_certname = disp.input( "Enter the new name for certificate {0}".format(certname), flag="--updated-cert-name") if code != display_util.OK or not new_certname: raise errors.Error("User ended interaction.") lineage = lineage_for_certname(config, certname) if not lineage: raise errors.ConfigurationError("No existing certificate with name " "{0} found.".format(certname)) storage.rename_renewal_config(certname, new_certname, renewer_config) disp.notification("Successfully renamed {0} to {1}.".format( certname, new_certname), pause=False)
def delete_irule(self, token): """delete iRule :param token: challenge token :type token: string :raises errors.ConfigurationError: deletion failed :return: True if deletion succeeded :rtype: bool """ try: irule_name = f"Certbot-Letsencrypt-{token}" rule = self.mgmt.tm.ltm.rules.rule.load(name=irule_name, partition=self.partition) rule.delete() return True except Exception as e: msg = f"iRule deletion from {self.active_device} failed. {os.linesep}{e}{os.linesep}" raise errors.ConfigurationError(msg)
def install(config, plugins): """Install a previously obtained cert in a server. :param config: Configuration object :type config: interfaces.IConfig :param plugins: List of plugins :type plugins: `list` of `str` :returns: `None` :rtype: None """ # XXX: Update for renewer/RenewableCert # FIXME: be consistent about whether errors are raised or returned from # this function ... try: installer, _ = plug_sel.choose_configurator_plugins( config, plugins, "install") except errors.PluginSelectionError as e: return str(e) # If cert-path is defined, populate missing (ie. not overridden) values. # Unfortunately this can't be done in argument parser, as certificate # manager needs the access to renewal directory paths if config.certname: config = _populate_from_certname(config) if config.key_path and config.cert_path: _check_certificate_and_key(config) domains, _ = _find_domains_or_certname(config, installer) le_client = _init_le_client(config, authenticator=None, installer=installer) _install_cert(config, le_client, domains) else: raise errors.ConfigurationError( "Path to certificate or key was not defined. " "If your certificate is managed by Certbot, please use --cert-name " "to define which certificate you would like to install.")
def profile_on_virtual(self, virtual_name, profile_type): """check for profiles which are attached to the virtual server :param virtual_name: virtual server name :type virtual_name: string :param profile_type: type of profile to look for :type profile_type: string :raises errors.ConfigurationError: check failed :return: True or False if profile of specified type exists :rtype: bool """ try: r = self._split_fullpath(virtual_name) virtual = self.mgmt.tm.ltm.virtuals.virtual.load(partition=r[0], subPath=r[1], name=r[2]) if virtual != "": for profile in virtual.profiles_s.get_collection(): r = self._split_fullpath(profile.fullPath) try: getattr( getattr( getattr(self.mgmt.tm.ltm.profile, f"{profile_type}s"), f"{profile_type}", ), "load", )(partition=r[0], subPath=r[1], name=r[2]) return True except Exception: pass return False else: return False except Exception as e: msg = f"Test for HTTP profile on virtual server on {self.active_device} failed. {os.linesep}{e}{os.linesep}" raise errors.ConfigurationError(msg)
def irules_on_virtual(self, virtual_name): """[summary] :param virtual_name: virtual server name :type virtual_name: string :raises errors.ConfigurationError: check failed :return: Dictionary with keys result and list of attached iRules :rtype: dict """ try: r = self._split_fullpath(virtual_name) virtual = self.mgmt.tm.ltm.virtuals.virtual.load(partition=r[0], subPath=r[1], name=r[2]) if virtual != "" and "rules" in virtual.raw: return {"result": True, "rules": virtual.rules} else: return {"result": False, "rules": []} except Exception as e: msg = f"Retrieval of iRules for virtual server on {self.active_device} failed. {os.linesep}{e}{os.linesep}" raise errors.ConfigurationError(msg)
def duplicate_lineage(config, certname, new_certname): """Create a duplicate of certname with name new_certname :raises .CertStorageError: for storage errors :raises .ConfigurationError: for cli and renewal configuration errors :raises IOError: for filename errors :raises OSError: for OS errors """ # copy renewal config file prev_filename = renewal_filename_for_lineagename(config, certname) new_filename = renewal_filename_for_lineagename(config, new_certname) if os.path.exists(new_filename): raise errors.ConfigurationError("The new certificate name " "is already in use.") try: shutil.copy2(prev_filename, new_filename) except (OSError, IOError): raise errors.ConfigurationError("Please specify a valid filename " "for the new certificate name.") logger.debug("Copied %s to %s", prev_filename, new_filename) # load config file try: renewal_config = configobj.ConfigObj(new_filename) except configobj.ConfigObjError: # config is corrupted logger.warning("Could not parse %s. Only the certificate has been renamed.", new_filename) raise errors.CertStorageError( "error parsing {0}".format(new_filename)) def copy_to_new_dir(prev_dir): """Replace certname with new_certname in prev_dir""" new_dir = prev_dir.replace(certname, new_certname) # make dir iff it doesn't exist shutil.copytree(prev_dir, new_dir, symlinks=True) logger.debug("Copied %s to %s", prev_dir, new_dir) return new_dir # archive dir prev_archive_dir = _full_archive_path(renewal_config, config, certname) new_archive_dir = prev_archive_dir if not certname in prev_archive_dir: raise errors.CertStorageError("Archive directory does not conform to defaults: " "{0} not in {1}", certname, prev_archive_dir) else: new_archive_dir = copy_to_new_dir(prev_archive_dir) # live dir # if things aren't in their default places, don't try to change things. prev_live_dir = _full_live_path(config, certname) prev_links = dict((kind, renewal_config.get(kind)) for kind in ALL_FOUR) if (certname not in prev_live_dir or len(set(os.path.dirname(renewal_config.get(kind)) for kind in ALL_FOUR)) != 1): raise errors.CertStorageError("Live directory does not conform to defaults.") else: copy_to_new_dir(prev_live_dir) new_links = dict((k, prev_links[k].replace(certname, new_certname)) for k in prev_links) # Update renewal config file def _update_and_write(renewal_config, temp_filename): renewal_config["archive_dir"] = new_archive_dir renewal_config["version"] = certbot.__version__ for kind in ALL_FOUR: renewal_config[kind] = new_links[kind] with open(temp_filename, "wb") as f: renewal_config.write(outfile=f) _modify_config_with_tempfile(new_filename, _update_and_write) # Update symlinks return RenewableCert(new_filename, config, update_symlinks=True)