Example #1
0
 def _check_response_status(self, response, **kwargs):
     if 200 <= response.status_code <= 299:
         return
     elif response.status_code in [401, 403]:
         raise errors.PluginError(
             f"Could not authenticate against deSEC API: {response.content}"
         )
     elif response.status_code == 404:
         raise errors.PluginError(
             f"Not found ({kwargs}): {response.content}")
     elif response.status_code == 429:
         raise errors.PluginError(
             f"deSEC throttled your request even after we waited the prescribed cool-down "
             f"time. Did you use the API in parallel? {response.content}")
     elif response.status_code >= 500:
         raise errors.PluginError(
             f"deSEC API server error (status {response.status_code}): {response.content}"
         )
     else:
         raise errors.PluginError(
             f"Unknown error when talking to deSEC (status {response.status_code}: "
             f"Request was on '{response.request.url}' with payload {response.request.body}. "
             f"Response was '{response.content}'.")
Example #2
0
def _validate_webroot(webroot_path):
    """Validates and returns the absolute path of webroot_path.

    :param str webroot_path: path to the webroot directory

    :returns: absolute path of webroot_path
    :rtype: str

    """
    if not os.path.isdir(webroot_path):
        raise errors.PluginError(webroot_path +
                                 " does not exist or is not a directory")

    return os.path.abspath(webroot_path)
Example #3
0
    def rollback_checkpoints(self, rollback=1):
        """Rollback saved checkpoints.

        :param int rollback: Number of checkpoints to revert

        :raises .errors.PluginError: If there is a problem with the input or
            the function is unable to correctly revert the configuration

        """
        try:
            self.reverter.rollback_checkpoints(rollback)
        except errors.ReverterError as err:
            raise errors.PluginError(str(err))
        self.parser.load()
Example #4
0
 def _prompt_for_new_webroot(self, domain, allowraise=False):
     code, webroot = ops.validated_directory(
         _validate_webroot,
         "Input the webroot for {0}:".format(domain),
         force_interactive=True)
     if code == display_util.CANCEL:
         if not allowraise:
             return None
         else:
             raise errors.PluginError(
                 "Every requested domain must have a "
                 "webroot when using the webroot plugin.")
     else:  # code == display_util.OK
         return _validate_webroot(webroot)
    def add_txt_record(self, domain, record_name, record_content, record_ttl):
        """
        Add a TXT record using the supplied information.

        :param str domain: The domain to use to look up the managed zone.
        :param str record_name: The record name (typically beginning with '_acme-challenge.').
        :param str record_content: The record content (typically the challenge validation).
        :param int record_ttl: The record TTL (number of seconds that the record may be cached).
        :raises certbot.errors.PluginError: if an error occurs communicating with the Google API
        """

        zone_id = self._find_managed_zone_id(domain)

        data = {
            "kind":
            "dns#change",
            "additions": [
                {
                    "kind": "dns#resourceRecordSet",
                    "type": "TXT",
                    "name": record_name + ".",
                    "rrdatas": [
                        record_content,
                    ],
                    "ttl": record_ttl,
                },
            ],
        }

        changes = self.dns.changes()  # changes | pylint: disable=no-member

        try:
            request = changes.create(project=self.project_id,
                                     managedZone=zone_id,
                                     body=data)
            response = request.execute()

            status = response['status']
            change = response['id']
            while status == 'pending':
                request = changes.get(project=self.project_id,
                                      managedZone=zone_id,
                                      changeId=change)
                response = request.execute()
                status = response['status']
        except googleapiclient_errors.Error as e:
            logger.error('Encountered error adding TXT record: %s', e)
            raise errors.PluginError(
                'Error communicating with the Google Cloud DNS API: {0}'.
                format(e))
Example #6
0
    def add_txt_record(self, domain, record_name, record_content, record_ttl=RECORD_TTL):
        """
        Add a TXT record using the supplied information.

        :param str domain: The domain to use to look up the managed zone.
        :param str record_name: The record name (typically beginning with '_acme-challenge.').
        :param str record_content: The record content (typically the challenge validation).
        :param int record_ttl: The record TTL (number of seconds that the record may be cached).
        :raises certbot.errors.PluginError: if an error occurs communicating with the EdgeDNS API
        """

        logger.debug("EDGEDNS: add_text_record. domain: {0}, name: {1}, content: {2}".format(domain, record_name, record_content))
        try:
            txt_recordset, zone = self.get_text_record(domain, record_name, record_ttl)
        except errors.PluginError as pe:
            raise pe
        except:
           raise errors.PluginError("{0}".format(sys.exc_info()[0]))

        self.recordset_semaphore.acquire()
        if self.session == None:
            self.session = requests.Session()
        with self.session as session:
            session.auth = self.edgegrid_auth
            try:
                self._process_add_record(session, zone, txt_recordset, record_content)
            except errors.PluginError as pe:
                self.recordset_semaphore.release()
                raise pe
            except:
                self.recordset_semaphore.release()
                raise errors.PluginError(
                    "EdgeDNS: API invocation resulted in a session error: {0}".format(sys.exc_info()[0])
                )

        self.recordset_semaphore.release()
        return
Example #7
0
    def del_txt_record(self, record_name, record_value):
        """
        Creates a TXT with given record_name and record_value
        :param str record_name: The record name (typically beginning with '_acme-challenge.').
        :param str record_value: The record value
        :raises certbot.errors.PluginError: if an error occurs communicating with the 1cloud API
        """
        try:
            parsed = self._split_record_name(record_name)
            domain_info = self._load_domain_info(parsed['domain'])
            text_1cloud_value = '"' + record_value + '"'
            for record in domain_info['LinkedRecords']:
                if record['TypeRecord'] == 'TXT' and record[
                        'HostName'] == record_name + '.' and record[
                            'Text'].strip() == text_1cloud_value:
                    record_id = record['ID']
                    domain_id = domain_info['ID']
                    response = requests.delete(
                        'https://api.1cloud.ru/dns/{0}/{1}'.format(
                            domain_id, record_id),
                        headers=self._create_headers())
                    response.raise_for_status()
                    return

            raise RecordNotFoundError()
        except requests.RequestException as e:
            logger.error('Encountered error removing TXT record: %d %s', e, e)
            raise errors.PluginError(
                'Error communicating with 1cloud API: {0}'.format(e))
        except RecordNotFoundError as e:
            logger.error('Encountered error removing TXT record: %d %s', e, e)
            raise errors.PluginError(
                'Error communicating with 1cloud API: {0}'.format(e))
        except DomainNotFoundError as e:
            logger.error('Encountered error removing TXT record: %d %s', e, e)
            raise errors.PluginError(
                'Error communicating with 1cloud API: {0}'.format(e))
Example #8
0
    def _enable_ocsp_stapling(self, domain, chain_path):
        """Include OCSP response in TLS handshake

        :param str domain: domain to enable OCSP response for
        :param chain_path: chain file path
        :type chain_path: `str` or `None`

        """
        vhost = self.choose_vhost(domain)
        if self.version < (1, 3, 7):
            raise errors.PluginError("Version 1.3.7 or greater of nginx "
                                     "is needed to enable OCSP stapling")

        if chain_path is None:
            raise errors.PluginError(
                "--chain-path is required to enable "
                "Online Certificate Status Protocol (OCSP) stapling "
                "on nginx >= 1.3.7.")

        stapling_directives = [
            ['\n    ', 'ssl_trusted_certificate', ' ', chain_path],
            ['\n    ', 'ssl_stapling', ' ', 'on'],
            ['\n    ', 'ssl_stapling_verify', ' ', 'on'], ['\n']]

        try:
            self.parser.add_server_directives(vhost,
                                              stapling_directives, replace=False)
        except errors.MisconfigurationError as error:
            logger.debug(error)
            raise errors.PluginError("An error occurred while enabling OCSP "
                                     "stapling for {0}.".format(vhost.names))

        self.save_notes += ("OCSP Stapling was enabled "
                            "on SSL Vhost: {0}.\n".format(vhost.filep))
        self.save_notes += "\tssl_trusted_certificate {0}\n".format(chain_path)
        self.save_notes += "\tssl_stapling on\n"
        self.save_notes += "\tssl_stapling_verify on\n"
    def _do_post(self, url, data):
        """
        Do request DNSPod API

        :param str url: URL for DNSPod API.
        :param Dict[str, Any] data: request parameters
        :returns: API response
        :rtype: Dict[str, Any]
        """
        if not data:
            data = {}

        common_data = {
            'login_token': self.api_token,
            'format': 'json',
            'error_on_empty': 'no',
            'lang': 'en'
        }

        data.update(common_data)

        headers = {'User-Agent': self.user_agent}

        resp = requests.post(url, data=data, headers=headers)
        if resp.status_code != 200:
            raise errors.PluginError(
                '[DNSPod] HTTP Error, status_code: {0}, url: {1}'.format(
                    resp.status_code, url))

        try:
            result = resp.json()
        except Exception:
            raise errors.PluginError(
                '[DNSPod] API response with non JSON, url: {0}, content: {1}'.
                format(url, resp.text))

        return result
Example #10
0
    def add_txt_record(self, domain_name, record_name, record_content):
        """
        Add a TXT record using the supplied information.

        :param str domain_name: The domain to use to associate the record with.
        :param str record_name: The record name (typically beginning with '_acme-challenge.').
        :param str record_content: The record content (typically the challenge validation).
        :raises certbot.errors.PluginError: if an error occurs communicating with the DigitalOcean
                                            API
        """

        try:
            domain = self._find_domain(domain_name)
        except digitalocean.Error as e:
            hint = None

            if str(e).startswith("Unable to authenticate"):
                hint = 'Did you provide a valid API token?'

            logger.debug('Error finding domain using the DigitalOcean API: %s', e)
            raise errors.PluginError('Error finding domain using the DigitalOcean API: {0}{1}'
                                     .format(e, ' ({0})'.format(hint) if hint else ''))

        try:
            result = domain.create_new_domain_record(
                type='TXT',
                ttl=30,
                name=self._compute_record_name(domain, record_name),
                data=record_content)

            record_id = result['domain_record']['id']

            logger.debug('Successfully added TXT record with id: %d', record_id)
        except digitalocean.Error as e:
            logger.debug('Error adding TXT record using the DigitalOcean API: %s', e)
            raise errors.PluginError('Error adding TXT record using the DigitalOcean API: {0}'
                                     .format(e))
Example #11
0
    def _get_private_key(self, domain):
        filename = 'certbot_django_id_rsa_{}'.format(domain).replace('.', '')
        private_key_file = os.path.join(self._get_key_dir(), filename)
        private_key = None

        try:
            with open(private_key_file, 'r') as keyfile:
                private_key = keyfile.read()
        except (IOError, OSError):
            if self.config.noninteractive_mode:
                raise errors.PluginError('Could not read file from %s' % self._get_key_dir())

        if not private_key:
            private_key, public_key = generate_key_pair()
            try:
                logger.info('Trying to load private key: %s' % private_key_file)
                with open(private_key_file, 'w') as keyfile:
                    keyfile.write(private_key)
            except (IOError, OSError):
                raise errors.PluginError('Could not write private key to %s' % self._get_key_dir())
            msg = (
                "Couldn't read private key from {private_key_file}, so a new key has been generated and saved. "
                "Please go your website ({url}) and add the following public key to the user '{username}'. "
                "Please also make sure that '{username}' is a staff user and has permission to add and "
                "delete AcmeChallenge objects."
                "\n\n"
                "{public_key}"
            )
            msg = msg.format(
                private_key_file=private_key_file,
                url="http://{}/admin/asymmetric_jwt_auth/publickey/add/".format(domain),
                username=self._get_username(),
                public_key=public_key)
            display = zope.component.getUtility(interfaces.IDisplay)
            display.notification(msg, wrap=False, force_interactive=True)

        return private_key
Example #12
0
    def __init__(self,
                 aug,
                 root,
                 vhostroot=None,
                 version=(2, 4),
                 configurator=None):
        # Note: Order is important here.

        # Needed for calling save() with reverter functionality that resides in
        # AugeasConfigurator superclass of ApacheConfigurator. This resolves
        # issues with aug.load() after adding new files / defines to parse tree
        self.configurator = configurator

        self.modules = set()  # type: Set[str]
        self.parser_paths = {}  # type: Dict[str, List[str]]
        self.variables = {}  # type: Dict[str, str]

        self.aug = aug
        # Find configuration root and make sure augeas can parse it.
        self.root = os.path.abspath(root)
        self.loc = {"root": self._find_config_root()}
        self.parse_file(self.loc["root"])

        if version >= (2, 4):
            # Look up variables from httpd and add to DOM if not already parsed
            self.update_runtime_variables()

        # This problem has been fixed in Augeas 1.0
        self.standardize_excl()

        # Parse LoadModule directives from configuration files
        self.parse_modules()

        # Set up rest of locations
        self.loc.update(self._set_locations())

        # list of the active include paths, before modifications
        self.existing_paths = copy.deepcopy(self.parser_paths)

        # Must also attempt to parse additional virtual host root
        if vhostroot:
            self.parse_file(
                os.path.abspath(vhostroot) + "/" +
                self.configurator.option("vhost_files"))

        # check to see if there were unparsed define statements
        if version < (2, 4):
            if self.find_dir("Define", exclude=False):
                raise errors.PluginError("Error parsing runtime variables")
Example #13
0
    def deploy_cert(self,
                    domain,
                    cert_path,
                    key_path,
                    chain_path=None,
                    fullchain_path=None):

        if not fullchain_path:
            raise errors.PluginError(
                "CASTLE Installer plugin requires --fullchain-path to generate a PKCS12 container."
            )
        logger.info("Generating PKCS12 container")
        logger.debug('Loading cert ')
        cert = x509.load_pem_x509_certificate(open(cert_path, 'rb').read())
        logger.debug('Loading key ')
        privkey = serialization.load_pem_private_key(open(key_path,
                                                          'rb').read(),
                                                     password=None)
        logger.debug('Loading chain ')
        chain = x509.load_pem_x509_certificate(open(chain_path, 'rb').read())
        passphrase = None
        if (not self.conf('no-passphrase')):
            if (self.conf('passphrase')):
                passphrase = self.conf('passphrase').encode('utf-8')
            else:
                text = 'A passphrase is needed for protecting the PKCS12 container. '
                display_util.notification(text, pause=False)
                pf = getpass.getpass('Enter passphrase: ')
                vpf = getpass.getpass('Re-enter passphrase: ')
                while (pf != vpf):
                    display_util.notify('Passphrases do not match.')
                    vpf = getpass.getpass('Re-enter passphrase: ')
                passphrase = pf.encode('utf-8')
        algo = serialization.BestAvailableEncryption(
            passphrase) if passphrase else serialization.NoEncryption()
        pfxdata = pkcs12.serialize_key_and_certificates(
            name=domain.encode('utf-8'),
            key=privkey,
            cert=cert,
            cas=[chain],
            encryption_algorithm=algo)
        path, _ = os.path.split(cert_path)
        pfx_f, pfx_filename = util.unique_file(os.path.join(path, 'cert.pfx'),
                                               0o600, "wb")
        with pfx_f:
            pfx_f.write(pfxdata)
        display_util.notification('PKCS12 container generated at ' +
                                  pfx_filename,
                                  pause=False)
Example #14
0
    def __init__(self, filename, mapper=lambda x: x):
        """
        :param str filename: A path to the configuration file.
        :param callable mapper: A transformation to apply to configuration key names
        :raises errors.PluginError: If the file does not exist or is not a valid format.
        """
        validate_file_permissions(filename)

        try:
            self.confobj = configobj.ConfigObj(filename)
        except configobj.ConfigObjError as e:
            logger.debug("Error parsing credentials configuration: %s", e, exc_info=True)
            raise errors.PluginError("Error parsing credentials configuration: {0}".format(e))

        self.mapper = mapper
 def _setup_credentials(self):
     token = os.getenv("INFOMANIAK_API_TOKEN")
     if token is None:
         self.credentials = self._configure_credentials(
             "credentials",
             "Infomaniak credentials INI file",
             {
                 "token": "Infomaniak API token.",
             },
         )
         if not self.credentials:
             raise errors.PluginError("INFOMANIAK API Token not defined")
         self.token = self.credentials.conf("token")
     else:
         self.token = token
Example #16
0
 def del_txt_record(self, domain, record_name):
     zone = self._find_zone(domain)
     if not record_name.endswith('.' + zone):
         raise PluginError("Record name {0} is not in DNS zone {1}.".format(
             record_name, zone))
     subdomain = record_name[:-len(zone) - 1]
     record = self._find_record_id(zone, subdomain)
     if record is not None:
         try:
             self.ovh.delete('/domain/zone/{zone}/record/{record}'.format(
                 zone=zone, record=record))
         except ovh.exceptions.APIError as e:
             raise errors.PluginError(
                 "Error deleting TXT record: {0}".format(e))
     self._refresh_zone(zone)
    def del_txt_record(self, domain, source, target):
        """Delete a TXT DNS record from a domain

        :param str source: record key in zone (left prefix before domain)
        :param str target: value of record
        """
        logger.debug("del_txt_record %s %s %s", domain, source, target)
        (domain_id, domain_name) = self._find_zone(domain)
        if source.endswith("." + domain_name):
            relative_source = source[:source.rfind("." + domain_name)]
        else:
            relative_source = source
        records = self._get_records(
            domain_name, domain_id,
            {"type": "TXT", "source": relative_source, "target": target},
        )
        if records is None:
            raise errors.PluginError("Record not found")
        if len(records) > 1:
            raise errors.PluginError("Several records match")
        record_id = records[0]["id"]

        self._delete_request("/1/domain/{domain_id}/dns/record/{record_id}".format(
            domain_id=domain_id, record_id=record_id))
    def _cleanup(self, domain: str, validation_name: str,
                 validation: str) -> None:
        """
        Clear the TXT record of the provided DuckDNS domain.

        :param domain: the DuckDNS domain
        :param validation_name: value to validate the dns challenge
        :param validation: the value for the TXT record
        :raise PluginError:  if the TXT record can not be cleared of something goes wrong
        """

        try:
            self._get_duckdns_client().clear_txt_record(domain)
        except Exception as e:
            raise errors.PluginError(e)
Example #19
0
    def del_txt_record(self, record_name, record_content):
        """
        Delete a TXT record using the supplied information.

        :param str record_name: The record name (typically beginning with '_acme-challenge.').
        :param str record_content: The record content (typically the challenge validation).
        :param int record_ttl: The record TTL (number of seconds that the record may be cached).
        :raises certbot.errors.PluginError: if an error occurs communicating with the DNS server
        """

        record_name = self.cname_check1(record_name, quiet=true)

        domain = self._find_domain(record_name)

        n = dns.name.from_text(record_name)
        o = dns.name.from_text(domain)
        rel = n.relativize(o)

        update = dns.update.Update(
            domain,
            keyring=self.keyring,
            keyalgorithm=self.algorithm)
        update.delete(rel, dns.rdatatype.TXT, record_content)

        try:
            response = dns.query.tcp(update, self.server, port=self.port)
        except Exception as e:
            raise errors.PluginError('Encountered error deleting TXT record: {0}'
                                     .format(e))
        rcode = response.rcode()

        if rcode == dns.rcode.NOERROR:
            logger.debug('Successfully deleted TXT record')
        else:
            raise errors.PluginError('Received response from server: {0}'
                                     .format(dns.rcode.to_text(rcode)))
    def add_txt_record(self, domain_name, record_name, record_content):
        """
        Add a TXT record using the supplied information.

        :param str domain_name: The domain to use to associate the record with.
        :param str record_name: The record name (typically beginning with '_acme-challenge.').
        :param str record_content: The record content (typically the challenge validation).
        :raises certbot.errors.PluginError: if an error occurs communicating with the SoftLayer
                                            API
        """

        # extract first level domain
        domain = get_tld(domain_name, as_object=True, fix_protocol=True)

        try:
            zone_id = self.dns.resolve_ids(domain.fld)[0]
        except (SoftLayerAPIError, IndexError) as e:
            logger.debug('Error finding domain using the SoftLayer API: %s', e)
            raise errors.PluginError(
                'Error finding domain using the SoftLayer API: {}'.format(e))

        try:
            logger.debug('Creating TXT record with name: %s', record_name)
            result = self.dns.create_record(
                zone_id, self._compute_record_name(domain.fld, record_name),
                'TXT', record_content)
            record_id = result['id']

            logger.debug('Successfully added TXT record with id: %d',
                         record_id)
        except SoftLayerAPIError as e:
            logger.debug('Error adding TXT record using the SoftLayer API: %s',
                         e)
            raise errors.PluginError(
                'Error adding TXT record using the SoftLayer API: {0}'.format(
                    e))
Example #21
0
    def _get_dns_entries(self, domain, retries=3, backoff=5):
        """
        Get all DNS entries for this domain.

        :param str domain: The domain to use to associate the record with.
        :raises certbot.errors.PluginError: if an error occurs communicating with the Transip
                                            API
        """
        dns_entries = []
        for _ in range(retries + 1):
            try:
                dns_entries = self.domain_service.get_info(
                    domain_name=domain).dnsEntries
            except suds.WebFault as error:
                self.logger.error(
                    'Error getting DNS records using the Transip API: %s',
                    error)
                raise errors.PluginError(
                    'Error finding DNS entries using the Transip API: {0}'.
                    format(domain))
            if dns_entries:
                break
            self.logger.warning(
                'Error getting DNS records using the Transip API: retry in {} seconds'
                .format(backoff))
            time.sleep(backoff)
            backoff = backoff * 2

        # If there are still no entries then the Transip API returned the wrong data
        if not dns_entries:
            error = 'Error finding DNS entries using the Transip API: Empty record set for {}'.format(
                domain)
            self.logger.error(error)
            raise errors.PluginError(error)

        return dns_entries
Example #22
0
    def _perform(self, domain, validation_name, validation):
        azure_domain, subscription_id, resource_group_name = self._get_ids_for_domain(
            domain)
        client = self._get_azure_client(subscription_id)
        relative_validation_name = self._get_relative_domain(
            validation_name, azure_domain)

        # Check to see if there are any existing TXT validation record values
        txt_value = {validation}
        try:
            existing_rr = client.record_sets.get(
                resource_group_name=resource_group_name,
                zone_name=azure_domain,
                relative_record_set_name=relative_validation_name,
                record_type='TXT')
            for record in existing_rr.txt_records:
                for value in record.value:
                    txt_value.add(value)
        except CloudError as err:
            if err.status_code != 404:  # Ignore RR not found
                raise errors.PluginError(
                    'Failed to check TXT record for domain '
                    '{}, error: {}'.format(domain, err))

        try:
            client.record_sets.create_or_update(
                resource_group_name=resource_group_name,
                zone_name=azure_domain,
                relative_record_set_name=relative_validation_name,
                record_type='TXT',
                parameters=RecordSet(
                    ttl=self.ttl,
                    txt_records=[TxtRecord(value=list(txt_value))]))
        except CloudError as err:
            raise errors.PluginError('Failed to add TXT record to domain '
                                     '{}, error: {}'.format(domain, err))
Example #23
0
    def _find_record_set(self, validation_domain_name):
        """Find the resource group, zone and record set for a given FQDN.
        
        :param str validation_domain_name: The validation record domain.
        :return: A 3-tuple (Resource group, Zone name, TXT resource name)
        """
        if not self.conf('resource-goup'):
            raise errors.PluginError("No resource groups specified.")

        zones = []
        for resourceGroup in self.conf('resource-goup'):
            for zone in self.dnsMgtClient.zones.list_by_resource_group(resourceGroup):
                if zone.zone_type != ZoneType.public:
                    continue
                if validation_domain_name.rstrip('.').endswith(zone.name.rstrip('.')):
                    zones.append((resourceGroup, zone.name))

        if not zones:
            raise errors.PluginError("Unable to find an Azure hosted zone for {0}".format(validation_domain_name))

        zones.sort(key=lambda z: len(z[1]), reverse=True) # Use the longest matching DNS zone.
        resourceGroup, zone = zones[0]
        record = validation_domain_name[0:len(validation_domain_name)- len(zone)].rstrip('.')
        return resourceGroup, zone, record
Example #24
0
    def add_txt_record(self, record_name: str, record_content: str,
                       record_ttl: int) -> None:
        """
        Add a TXT record using the supplied information.

        :param str record_name: The record name (typically beginning with '_acme-challenge.').
        :param str record_content: The record content (typically the challenge validation).
        :param int record_ttl: The record TTL (number of seconds that the record may be cached).
        :raises certbot.errors.PluginError: if an error occurs communicating with the DNS server
        """

        domain = self._find_domain(record_name)

        n = dns.name.from_text(record_name)
        o = dns.name.from_text(domain)
        rel = n.relativize(o)

        update = dns.update.Update(domain,
                                   keyring=self.keyring,
                                   keyalgorithm=self.algorithm)
        update.add(rel, record_ttl, dns.rdatatype.TXT, record_content)

        try:
            response = dns.query.tcp(update, self.server,
                                     self._default_timeout, self.port)
        except Exception as e:
            raise errors.PluginError(
                'Encountered error adding TXT record: {0}'.format(e))
        rcode = response.rcode()  # type: ignore[attr-defined]

        if rcode == dns.rcode.NOERROR:
            logger.debug('Successfully added TXT record %s', record_name)
        else:
            raise errors.PluginError(
                'Received response from server: {0}'.format(
                    dns.rcode.to_text(rcode)))
Example #25
0
 def _perform(self, domain, validation_name, validation):
     _, domain = domain.split('.', 1)
     validation_name = '.'.join(validation_name.split('.')[0:2])
     result = requests.post('https://i.hostker.com/api/dnsAddRecord',
                            data={
                                'email': self.credentials.conf('email'),
                                'token': self.credentials.conf('token'),
                                'domain': domain,
                                'header': validation_name,
                                'data': validation,
                                'type': 'TXT',
                                'ttl': self.ttl,
                            })
     if int(result.json()['success']) == 0:
         raise errors.PluginError(result.json())
Example #26
0
    def _find_zone_id_for_domain(self, domain):
        """Find the zone id responsible a given FQDN.

           That is, the id for the zone whose name is the longest parent of the
           domain.
        """
        if self.conf('zone-id'):
            zone = self.r53.get_hosted_zone(Id=self.conf('zone-id'))
            if not zone:
                raise errors.PluginError(
                    "Unable to find a Route53 hosted zone with id {0}".format(
                        self.conf('zone-id')))
            return zone["HostedZone"]["Id"]

        paginator = self.r53.get_paginator("list_hosted_zones")
        zones = []
        target_labels = domain.rstrip(".").split(".")
        for page in paginator.paginate():
            for zone in page["HostedZones"]:
                if zone["Config"]["PrivateZone"]:
                    continue

                candidate_labels = zone["Name"].rstrip(".").split(".")
                if candidate_labels == target_labels[-len(candidate_labels):]:
                    zones.append((zone["Name"], zone["Id"]))

        if not zones:
            raise errors.PluginError(
                "Unable to find a Route53 hosted zone for {0}".format(domain))

        # Order the zones that are suffixes for our desired to domain by
        # length, this puts them in an order like:
        # ["foo.bar.baz.com", "bar.baz.com", "baz.com", "com"]
        # And then we choose the first one, which will be the most specific.
        zones.sort(key=lambda z: len(z[0]), reverse=True)
        return zones[0][1]
Example #27
0
    def _prompt_with_webroot_list(self, domain, known_webroots):
        display = zope.component.getUtility(interfaces.IDisplay)
        path_flag = "--" + self.option_name("path")

        while True:
            code, index = display.menu(
                "Select the webroot for {0}:".format(domain),
                ["Enter a new webroot"] + known_webroots,
                cli_flag=path_flag, force_interactive=True)
            if code == display_util.CANCEL:
                raise errors.PluginError(
                    "Every requested domain must have a "
                    "webroot when using the webroot plugin.")
            else:  # code == display_util.OK
                return None if index == 0 else known_webroots[index - 1]
Example #28
0
    def _relevant_vhosts(self):
        http01_port = str(self.configurator.config.http01_port)
        relevant_vhosts = []
        for vhost in self.configurator.vhosts:
            if any(a.is_wildcard() or a.get_port() == http01_port for a in vhost.addrs):
                if not vhost.ssl:
                    relevant_vhosts.append(vhost)
        if not relevant_vhosts:
            raise errors.PluginError(
                "Unable to find a virtual host listening on port {0} which is"
                " currently needed for Certbot to prove to the CA that you"
                " control your domain. Please add a virtual host for port"
                " {0}.".format(http01_port))

        return relevant_vhosts
Example #29
0
    def add_txt_record(self, domain_name, record_name, record_content, ttl):
        zone_name = self._find_zone_name(domain_name)
        if zone_name is None:
            raise errors.PluginError(
                'Cannot find zone for domain name {}'.format(domain_name))

        try:
            logger.debug('Update TXT record with data: %s', record_content)
            update_rr_set_details = UpdateRRSetDetails(items=[
                RecordDetails(domain=record_name,
                              rdata=record_content,
                              rtype='TXT',
                              ttl=ttl)
            ])
            self.client.update_rr_set(
                zone_name_or_id=zone_name,
                domain=record_name,
                rtype='TXT',
                update_rr_set_details=update_rr_set_details)
        except ServiceError as e:
            logger.warning(
                'Error updating TXT record %s using the OCI API: %s',
                record_name, e)
            raise errors.PluginError('Cannot create TXT record: {}'.format(e))
    def __call__(self, parser, namespace, webroot_path, option_string=None):
        if self._domain_before_webroot:
            raise errors.PluginError(
                "If you specify multiple webroot paths, "
                "one of them must precede all domain flags")

        if namespace.webroot_path:
            # Apply previous webroot to all matched
            # domains before setting the new webroot path
            prev_webroot = namespace.webroot_path[-1]
            for domain in namespace.domains:
                namespace.webroot_map.setdefault(domain, prev_webroot)
        elif namespace.domains:
            self._domain_before_webroot = True

        namespace.webroot_path.append(_validate_webroot(webroot_path))