def testParse(self): self.assertEqual(iscpy.Explode(iscpy.ScrubComments(self.named_file)), [ 'include "/home/jcollins/roster-dns-management/test/test_data/rndc.key"', ';', 'options', '{', 'pid-file "test_data/named.pid"', ';', '}', ';', 'controls', '{', 'inet 127.0.0.1 port 35638 allow', '{', 'localhost', ';', '}', 'keys', '{', 'rndc-key', ';', '}', ';', '}', ';' ]) self.assertEqual( iscpy.ParseISCString(self.named_file), { 'include': '"/home/jcollins/roster-dns-management/test/test_data/rndc.key"', 'options': { 'pid-file': '"test_data/named.pid"' }, 'controls': [{ 'inet 127.0.0.1 port 35638 allow': { 'localhost': True } }, { 'keys': { 'rndc-key': True } }] }) self.assertEqual( iscpy.MakeISC(iscpy.ParseISCString(self.named_file)), 'include "/home/jcollins/roster-dns-management/test/test_data/rndc.key";\n' 'options { pid-file "test_data/named.pid"; };\n' 'controls { inet 127.0.0.1 port 35638 allow { localhost; } keys { rndc-key; }; };' )
def get_isc_key(): try: import iscpy except ImportError: logger.debug("", exc_info=True) logger.fatal( """The 'iscpy' module is required to read keys from isc-config file. Alternatively set key_name/key_secret in the configuration file""") sys.exit(1) key_file = os.environ.get('DDNS_HOOK_KEY_FILE') # Open the key file for reading try: f = open(key_file, 'rU') except IOError: logger.debug("", exc_info=True) logger.fatal("""Unable to read isc-config file! Did you set the DDNS_HOOK_KEY_FILE env? Alternatively set key_name/key_secret in the configuration file""") sys.exit(1) # Parse the key file parsed_key_file = iscpy.ParseISCString(f.read()) # Grab the keyname, cut out the substring "key " # and remove the extra quotes key_name = parsed_key_file.keys()[0][4:].strip('\"') # Grab the secret key secret = parsed_key_file.values()[0]['secret'].strip('\"') algorithm = parsed_key_file.values()[0]['algorithm'].strip('\"') f.close() return (key_name, algorithm, secret)
def get_log_location(server, conf_filename): """Get the log location of each server""" global LOG_DIR with open(conf_filename, "r") as fin: conf_dict, abcd = iscpy.ParseISCString(fin.read()) log_location = ZONE_DIR[server] + conf_dict['logging'][ 'channel default_debug']['file'].strip('"') LOG_DIR[server] = log_location
def testMakeISC(self): self.assertEqual(iscpy.MakeISC( {'level1': {'level2': {'level3': {'level4': { 'test1': True, 'test2': True, 'test3': True}}}}, 'newarg': 'newval', 'new_stanza': {'test': True}}), 'new_stanza { test; };\n' 'level1 { level2 { level3 { level4 { test1;\n' 'test3;\n' 'test2; }; }; }; };\n' 'newarg newval;') self.assertEqual(iscpy.MakeISC(iscpy.ParseISCString(self.named_file)), 'acl control-hosts { 127.0.0.1/32;\n' '192.168.1.3/32; };\n' 'acl admin { 192.168.1.2/32;\n' '192.168.1.4/32;\n' '192.168.0.0/16; };\n' 'view "authorized" { zone "smtp.university.edu" { masters { 192.168.11.37; };\n' 'type master;\n' 'file "test_data/test_zone.db"; };\n' 'allow-query-cache { network-authorized; };\n' 'allow-recursion { network-authorized; };\n' 'recursion yes;\n' 'zone "university.edu" { check-names ignore;\n' 'masters { 192.168.11.37; };\n' 'type slave;\n' 'file "test_data/university.db.bak"; };\n' 'match-clients { network-authorized; };\n' 'zone "." { type hint;\n' 'file "named.ca"; };\n' 'additional-from-cache yes;\n' 'additional-from-auth yes; };\n' 'controls { inet * allow { control-hosts; } keys { rndc-key; }; };\n' 'view "unauthorized" { zone "1.210.128.in-addr.arpa" { allow-query { network-unauthorized; };\n' 'type master;\n' 'file "test_data/test_reverse_zone.db"; };\n' 'recursion no;\n' 'match-clients { network-unauthorized; };\n' 'zone "." { type hint;\n' 'file "named.ca"; };\n' 'zone "0.0.127.in-addr.arpa" { masters { 192.168.1.3; };\n' 'type slave;\n' 'file "test_data/university.rev.bak"; };\n' 'additional-from-cache no;\n' 'additional-from-auth no; };\n' 'logging { category "update-security" { "security"; };\n' 'category "queries" { "query_logging"; };\n' 'channel "query_logging" { syslog local5;\n' 'severity info; };\n' 'category "client" { "null"; };\n' 'channel "security" { file "/var/log/named-security.log" versions 10 size 10m;\n' 'print-time yes; }; };\n' 'include "/etc/rndc.key";\n' 'options { directory "/var/domain";\n' 'recursion yes;\n' 'allow-query { any; };\n' 'max-cache-size 512M; };')
def GetNamedZoneToolArgs(self, dns_server, view, zone_file_name): """Generates the arg flags for named-checkzone and named-compilezone for a specific zone-view-server combination. Inputs: dns_server: name of a DNS server view: name of a view zone_file_name: the file name of a zone Outputs: string: string of the command flags and their values for named-checkzone and named-compilezone. """ zone_name = zone_file_name.rstrip('.db') server_directory = '%s/%s' % (self.root_config_dir, dns_server) named_file_name = '%s/named.conf.a' % server_directory try: named_file_handle = open(named_file_name, 'r') named_file_string = named_file_handle.read() finally: named_file_handle.close() named_file_dict = iscpy.ParseISCString(named_file_string) global_options_dict = named_file_dict['options'] if ('view "%s"' % view not in named_file_dict): raise ConfigManagerError('Could not find view %s in named.conf' % view) view_dict = named_file_dict['view "%s"' % view] zone_dict = None for zone in view_dict: #Making sure we're checking a dictionary if (type(view_dict[zone]) == type({})): if ('file' in view_dict[zone].keys()): if (view_dict[zone]['file'].strip('"').endswith( zone_file_name)): zone_dict = view_dict[zone] break else: raise ConfigManagerError( 'Could not find zone %s in view %s within named.conf' % (zone_name, view)) options_dict = self.MergeOptionsDicts(global_options_dict, view_dict, zone_dict) additional_args = self.GenerateAdditionalNamedCheckzoneArgs( options_dict) return ' '.join(additional_args)
def NamedHeaderChangeDirectory(self, named_conf_header, new_directory): """Adds/Changes directory in named.conf header Inputs: named_conf_header: string of namedconf header new_directory: {} Outputs: string: string of namedconf header """ named_conf_header_contents = iscpy.ParseISCString(named_conf_header) if ('options' not in named_conf_header_contents): named_conf_header_contents['options'] = {} named_conf_header_contents['options'][ 'directory'] = '"%s"' % new_directory return iscpy.MakeISC(named_conf_header_contents)
def get_zone(server, conf_filename, relative_remote_dir='/etc/'): """Read DEFAULT_CONF_FILENAME and parse the config file. This method will read all zonefile to get every zonefile available and also assign ZONE_DICT which server has a certain zonefile. """ global ZONE_DIR, FILE_LOCATION, ZONE_DICT, ZONE_SLAVES logger.debug('get_zone on: ' + server + ' ' + conf_filename + ' ' + relative_remote_dir) with open(conf_filename, "r") as fin: conf_dict, abcd = iscpy.ParseISCString(fin.read()) try: bind_working_dir = conf_dict['options']['directory'].strip('"') + '/' except KeyError: bind_working_dir = relative_remote_dir ZONE_DIR[server] = bind_working_dir for key in conf_dict: if (("zone" in key) and ("master" in conf_dict[key]['type'])): zone_name = re.search(r'"(.*)"', key).group(1) if zone_name not in IGNORED_ZONE: FILE_LOCATION[zone_name] = get_local_filename( conf_dict[key]['file'], bind_working_dir, LOCAL_MNT_DIR[server]) ZONE_DICT[server].append(zone_name) ZONE_SLAVES[server][zone_name] = {} if "allow-transfer" in conf_dict[key]: slaves = conf_dict[key]['allow-transfer'].keys() for slave in slaves: if slave != '': ZONE_SLAVES[server][zone_name][slave] = "" elif ("include" in key): # logger.log("get include :" + strconf_dict['include']) for (_, value) in conf_dict['include'].items(): local_filename = get_local_filename(value, bind_working_dir, LOCAL_MNT_DIR[server]) logger.debug("reading include on: " + value + "\nworking dir:" + bind_working_dir) get_zone(server, local_filename, bind_working_dir)
def get_dns_config(): """ Function to get zone config, handle zone and associate file """ # Config # TODO : to put in a external config file zones_config_file = '/etc/named/zones.conf' # load bind zone config file zones_config = iscpy.ParseISCString(open(zones_config_file, 'r').read()) # Build zone tab to store zone name / config file zone_dict = dict() for z in zones_config: zone = z.split(' ') # check if the dns is master for this zone if zones_config[z]['type'] == 'master': zone_name = zone[1].replace("\"", "") zone_file = zones_config[z]['file'].replace("\"", "") zone_dict.update({zone_name : {'name': zone_name, 'file': zone_file}}) return zone_dict
def post(self, request, dns_server): """POST Method handler, used to create a new zone record. This endpoint recieve the following JSON file: { "directives": { "directive1": "value1", ... } "soa_record": { "authoritative_server": "", "admin_email": "", "serial_no": "", "slv_refresh_period": "", "slv_retry": "", "slv_expire": "", "max_time_cache": "" } "zone": { "zone ZONENAME": { "file": "ZONE_FILE_PATH", "type": "TYPE" ... } } } Note that rclass and TTL are optional. This endpoint will return { "status" : "ok" } if adding a new record is successfull and {"status" : "fail"} otherwise """ try: # Load input parameters body = json.loads(request.body.decode('utf-8')) body_zone = body['zone'] zone = str(body_zone.keys()[0]) if "master" in body_zone[zone]['type']: body_directives = body['directives'] body_soa = body['soa_record'] # add_absolute_path(body_zone) named_file = str(LOCAL_MNT_DIR[dns_server]) + DEFAULT_CONF_FILENAME logger.debug("Write named file to directory " + named_file) # Add zone to named config file named_dict, named_keys = iscpy.ParseISCString(open(named_file).read()) new_dict = iscpy.AddZone(body_zone, named_dict) iscpy.WriteToFile(new_dict, named_keys, named_file) # Make new zone file if "master" in body_zone[zone]['type']: soa = SOARecordData() soa.fromJSON(body_soa) soa_record = DNSResourceRecord("@", "", "", "SOA", soa) resourcerecord = [] resourcerecord.append(soa_record) ns_record = DNSResourceRecord("@", "", "", "NS", RecordData(body_soa['authoritative_server'])) resourcerecord.append(ns_record) new_zone = DNSZone(body_directives, resourcerecord) zone_file = body_zone[zone]['file'].split('"')[1] zone_file = ZONE_DIR[dns_server] + zone_file local_zone_file = zone_file.replace(REMOTE_MNT_DIR, LOCAL_MNT_DIR[dns_server], 1) logger.debug("Write zone file to directory " + local_zone_file) logger.debug("Zone to write: " + new_zone.toJSON()) new_zone.write_to_file(local_zone_file) restart_bind(dns_server) init_data() return HttpResponse('{ "status" : "ok" }') except ValueError as v_err: logger.warning(v_err.args) logger.warning(traceback.format_exc(2) + "\n\n\n") return HttpResponse('{"status" : "Invalid JSON arguments"}', status=500) except ZoneError as z_err: logger.error(z_err.args) logger.error(traceback.format_exc() + "\n\n\n") return HttpResponse('{"status" : "'+str(z_err.args[0])+'"}', status=500) except BindError as b_err: logger.error(b_err.args) logger.error(traceback.format_exc() + "\n\n\n") if b_err.args[0]['file_type']: backup_restore_file('restore', b_err.args[0]['file_type'], b_err.args[0]['origin'], '.bak') return HttpResponse('{"status" : "'+str(b_err.args[0]['msg'])+'"}', status=500) except Exception as b_err: logger.error(b_err.args) logger.error(traceback.format_exc() + "\n\n\n") return HttpResponse('{"status" : "'+str(b_err)+'"}', status=500)