def run(config, config_path): try: username, keyfile, a_records, expire_time = config['username'], config['keyfile'], config['A'], config['expire'] expire_time = int(expire_time) except KeyError as e: raise ConfigError('Configuration is missing field {}'.format(str(e))) # If keyfile is a relative path, make it an absolute path by resolving it from the config file path if not os.path.isabs(keyfile): keyfile = os.path.abspath(os.path.join(os.path.dirname(config_path), keyfile)) with open(keyfile, 'r') as f: key = f.read() try: domain_service = DomainService(username, key) domains = domain_service.get_domain_names() log.debug("Domains managed by TransIP on this account: {}".format(domains)) update_a_records(a_records, domains, expire_time, domain_service) except (WebFault, urllib.error.URLError) as err: log.error("Could not query TransIP: {}".format(err)) sys.exit(1)
def main(): """ The main method """ parser = argparse.ArgumentParser() parser.add_argument('-l', '--login-name', help='TransIP username', dest='loginname') parser.add_argument('-s', '--show-dns-entries', help='show all DNS entries for a domain', action='store_true') parser.add_argument('-a', '--add-dns-entry', help='add an entry in the DNS', action='store_true') parser.add_argument('-u', '--update-dns-entry', help='update an entry in the DNS', action='store_true') parser.add_argument('-d', '--delete-dns-entry', help='delete an entry in the DNS', action='store_true') parser.add_argument('--domain-name', help='domain name to use', dest='domain_name') parser.add_argument('--entry-name', help='name of the DNS entry', dest='entry_name') parser.add_argument('--entry-expire', help='expire time of the DNS entry', dest='entry_expire', type=int) parser.add_argument('--entry-type', help='type of the DNS entry', dest='entry_type') parser.add_argument('--entry-content', help='content of the DNS entry', dest='entry_content') parser.add_argument('--api-key', help='TransIP private key', dest='api_key_file') args = parser.parse_args() if not args.loginname: print('Please provide your TransIP username.') sys.exit(1) if not args.api_key_file: args.api_key_file = 'decrypted_key' domain_service = DomainService(args.loginname, private_key_file=args.api_key_file) if args.add_dns_entry or args.update_dns_entry or args.delete_dns_entry: if [args.add_dns_entry, args.update_dns_entry, args.delete_dns_entry ].count(True) > 1: print( 'Please use only one of the options: ' '-a/--add-dns-entry, -u/--update-dns-entry, -d/--delete-dns-entry' ) sys.exit(1) if args.domain_name and args.entry_name and args.entry_expire and args.entry_type and args.entry_content: update_dns(domain_service, args) else: print('Please provide the details of the DNS entry.') sys.exit(1) elif args.show_dns_entries: if args.domain_name: show_dns_entries(domain_service, args.domain_name) else: print('Please provide the domain name.') sys.exit(1) else: names = domain_service.get_domain_names() print(names)
class _TransipClient(object): """Encapsulates all communication with the Transip API.""" def __init__(self, username, key_file): self.logger = logger.getChild(self.__class__.__name__) self.domain_service = DomainService(login=username, private_key_file=key_file) 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 Transip API """ try: domain = self._find_domain(domain_name) except suds.WebFault as e: self.logger.error('Error finding domain using the Transip API: %s', e) raise errors.PluginError('Error finding domain using the Transip API: {0}' .format(e)) domain_records = self.get_dns_entries(domain_name) try: new_record = DnsEntry( name=self._compute_record_name(domain, record_name), record_type='TXT', content=record_content, expire=1, ) except suds.WebFault as e: self.logger.error('Error getting DNS records using the Transip API: %s', e) return domain_records.append(new_record) try: self.domain_service.set_dns_entries(domain_name=domain, dns_entries=domain_records) self.logger.info('Successfully added TXT record') except suds.WebFault as e: self.logger.error('Error adding TXT record using the Transip API: %s', e) raise errors.PluginError('Error adding TXT record using the Transip API: {0}' .format(e)) def del_txt_record(self, domain_name, record_name, record_content): """ Delete a TXT record using the supplied information. Note that both the record's name and content are used to ensure that similar records created concurrently (e.g., due to concurrent invocations of this plugin) are not deleted. Failures are logged, but not raised. :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). """ try: domain = self._find_domain(domain_name) except suds.WebFault as e: self.logger.error('Error finding domain using the Transip API: %s', e) return domain_records = self._get_dns_entries(domain_name=domain) matching_records = [record for record in domain_records if record.type == 'TXT' and record.name == self._compute_record_name(domain, record_name) and record.content == record_content] for record in matching_records: self.logger.info('Removing TXT record with name: %s', record.name) del domain_records[domain_records.index(record)] try: self.domain_service.set_dns_entries(domain_name=domain, dns_entries=domain_records) except suds.WebFault as e: self.logger.error('Error while storing DNS records: %s', e) def _get_dns_entries(self, domain, retries=3, backoff=5): """ Get all DNS entries for this domain. :param str domain_name: The domain to use to associate the record with. :raises certbot.errors.PluginError: if an error occurs communicating with the Transip API """ def _get_dns_entries_transip(domain): try: dns_entries = self.domain_service.get_info(domain_name=domain).dnsEntries except suds.WebFault as e: self.logger.error('Error getting DNS records using the Transip API: %s', e) raise errors.PluginError('Error finding DNS entries using the Transip API: {0}' .format(domain)) return dns_entries dns_entries = _get_dns_entries_transip(domain) # If there are no DNS entries try again # Retry after 5 seconds, 10 seconds and 20 seconds if not dns_entries: self.logger.error('Error getting DNS records using the Transip API: ' 'retry in {} seconds'.format(backoff)) for retry in range(retries): time.sleep(backoff) dns_entries = _get_dns_entries_transip(domain) backoff = backoff * 2 if dns_entries: break # If there are still no entries the Transip API gives back the wrong data if not dns_entries: self.logger.error('Error getting DNS records using the Transip API: ' 'Empty record set for {}'.format(domain)) raise errors.PluginError('Error finding DNS entries using the Transip API: ' 'Empty record set for {}'.format(domain)) return dns_entries def _find_domain(self, domain_name): """ Find the domain object for a given domain name. :param str domain_name: The domain name for which to find the corresponding Domain. :returns: The Domain, if found. :rtype: `str` :raises certbot.errors.PluginError: if no matching Domain is found. """ domain_name_guesses = dns_common.base_domain_name_guesses(domain_name) domains = self.domain_service.get_domain_names() for guess in domain_name_guesses: if guess in domains: self.logger.debug('Found base domain for %s using name %s', domain_name, guess) return guess raise errors.PluginError('Unable to determine base domain for {0} using names: {1}.' # .format(domain_name, domain_name_guesses) ) @staticmethod def _compute_record_name(domain, full_record_name): # The domain, from Transip's point of view, is automatically appended. return full_record_name.rpartition("." + domain)[0]
def main(): """ Updates all DNS entries in a TransIP account to the value of the IP number of the internet connection that is used when calling this script. Handy when running a server at home and the ISP has given a new IP number. The IP number of 'check-host' is resolved and compared to the current public IP number. If they differ, all dns entries that have the 'old' ip number will be updated to 'new'. For exanple: ./bulk_update_account.py --check-host check.example.org --user-name example-user Warning: this will bulk update all relevant DNS entries in all domains for an account! """ parser = argparse.ArgumentParser() parser.add_argument('-u', '--user-name', help='TransIP username', dest='user_name') parser.add_argument('-c', '--check-host', help='Check host', dest='check_host') parser.add_argument('--dns-server', help='DNS server to use', dest='dns_server') parser.add_argument('--api-key', help='TransIP private key', dest='api_key_file') args = parser.parse_args() if not args.user_name: print('Please provide your TransIP username.') exit(1) if not args.check_host: print( 'Please provide a hostname to be checked for a changed public IP address.' ) exit(1) if not args.api_key_file: args.api_key_file = 'decrypted_key' if not args.dns_server: # TransIP DNS server args.dns_server = '195.135.195.195' domain_service = DomainService(args.user_name, args.api_key_file) # Validate that check-host belongs to the account given if len([ domain_name for domain_name in domain_service.get_domain_names() if args.check_host.endswith('.' + domain_name) ]) != 1: print( 'The check-host parameter does not match with any of your domain names.' ) exit(1) # Find 'old' and 'new' IP number res = resolver.Resolver() res.nameservers = [args.dns_server] old_ip = res.query(args.check_host)[0].address current_ip = requests.get('https://api.ipify.org?format=json').json()['ip'] if old_ip != current_ip: print('Old ip: {}, current ip: {}. Updating DNS entries.'.format( old_ip, current_ip)) for update_domain in domain_service.get_domain_names(): print('Updating {}'.format(update_domain)) # Create a new set with entries to be updated dns_entries = [ DnsEntry( entry['name'], entry['expire'], entry['type'], current_ip if entry['content'] == old_ip else entry['content']) for entry in domain_service.get_info(update_domain).dnsEntries ] try: result = domain_service.set_dns_entries( update_domain, dns_entries) if result is not None: print(result) except WebFault as err: print(err) exit(1) else: print( 'Old ip and current ip ({}) are the same. Not updating DNS entries.' .format(old_ip))
def main(): """ The main method """ domain_service = DomainService() names = domain_service.get_domain_names() print names