def cli_zones_export(output, user_id, search, tags, include_records): provider = Provider() zones = provider.dns_zones() tags = tags.split(',') if tags is not None else None result = zones.export(user_id=user_id, search=search, tags=tags, export_zones=True, export_records=include_records) if not result: print("Could not export data") return False os.rename(result['zones'], output) print("Zone export saved as: {0}".format(output)) if include_records: path = os.path.dirname(output) file = os.path.basename(output).split('.') if len(file) > 1: extension = file[-1] del file[-1] file.append('records') file.append(extension) else: file.append('records') save_records_as = os.path.join(path, '.'.join(file)) os.rename(result['records'], save_records_as) print("Record export saved as: {0}".format(save_records_as)) return True
def zone_create_from_log(query_log_id): provider = Provider() logging = provider.dns_logs() zones = provider.dns_zones() log = logging.get(query_log_id) if not log: flash('Could not retrieve log record', 'error') return redirect(url_for('home.index')) if log.dns_zone_id > 0: # This means that the zone exists. if not zones.can_access(log.dns_zone_id, current_user.id): # This error is misleading on purpose to prevent zone enumeration. Not that it's important by meh. flash('Could not retrieve log record', 'error') return redirect(url_for('home.index')) flash('Zone already exists', 'error') return redirect(url_for('dns.zone_view', dns_zone_id=log.dns_zone_id)) zone = zones.new(log.domain, True, False, False, current_user.id, update_old_logs=True) if isinstance(zone, list): for error in zone: flash(error, 'error') return redirect(url_for('dns.zone_edit', dns_zone_id=0)) flash('Zone created', 'success') return redirect(url_for('dns.zone_view', dns_zone_id=zone.id))
def index(): results_per_page = 20 provider = Provider() zones = provider.dns_zones() tags = provider.tags() search = request.args.get('search', '').strip() search_tags = request.args.getlist('tags') page = int(request.args.get('page', 1)) if page <= 0: page = 1 user_id = None if current_user.admin else current_user.id page_url = 'tags=' + '&tags='.join(search_tags) page_url += "&search={0}&page=".format(search) return render_template('dns/zones/index.html', zones=zones.get_user_zones_paginated( user_id, order_by='domain', page=page, per_page=results_per_page, search=search, tags=search_tags), page=page, per_page=results_per_page, page_url=page_url, search=search, search_tags=search_tags, tags=tags.all(user_id=user_id, order_by='asc', order_column='name'))
def cli_zones_update(domain, active, catch_all, forwarding, regex): provider = Provider() zones = provider.dns_zones() zone = zones.find(domain) if not zone: print("Could not find domain") return False if active is not None: zone.active = (active in ['yes', 'true']) if catch_all is not None: zone.catch_all = (catch_all in ['yes', 'true']) if forwarding is not None: zone.forwarding = (forwarding in ['yes', 'true']) if regex is not None: zone.regex = (regex in ['yes', 'true']) zone.save() print("Zone updated") return True
def zone_notifications_settings(dns_zone_id, item): provider = Provider() zones = provider.dns_zones() notifications = provider.notifications() if not zones.can_access(dns_zone_id, current_user.id): flash('Access Denied', 'error') return redirect(url_for('dns.index')) zone = zones.get(dns_zone_id) if not zone: flash('Zone not found', 'error') return redirect(url_for('dns.index')) notification_provider = notifications.providers.get(item) if not notification_provider: flash('Invalid notification provider', 'error') return redirect(url_for('dns.index')) elif not notification_provider.enabled: flash('Notification provider is not enabled', 'error') return redirect(url_for('dns.index')) elif not notification_provider.has_settings: flash('Notification provider has no settings', 'error') return redirect(url_for('dns.index')) return render_template( 'dns/zones/view.html', zone=zone, tab='notifications', section='{0}_settings'.format(item), has_enabled_providers=notifications.providers.has_enabled(), subscription=zone.notifications.get(item) )
def all(self, user_id): provider = Provider() zones = provider.dns_zones() page = self.get_request_param('page', 1, int) per_page = self.get_request_param('per_page', 50, int) search = self.get_request_param('search', '', str) tags = self.get_request_param('tags', '', str).split(',') results = zones.get_user_zones_paginated(user_id, page=page, per_page=per_page, search=search, tags=tags) response = { 'page': results['results'].page, 'pages': results['results'].pages, 'per_page': results['results'].per_page, 'total': results['results'].total, 'data': [] } data = [] for result in results['zones']: data.append(self.__load_zone(result)) response['data'] = data return self.send_valid_response(response)
def zone_restrictions_edit_save(dns_zone_id, restriction_id): provider = Provider() zones = provider.dns_zones() restrictions = provider.dns_restrictions() if not zones.can_access(dns_zone_id, current_user.id): flash('Access Denied', 'error') return redirect(url_for('home.index')) zone = zones.get(dns_zone_id) if not zone: flash('Zone not found', 'error') return redirect(url_for('home.index')) ip_range = request.form['ip_range'].strip() type = int(request.form['type'].strip()) enabled = True if int(request.form.get('enabled', 0)) == 1 else False if len(ip_range) == 0 or not restrictions.is_valid_ip_or_range(ip_range): flash('Invalid IP/Range', 'error') return redirect(url_for('dns.zone_restrictions', dns_zone_id=zone.id)) elif type not in [1, 2]: flash('Invalid type', 'error') return redirect(url_for('dns.zone_restrictions', dns_zone_id=zone.id)) restriction = restrictions.create(zone_id=zone.id) if restriction_id == 0 else zone.restrictions.get(restriction_id) if not restriction: flash('Could not load restriction', 'error') return redirect(url_for('dns.zone_restrictions', dns_zone_id=zone.id)) restrictions.save(restriction, zone.id, ip_range, type, enabled) flash('Restriction saved', 'success') return redirect(url_for('dns.zone_restrictions', dns_zone_id=zone.id))
def cli_restrictions_delete(domain, iprange, id): provider = Provider() zones = provider.dns_zones() if iprange is None and id is None: print("One of --iprange or --id should be used") return False zone = zones.find(domain) if not zone: print("Could not find domain") return False if id is not None: restriction = zone.restrictions.get(id) if not restriction: print("Could not find restriction with id {0}".format(id)) return False elif iprange is not None: for restriction in zone.restrictions.all(): if iprange == 'all': restriction.delete() elif iprange == restriction.ip_range: restriction.delete() print("Restriction(s) deleted") return True
def zone_edit(dns_zone_id): provider = Provider() zones = provider.dns_zones() tags = provider.tags() zone = None dns_zone_id = 0 if dns_zone_id < 0 else dns_zone_id if dns_zone_id > 0: if not zones.can_access(dns_zone_id, current_user.id): flash('Access Denied', 'error') return redirect(url_for('home.index')) zone = zones.get(dns_zone_id) if not zone: flash('Zone not found', 'error') return redirect(url_for('home.index')) username = current_user.username if zone is None else zone.username user_id = zone.user_id if dns_zone_id > 0 else current_user.id return render_template('dns/zones/edit.html', dns_zone_id=dns_zone_id, user_domain=zones.get_user_base_domain(username), zone=zone, tags=tags.all(user_id=user_id, order_column='name', order_by='asc'))
def index(): # This function deliberately doesn't have a @login_required parameter because we want to run a check for a # 'first-visit' type scenario, in order to create the administrator. provider = Provider() zones = provider.dns_zones() users = provider.users() if users.count() == 0: # Looks like we need to setup the administrator. return redirect(url_for('install.index')) if not current_user.is_authenticated: return redirect(url_for('auth.login')) search = provider.search() results = search.search_from_request(request) aliases = provider.aliases() return render_template( 'home/index.html', results=results['results'], params=results['params'], page_url='home.index', zone_count=zones.count(user_id=current_user.id), aliases=aliases.get_dict( None if current_user.admin else current_user.id))
def cli_restrictions_update(domain, id, iprange, type, enabled): provider = Provider() zones = provider.dns_zones() restrictions = provider.dns_restrictions() zone = zones.find(domain) if not zone: print("Could not find domain") return False restriction = zone.restrictions.get(id) if not restriction: print("Could not find restriction") return False iprange = restriction.ip_range if iprange is None else iprange if len(iprange) == 0 or not restrictions.is_valid_ip_or_range(iprange): print("Invalid IP Range") return False enabled = restriction.enabled if enabled is None else enabled in ['yes', 'true'] type = restriction.type if type is None else (1 if type == 'allow' else 2) restrictions.save(restriction, zone.id, iprange, type, enabled) print("Restriction updated") return True
def create(self, user_id, zone_id=None, domain=None): provider = Provider() zones = provider.dns_zones() restrictions = provider.dns_restrictions() zone = zones.get(zone_id, user_id) if zone_id is not None else zones.find( domain, user_id=user_id) if not zone: return self.send_not_found_response() required_fields = ['type', 'enabled', 'ip_or_range'] data = self.get_json(required_fields) if data is False: return self.send_error_response( 5000, 'Missing fields', 'Required fields are: {0}'.format(', '.join(required_fields))) if data['type'] not in ['allow', 'block']: return self.send_error_response(5005, 'Invalid restriction type', '') elif len( data['ip_or_range'] ) == 0 or not restrictions.is_valid_ip_or_range(data['ip_or_range']): return self.send_error_response(5005, 'Invalid IP or Range', '') data['enabled'] = True if data['enabled'] else False data['type'] = 1 if data['type'] == 'allow' else 2 restriction = restrictions.create(zone_id=zone.id) restriction = restrictions.save(restriction, zone.id, data['ip_or_range'], data['type'], data['enabled']) return self.one(user_id, restriction.id, zone_id=zone.id)
def record_edit(dns_zone_id, dns_record_id): provider = Provider() zones = provider.dns_zones() records = provider.dns_records() if not zones.can_access(dns_zone_id, current_user.id): flash('Access Denied', 'error') return redirect(url_for('home.index')) elif dns_record_id > 0: if not records.can_access(dns_zone_id, dns_record_id): flash('Access Denied', 'error') return redirect(url_for('home.index')) zone = zones.get(dns_zone_id) if not zone: flash('Zone not found', 'error') return redirect(url_for('home.index')) record = records.get(dns_record_id, zone.id) if dns_record_id > 0: if not record: flash('Could not load record', 'error') return redirect(url_for('home.index')) dns_types = records.get_types() dns_classes = records.get_classes() return render_template('dns/zones/view.html', dns_record_id=dns_record_id, dns_types=dns_types, dns_classes=dns_classes, zone=zone, record=record, section='records_edit', tab='records')
def __zone_create(): provider = Provider() zones = provider.dns_zones() dns_zone_id = 0 domain = request.form['domain'].strip().lower() active = True if int(request.form.get('active', 0)) == 1 else False catch_all = True if int(request.form.get('catch_all', 0)) == 1 else False forwarding = True if int(request.form.get('forwarding', 0)) == 1 else False regex = True if int(request.form.get('regex', 0)) == 1 else False tags = request.form.getlist('tags') zone = zones.new(domain, active, catch_all, forwarding, regex, current_user.id, update_old_logs=True) if isinstance(zone, list): for error in zone: flash(error, 'error') return redirect(url_for('dns.zone_edit', dns_zone_id=dns_zone_id)) zone = zones.save_tags(zone, tags) if not zone: flash('Could not save zone tags', 'error') return redirect(url_for('dns.zone_edit', dns_zone_id=dns_zone_id)) flash('Zone created', 'success') return redirect(url_for('dns.zone_view', dns_zone_id=zone.id))
def cli_records_list(domain, type, id): provider = Provider() zones = provider.dns_zones() records = provider.dns_records() if type is None and id is None: print("At least one of --type and --id has to be used") return False zone = zones.find(domain) if not zone: print("Could not find domain") return False results = records.get_zone_records(zone.id, order_column='type') record_deleted = False for record in results: if type is not None: if record.type == type.upper(): record_deleted = True records.delete(record) elif id is not None: if record.id == id: record_deleted = True records.delete(record) message = "Record deleted" if record_deleted else "Record not found" print(message) return True
def zone_notifications_settings_save(dns_zone_id, item): provider = Provider() zones = provider.dns_zones() notifications = provider.notifications() if not zones.can_access(dns_zone_id, current_user.id): flash('Access Denied', 'error') return redirect(url_for('dns.index')) zone = zones.get(dns_zone_id) if not zone: flash('Zone not found', 'error') return redirect(url_for('dns.index')) notification_provider = notifications.providers.get(item) if not notification_provider: flash('Invalid notification provider', 'error') return redirect(url_for('dns.index')) elif not notification_provider.enabled: flash('Notification provider is not enabled', 'error') return redirect(url_for('dns.index')) elif not notification_provider.has_settings: flash('Notification provider has no settings', 'error') return redirect(url_for('dns.index')) if item == 'email': recipients = request.form.getlist('recipients[]') valid_recipients = [] email_regex = re.compile(r"[^@]+@[^@]+\.[^@]+") for recipient in recipients: recipient = recipient.strip().lower() if len(recipient) > 0 and email_regex.match(recipient): valid_recipients.append(recipient) # Remove duplicates. valid_recipients = list(dict.fromkeys(valid_recipients)) subscription = zone.notifications.get(item) if subscription: subscription.data = valid_recipients subscription.save() elif item == 'slack': slack_webhook_url = request.form['slack_webhook_url'].strip() subscription = zone.notifications.get(item) if subscription: subscription.data = slack_webhook_url subscription.save() elif item == 'teams': teams_webhook_url = request.form['teams_webhook_url'].strip() subscription = zone.notifications.get(item) if subscription: subscription.data = teams_webhook_url subscription.save() flash('Notification settings saved.') return redirect(url_for('dns.zone_notifications_settings', dns_zone_id=dns_zone_id, item=item))
def zone_group_delete(): provider = Provider() zones = provider.dns_zones() search = request.form['search'].strip() search_tags = request.form['tags'].strip().split(',') zones.group_delete(current_user.id, search=search, tags=search_tags) flash('Zone(s) deleted', 'success') return redirect(url_for('dns.index'))
def one(self, user_id, zone_id=None, domain=None): provider = Provider() zones = provider.dns_zones() zone = zones.get(zone_id, user_id) if zone_id is not None else zones.find( domain, user_id=user_id) if not zone: return self.send_not_found_response() return self.send_valid_response(self.__load_zone(zone))
def cli_zones_delete(domain): provider = Provider() zones = provider.dns_zones() zone = zones.find(domain) if not zone: print("Could not find domain") return False zones.delete(zone.id) print("Domain {0} deleted".format(domain)) return True
def delete(self, user_id, zone_id=None, domain=None): provider = Provider() zones = provider.dns_zones() zone = zones.get(zone_id, user_id) if zone_id is not None else zones.find( domain, user_id=user_id) if not zone: return self.send_not_found_response() zones.delete(zone.id) return self.send_success_response()
def cli_zones_add(domain, user_id, active, catch_all, forwarding, regex): provider = Provider() zones = provider.dns_zones() zone = zones.new(domain, active, catch_all, forwarding, regex, user_id) if isinstance(zone, list): for error in zone: print(error) return False print("Zone created") return True
def zone_restriction_create_from_log(query_log_id): provider = Provider() logging = provider.dns_logs() zones = provider.dns_zones() restrictions = provider.dns_restrictions() log = logging.get(query_log_id) if not log: flash('Could not retrieve log record', 'error') return redirect(url_for('home.index')) if log.dns_zone_id > 0: # This means that the zone exists. if not zones.can_access(log.dns_zone_id, current_user.id): # This error is misleading on purpose to prevent zone enumeration. Not that it's important by meh. flash('Could not retrieve log record', 'error') return redirect(url_for('home.index')) zone = zones.get(log.dns_zone_id) if not zone: flash('Could not load zone', 'error') return redirect(url_for('home.index')) else: # There's a chance that the dns_zone_id equals to zero but the domain exists. This can happen if the zone was # created from the log files, as the IDs aren't updated after a domain is created (after it's been logged). zone = zones.find(log.domain, user_id=current_user.id) if not zone: # If we still can't find it, create it. zone = zones.new(log.domain, True, True, False, current_user.id) if isinstance(zone, list): for error in zone: flash(error, 'error') return redirect(url_for('home.index')) # One last check as it may have been loaded by domain. if not zones.can_access(zone.id, current_user.id): # This error is misleading on purpose to prevent zone enumeration. Not that it's important by meh. flash('Could not retrieve log record', 'error') return redirect(url_for('home.index')) # At this point we should have a valid zone object. First check if the restriction exists. restriction = restrictions.find(zone_id=zone.id, ip_range=log.source_ip, type=2) if not restriction: # Doesn't exist - create it. restriction = restrictions.create(zone_id=zone.id) # Now update and save. restriction = restrictions.save(restriction, zone.id, log.source_ip, 2, True) flash('Restriction rule created', 'success') return redirect(url_for('dns.zone_restrictions', dns_zone_id=zone.id))
def all(self, user_id, zone_id=None, domain=None): provider = Provider() zones = provider.dns_zones() zone = zones.get(zone_id, user_id) if zone_id is not None else zones.find( domain, user_id=user_id) if not zone: return self.send_not_found_response() data = [] for restriction in zone.restrictions.all(): data.append(self.__load_restriction(restriction)) return self.send_valid_response(data)
def __zone_update(dns_zone_id): provider = Provider() zones = provider.dns_zones() if not zones.can_access(dns_zone_id, current_user.id): flash('Access Denied', 'error') return redirect(url_for('home.index')) zone = zones.get(dns_zone_id) if not zone: flash('Could not get zone', 'error') return redirect(url_for('dns.zone_edit', dns_zone_id=dns_zone_id)) domain = request.form['domain'].strip().lower( ) if not zone.master else zone.domain active = True if int(request.form.get('active', 0)) == 1 else False catch_all = True if int(request.form.get('catch_all', 0)) == 1 else False forwarding = True if int(request.form.get('forwarding', 0)) == 1 else False regex = True if int(request.form.get('regex', 0)) == 1 else False tags = request.form.getlist('tags') if len(domain) == 0: flash('Invalid domain', 'error') return redirect(url_for('dns.zone_edit', dns_zone_id=dns_zone_id)) if zones.has_duplicate(dns_zone_id, domain): flash('This domain already exists.', 'error') return redirect(url_for('dns.zone_edit', dns_zone_id=dns_zone_id)) zone = zones.update(zone.id, domain, active, catch_all, forwarding, regex, zone.user_id, master=zone.master, update_old_logs=True) if isinstance(zone, list): for error in zone: flash(error, 'error') return redirect(url_for('dns.zone_edit', dns_zone_id=dns_zone_id)) zone = zones.save_tags(zone, tags) if not zone: flash('Could not save zone tags', 'error') return redirect(url_for('dns.zone_edit', dns_zone_id=dns_zone_id)) flash('Zone saved', 'success') return redirect(url_for('dns.zone_view', dns_zone_id=zone.id))
def zones_export(): provider = Provider() zones = provider.dns_zones() search = request.form['search'].strip() search_tags = request.form['tags'].strip().split(',') result = zones.export(user_id=current_user.id, export_zones=True, export_records=True, compress_export=True, search=search, tags=search_tags) if not result: flash('Could not generate export file.', 'error') return redirect(url_for('dns.index')) # And download. return send_file(result['zip'], attachment_filename='snitch_export.zip', as_attachment=True)
def update(self, user_id, type_name, zone_id=None, domain=None): provider = Provider() zones = provider.dns_zones() notifications = provider.notifications() logs = provider.dns_logs() zone = zones.get(zone_id, user_id) if zone_id is not None else zones.find( domain, user_id=user_id) if not zone: return self.send_not_found_response() type = notifications.types.get(name=type_name) if not type: return self.send_error_response( 5006, 'Invalid type: {0}'.format(type_name), '') notification_provider = notifications.providers.get(type_name) if not notification_provider: return self.send_error_response( 5006, 'Internal Error: Invalid provider', '') elif not notification_provider.enabled: return self.send_error_response( 5009, 'Notification provider is disabled', '') subscription = zone.notifications.get(type.name) if not subscription: return self.send_error_response( 5007, 'Invalid notification subscription', '') data = self.get_json([]) if not data: return self.send_error_response(5008, 'No data sent', '') if 'enabled' in data: subscription.enabled = True if data['enabled'] else False # We need to set the last query log id as well. subscription.last_query_log_id = logs.get_last_log_id(zone.id) if 'data' in data: if type.name == 'email': subscription.data = self.__get_valid_emails(data['data']) else: subscription.data = data['data'].strip() subscription.save() return self.get(user_id, type_name, zone_id=zone.id)
def all(self, user_id, zone_id=None, domain=None): provider = Provider() zones = provider.dns_zones() records = provider.dns_records() zone = zones.get(zone_id, user_id) if zone_id is not None else zones.find(domain, user_id=user_id) if not zone: return self.send_not_found_response() results = records.get_zone_records(zone.id) data = [] for result in results: data.append(self.__load_record(result)) return self.send_valid_response(data)
def all(self, user_id, zone_id=None, domain=None): provider = Provider() zones = provider.dns_zones() zone = zones.get(zone_id, user_id) if zone_id is not None else zones.find( domain, user_id=user_id) if not zone: return self.send_not_found_response() results = [] for name, subscription in zone.notifications.all().items(): results.append(self.__load_subscription(name, subscription)) return self.send_valid_response(results)
def cli_records_add(domain, type, cls, ttl, active, property): # Convert tuple to dict. properties = {} for p in property: properties[p[0]] = p[1] provider = Provider() zones = provider.dns_zones() records = provider.dns_records() if ttl < 0: print("Invalid TTL value") return False zone = zones.find(domain) if not zone: print("Could not find domain") return False expected_properties = records.get_record_type_properties(type, clean=True) type_data = {} for property_name, property_type in expected_properties.items(): if property_name not in properties: print('Missing type property {0}'.format(property_name)) return False value = properties[property_name] if (type == 'int') and (isinstance(value, str)): if not value.isdigit(): print('Invalid {0} value'.format(property_name)) return False value = int(value) if (type == 'str') and (len(value) == 0): print('Invalid {0} value'.format(property_name)) return False elif (type == 'int') and (value < 0): print('Invalid {0} value'.format(property_name)) return False type_data[property_name] = value record = records.create() record = records.save(record, zone.id, ttl, cls, type, type_data, active) print("Record created") return True
def cli_users_add(username, password, full_name, email, active, admin, ldap, create_zone): provider = Provider() users = provider.users() zones = provider.dns_zones() active = (active in ['true', 'yes']) admin = (admin in ['true', 'yes']) ldap = (ldap in ['true', 'yes']) ask_for_password = False if len(password) == 0: # If it's an LDAP user, we don't need it. if not ldap: ask_for_password = True if ask_for_password: password = click.prompt('Password', hide_input=True, confirmation_prompt=True) # If the user entered the password manually it's in plaintext so we can check for complexity. user = users.save(0, username, password, full_name, email, admin, ldap, active, check_complexity=ask_for_password, hash_password=ask_for_password) if not user: print(users.last_error) return False if create_zone: if not zones.create_user_base_zone(user): print( 'User has been created but there was a problem creating their base domain. Make sure the DNS Base Domain has been set.' ) return False print("User created") return True