diff --git a/README.md b/README.md deleted file mode 100644 index 95d09f2..0000000 --- a/README.md +++ /dev/null @@ -1 +0,0 @@ -hello world \ No newline at end of file diff --git a/__pycache__/fw_settings.cpython-313.pyc b/__pycache__/fw_settings.cpython-313.pyc new file mode 100644 index 0000000..0affbcb Binary files /dev/null and b/__pycache__/fw_settings.cpython-313.pyc differ diff --git a/app/app.py b/app/app.py new file mode 100644 index 0000000..6232150 --- /dev/null +++ b/app/app.py @@ -0,0 +1,664 @@ +""" +app.py — Flask backend для Firewall Rules Builder +""" +import json +import os +import sys +import re +from flask import Flask, jsonify, request, render_template, send_from_directory + +app = Flask(__name__, template_folder='templates', static_folder='static') + +# ─── Хранилище данных ──────────────────────────────────────────────────────── +DATA_FILE = os.path.join(os.path.dirname(__file__), 'data.json') + +EMPTY_DATA = { + "servers": {}, + "nets": {}, + "groups": {}, + "services": {}, + "service_groups": {}, + "rules": [] +} + + +def load_data(): + if os.path.exists(DATA_FILE): + with open(DATA_FILE, 'r', encoding='utf-8') as f: + return json.load(f) + return json.loads(json.dumps(EMPTY_DATA)) + + +def save_data(data): + with open(DATA_FILE, 'w', encoding='utf-8') as f: + json.dump(data, f, ensure_ascii=False, indent=2) + + +# ─── Главная страница ───────────────────────────────────────────────────────── +@app.route('/') +def index(): + return render_template('index.html') + + +# ─── API: Объекты (servers + nets) ─────────────────────────────────────────── +@app.route('/api/objects', methods=['GET']) +def get_objects(): + data = load_data() + result = {} + for k, v in data['servers'].items(): + result[k] = v + for k, v in data['nets'].items(): + result[k] = v + return jsonify(result) + + +@app.route('/api/objects', methods=['POST']) +def create_object(): + data = load_data() + obj = request.json + key = obj.get('key', '').strip() + if not key: + return jsonify({'error': 'Ключ обязателен'}), 400 + obj_type = obj.get('type', 'host') + obj_data = { + 'hostname': obj.get('hostname', key), + 'ip': obj.get('ip', ''), + 'prefix': obj.get('prefix', '24'), + 'gw': obj.get('gw', ''), + 'domain': obj.get('domain', ''), + 'description': obj.get('description', ''), + 'type': obj_type, + 'affinity': obj.get('affinity', []), + } + if obj_type == 'network': + if key in data['nets']: + return jsonify({'error': f'Объект "{key}" уже существует'}), 400 + data['nets'][key] = obj_data + else: + if key in data['servers']: + return jsonify({'error': f'Объект "{key}" уже существует'}), 400 + data['servers'][key] = obj_data + save_data(data) + return jsonify({'ok': True, 'key': key}) + + +@app.route('/api/objects/', methods=['PUT']) +def update_object(key): + data = load_data() + obj = request.json + obj_type = obj.get('type', 'host') + obj_data = { + 'hostname': obj.get('hostname', key), + 'ip': obj.get('ip', ''), + 'prefix': obj.get('prefix', '24'), + 'gw': obj.get('gw', ''), + 'domain': obj.get('domain', ''), + 'description': obj.get('description', ''), + 'type': obj_type, + 'affinity': obj.get('affinity', []), + } + # Удаляем из обоих словарей (тип мог измениться) + data['servers'].pop(key, None) + data['nets'].pop(key, None) + if obj_type == 'network': + data['nets'][key] = obj_data + else: + data['servers'][key] = obj_data + save_data(data) + return jsonify({'ok': True}) + + +@app.route('/api/objects/', methods=['DELETE']) +def delete_object(key): + data = load_data() + data['servers'].pop(key, None) + data['nets'].pop(key, None) + save_data(data) + return jsonify({'ok': True}) + + +# ─── API: Группы объектов ───────────────────────────────────────────────────── +@app.route('/api/groups', methods=['GET']) +def get_groups(): + data = load_data() + return jsonify(data['groups']) + + +@app.route('/api/groups', methods=['POST']) +def create_group(): + data = load_data() + grp = request.json + key = grp.get('key', '').strip() + if not key: + return jsonify({'error': 'Ключ обязателен'}), 400 + if key in data['groups']: + return jsonify({'error': f'Группа "{key}" уже существует'}), 400 + data['groups'][key] = { + 'name': grp.get('name', key), + 'items': grp.get('items', []) + } + save_data(data) + return jsonify({'ok': True, 'key': key}) + + +@app.route('/api/groups/', methods=['PUT']) +def update_group(key): + data = load_data() + grp = request.json + data['groups'][key] = { + 'name': grp.get('name', key), + 'items': grp.get('items', []) + } + save_data(data) + return jsonify({'ok': True}) + + +@app.route('/api/groups/', methods=['DELETE']) +def delete_group(key): + data = load_data() + data['groups'].pop(key, None) + save_data(data) + return jsonify({'ok': True}) + + +# ─── API: Сервисы ───────────────────────────────────────────────────────────── +@app.route('/api/services', methods=['GET']) +def get_services(): + data = load_data() + return jsonify(data['services']) + + +@app.route('/api/services', methods=['POST']) +def create_service(): + data = load_data() + svc = request.json + key = svc.get('key', '').strip() + if not key: + return jsonify({'error': 'Ключ обязателен'}), 400 + if key in data['services']: + return jsonify({'error': f'Сервис "{key}" уже существует'}), 400 + data['services'][key] = { + 'name': svc.get('name', key), + 'sport': svc.get('sport', 'any'), + 'dport': svc.get('dport', ''), + 'proto': svc.get('proto', 'tcp'), + } + save_data(data) + return jsonify({'ok': True, 'key': key}) + + +@app.route('/api/services/', methods=['PUT']) +def update_service(key): + data = load_data() + svc = request.json + data['services'][key] = { + 'name': svc.get('name', key), + 'sport': svc.get('sport', 'any'), + 'dport': svc.get('dport', ''), + 'proto': svc.get('proto', 'tcp'), + } + save_data(data) + return jsonify({'ok': True}) + + +@app.route('/api/services/', methods=['DELETE']) +def delete_service(key): + data = load_data() + data['services'].pop(key, None) + save_data(data) + return jsonify({'ok': True}) + + +# ─── API: Группы сервисов ───────────────────────────────────────────────────── +@app.route('/api/service_groups', methods=['GET']) +def get_service_groups(): + data = load_data() + return jsonify(data['service_groups']) + + +@app.route('/api/service_groups', methods=['POST']) +def create_service_group(): + data = load_data() + sg = request.json + key = sg.get('key', '').strip() + if not key: + return jsonify({'error': 'Ключ обязателен'}), 400 + if key in data['service_groups']: + return jsonify({'error': f'Группа сервисов "{key}" уже существует'}), 400 + data['service_groups'][key] = { + 'name': sg.get('name', key), + 'items': sg.get('items', []) + } + save_data(data) + return jsonify({'ok': True, 'key': key}) + + +@app.route('/api/service_groups/', methods=['PUT']) +def update_service_group(key): + data = load_data() + sg = request.json + data['service_groups'][key] = { + 'name': sg.get('name', key), + 'items': sg.get('items', []) + } + save_data(data) + return jsonify({'ok': True}) + + +@app.route('/api/service_groups/', methods=['DELETE']) +def delete_service_group(key): + data = load_data() + data['service_groups'].pop(key, None) + save_data(data) + return jsonify({'ok': True}) + + +# ─── API: Правила ───────────────────────────────────────────────────────────── +@app.route('/api/rules', methods=['GET']) +def get_rules(): + data = load_data() + return jsonify(data['rules']) + + +@app.route('/api/rules', methods=['POST']) +def create_rule(): + data = load_data() + rule = request.json + data['rules'].append(rule) + save_data(data) + return jsonify({'ok': True, 'index': len(data['rules']) - 1}) + + +@app.route('/api/rules/', methods=['PUT']) +def update_rule(idx): + data = load_data() + if idx < 0 or idx >= len(data['rules']): + return jsonify({'error': 'Индекс вне диапазона'}), 404 + data['rules'][idx] = request.json + save_data(data) + return jsonify({'ok': True}) + + +@app.route('/api/rules/', methods=['DELETE']) +def delete_rule(idx): + data = load_data() + if idx < 0 or idx >= len(data['rules']): + return jsonify({'error': 'Индекс вне диапазона'}), 404 + data['rules'].pop(idx) + save_data(data) + return jsonify({'ok': True}) + + +@app.route('/api/rules/reorder', methods=['POST']) +def reorder_rules(): + data = load_data() + new_order = request.json.get('order', []) + try: + data['rules'] = [data['rules'][i] for i in new_order] + except IndexError: + return jsonify({'error': 'Неверный порядок'}), 400 + save_data(data) + return jsonify({'ok': True}) + + +# ─── API: Экспорт fw_settings.py ───────────────────────────────────────────── +@app.route('/api/export', methods=['GET']) +def export_settings(): + data = load_data() + content = generate_fw_settings(data) + return app.response_class( + response=content, + status=200, + mimetype='text/plain; charset=utf-8', + headers={'Content-Disposition': 'attachment; filename=fw_settings.py'} + ) + + +def py_repr(val): + """Представление Python-значения в виде строки.""" + if isinstance(val, str): + # Используем двойные кавычки + escaped = val.replace('\\', '\\\\').replace('"', '\\"') + return f'"{escaped}"' + elif isinstance(val, list): + items = ', '.join(py_repr(i) for i in val) + return f'[{items}]' + elif isinstance(val, dict): + pairs = ', '.join(f'{py_repr(k)}: {py_repr(v)}' for k, v in val.items()) + return '{' + pairs + '}' + elif isinstance(val, bool): + return 'True' if val else 'False' + elif val is None: + return 'None' + else: + return repr(val) + + +def generate_fw_settings(data): + lines = [] + + # servers + lines.append('servers = {') + for key, obj in data['servers'].items(): + lines.append(f' {py_repr(key)}: {{') + lines.append(f' "hostname": {py_repr(obj.get("hostname", key))},') + lines.append(f' "ip": {py_repr(obj.get("ip", ""))},') + lines.append(f' "prefix": {py_repr(obj.get("prefix", "24"))},') + lines.append(f' "gw": {py_repr(obj.get("gw", ""))},') + lines.append(f' "domain": {py_repr(obj.get("domain", ""))},') + lines.append(f' "description": {py_repr(obj.get("description", ""))},') + lines.append(f' "type": "host",') + affinity = obj.get('affinity', []) + lines.append(f' "affinity": {py_repr(affinity)},') + lines.append(f' }},') + lines.append('}') + lines.append('') + + # nets + lines.append('# networks') + lines.append('nets = {') + for key, obj in data['nets'].items(): + lines.append(f' {py_repr(key)}: {{') + lines.append(f' "hostname": {py_repr(obj.get("hostname", key))},') + lines.append(f' "description": {py_repr(obj.get("description", ""))},') + lines.append(f' "domain": {py_repr(obj.get("domain", ""))},') + lines.append(f' "ip": {py_repr(obj.get("ip", ""))},') + prefix = obj.get('prefix', 24) + try: + prefix = int(prefix) + except (ValueError, TypeError): + pass + lines.append(f' "prefix": {prefix},') + lines.append(f' "type": "network",') + affinity = obj.get('affinity', []) + lines.append(f' "affinity": {py_repr(affinity)},') + lines.append(f' }},') + lines.append('}') + lines.append('') + lines.append('') + + # groups + lines.append('groups = {') + for key, grp in data['groups'].items(): + items = grp.get('items', []) + items_repr = '[' + ', '.join(f'{{"hostname": {py_repr(i.get("hostname",""))}}}' for i in items) + ']' + lines.append(f' {py_repr(key)}: {{"name": {py_repr(grp.get("name", key))}, "items": {items_repr}}},') + lines.append('}') + lines.append('') + lines.append('') + + # services + lines.append('# services') + lines.append('services = {') + for key, svc in data['services'].items(): + lines.append(f' {py_repr(key)}: {{') + lines.append(f' "name": {py_repr(svc.get("name", key))},') + lines.append(f' "sport": {py_repr(svc.get("sport", "any"))},') + lines.append(f' "dport": {py_repr(svc.get("dport", ""))},') + lines.append(f' "proto": {py_repr(svc.get("proto", "tcp"))},') + lines.append(f' }},') + lines.append('}') + lines.append('') + + # service groups + lines.append('# service groups') + lines.append('service_groups = {') + for key, sg in data['service_groups'].items(): + items = sg.get('items', []) + lines.append(f' {py_repr(key)}: {{') + lines.append(f' "name": {py_repr(sg.get("name", key))},') + lines.append(f' "items": [') + for svc_key in items: + lines.append(f' services[{py_repr(svc_key)}],') + lines.append(f' ],') + lines.append(f' }},') + lines.append('}') + lines.append('') + + # rules + lines.append('# rules') + lines.append('rules = [') + for rule in data['rules']: + lines.append(' {') + lines.append(f' "name": {py_repr(rule.get("name", ""))},') + lines.append(f' "order": {rule.get("order", 0)},') + rtype = rule.get('type', 'rule') + lines.append(f' "type": {py_repr(rtype)},') + if rtype == 'span': + affinity = rule.get('affinity', []) + lines.append(f' "affinity": {py_repr(affinity)},') + else: + lines.append(f' "description": {py_repr(rule.get("description", ""))},') + # src_list + src_list = rule.get('src_list', []) + src_parts = _rule_ref_list(src_list) + lines.append(f' "src_list": [{", ".join(src_parts)}],') + # dst_list + dst_list = rule.get('dst_list', []) + dst_parts = _rule_ref_list(dst_list) + lines.append(f' "dst_list": [{", ".join(dst_parts)}],') + # service_list + svc_list = rule.get('service_list', []) + svc_parts = [f'services[{py_repr(s)}]' for s in svc_list] if svc_list else [] + lines.append(f' "service_list": [{", ".join(svc_parts)}],') + # service_group_list + sg_list = rule.get('service_group_list', []) + sg_parts = [f'service_groups[{py_repr(s)}]' for s in sg_list] if sg_list else [] + sg_val = f'[{", ".join(sg_parts)}]' if sg_parts else 'None' + lines.append(f' "service_group_list": {sg_val},') + lines.append(f' "action": {py_repr(rule.get("action", "allow"))},') + lines.append(f' "log": {py_repr(rule.get("log", "false"))},') + lines.append(f' "idp": {py_repr(rule.get("idp", "false"))},') + affinity = rule.get('affinity', []) + lines.append(f' "affinity": {py_repr(affinity)},') + lines.append(' },') + lines.append(']') + lines.append('') + + return '\n'.join(lines) + + +def _rule_ref_list(items): + """Генерирует Python-ссылки для src_list/dst_list.""" + parts = [] + for item in items: + ref_type = item.get('ref_type', 'group') + ref_key = item.get('ref_key', '') + if ref_type == 'server': + parts.append(f'servers[{py_repr(ref_key)}]') + elif ref_type == 'net': + parts.append(f'nets[{py_repr(ref_key)}]') + else: + parts.append(f'groups[{py_repr(ref_key)}]') + return parts + + +# ─── API: Импорт fw_settings.py ────────────────────────────────────────────── +@app.route('/api/import', methods=['POST']) +def import_settings(): + """Импортирует данные из fw_settings.py через exec().""" + try: + fw_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'fw_settings.py') + if not os.path.exists(fw_path): + return jsonify({'error': 'fw_settings.py не найден'}), 404 + + ns = {} + with open(fw_path, 'r', encoding='utf-8') as f: + source = f.read() + exec(compile(source, fw_path, 'exec'), ns) + + raw_servers = ns.get('servers', {}) + raw_nets = ns.get('nets', {}) + raw_groups = ns.get('groups', {}) + raw_services = ns.get('services', {}) + raw_service_groups = ns.get('service_groups', {}) + raw_rules = ns.get('rules', []) + + data = { + 'servers': {}, + 'nets': {}, + 'groups': {}, + 'services': {}, + 'service_groups': {}, + 'rules': [] + } + + # Серверы + for k, v in raw_servers.items(): + data['servers'][k] = { + 'hostname': v.get('hostname', k), + 'ip': v.get('ip', ''), + 'prefix': str(v.get('prefix', '24')), + 'gw': v.get('gw', ''), + 'domain': v.get('domain', ''), + 'description': v.get('description', ''), + 'type': 'host', + 'affinity': v.get('affinity', []), + } + + # Сети + for k, v in raw_nets.items(): + data['nets'][k] = { + 'hostname': v.get('hostname', k), + 'ip': v.get('ip', ''), + 'prefix': str(v.get('prefix', '24')), + 'gw': v.get('gw', ''), + 'domain': v.get('domain', ''), + 'description': v.get('description', ''), + 'type': 'network', + 'affinity': v.get('affinity', []), + } + + # Группы + for k, v in raw_groups.items(): + items = [] + for item in v.get('items', []): + if isinstance(item, dict): + items.append({'hostname': item.get('hostname', '')}) + data['groups'][k] = { + 'name': v.get('name', k), + 'items': items + } + + # Сервисы + for k, v in raw_services.items(): + data['services'][k] = { + 'name': v.get('name', k), + 'sport': v.get('sport', 'any'), + 'dport': v.get('dport', ''), + 'proto': v.get('proto', 'tcp'), + } + + # Группы сервисов — сохраняем ключи сервисов + svc_by_name = {v['name']: k for k, v in raw_services.items()} + for k, v in raw_service_groups.items(): + item_keys = [] + for svc in v.get('items', []): + if isinstance(svc, dict): + svc_name = svc.get('name', '') + # Ищем ключ по имени + found_key = svc_by_name.get(svc_name) + if found_key: + item_keys.append(found_key) + else: + # Ищем по совпадению значений + for sk, sv in raw_services.items(): + if sv == svc: + item_keys.append(sk) + break + data['service_groups'][k] = { + 'name': v.get('name', k), + 'items': item_keys + } + + # Правила — конвертируем ссылки в ref_type/ref_key + all_server_ids = {id(v): k for k, v in raw_servers.items()} + all_net_ids = {id(v): k for k, v in raw_nets.items()} + all_group_ids = {id(v): k for k, v in raw_groups.items()} + all_svc_ids = {id(v): k for k, v in raw_services.items()} + all_sg_ids = {id(v): k for k, v in raw_service_groups.items()} + + for rule in raw_rules: + rtype = rule.get('type', 'rule') + new_rule = { + 'name': rule.get('name', ''), + 'order': rule.get('order', 0), + 'type': rtype, + 'affinity': rule.get('affinity', []), + } + if rtype != 'span': + new_rule['description'] = rule.get('description', '') + new_rule['action'] = rule.get('action', 'allow') + new_rule['log'] = str(rule.get('log', 'false')) + new_rule['idp'] = str(rule.get('idp', 'false')) + + # src_list + src_list = [] + for item in (rule.get('src_list') or []): + ref = _resolve_ref(item, all_server_ids, all_net_ids, all_group_ids) + if ref: + src_list.append(ref) + new_rule['src_list'] = src_list + + # dst_list + dst_list = [] + for item in (rule.get('dst_list') or []): + ref = _resolve_ref(item, all_server_ids, all_net_ids, all_group_ids) + if ref: + dst_list.append(ref) + new_rule['dst_list'] = dst_list + + # service_list + svc_list = [] + for svc in (rule.get('service_list') or []): + key = all_svc_ids.get(id(svc)) + if key: + svc_list.append(key) + new_rule['service_list'] = svc_list + + # service_group_list + sg_list = [] + for sg in (rule.get('service_group_list') or []): + key = all_sg_ids.get(id(sg)) + if key: + sg_list.append(key) + new_rule['service_group_list'] = sg_list + + data['rules'].append(new_rule) + + save_data(data) + return jsonify({'ok': True, 'message': 'Импорт выполнен успешно'}) + + except Exception as e: + return jsonify({'error': str(e)}), 500 + + +def _resolve_ref(item, server_ids, net_ids, group_ids): + obj_id = id(item) + if obj_id in server_ids: + return {'ref_type': 'server', 'ref_key': server_ids[obj_id]} + if obj_id in net_ids: + return {'ref_type': 'net', 'ref_key': net_ids[obj_id]} + if obj_id in group_ids: + return {'ref_type': 'group', 'ref_key': group_ids[obj_id]} + # Попробуем по hostname + if isinstance(item, dict): + hostname = item.get('hostname', '') + if hostname in server_ids.values(): + return {'ref_type': 'server', 'ref_key': hostname} + if hostname in net_ids.values(): + return {'ref_type': 'net', 'ref_key': hostname} + name = item.get('name', '') + if name in group_ids.values(): + return {'ref_type': 'group', 'ref_key': name} + return None + + +# ─── API: Получить все данные сразу ────────────────────────────────────────── +@app.route('/api/all', methods=['GET']) +def get_all(): + return jsonify(load_data()) + + +if __name__ == '__main__': + app.run(debug=True, port=5000) diff --git a/app/data.json b/app/data.json new file mode 100644 index 0000000..dece3b3 --- /dev/null +++ b/app/data.json @@ -0,0 +1,2080 @@ +{ + "servers": { + "cr": { + "hostname": "cr", + "ip": "172.19.20.2", + "prefix": "24", + "gw": "172.19.20.1", + "domain": "avndr.ru", + "description": "ЦР ПУЦ + TLS", + "type": "host", + "affinity": [ + "fw_cr", + "fw_cr_ca" + ] + }, + "cs": { + "hostname": "cs", + "ip": "172.19.20.3", + "prefix": "24", + "gw": "172.19.20.1", + "domain": "avndr.ru", + "description": "ЦС ПУЦ + TLS", + "type": "host", + "affinity": [ + "fw_cr", + "fw_cr_ca" + ] + }, + "rk-uc": { + "hostname": "rk-uc", + "ip": "172.19.40.3", + "prefix": "24", + "gw": "172.19.40.1", + "domain": "avndr.ru", + "description": "Сервер РК", + "type": "host", + "affinity": [ + "fw_cr", + "fw_cr_ca" + ] + }, + "ntp": { + "hostname": "ntp", + "ip": "172.19.40.4", + "prefix": "24", + "gw": "172.19.40.1", + "domain": "avndr.ru", + "description": "Сервер точного времени-1", + "type": "host", + "affinity": [ + "fw_cr", + "fw_cr_ca" + ] + }, + "pki": { + "hostname": "pki", + "ip": "172.19.100.4", + "prefix": "24", + "gw": "172.19.100.1", + "domain": "avndr.ru", + "description": "PKI-кластер", + "type": "host", + "affinity": [ + "fw_cr", + "fw_cr_ca" + ] + }, + "mps": { + "hostname": "mps", + "ip": "172.19.100.5", + "prefix": "24", + "gw": "172.19.100.1", + "domain": "avndr.ru", + "description": "МПС", + "type": "host", + "affinity": [ + "fw_cr", + "fw_cr_ca" + ] + }, + "esia": { + "hostname": "esia", + "ip": "172.19.150.4", + "prefix": "24", + "gw": "172.19.150.1", + "domain": "avndr.ru", + "description": "ТР-ЕСИА", + "type": "host", + "affinity": [ + "fw_cr", + "fw_cr_ca" + ] + }, + "ko-app": { + "hostname": "ko-app", + "ip": "172.19.110.4", + "prefix": "24", + "gw": "172.19.110.1", + "domain": "avndr.ru", + "description": "Сервер КО", + "type": "host", + "affinity": [ + "fw_cr", + "fw_cr_ca" + ] + }, + "ko-db": { + "hostname": "ko-db", + "ip": "172.19.110.5", + "prefix": "24", + "gw": "172.19.110.1", + "domain": "avndr.ru", + "description": "Сервер КО СУБД", + "type": "host", + "affinity": [ + "fw_cr", + "fw_cr_ca" + ] + }, + "ko-csp": { + "hostname": "ko-csp", + "ip": "172.19.110.6", + "prefix": "24", + "gw": "172.19.110.1", + "domain": "avndr.ru", + "description": "Сервер КО СКЗИ", + "type": "host", + "affinity": [ + "fw_cr", + "fw_cr_ca" + ] + }, + "kk-app": { + "hostname": "kk-app", + "ip": "172.19.120.4", + "prefix": "24", + "gw": "172.19.120.1", + "domain": "avndr.ru", + "description": "Сервер КК", + "type": "host", + "affinity": [ + "fw_cr", + "fw_cr_ca" + ] + }, + "kk-db": { + "hostname": "kk-db", + "ip": "172.19.120.5", + "prefix": "24", + "gw": "172.19.120.1", + "domain": "avndr.ru", + "description": "Сервер КК СУБД", + "type": "host", + "affinity": [ + "fw_cr", + "fw_cr_ca" + ] + }, + "kk-csp": { + "hostname": "kk-csp", + "ip": "172.19.120.6", + "prefix": "24", + "gw": "172.19.120.1", + "domain": "avndr.ru", + "description": "Сервер КК СКЗИ", + "type": "host", + "affinity": [ + "fw_cr", + "fw_cr_ca" + ] + }, + "core": { + "hostname": "core", + "ip": "172.19.130.4", + "prefix": "24", + "gw": "172.19.130.1", + "domain": "avndr.ru", + "description": "Ядро ВВС", + "type": "host", + "affinity": [ + "fw_cr", + "fw_cr_ca" + ] + }, + "core-db": { + "hostname": "core-db", + "ip": "172.19.130.5", + "prefix": "24", + "gw": "172.19.130.1", + "domain": "avndr.ru", + "description": "СУБД Ядро ВВС", + "type": "host", + "affinity": [ + "fw_cr", + "fw_cr_ca" + ] + }, + "arch": { + "hostname": "arch", + "ip": "172.19.130.6", + "prefix": "24", + "gw": "172.19.130.1", + "domain": "avndr.ru", + "description": "Модуль архивирования", + "type": "host", + "affinity": [ + "fw_cr", + "fw_cr_ca" + ] + }, + "arch-db": { + "hostname": "arch-db", + "ip": "172.19.130.7", + "prefix": "24", + "gw": "172.19.130.1", + "domain": "avndr.ru", + "description": "СУБД Модуль архивирования", + "type": "host", + "affinity": [ + "fw_cr", + "fw_cr_ca" + ] + }, + "web-apps": { + "hostname": "web-apps", + "ip": "172.19.130.8", + "prefix": "24", + "gw": "172.19.130.1", + "domain": "avndr.ru", + "description": "Сервер веб-приложений СС", + "type": "host", + "affinity": [ + "fw_cr", + "fw_cr_ca" + ] + }, + "aldp": { + "hostname": "aldp", + "ip": "172.19.140.4", + "prefix": "24", + "gw": "172.19.140.1", + "domain": "avndr.ru", + "description": "Сервер ИБ-1 (ALD Pro)", + "type": "host", + "affinity": [ + "fw_cr", + "fw_cr_ca" + ] + }, + "alds": { + "hostname": "alds", + "ip": "172.19.140.5", + "prefix": "24", + "gw": "172.19.140.1", + "domain": "avndr.ru", + "description": "Сервер ИБ-2 (ALD Pro)", + "type": "host", + "affinity": [ + "fw_cr", + "fw_cr_ca" + ] + }, + "rk": { + "hostname": "rk", + "ip": "172.19.140.6", + "prefix": "24", + "gw": "172.19.140.1", + "domain": "avndr.ru", + "description": "Сервер РК", + "type": "host", + "affinity": [ + "fw_cr", + "fw_cr_ca" + ] + }, + "ksc": { + "hostname": "ksc", + "ip": "172.19.140.7", + "prefix": "24", + "gw": "172.19.140.1", + "domain": "avndr.ru", + "description": "ВМ Kaspersky Security Center", + "type": "host", + "affinity": [ + "fw_cr", + "fw_cr_ca" + ] + }, + "log": { + "hostname": "log", + "ip": "172.19.140.8", + "prefix": "24", + "gw": "172.19.140.1", + "domain": "avndr.ru", + "description": "ВМ Сервер журналирования", + "type": "host", + "affinity": [ + "fw_cr", + "fw_cr_ca" + ] + }, + "repo": { + "hostname": "repo", + "ip": "172.19.140.9", + "prefix": "24", + "gw": "172.19.140.1", + "domain": "avndr.ru", + "description": "ВМ Сервер репозиторий ПО", + "type": "host", + "affinity": [ + "fw_cr", + "fw_cr_ca" + ] + }, + "zbx": { + "hostname": "zbx", + "ip": "172.19.140.10", + "prefix": "24", + "gw": "172.19.140.1", + "domain": "avndr.ru", + "description": "ВМ Сервер мониторинга (ZbxProxy)", + "type": "host", + "affinity": [ + "fw_cr", + "fw_cr_ca" + ] + }, + "arm-cont4": { + "hostname": "arm-cont4", + "ip": "172.19.210.2", + "prefix": "24", + "gw": "172.19.210.1", + "domain": "avndr.ru", + "description": "АРМ ЦУС Континент 4", + "type": "host", + "affinity": [ + "fw_cr", + "fw_cr_ca" + ] + }, + "arm-web-oper": { + "hostname": "arm-web-oper", + "ip": "172.19.220.2", + "prefix": "24", + "gw": "172.19.220.1", + "domain": "avndr.ru", + "description": "ВВС АРМ WEB (1)", + "type": "host", + "affinity": [ + "fw_cr", + "fw_cr_ca" + ] + }, + "arm-web-adm": { + "hostname": "arm-web-adm", + "ip": "172.19.230.2", + "prefix": "24", + "gw": "172.19.230.1", + "domain": "avndr.ru", + "description": "ВВС АРМ WEB (2)", + "type": "host", + "affinity": [ + "fw_cr", + "fw_cr_ca" + ] + }, + "arm-web-pki": { + "hostname": "arm-web-pki", + "ip": "172.19.230.2", + "prefix": "24", + "gw": "172.19.230.1", + "domain": "avndr.ru", + "description": "АРМ адм САВС", + "type": "host", + "affinity": [ + "fw_cr", + "fw_cr_ca" + ] + } + }, + "nets": { + "net_any": { + "hostname": "net_any", + "ip": "0.0.0.0", + "prefix": "0", + "gw": "", + "domain": "avndr.ru", + "description": "Any", + "type": "network", + "affinity": [ + "fw_cr", + "fw_cr_ca" + ] + }, + "net_uc_srv": { + "hostname": "net_uc_srv", + "ip": "172.19.20.0", + "prefix": "24", + "gw": "", + "domain": "avndr.ru", + "description": "Сегмент УЦ ПУЦ+TLS", + "type": "network", + "affinity": [ + "fw_cr", + "fw_cr_ca" + ] + }, + "net_uc_adm_srv": { + "hostname": "net_uc_adm_srv", + "ip": "172.19.40.0", + "prefix": "24", + "gw": "", + "domain": "avndr.ru", + "description": "Административный сегмент УЦ", + "type": "network", + "affinity": [ + "fw_cr", + "fw_cr_ca" + ] + }, + "net_dr_pki": { + "hostname": "net_dr_pki", + "ip": "172.19.100.0", + "prefix": "24", + "gw": "", + "domain": "avndr.ru", + "description": "Сегмент САВС", + "type": "network", + "affinity": [ + "fw_cr", + "fw_cr_ca" + ] + }, + "net_dr_ko": { + "hostname": "net_dr_ko", + "ip": "172.19.110.0", + "prefix": "24", + "gw": "", + "domain": "avndr.ru", + "description": "Сегмент КО", + "type": "network", + "affinity": [ + "fw_cr", + "fw_cr_ca" + ] + }, + "net_dr_kk": { + "hostname": "net_dr_kk", + "ip": "172.19.120.0", + "prefix": "24", + "gw": "", + "domain": "avndr.ru", + "description": "Сегмент КК", + "type": "network", + "affinity": [ + "fw_cr", + "fw_cr_ca" + ] + }, + "net_dr_core_srv": { + "hostname": "net_dr_core_srv", + "ip": "172.19.130.0", + "prefix": "24", + "gw": "", + "domain": "avndr.ru", + "description": "Сегмент интеграции", + "type": "network", + "affinity": [ + "fw_cr", + "fw_cr_ca" + ] + }, + "net_dr_adm_srv": { + "hostname": "net_dr_adm_srv", + "ip": "172.19.140.0", + "prefix": "24", + "gw": "", + "domain": "avndr.ru", + "description": "Административный сегмент", + "type": "network", + "affinity": [ + "fw_cr", + "fw_cr_ca" + ] + }, + "net_dr_gis_esia": { + "hostname": "net_dr_gis_esia", + "ip": "172.19.150.0", + "prefix": "24", + "gw": "", + "domain": "avndr.ru", + "description": "Сегмент ГИС ЕСИА", + "type": "network", + "affinity": [ + "fw_cr", + "fw_cr_ca" + ] + }, + "net_dr_adm_arm_cont4": { + "hostname": "net_dr_adm_arm_cont4", + "ip": "172.19.210.0", + "prefix": "24", + "gw": "", + "domain": "avndr.ru", + "description": "Сегмент администраторов ЦР", + "type": "network", + "affinity": [ + "fw_cr", + "fw_cr_ca" + ] + }, + "net_dr_adm_arm_web1": { + "hostname": "net_dr_adm_arm_web1", + "ip": "172.19.220.0", + "prefix": "24", + "gw": "", + "domain": "avndr.ru", + "description": "Сегмент администраторов ЦР", + "type": "network", + "affinity": [ + "fw_cr", + "fw_cr_ca" + ] + }, + "net_dr_adm_arm_web2": { + "hostname": "net_dr_adm_arm_web2", + "ip": "172.19.230.0", + "prefix": "24", + "gw": "", + "domain": "avndr.ru", + "description": "Сегмент администраторов ЦР", + "type": "network", + "affinity": [ + "fw_cr", + "fw_cr_ca" + ] + }, + "net_dr_adm_arm_pki": { + "hostname": "net_dr_adm_arm_pki", + "ip": "172.19.230.0", + "prefix": "24", + "gw": "", + "domain": "avndr.ru", + "description": "Сегмент администраторов ЦР", + "type": "network", + "affinity": [ + "fw_cr", + "fw_cr_ca" + ] + }, + "net_dr_adm_arm_ngate": { + "hostname": "net_dr_adm_arm_ngate", + "ip": "172.19.250.0", + "prefix": "24", + "gw": "", + "domain": "avndr.ru", + "description": "Сегмент администраторов ЦР", + "type": "network", + "affinity": [ + "fw_cr", + "fw_cr_ca" + ] + } + }, + "groups": { + "net_any": { + "name": "net_any", + "items": [ + { + "hostname": "0.0.0.0/0" + } + ] + }, + "prot_set_uc_adm": { + "name": "prot_set_uc_adm", + "items": [ + { + "hostname": "arm-cont3" + }, + { + "hostname": "arm-cr" + } + ] + }, + "prot_set_uc_arm_reg_dr": { + "name": "prot_set_uc_arm_reg_dr", + "items": [ + { + "hostname": "arm-cr" + } + ] + }, + "prot_set_uc_arm_reg_tls": { + "name": "prot_set_uc_arm_reg_tls", + "items": [ + { + "hostname": "arm-cr" + } + ] + }, + "prot_set_uc_reg_dr": { + "name": "prot_set_uc_reg_dr", + "items": [ + { + "hostname": "cr" + } + ] + }, + "prot_set_uc_reg_tls": { + "name": "prot_set_uc_reg_tls", + "items": [ + { + "hostname": "cr" + } + ] + }, + "set_abs": { + "name": "set_abs", + "items": [] + }, + "set_cdp": { + "name": "set_cdp", + "items": [] + }, + "set_dbo": { + "name": "set_dbo", + "items": [] + }, + "set_dns": { + "name": "set_dns", + "items": [] + }, + "set_dr": { + "name": "set_dr", + "items": [ + { + "hostname": "net_dr_pki" + }, + { + "hostname": "net_dr_gis_esia" + }, + { + "hostname": "net_dr_ko" + }, + { + "hostname": "net_dr_kk" + }, + { + "hostname": "net_dr_core_srv" + }, + { + "hostname": "net_dr_adm_arm_cont4" + }, + { + "hostname": "net_dr_adm_arm_web1" + }, + { + "hostname": "net_dr_adm_arm_web2" + }, + { + "hostname": "net_dr_adm_arm_pki" + }, + { + "hostname": "net_dr_adm_arm_ngate" + } + ] + }, + "set_dr_adm_pki_cl": { + "name": "set_dr_adm_pki_cl", + "items": [ + { + "hostname": "pki.avndr.ru" + } + ] + }, + "set_dr_adm_web_adm": { + "name": "set_dr_adm_web_adm", + "items": [ + { + "hostname": "arm-web-adm.avndr.ru" + } + ] + }, + "set_dr_adm_web_oper": { + "name": "set_dr_adm_web_oper", + "items": [ + { + "hostname": "arm-web-oper.avndr.ru" + } + ] + }, + "set_dr_arm_ngate": { + "name": "set_dr_arm_ngate", + "items": [ + { + "hostname": "arm-ngate.avndr.ru" + } + ] + }, + "set_dr_esia_tr": { + "name": "set_dr_esia_tr", + "items": [ + { + "hostname": "esia.avndr.ru" + } + ] + }, + "set_dr_gateout": { + "name": "set_dr_gateout", + "items": [ + { + "hostname": "core.avndr.ru" + } + ] + }, + "set_dr_kk_be": { + "name": "set_dr_kk_be", + "items": [ + { + "hostname": "kk-app.avndr.ru" + } + ] + }, + "set_dr_kk_crypto": { + "name": "set_dr_kk_crypto", + "items": [ + { + "hostname": "kk-csp.avnd.ru" + } + ] + }, + "set_dr_kk_db": { + "name": "set_dr_kk_db", + "items": [ + { + "hostname": "kk-db.avndr.ru" + } + ] + }, + "set_dr_ko_be": { + "name": "set_dr_ko_be", + "items": [ + { + "hostname": "ko-app.avndr.ru" + } + ] + }, + "set_dr_ko_crypto": { + "name": "set_dr_ko_crypto", + "items": [ + { + "hostname": "ko-csp.avndr.ru" + } + ] + }, + "set_dr_ko_db": { + "name": "set_dr_ko_db", + "items": [ + { + "hostname": "ko-db.avndr.ru" + } + ] + }, + "set_dr_ngate": { + "name": "set_dr_ngate", + "items": [ + { + "hostname": "ngate-mgmt" + }, + { + "hostname": "ngate-node01" + }, + { + "hostname": "ngate-node02" + } + ] + }, + "set_dr_ngate_mgmt": { + "name": "set_dr_ngate_mgmt", + "items": [ + { + "hostname": "ngate-mgmt" + } + ] + }, + "set_dr_ngate_nodes": { + "name": "set_dr_ngate_nodes", + "items": [ + { + "hostname": "ngate-node01" + }, + { + "hostname": "ngate-node02" + } + ] + }, + "set_dr_pki_cluster": { + "name": "set_dr_pki_cluster", + "items": [ + { + "hostname": "pki.avndr.ru" + } + ] + }, + "set_dr_plcr": { + "name": "set_dr_plcr", + "items": [ + { + "hostname": "cbr_cd-tuz01" + }, + { + "hostname": "cbr_cd-tuz02" + }, + { + "hostname": "cbr_cd-tuz03" + }, + { + "hostname": "cbr_cd-tuz04" + } + ] + }, + "set_dr_savs_mps": { + "name": "set_dr_savs_mps", + "items": [ + { + "hostname": "mps.avndr.ru" + } + ] + }, + "set_dr_savs_mps_be": { + "name": "set_dr_savs_mps_be", + "items": [ + { + "hostname": "mps.avndr.ru" + } + ] + }, + "set_dr_savs_mps_crypto": { + "name": "set_dr_savs_mps_crypto", + "items": [ + { + "hostname": "mps.avndr.ru" + } + ] + }, + "set_dr_savs_mps_db": { + "name": "set_dr_savs_mps_db", + "items": [ + { + "hostname": "mps.avndr.ru" + } + ] + }, + "set_dr_ss_arch_be": { + "name": "set_dr_ss_arch_be", + "items": [ + { + "hostname": "core.avndr.ru" + } + ] + }, + "set_dr_ss_arch_db": { + "name": "set_dr_ss_arch_db", + "items": [ + { + "hostname": "arch-db.avndr.ru" + } + ] + }, + "set_dr_ss_core_bbs": { + "name": "set_dr_ss_core_bbs", + "items": [ + { + "hostname": "core.avndr.ru" + } + ] + }, + "set_dr_ss_core_bbs_db": { + "name": "set_dr_ss_core_bbs_db", + "items": [ + { + "hostname": "core-db.avndr.ru" + } + ] + }, + "set_dr_ss_integr_be": { + "name": "set_dr_ss_integr_be", + "items": [ + { + "hostname": "core.avndr.ru" + } + ] + }, + "set_dr_ss_keycloak": { + "name": "set_dr_ss_keycloak", + "items": [ + { + "hostname": "core.avndr.ru" + } + ] + }, + "set_dr_ss_nginx": { + "name": "set_dr_ss_nginx", + "items": [ + { + "hostname": "core.avndr.ru" + } + ] + }, + "set_dr_tech_server": { + "name": "set_dr_tech_server", + "items": [ + { + "hostname": "-" + } + ] + }, + "set_hsm": { + "name": "set_hsm", + "items": [ + { + "hostname": "-" + } + ] + }, + "set_ksc": { + "name": "set_ksc", + "items": [ + { + "hostname": "ksc.avndr.ru" + } + ] + }, + "set_ntp": { + "name": "set_ntp", + "items": [] + }, + "set_rubackup_servers": { + "name": "set_rubackup_servers", + "items": [ + { + "hostname": "rk.avndr.ru" + } + ] + }, + "set_siem": { + "name": "set_siem", + "items": [] + }, + "set_uc": { + "name": "set_uc", + "items": [ + { + "hostname": "net_uc_srv" + }, + { + "hostname": "net_uc_adm_srv" + }, + { + "hostname": "net_uc_cus_adm" + }, + { + "hostname": "net_uc_arm_ra" + } + ] + }, + "set_uc_adm_arm_reg": { + "name": "set_uc_adm_arm_reg", + "items": [ + { + "hostname": "arm-cr" + } + ] + }, + "set_uc_arm_hsm": { + "name": "set_uc_arm_hsm", + "items": [ + { + "hostname": "arm-hsm" + } + ] + }, + "set_uc_cgw_ncc3": { + "name": "set_uc_cgw_ncc3", + "items": [ + { + "hostname": "gw-uc" + }, + { + "hostname": "ncc-uc" + } + ] + }, + "set_uc_cgw_ncc4": { + "name": "set_uc_cgw_ncc4", + "items": [ + { + "hostname": "ncc.avndr.ru" + }, + { + "hostname": "gw.avndr.ru" + }, + { + "hostname": "gw02.avndr.ru" + }, + { + "hostname": "gw.avndr.ru" + } + ] + }, + "set_uc_cgw3": { + "name": "set_uc_cgw3", + "items": [ + { + "hostname": "gw-uc" + } + ] + }, + "set_uc_cgw4": { + "name": "set_uc_cgw4", + "items": [ + { + "hostname": "gw.avndr.ru" + }, + { + "hostname": "gw02.avndr.ru" + }, + { + "hostname": "gw.avndr.ru" + } + ] + }, + "set_uc_ncc3": { + "name": "set_uc_ncc3", + "items": [ + { + "hostname": "ncc-uc" + } + ] + }, + "set_uc_ncc4": { + "name": "set_uc_ncc4", + "items": [ + { + "hostname": "ncc.avndr.ru" + } + ] + }, + "set_uc_ntp": { + "name": "set_uc_ntp", + "items": [ + { + "hostname": "ntp" + } + ] + }, + "set_uc_ntp_prot": { + "name": "set_uc_ntp_prot", + "items": [ + { + "hostname": "ntp" + } + ] + }, + "set_uc_reg_dr": { + "name": "set_uc_reg_dr", + "items": [ + { + "hostname": "cs" + } + ] + }, + "set_uc_reg_tls": { + "name": "set_uc_reg_tls", + "items": [ + { + "hostname": "cr" + } + ] + }, + "set_uc_rubackup_servers": { + "name": "set_uc_rubackup_servers", + "items": [ + { + "hostname": "rk-uc" + } + ] + }, + "set_zabbix": { + "name": "set_zabbix", + "items": [] + }, + "set_uc_cert_tls": { + "name": "set_uc_cert_tls", + "items": [ + { + "hostname": "cs" + } + ] + }, + "set_uc_dr": { + "name": "set_uc_dr", + "items": [ + { + "hostname": "cs" + } + ] + }, + "grp_web_servers": { + "name": "grp_web_servers", + "items": [ + { + "hostname": "web01" + }, + { + "hostname": "web02" + }, + { + "hostname": "net_dmz" + } + ] + } + }, + "services": { + "dc-locator": { + "name": "dc-locator-389-udp", + "sport": "any", + "dport": "389", + "proto": "udp" + }, + "dns-tcp": { + "name": "dns-53-tcp", + "sport": "any", + "dport": "53", + "proto": "tcp" + }, + "dns-udp": { + "name": "dns-53-udp", + "sport": "any", + "dport": "53", + "proto": "udp" + }, + "globalcatalog-tcp": { + "name": "globalcatalog-3268-tcp", + "sport": "any", + "dport": "3268", + "proto": "tcp" + }, + "globalcatalog-udp": { + "name": "globalcatalog-3268-udp", + "sport": "any", + "dport": "3268", + "proto": "udp" + }, + "ngate-webcon": { + "name": "ngate-webcon-8000-tcp", + "sport": "any", + "dport": "8000", + "proto": "tcp" + }, + "icmp": { + "name": "icmp-echo", + "sport": "-", + "dport": "-", + "proto": "icmp-request" + }, + "syslog-tcp": { + "name": "syslog-514-tcp", + "sport": "any", + "dport": "514", + "proto": "tcp" + }, + "syslog-udp": { + "name": "syslog-514-udp", + "sport": "any", + "dport": "514", + "proto": "udp" + }, + "syslog-10514-udp": { + "name": "syslog-10514-udp", + "sport": "any", + "dport": "10514", + "proto": "udp" + }, + "ssh": { + "name": "ssh-22-tcp", + "sport": "any", + "dport": "22", + "proto": "tcp" + }, + "smtp": { + "name": "smtp-25-tcp", + "sport": "any", + "dport": "25", + "proto": "tcp" + }, + "smtp-tls": { + "name": "smtp-tls-587-tcp", + "sport": "any", + "dport": "587", + "proto": "tcp" + }, + "smtp-ssl": { + "name": "smtp-ssl-465-tcp", + "sport": "any", + "dport": "465", + "proto": "tcp" + }, + "smb": { + "name": "smb-445-tcp", + "sport": "any", + "dport": "445", + "proto": "tcp" + }, + "sn-tls": { + "name": "sn-tls-443-tcp", + "sport": "any", + "dport": "443", + "proto": "tcp" + }, + "sn-pwd-change-tcp": { + "name": "sn-pwd-change-42464-tcp", + "sport": "any", + "dport": "42464", + "proto": "tcp" + }, + "sn-pwd-change-udp": { + "name": "sn-pwd-change-42464-udp", + "sport": "any", + "dport": "42464", + "proto": "udp" + }, + "sn-lds-tls": { + "name": "sn-lds-tls-50001-tcp", + "sport": "any", + "dport": "30001", + "proto": "tcp" + }, + "sn-lds": { + "name": "sn-lds-50000-tcp", + "sport": "any", + "dport": "30000", + "proto": "tcp" + }, + "sn-kerberos-tcp": { + "name": "sn-kerberos-42088-tcp", + "sport": "any", + "dport": "42088", + "proto": "tcp" + }, + "sn-kerberos-udp": { + "name": "sn-kerberos-42088-udp", + "sport": "any", + "dport": "42088", + "proto": "udp" + }, + "sn-gc-lds-tls": { + "name": "sn-gc-lds-tls-50003-tcp", + "sport": "any", + "dport": "30003", + "proto": "tcp" + }, + "sn-gc-lds": { + "name": "sn-gc-lds-50002-tcp", + "sport": "any", + "dport": "30002", + "proto": "tcp" + }, + "snmp-trap-162-udp": { + "name": "snmp-trap-162-udp", + "sport": "any", + "dport": "162", + "proto": "udp" + }, + "snmp-161-udp": { + "name": "snmp-161-udp", + "sport": "any", + "dport": "161", + "proto": "udp" + }, + "tls-pcr-processing-ul": { + "name": "tls-pcr-processing-ul-443-tcp (change)", + "sport": "any", + "dport": "443", + "proto": "tcp" + }, + "tls-pcr-processing-fl": { + "name": "tls-pcr-processing-fl-443-tcl (change)", + "sport": "any", + "dport": "443", + "proto": "tcp" + }, + "tls-pcr-processing-fp": { + "name": "tls-pcr-processing-fp-443-tcp (change)", + "sport": "any", + "dport": "443", + "proto": "tcp" + }, + "rdp-tcp": { + "name": "rdp-3389-tcp", + "sport": "any", + "dport": "3389", + "proto": "tcp" + }, + "rdp-udp": { + "name": "rdp-3389-udp", + "sport": "any", + "dport": "3389", + "proto": "udp" + }, + "psql-tcp": { + "name": "psql-5432-tcp", + "sport": "any", + "dport": "5432", + "proto": "tcp" + }, + "ntp": { + "name": "ntp-123-udp", + "sport": "any", + "dport": "123", + "proto": "udp" + }, + "netbios-137-udp": { + "name": "netbios-137-udp", + "sport": "any", + "dport": "137", + "proto": "udp" + }, + "netbios-138-udp": { + "name": "netbios-138-udp", + "sport": "any", + "dport": "138", + "proto": "udp" + }, + "netbios-139-tcp": { + "name": "netbios-139-tcp", + "sport": "any", + "dport": "139", + "proto": "tcp" + }, + "ldaps": { + "name": "ldaps-636-tcp", + "sport": "any", + "dport": "636", + "proto": "tcp" + }, + "ldap": { + "name": "ldap-389-tcp", + "sport": "any", + "dport": "389", + "proto": "tcp" + }, + "ksc-klserver-13000-udp": { + "name": "ksc-klserver-13000-udp", + "sport": "any", + "dport": "13000", + "proto": "udp" + }, + "ksc-klserver-13000-tcp": { + "name": "ksc-klserver-13000-tcp", + "sport": "any", + "dport": "13000", + "proto": "tcp" + }, + "ksc-klnagent-14000-tcp": { + "name": "ksc-klnagent-14000-tcp", + "sport": "any", + "dport": "14000", + "proto": "tcp" + }, + "ksc-distribution-tls": { + "name": "ksc-distribution-tls-8061-tcp", + "sport": "any", + "dport": "8061", + "proto": "tcp" + }, + "ksc-distribution": { + "name": "ksc-distribution-8060-tcp", + "sport": "any", + "dport": "8060", + "proto": "tcp" + }, + "ksc-webcon": { + "name": "ksc-webcon-8080-tcp", + "sport": "any", + "dport": "8080", + "proto": "tcp" + }, + "klnagent": { + "name": "klnagent-15000-udp", + "sport": "any", + "dport": "15000", + "proto": "udp" + }, + "krb-password-tcp": { + "name": "krb-password-464-tcp", + "sport": "any", + "dport": "464", + "proto": "tcp" + }, + "krb-password-udp": { + "name": "krb-password-464-udp", + "sport": "any", + "dport": "464", + "proto": "udp" + }, + "krb-88-udp": { + "name": "krb-88-udp", + "sport": "any", + "dport": "88", + "proto": "udp" + }, + "krb-88-tcp": { + "name": "krb-88-tcp", + "sport": "any", + "dport": "88", + "proto": "tcp" + }, + "k3-vpn": { + "name": "k3-vpn-10000-10031-udp", + "sport": "10000-10031", + "dport": "10000-10031", + "proto": "udp" + }, + "k3-sd-to-ap": { + "name": "k3-sd-to-ap-7500-udp", + "sport": "any", + "dport": "7500", + "proto": "udp" + }, + "k3-filetransfer-5103": { + "name": "k3-filetransfer-5103-tcp", + "sport": "any", + "dport": "5103", + "proto": "tcp" + }, + "k3-messages-5100": { + "name": "k3-messages-5100-udp", + "sport": "any", + "dport": "5100", + "proto": "udp" + }, + "k3-messages-5106-5107": { + "name": "k3-messages-5106-5107-udp", + "sport": "any", + "dport": "5106,5107", + "proto": "udp" + }, + "k3-messages-5109": { + "name": "k3-messages-5109-udp", + "sport": "5100", + "dport": "5109", + "proto": "udp" + }, + "k3-messages-5109-tcp": { + "name": "k3-messages-5109-tcp", + "sport": "5100", + "dport": "5109", + "proto": "tcp" + }, + "zabbix-agent-active": { + "name": "zabbix-agent(active)-10051-tcp", + "sport": "any", + "dport": "10051", + "proto": "tcp" + }, + "zabbix-agent": { + "name": "zabbix-agent-10050-tcp", + "sport": "any", + "dport": "10050", + "proto": "tcp" + }, + "http": { + "name": "http-80-tcp", + "sport": "any", + "dport": "80", + "proto": "tcp" + }, + "TLS": { + "name": "TLS", + "sport": "any", + "dport": "443", + "proto": "tcp" + }, + "nats-tech-4223": { + "name": "nats-tech-4223-tcp", + "sport": "any", + "dport": "4223", + "proto": "tcp" + }, + "nats-digrub-4222": { + "name": "nats-digrub-4222-tcp", + "sport": "any", + "dport": "4222", + "proto": "tcp" + }, + "nats-tls-4224": { + "name": "nats-tls-4224-tcp", + "sport": "any", + "dport": "4224", + "proto": "tcp" + }, + "ra-tech-1443": { + "name": "ra-tech-442-tcp", + "sport": "any", + "dport": "1443", + "proto": "tcp" + }, + "ra-digrub-443": { + "name": "ra-digrub-443-tcp", + "sport": "any", + "dport": "443", + "proto": "tcp" + }, + "ra-tls-2443": { + "name": "ra-tls-444-tcp", + "sport": "any", + "dport": "2443", + "proto": "tcp" + }, + "drweb-ess-2193-tcp": { + "name": "drweb-ess-2193-tcp", + "sport": "any", + "dport": "2193", + "proto": "tcp" + } + }, + "service_groups": { + "sg_dns": { + "name": "sg_dns", + "items": [ + "dns-tcp", + "dns-udp" + ] + }, + "sn-in": { + "name": "SecretNet-In", + "items": [ + "sn-pwd-change-tcp", + "sn-pwd-change-udp", + "sn-lds-tls", + "sn-lds", + "sn-kerberos-tcp", + "sn-kerberos-udp", + "sn-gc-lds-tls", + "sn-gc-lds" + ] + }, + "ad-ds-in": { + "name": "ADDS-In", + "items": [ + "dns-tcp", + "dns-udp", + "globalcatalog-tcp", + "globalcatalog-udp", + "ntp", + "netbios-137-udp", + "netbios-138-udp", + "netbios-139-tcp", + "ldaps", + "ldap", + "krb-password-tcp", + "krb-password-udp", + "krb-88-udp", + "krb-88-tcp", + "dc-locator", + "smb" + ] + }, + "ksc-in": { + "name": "KasperskySecurityCenter-In", + "items": [ + "ksc-klserver-13000-udp", + "ksc-klserver-13000-tcp", + "ksc-klnagent-14000-tcp", + "ksc-distribution-tls", + "ksc-distribution" + ] + }, + "klnagent-in": { + "name": "KasperskyLabsNetworkAgent-In", + "items": [ + "klnagent" + ] + }, + "cyberbackup-in": { + "name": "Cyberbackup-In", + "items": [ + "cyberbackup-7780", + "cyberbackup-9877", + "smb" + ] + } + }, + "rules": [ + { + "name": "Инфраструктурные правила", + "order": 1000, + "type": "span", + "affinity": [ + "fw_cr" + ] + }, + { + "name": "ICMP Echo", + "order": 1010, + "type": "rule", + "affinity": [ + "fw_ca_cgw", + "fw_core" + ], + "description": "Разрешить ICMP", + "action": "allow", + "log": "false", + "idp": "false", + "src_list": [ + { + "ref_type": "group", + "ref_key": "set_dr" + } + ], + "dst_list": [ + { + "ref_type": "group", + "ref_key": "net_any" + } + ], + "service_list": [ + "icmp" + ], + "service_group_list": [] + }, + { + "name": "ICMP Echo-ext", + "order": 1020, + "type": "rule", + "affinity": [ + "fw_ca_cgw", + "fw_core" + ], + "description": "Разрешить ICMP", + "action": "allow", + "log": "false", + "idp": "false", + "src_list": [ + { + "ref_type": "group", + "ref_key": "net_any" + } + ], + "dst_list": [ + { + "ref_type": "group", + "ref_key": "set_dr" + } + ], + "service_list": [ + "icmp" + ], + "service_group_list": [] + }, + { + "name": "to_dns", + "order": 1030, + "type": "rule", + "affinity": [ + "fw_ca_cgw", + "fw_core" + ], + "description": "Разрешить доступ к DNS", + "action": "allow", + "log": "false", + "idp": "false", + "src_list": [ + { + "ref_type": "group", + "ref_key": "set_dr" + } + ], + "dst_list": [ + { + "ref_type": "group", + "ref_key": "set_dns" + } + ], + "service_list": [ + "ssh" + ], + "service_group_list": [] + }, + { + "name": "to_syslog", + "order": 1040, + "type": "rule", + "affinity": [ + "fw_ca_cgw", + "fw_core" + ], + "description": "Разрешить доступ к Syslog", + "action": "allow", + "log": "false", + "idp": "false", + "src_list": [ + { + "ref_type": "group", + "ref_key": "set_dr" + } + ], + "dst_list": [ + { + "ref_type": "group", + "ref_key": "set_siem" + } + ], + "service_list": [ + "syslog-tcp" + ], + "service_group_list": [] + }, + { + "name": "to_ksc", + "order": 1050, + "type": "rule", + "affinity": [ + "fw_ca_cgw", + "fw_core" + ], + "description": "Разрешить доступ к Kaspersky Security Center", + "action": "allow", + "log": "false", + "idp": "false", + "src_list": [ + { + "ref_type": "group", + "ref_key": "set_dr" + } + ], + "dst_list": [ + { + "ref_type": "group", + "ref_key": "set_ksc" + } + ], + "service_list": [ + "ssh" + ], + "service_group_list": [] + }, + { + "name": "to_kaspersky_updates", + "order": 1060, + "type": "rule", + "affinity": [ + "fw_ca_cgw", + "fw_core" + ], + "description": "Разрешить доступ к папке обновлений Kaspersky", + "action": "allow", + "log": "false", + "idp": "false", + "src_list": [ + { + "ref_type": "group", + "ref_key": "set_dr" + } + ], + "dst_list": [ + { + "ref_type": "group", + "ref_key": "set_ksc" + } + ], + "service_list": [ + "ssh" + ], + "service_group_list": [] + }, + { + "name": "to_zabbix", + "order": 1070, + "type": "rule", + "affinity": [ + "fw_ca_cgw", + "fw_core" + ], + "description": "Разрешить доступ к серверам Zabbix", + "action": "allow", + "log": "false", + "idp": "false", + "src_list": [ + { + "ref_type": "group", + "ref_key": "set_dr" + } + ], + "dst_list": [ + { + "ref_type": "group", + "ref_key": "set_zabbix" + } + ], + "service_list": [ + "ssh" + ], + "service_group_list": [] + }, + { + "name": "Взаимодействие в УЦ", + "order": 1080, + "type": "span", + "affinity": [ + "fw_cr" + ] + }, + { + "name": "pki_cluster_tls", + "order": 1090, + "type": "rule", + "affinity": [ + "fw_ca_cgw" + ], + "description": "Разрешить обращения PKI-кластер к Центру регистрации УЦ TLS", + "action": "allow", + "log": "false", + "idp": "false", + "src_list": [ + { + "ref_type": "server", + "ref_key": "pki" + }, + { + "ref_type": "group", + "ref_key": "set_dr_pki_cluster" + } + ], + "dst_list": [ + { + "ref_type": "group", + "ref_key": "set_uc_reg_tls" + } + ], + "service_list": [ + "ssh" + ], + "service_group_list": [] + }, + { + "name": "pki_cluster_dr", + "order": 1100, + "type": "rule", + "affinity": [ + "fw_ca_cgw" + ], + "description": "Разрешить обращения PKI-кластер к Центру регистрации УЦ УНЭП", + "action": "allow", + "log": "false", + "idp": "false", + "src_list": [ + { + "ref_type": "group", + "ref_key": "set_dr_pki_cluster" + } + ], + "dst_list": [ + { + "ref_type": "group", + "ref_key": "set_uc_reg_dr" + } + ], + "service_list": [ + "ssh" + ], + "service_group_list": [] + }, + { + "name": "crl_request_tls_external", + "order": 1110, + "type": "rule", + "affinity": [ + "fw_ca_cgw" + ], + "description": "Разрешить доступ к CRL из сети предприятия", + "action": "allow", + "log": "false", + "idp": "false", + "src_list": [ + { + "ref_type": "group", + "ref_key": "net_any" + } + ], + "dst_list": [ + { + "ref_type": "group", + "ref_key": "set_uc_reg_tls" + } + ], + "service_list": [ + "ssh" + ], + "service_group_list": [] + }, + { + "name": "crl_request_dr_external", + "order": 1120, + "type": "rule", + "affinity": [ + "fw_ca_cgw" + ], + "description": "Разрешить доступ к CRL из сети предприятия", + "action": "allow", + "log": "false", + "idp": "false", + "src_list": [ + { + "ref_type": "group", + "ref_key": "net_any" + } + ], + "dst_list": [ + { + "ref_type": "group", + "ref_key": "set_uc_reg_dr" + } + ], + "service_list": [ + "ssh" + ], + "service_group_list": [] + }, + { + "name": "rubackup-cmd", + "order": 1130, + "type": "rule", + "affinity": [ + "fw_ca_cgw" + ], + "description": "Управление операциями на клиенте резервного копирования", + "action": "allow", + "log": "false", + "idp": "false", + "src_list": [ + { + "ref_type": "group", + "ref_key": "set_dr" + } + ], + "dst_list": [ + { + "ref_type": "group", + "ref_key": "set_rubackup_servers" + } + ], + "service_list": [ + "ssh" + ], + "service_group_list": [] + }, + { + "name": "rubackup-media", + "order": 1140, + "type": "rule", + "affinity": [ + "fw_ca_cgw" + ], + "description": "Передача данных между медиасервером и клиентом", + "action": "allow", + "log": "false", + "idp": "false", + "src_list": [ + { + "ref_type": "group", + "ref_key": "set_dr" + } + ], + "dst_list": [ + { + "ref_type": "group", + "ref_key": "set_rubackup_servers" + } + ], + "service_list": [ + "ssh" + ], + "service_group_list": [] + }, + { + "name": "rubackup-api", + "order": 1150, + "type": "rule", + "affinity": [ + "fw_ca_cgw" + ], + "description": "Управление операциями RuBackup через REST API", + "action": "allow", + "log": "false", + "idp": "false", + "src_list": [ + { + "ref_type": "group", + "ref_key": "set_dr" + } + ], + "dst_list": [ + { + "ref_type": "group", + "ref_key": "set_rubackup_servers" + } + ], + "service_list": [ + "ssh" + ], + "service_group_list": [] + }, + { + "name": "repo", + "order": 1160, + "type": "rule", + "affinity": [ + "fw_cr" + ], + "description": "Внутренний репозиторий", + "action": "allow", + "log": "false", + "idp": "false", + "src_list": [ + { + "ref_type": "group", + "ref_key": "set_dr" + } + ], + "dst_list": [ + { + "ref_type": "group", + "ref_key": "set_dr_tech_server" + } + ], + "service_list": [ + "ssh" + ], + "service_group_list": [] + }, + { + "name": "CC", + "order": 1170, + "type": "span", + "affinity": [ + "fw_cr" + ] + }, + { + "name": "cc_mps_to_pki_cluster", + "order": 2000, + "type": "rule", + "description": "Обращения от МПС до PKI-кластера", + "action": "allow", + "log": "false", + "idp": "false", + "affinity": [ + "fw_cr" + ], + "src_list": [ + { + "ref_key": "set_dr_savs_mps", + "ref_type": "group" + } + ], + "dst_list": [ + { + "ref_key": "set_dr_pki_cluster", + "ref_type": "group" + } + ], + "service_list": [ + "TLS" + ], + "service_group_list": [] + } + ] +} \ No newline at end of file diff --git a/app/static/app.js b/app/static/app.js new file mode 100644 index 0000000..3405f72 --- /dev/null +++ b/app/static/app.js @@ -0,0 +1,1073 @@ +/** + * app.js — Firewall Rules Builder UI + */ + +// ─── Глобальное состояние ──────────────────────────────────────────────────── +const State = { + objects: {}, // servers + nets + groups: {}, + services: {}, + service_groups: {}, + rules: [], +}; + +// ─── Утилиты ───────────────────────────────────────────────────────────────── +function showToast(msg, type = 'success') { + const container = document.getElementById('toast-container'); + const id = 'toast-' + Date.now(); + const bg = type === 'success' ? 'bg-success' : type === 'danger' ? 'bg-danger' : 'bg-warning'; + container.insertAdjacentHTML('beforeend', ` + `); + const el = document.getElementById(id); + const t = new bootstrap.Toast(el, { delay: 3000 }); + t.show(); + el.addEventListener('hidden.bs.toast', () => el.remove()); +} + +function showError(elId, msg) { + const el = document.getElementById(elId); + if (el) { el.textContent = msg; el.classList.remove('d-none'); } +} + +function hideError(elId) { + const el = document.getElementById(elId); + if (el) el.classList.add('d-none'); +} + +function parseAffinity(str) { + return str.split(',').map(s => s.trim()).filter(Boolean); +} + +function affinityStr(arr) { + return Array.isArray(arr) ? arr.join(', ') : (arr || ''); +} + +async function api(method, url, body) { + const opts = { method, headers: { 'Content-Type': 'application/json' } }; + if (body !== undefined) opts.body = JSON.stringify(body); + const r = await fetch(url, opts); + return r.json(); +} + +function escHtml(s) { + return String(s || '').replace(/&/g,'&').replace(//g,'>'); +} + +// ─── Загрузка всех данных ───────────────────────────────────────────────────── +async function loadAll() { + const data = await api('GET', '/api/all'); + State.objects = {}; + for (const [k, v] of Object.entries(data.servers || {})) State.objects[k] = v; + for (const [k, v] of Object.entries(data.nets || {})) State.objects[k] = v; + State.groups = data.groups || {}; + State.services = data.services || {}; + State.service_groups = data.service_groups || {}; + State.rules = data.rules || []; +} + +// ─── App (импорт) ───────────────────────────────────────────────────────────── +const App = { + async importSettings() { + if (!confirm('Импортировать данные из fw_settings.py? Текущие данные будут заменены.')) return; + const r = await api('POST', '/api/import'); + if (r.ok) { + showToast('Импорт выполнен успешно'); + await loadAll(); + Objects.render(); + Groups.render(); + Services.render(); + SvcGroups.render(); + Rules.render(); + } else { + showToast('Ошибка импорта: ' + (r.error || ''), 'danger'); + } + } +}; + +// ═══════════════════════════════════════════════════════════════════════════════ +// OBJECTS +// ═══════════════════════════════════════════════════════════════════════════════ +const Objects = { + modal: null, + _filter: '', + _typeFilter: '', + + init() { + this.modal = new bootstrap.Modal(document.getElementById('modal-object')); + }, + + render() { + const tbody = document.getElementById('objects-tbody'); + const q = this._filter.toLowerCase(); + const tf = this._typeFilter; + const rows = []; + for (const [key, obj] of Object.entries(State.objects)) { + if (q && !key.toLowerCase().includes(q) && !(obj.description || '').toLowerCase().includes(q) && !(obj.ip || '').includes(q)) continue; + if (tf && obj.type !== tf) continue; + const typeBadge = obj.type === 'network' + ? `network` + : `host`; + rows.push(` + ${escHtml(key)} + ${typeBadge} + ${escHtml(obj.ip)} + ${escHtml(obj.prefix)} + ${escHtml(obj.gw)} + ${escHtml(obj.domain)} + ${escHtml(obj.description)} + ${escHtml(affinityStr(obj.affinity))} + + + + + `); + } + tbody.innerHTML = rows.join('') || 'Нет объектов'; + }, + + filter(val) { + if (val !== undefined) this._filter = val; + this._typeFilter = document.getElementById('obj-type-filter').value; + this.render(); + }, + + openModal(key) { + hideError('obj-error'); + const isEdit = !!key; + document.getElementById('modal-object-title').textContent = isEdit ? 'Редактировать объект' : 'Новый объект'; + document.getElementById('obj-edit-key').value = key || ''; + document.getElementById('obj-key').disabled = isEdit; + + if (isEdit) { + const obj = State.objects[key]; + document.getElementById('obj-key').value = key; + document.getElementById('obj-hostname').value = obj.hostname || ''; + document.getElementById('obj-type').value = obj.type || 'host'; + document.getElementById('obj-ip').value = obj.ip || ''; + document.getElementById('obj-prefix').value = obj.prefix || '24'; + document.getElementById('obj-gw').value = obj.gw || ''; + document.getElementById('obj-domain').value = obj.domain || ''; + document.getElementById('obj-description').value = obj.description || ''; + document.getElementById('obj-affinity').value = affinityStr(obj.affinity); + } else { + document.getElementById('form-object').reset(); + document.getElementById('obj-key').disabled = false; + } + this.modal.show(); + }, + + async save() { + hideError('obj-error'); + const editKey = document.getElementById('obj-edit-key').value; + const key = editKey || document.getElementById('obj-key').value.trim(); + if (!key) { showError('obj-error', 'Ключ обязателен'); return; } + + const payload = { + key, + hostname: document.getElementById('obj-hostname').value.trim() || key, + type: document.getElementById('obj-type').value, + ip: document.getElementById('obj-ip').value.trim(), + prefix: document.getElementById('obj-prefix').value.trim() || '24', + gw: document.getElementById('obj-gw').value.trim(), + domain: document.getElementById('obj-domain').value.trim(), + description: document.getElementById('obj-description').value.trim(), + affinity: parseAffinity(document.getElementById('obj-affinity').value), + }; + + let r; + if (editKey) { + r = await api('PUT', `/api/objects/${encodeURIComponent(editKey)}`, payload); + } else { + r = await api('POST', '/api/objects', payload); + } + + if (r.ok) { + showToast(editKey ? 'Объект обновлён' : 'Объект создан'); + this.modal.hide(); + await loadAll(); + this.render(); + } else { + showError('obj-error', r.error || 'Ошибка'); + } + }, + + async delete(key) { + if (!confirm(`Удалить объект "${key}"?`)) return; + const r = await api('DELETE', `/api/objects/${encodeURIComponent(key)}`); + if (r.ok) { + showToast('Объект удалён', 'warning'); + await loadAll(); + this.render(); + } + } +}; + +// ═══════════════════════════════════════════════════════════════════════════════ +// GROUPS +// ═══════════════════════════════════════════════════════════════════════════════ +const Groups = { + modal: null, + _selectedItems: [], // [{hostname}] + _filter: '', + + init() { + this.modal = new bootstrap.Modal(document.getElementById('modal-group')); + }, + + render() { + const tbody = document.getElementById('groups-tbody'); + const q = this._filter.toLowerCase(); + const rows = []; + for (const [key, grp] of Object.entries(State.groups)) { + if (q && !key.toLowerCase().includes(q) && !(grp.name || '').toLowerCase().includes(q)) continue; + const items = (grp.items || []).map(i => `${escHtml(i.hostname)}`).join(' '); + rows.push(` + ${escHtml(key)} + ${escHtml(grp.name)} +
${items || ''}
+ + + + + `); + } + tbody.innerHTML = rows.join('') || 'Нет групп'; + }, + + filter(val) { + if (val !== undefined) this._filter = val; + this.render(); + }, + + openModal(key) { + hideError('grp-error'); + const isEdit = !!key; + document.getElementById('modal-group-title').textContent = isEdit ? 'Редактировать группу' : 'Новая группа'; + document.getElementById('grp-edit-key').value = key || ''; + document.getElementById('grp-key').disabled = isEdit; + document.getElementById('grp-item-search').value = ''; + document.getElementById('grp-item-type-filter').value = ''; + + if (isEdit) { + const grp = State.groups[key]; + document.getElementById('grp-key').value = key; + document.getElementById('grp-name').value = grp.name || ''; + this._selectedItems = [...(grp.items || [])]; + } else { + document.getElementById('form-group').reset(); + document.getElementById('grp-key').disabled = false; + this._selectedItems = []; + } + this.renderPicker(); + this.modal.show(); + }, + + filterItems() { + this.renderPicker(); + }, + + renderPicker() { + const q = (document.getElementById('grp-item-search').value || '').toLowerCase(); + const tf = document.getElementById('grp-item-type-filter').value; + const selectedHostnames = new Set(this._selectedItems.map(i => i.hostname)); + + // Доступные + const avail = document.getElementById('grp-available-list'); + const availItems = []; + for (const [key, obj] of Object.entries(State.objects)) { + if (selectedHostnames.has(key)) continue; + if (q && !key.toLowerCase().includes(q) && !(obj.description || '').toLowerCase().includes(q)) continue; + if (tf && obj.type !== tf) continue; + const badge = obj.type === 'network' ? `net` : `host`; + availItems.push(`
+ ${escHtml(key)} + ${badge} + +
`); + } + avail.innerHTML = availItems.join('') || '
Нет объектов
'; + + // Выбранные + const sel = document.getElementById('grp-selected-list'); + const selItems = this._selectedItems.map((item, idx) => { + const obj = State.objects[item.hostname]; + const badge = obj ? (obj.type === 'network' ? `net` : `host`) : ''; + return `
+ ${escHtml(item.hostname)} + ${badge} + +
`; + }); + sel.innerHTML = selItems.join('') || '
Ничего не выбрано
'; + document.getElementById('grp-selected-count').textContent = this._selectedItems.length; + }, + + addItem(hostname) { + if (!this._selectedItems.find(i => i.hostname === hostname)) { + this._selectedItems.push({ hostname }); + } + this.renderPicker(); + }, + + removeItem(idx) { + this._selectedItems.splice(idx, 1); + this.renderPicker(); + }, + + async save() { + hideError('grp-error'); + const editKey = document.getElementById('grp-edit-key').value; + const key = editKey || document.getElementById('grp-key').value.trim(); + if (!key) { showError('grp-error', 'Ключ обязателен'); return; } + + const payload = { + key, + name: document.getElementById('grp-name').value.trim() || key, + items: this._selectedItems, + }; + + let r; + if (editKey) { + r = await api('PUT', `/api/groups/${encodeURIComponent(editKey)}`, payload); + } else { + r = await api('POST', '/api/groups', payload); + } + + if (r.ok) { + showToast(editKey ? 'Группа обновлена' : 'Группа создана'); + this.modal.hide(); + await loadAll(); + this.render(); + } else { + showError('grp-error', r.error || 'Ошибка'); + } + }, + + async delete(key) { + if (!confirm(`Удалить группу "${key}"?`)) return; + const r = await api('DELETE', `/api/groups/${encodeURIComponent(key)}`); + if (r.ok) { + showToast('Группа удалена', 'warning'); + await loadAll(); + this.render(); + } + } +}; + +// ═══════════════════════════════════════════════════════════════════════════════ +// SERVICES +// ═══════════════════════════════════════════════════════════════════════════════ +const Services = { + modal: null, + _filter: '', + _protoFilter: '', + + init() { + this.modal = new bootstrap.Modal(document.getElementById('modal-service')); + }, + + render() { + const tbody = document.getElementById('services-tbody'); + const q = this._filter.toLowerCase(); + const pf = this._protoFilter; + const rows = []; + for (const [key, svc] of Object.entries(State.services)) { + if (q && !key.toLowerCase().includes(q) && !(svc.name || '').toLowerCase().includes(q) && !(svc.dport || '').includes(q)) continue; + if (pf && svc.proto !== pf) continue; + const protoBadge = `${escHtml(svc.proto)}`; + rows.push(` + ${escHtml(key)} + ${escHtml(svc.name)} + ${protoBadge} + ${escHtml(svc.sport)} + ${escHtml(svc.dport)} + + + + + `); + } + tbody.innerHTML = rows.join('') || 'Нет сервисов'; + }, + + filter(val) { + if (val !== undefined) this._filter = val; + this._protoFilter = document.getElementById('svc-proto-filter').value; + this.render(); + }, + + openModal(key) { + hideError('svc-error'); + const isEdit = !!key; + document.getElementById('modal-service-title').textContent = isEdit ? 'Редактировать сервис' : 'Новый сервис'; + document.getElementById('svc-edit-key').value = key || ''; + document.getElementById('svc-key').disabled = isEdit; + + if (isEdit) { + const svc = State.services[key]; + document.getElementById('svc-key').value = key; + document.getElementById('svc-name').value = svc.name || ''; + document.getElementById('svc-proto').value = svc.proto || 'tcp'; + document.getElementById('svc-sport').value = svc.sport || 'any'; + document.getElementById('svc-dport').value = svc.dport || ''; + } else { + document.getElementById('form-service').reset(); + document.getElementById('svc-key').disabled = false; + document.getElementById('svc-sport').value = 'any'; + } + this.modal.show(); + }, + + async save() { + hideError('svc-error'); + const editKey = document.getElementById('svc-edit-key').value; + const key = editKey || document.getElementById('svc-key').value.trim(); + if (!key) { showError('svc-error', 'Ключ обязателен'); return; } + + const payload = { + key, + name: document.getElementById('svc-name').value.trim() || key, + proto: document.getElementById('svc-proto').value, + sport: document.getElementById('svc-sport').value.trim() || 'any', + dport: document.getElementById('svc-dport').value.trim(), + }; + + let r; + if (editKey) { + r = await api('PUT', `/api/services/${encodeURIComponent(editKey)}`, payload); + } else { + r = await api('POST', '/api/services', payload); + } + + if (r.ok) { + showToast(editKey ? 'Сервис обновлён' : 'Сервис создан'); + this.modal.hide(); + await loadAll(); + this.render(); + } else { + showError('svc-error', r.error || 'Ошибка'); + } + }, + + async delete(key) { + if (!confirm(`Удалить сервис "${key}"?`)) return; + const r = await api('DELETE', `/api/services/${encodeURIComponent(key)}`); + if (r.ok) { + showToast('Сервис удалён', 'warning'); + await loadAll(); + this.render(); + } + } +}; + +// ═══════════════════════════════════════════════════════════════════════════════ +// SERVICE GROUPS +// ═══════════════════════════════════════════════════════════════════════════════ +const SvcGroups = { + modal: null, + _selectedItems: [], // [svc_key] + + init() { + this.modal = new bootstrap.Modal(document.getElementById('modal-svcgroup')); + }, + + render() { + const tbody = document.getElementById('svcgroups-tbody'); + const rows = []; + for (const [key, sg] of Object.entries(State.service_groups)) { + const items = (sg.items || []).map(svcKey => { + const svc = State.services[svcKey]; + const label = svc ? `${svcKey} (${svc.proto}:${svc.dport})` : svcKey; + return `${escHtml(label)}`; + }).join(' '); + rows.push(` + ${escHtml(key)} + ${escHtml(sg.name)} +
${items || ''}
+ + + + + `); + } + tbody.innerHTML = rows.join('') || 'Нет групп сервисов'; + }, + + openModal(key) { + hideError('sg-error'); + const isEdit = !!key; + document.getElementById('modal-svcgroup-title').textContent = isEdit ? 'Редактировать группу сервисов' : 'Новая группа сервисов'; + document.getElementById('sg-edit-key').value = key || ''; + document.getElementById('sg-key').disabled = isEdit; + document.getElementById('sg-svc-search').value = ''; + + if (isEdit) { + const sg = State.service_groups[key]; + document.getElementById('sg-key').value = key; + document.getElementById('sg-name').value = sg.name || ''; + this._selectedItems = [...(sg.items || [])]; + } else { + document.getElementById('form-svcgroup').reset(); + document.getElementById('sg-key').disabled = false; + this._selectedItems = []; + } + this.renderPicker(); + this.modal.show(); + }, + + filterItems(val) { + this.renderPicker(val); + }, + + renderPicker(q) { + if (q === undefined) q = document.getElementById('sg-svc-search').value || ''; + q = q.toLowerCase(); + const selectedSet = new Set(this._selectedItems); + + const avail = document.getElementById('sg-available-list'); + const availItems = []; + for (const [key, svc] of Object.entries(State.services)) { + if (selectedSet.has(key)) continue; + if (q && !key.toLowerCase().includes(q) && !(svc.name || '').toLowerCase().includes(q) && !(svc.dport || '').includes(q)) continue; + availItems.push(`
+ ${escHtml(key)} + ${escHtml(svc.proto)} + ${escHtml(svc.dport)} + +
`); + } + avail.innerHTML = availItems.join('') || '
Нет сервисов
'; + + const sel = document.getElementById('sg-selected-list'); + const selItems = this._selectedItems.map((svcKey, idx) => { + const svc = State.services[svcKey]; + const label = svc ? `${svcKey} (${svc.proto}:${svc.dport})` : svcKey; + return `
+ ${escHtml(label)} + +
`; + }); + sel.innerHTML = selItems.join('') || '
Ничего не выбрано
'; + document.getElementById('sg-selected-count').textContent = this._selectedItems.length; + }, + + addItem(key) { + if (!this._selectedItems.includes(key)) this._selectedItems.push(key); + this.renderPicker(); + }, + + removeItem(idx) { + this._selectedItems.splice(idx, 1); + this.renderPicker(); + }, + + async save() { + hideError('sg-error'); + const editKey = document.getElementById('sg-edit-key').value; + const key = editKey || document.getElementById('sg-key').value.trim(); + if (!key) { showError('sg-error', 'Ключ обязателен'); return; } + + const payload = { + key, + name: document.getElementById('sg-name').value.trim() || key, + items: this._selectedItems, + }; + + let r; + if (editKey) { + r = await api('PUT', `/api/service_groups/${encodeURIComponent(editKey)}`, payload); + } else { + r = await api('POST', '/api/service_groups', payload); + } + + if (r.ok) { + showToast(editKey ? 'Группа сервисов обновлена' : 'Группа сервисов создана'); + this.modal.hide(); + await loadAll(); + this.render(); + } else { + showError('sg-error', r.error || 'Ошибка'); + } + }, + + async delete(key) { + if (!confirm(`Удалить группу сервисов "${key}"?`)) return; + const r = await api('DELETE', `/api/service_groups/${encodeURIComponent(key)}`); + if (r.ok) { + showToast('Группа сервисов удалена', 'warning'); + await loadAll(); + this.render(); + } + } +}; + +// ═══════════════════════════════════════════════════════════════════════════════ +// RULES +// ═══════════════════════════════════════════════════════════════════════════════ +const Rules = { + modal: null, + spanModal: null, + _srcSelected: [], // [{ref_type, ref_key}] + _dstSelected: [], + _svcSelected: [], // [{ref_type: 'svc'|'sg', ref_key}] + + init() { + this.modal = new bootstrap.Modal(document.getElementById('modal-rule')); + this.spanModal = new bootstrap.Modal(document.getElementById('modal-span')); + }, + + // ─── Рендер таблицы правил ───────────────────────────────────────────────── + render() { + const tbody = document.getElementById('rules-tbody'); + const rows = []; + let ruleNum = 0; + + State.rules.forEach((rule, idx) => { + if (rule.type === 'span') { + rows.push(` + ${escHtml(rule.name)} + ${escHtml(affinityStr(rule.affinity))} + + + + + `); + return; + } + + ruleNum++; + const action = rule.action || 'allow'; + const rowClass = `rule-${action}`; + + const srcTags = this._renderRefList(rule.src_list || []); + const dstTags = this._renderRefList(rule.dst_list || []); + const svcTags = this._renderSvcList(rule.service_list || [], rule.service_group_list || []); + + const actionBadge = `${escHtml(action)}`; + + rows.push(` + ${ruleNum} + ${escHtml(rule.order || '')} + ${escHtml(rule.name)} + ${escHtml(rule.description || '')} +
${srcTags}
+
${dstTags}
+
${svcTags}
+ ${actionBadge} + ${escHtml(rule.log || '')} + ${escHtml(rule.idp || '')} + ${escHtml(affinityStr(rule.affinity))} + + + + + `); + }); + + tbody.innerHTML = rows.join('') || 'Нет правил'; + this._initDragDrop(); + }, + + _renderRefList(list) { + return (list || []).map(item => { + const rt = item.ref_type || 'group'; + const rk = item.ref_key || ''; + let cls = 'cell-tag-group'; + let label = rk; + if (rt === 'server') { cls = 'cell-tag-server'; const o = State.objects[rk]; label = rk + (o ? ` (${o.ip})` : ''); } + else if (rt === 'net') { cls = 'cell-tag-net'; const o = State.objects[rk]; label = rk + (o ? ` (${o.ip}/${o.prefix})` : ''); } + return `${escHtml(label)}`; + }).join(''); + }, + + _renderSvcList(svcList, sgList) { + const parts = []; + (svcList || []).forEach(key => { + const svc = State.services[key]; + const label = svc ? `${key} (${svc.proto}:${svc.dport})` : key; + parts.push(`${escHtml(label)}`); + }); + (sgList || []).forEach(key => { + parts.push(`[${escHtml(key)}]`); + }); + return parts.join(''); + }, + + // ─── Drag & Drop ─────────────────────────────────────────────────────────── + _initDragDrop() { + const tbody = document.getElementById('rules-tbody'); + let dragIdx = null; + + tbody.querySelectorAll('tr[data-idx]').forEach(row => { + row.addEventListener('dragstart', e => { + dragIdx = parseInt(row.dataset.idx); + row.classList.add('dragging'); + e.dataTransfer.effectAllowed = 'move'; + }); + row.addEventListener('dragend', () => { + row.classList.remove('dragging'); + tbody.querySelectorAll('tr').forEach(r => r.classList.remove('drag-over')); + }); + row.addEventListener('dragover', e => { + e.preventDefault(); + tbody.querySelectorAll('tr').forEach(r => r.classList.remove('drag-over')); + row.classList.add('drag-over'); + }); + row.addEventListener('drop', async e => { + e.preventDefault(); + const targetIdx = parseInt(row.dataset.idx); + if (dragIdx === null || dragIdx === targetIdx) return; + + // Перемещаем элемент + const newRules = [...State.rules]; + const [moved] = newRules.splice(dragIdx, 1); + newRules.splice(targetIdx, 0, moved); + + // Сохраняем новый порядок + const r = await api('POST', '/api/rules/reorder', { order: newRules.map((_, i) => i) }); + // Обновляем через полную перезагрузку + await loadAll(); + // Применяем новый порядок локально + State.rules = newRules; + this.render(); + dragIdx = null; + }); + }); + }, + + // ─── Модал правила ───────────────────────────────────────────────────────── + openModal(idx) { + hideError('rule-error'); + const isEdit = idx !== undefined; + document.getElementById('modal-rule-title').textContent = isEdit ? 'Редактировать правило' : 'Новое правило'; + document.getElementById('rule-edit-idx').value = isEdit ? idx : ''; + + this._srcSelected = []; + this._dstSelected = []; + this._svcSelected = []; + + if (isEdit) { + const rule = State.rules[idx]; + document.getElementById('rule-name').value = rule.name || ''; + document.getElementById('rule-order').value = rule.order || ''; + document.getElementById('rule-action').value = rule.action || 'allow'; + document.getElementById('rule-log').value = rule.log || 'false'; + document.getElementById('rule-idp').value = rule.idp || 'false'; + document.getElementById('rule-description').value = rule.description || ''; + document.getElementById('rule-affinity').value = affinityStr(rule.affinity); + this._srcSelected = [...(rule.src_list || [])]; + this._dstSelected = [...(rule.dst_list || [])]; + // Сервисы + (rule.service_list || []).forEach(k => this._svcSelected.push({ ref_type: 'svc', ref_key: k })); + (rule.service_group_list || []).forEach(k => this._svcSelected.push({ ref_type: 'sg', ref_key: k })); + } else { + document.getElementById('form-rule').reset(); + } + + document.getElementById('rule-src-search').value = ''; + document.getElementById('rule-dst-search').value = ''; + document.getElementById('rule-svc-search').value = ''; + + this.renderSrcPicker(); + this.renderDstPicker(); + this.renderSvcPicker(); + this.modal.show(); + }, + + // ─── Пикер источника ─────────────────────────────────────────────────────── + filterSrc(q) { this.renderSrcPicker(q); }, + + renderSrcPicker(q) { + if (q === undefined) q = document.getElementById('rule-src-search').value || ''; + q = q.toLowerCase(); + const selectedKeys = new Set(this._srcSelected.map(i => `${i.ref_type}:${i.ref_key}`)); + + const avail = document.getElementById('rule-src-available'); + const items = this._buildObjectGroupItems(q, selectedKeys, 'src'); + avail.innerHTML = items || '
Нет объектов
'; + + const sel = document.getElementById('rule-src-selected'); + sel.innerHTML = this._renderSelectedRefs(this._srcSelected, 'src') || '
Ничего не выбрано
'; + document.getElementById('rule-src-count').textContent = this._srcSelected.length; + }, + + // ─── Пикер назначения ────────────────────────────────────────────────────── + filterDst(q) { this.renderDstPicker(q); }, + + renderDstPicker(q) { + if (q === undefined) q = document.getElementById('rule-dst-search').value || ''; + q = q.toLowerCase(); + const selectedKeys = new Set(this._dstSelected.map(i => `${i.ref_type}:${i.ref_key}`)); + + const avail = document.getElementById('rule-dst-available'); + avail.innerHTML = this._buildObjectGroupItems(q, selectedKeys, 'dst') || '
Нет объектов
'; + + const sel = document.getElementById('rule-dst-selected'); + sel.innerHTML = this._renderSelectedRefs(this._dstSelected, 'dst') || '
Ничего не выбрано
'; + document.getElementById('rule-dst-count').textContent = this._dstSelected.length; + }, + + // ─── Пикер сервисов ──────────────────────────────────────────────────────── + filterSvc(q) { this.renderSvcPicker(q); }, + + renderSvcPicker(q) { + if (q === undefined) q = document.getElementById('rule-svc-search').value || ''; + q = q.toLowerCase(); + const selectedKeys = new Set(this._svcSelected.map(i => `${i.ref_type}:${i.ref_key}`)); + + const avail = document.getElementById('rule-svc-available'); + const items = []; + + // Сервисы + for (const [key, svc] of Object.entries(State.services)) { + if (selectedKeys.has(`svc:${key}`)) continue; + if (q && !key.toLowerCase().includes(q) && !(svc.name || '').toLowerCase().includes(q) && !(svc.dport || '').includes(q)) continue; + items.push(`
+ ${escHtml(key)} + ${escHtml(svc.proto)} + ${escHtml(svc.dport)} + +
`); + } + + // Группы сервисов + for (const [key, sg] of Object.entries(State.service_groups)) { + if (selectedKeys.has(`sg:${key}`)) continue; + if (q && !key.toLowerCase().includes(q) && !(sg.name || '').toLowerCase().includes(q)) continue; + items.push(`
+ [${escHtml(key)}] + group + +
`); + } + + avail.innerHTML = items.join('') || '
Нет сервисов
'; + + const sel = document.getElementById('rule-svc-selected'); + const selItems = this._svcSelected.map((item, idx) => { + const isSg = item.ref_type === 'sg'; + const label = isSg ? `[${item.ref_key}]` : item.ref_key; + const badge = isSg ? `group` : `${escHtml((State.services[item.ref_key] || {}).proto || '')}`; + return `
+ ${escHtml(label)} + ${badge} + +
`; + }); + sel.innerHTML = selItems.join('') || '
Ничего не выбрано
'; + document.getElementById('rule-svc-count').textContent = this._svcSelected.length; + }, + + // ─── Вспомогательные методы пикеров ─────────────────────────────────────── + _buildObjectGroupItems(q, selectedKeys, side) { + const items = []; + + // Группы объектов + for (const [key, grp] of Object.entries(State.groups)) { + if (selectedKeys.has(`group:${key}`)) continue; + if (q && !key.toLowerCase().includes(q) && !(grp.name || '').toLowerCase().includes(q)) continue; + items.push(`
+ ${escHtml(key)} + group + +
`); + } + + // Серверы + for (const [key, obj] of Object.entries(State.objects)) { + if (obj.type !== 'host') continue; + if (selectedKeys.has(`server:${key}`)) continue; + if (q && !key.toLowerCase().includes(q) && !(obj.ip || '').includes(q) && !(obj.description || '').toLowerCase().includes(q)) continue; + items.push(`
+ ${escHtml(key)} ${escHtml(obj.ip)} + host + +
`); + } + + // Сети + for (const [key, obj] of Object.entries(State.objects)) { + if (obj.type !== 'network') continue; + if (selectedKeys.has(`net:${key}`)) continue; + if (q && !key.toLowerCase().includes(q) && !(obj.ip || '').includes(q) && !(obj.description || '').toLowerCase().includes(q)) continue; + items.push(`
+ ${escHtml(key)} ${escHtml(obj.ip)}/${escHtml(obj.prefix)} + net + +
`); + } + + return items.join(''); + }, + + _renderSelectedRefs(list, side) { + return list.map((item, idx) => { + const rt = item.ref_type; + const rk = item.ref_key; + let badge = ''; + let label = rk; + if (rt === 'group') { badge = `group`; } + else if (rt === 'server') { badge = `host`; const o = State.objects[rk]; if (o) label += ` (${o.ip})`; } + else if (rt === 'net') { badge = `net`; const o = State.objects[rk]; if (o) label += ` (${o.ip}/${o.prefix})`; } + return `
+ ${escHtml(label)} + ${badge} + +
`; + }).join(''); + }, + + addRef(side, refType, refKey) { + const list = side === 'src' ? this._srcSelected : this._dstSelected; + if (!list.find(i => i.ref_type === refType && i.ref_key === refKey)) { + list.push({ ref_type: refType, ref_key: refKey }); + } + if (side === 'src') this.renderSrcPicker(); else this.renderDstPicker(); + }, + + removeRef(side, idx) { + const list = side === 'src' ? this._srcSelected : this._dstSelected; + list.splice(idx, 1); + if (side === 'src') this.renderSrcPicker(); else this.renderDstPicker(); + }, + + addSvc(refType, refKey) { + if (!this._svcSelected.find(i => i.ref_type === refType && i.ref_key === refKey)) { + this._svcSelected.push({ ref_type: refType, ref_key: refKey }); + } + this.renderSvcPicker(); + }, + + removeSvc(idx) { + this._svcSelected.splice(idx, 1); + this.renderSvcPicker(); + }, + + // ─── Сохранение правила ──────────────────────────────────────────────────── + async save() { + hideError('rule-error'); + const editIdx = document.getElementById('rule-edit-idx').value; + const name = document.getElementById('rule-name').value.trim(); + if (!name) { showError('rule-error', 'Имя правила обязательно'); return; } + + const svcList = this._svcSelected.filter(i => i.ref_type === 'svc').map(i => i.ref_key); + const sgList = this._svcSelected.filter(i => i.ref_type === 'sg').map(i => i.ref_key); + + const rule = { + name, + order: parseInt(document.getElementById('rule-order').value) || 0, + type: 'rule', + description: document.getElementById('rule-description').value.trim(), + action: document.getElementById('rule-action').value, + log: document.getElementById('rule-log').value, + idp: document.getElementById('rule-idp').value, + affinity: parseAffinity(document.getElementById('rule-affinity').value), + src_list: this._srcSelected, + dst_list: this._dstSelected, + service_list: svcList, + service_group_list: sgList, + }; + + let r; + if (editIdx !== '') { + r = await api('PUT', `/api/rules/${editIdx}`, rule); + } else { + r = await api('POST', '/api/rules', rule); + } + + if (r.ok) { + showToast(editIdx !== '' ? 'Правило обновлено' : 'Правило создано'); + this.modal.hide(); + await loadAll(); + this.render(); + } else { + showError('rule-error', r.error || 'Ошибка'); + } + }, + + // ─── Разделитель (span) ──────────────────────────────────────────────────── + openSpanModal(idx) { + document.getElementById('span-edit-idx').value = idx !== undefined ? idx : ''; + if (idx !== undefined) { + const rule = State.rules[idx]; + document.getElementById('span-name').value = rule.name || ''; + document.getElementById('span-order').value = rule.order || ''; + document.getElementById('span-affinity').value = affinityStr(rule.affinity); + } else { + document.getElementById('span-name').value = ''; + document.getElementById('span-order').value = ''; + document.getElementById('span-affinity').value = ''; + } + this.spanModal.show(); + }, + + async saveSpan() { + const editIdx = document.getElementById('span-edit-idx').value; + const rule = { + name: document.getElementById('span-name').value.trim(), + order: parseInt(document.getElementById('span-order').value) || 0, + type: 'span', + affinity: parseAffinity(document.getElementById('span-affinity').value), + }; + + let r; + if (editIdx !== '') { + r = await api('PUT', `/api/rules/${editIdx}`, rule); + } else { + r = await api('POST', '/api/rules', rule); + } + + if (r.ok) { + showToast('Разделитель сохранён'); + this.spanModal.hide(); + await loadAll(); + this.render(); + } else { + showToast('Ошибка: ' + (r.error || ''), 'danger'); + } + }, + + async delete(idx) { + const rule = State.rules[idx]; + if (!confirm(`Удалить "${rule.name}"?`)) return; + const r = await api('DELETE', `/api/rules/${idx}`); + if (r.ok) { + showToast('Удалено', 'warning'); + await loadAll(); + this.render(); + } + } +}; + +// ─── Инициализация ──────────────────────────────────────────────────────────── +document.addEventListener('DOMContentLoaded', async () => { + Objects.init(); + Groups.init(); + Services.init(); + SvcGroups.init(); + Rules.init(); + + await loadAll(); + Objects.render(); + Groups.render(); + Services.render(); + SvcGroups.render(); + Rules.render(); + + // Перерисовка при переключении вкладок + document.querySelectorAll('[data-bs-toggle="tab"]').forEach(tab => { + tab.addEventListener('shown.bs.tab', e => { + const target = e.target.getAttribute('href'); + if (target === '#tab-objects') Objects.render(); + else if (target === '#tab-groups') Groups.render(); + else if (target === '#tab-services') Services.render(); + else if (target === '#tab-svcgroups') SvcGroups.render(); + else if (target === '#tab-rules') Rules.render(); + }); + }); +}); diff --git a/app/static/style.css b/app/static/style.css new file mode 100644 index 0000000..e1c5fad --- /dev/null +++ b/app/static/style.css @@ -0,0 +1,239 @@ +/* ─── Общие стили ─────────────────────────────────────────────────────────── */ +body { + background: #f4f6f9; + font-size: 13px; +} + +.navbar-brand { + font-size: 1.1rem; + letter-spacing: 0.5px; +} + +/* ─── Таблицы ─────────────────────────────────────────────────────────────── */ +.table th { + font-size: 12px; + white-space: nowrap; +} + +.table td { + vertical-align: middle; + font-size: 12px; +} + +.table-responsive { + max-height: calc(100vh - 200px); + overflow-y: auto; +} + +/* Закреплённый заголовок таблицы */ +.table thead th { + position: sticky; + top: 0; + z-index: 2; +} + +/* ─── Строки правил ───────────────────────────────────────────────────────── */ +tr.rule-allow { + background-color: #e8f5e9 !important; +} + +tr.rule-deny, tr.rule-drop, tr.rule-reject { + background-color: #fce4d6 !important; +} + +tr.rule-span td { + background-color: #d6e4f0 !important; + color: #1f4e79; + font-weight: bold; + text-align: center; + font-size: 13px; +} + +/* ─── Бейджи типов ────────────────────────────────────────────────────────── */ +.badge-host { + background-color: #0d6efd; + color: white; + padding: 2px 7px; + border-radius: 4px; + font-size: 11px; +} + +.badge-network { + background-color: #198754; + color: white; + padding: 2px 7px; + border-radius: 4px; + font-size: 11px; +} + +.badge-action-allow { + background-color: #198754; + color: white; + padding: 2px 8px; + border-radius: 4px; + font-size: 11px; + font-weight: bold; +} + +.badge-action-deny, .badge-action-drop, .badge-action-reject { + background-color: #dc3545; + color: white; + padding: 2px 8px; + border-radius: 4px; + font-size: 11px; + font-weight: bold; +} + +/* ─── Элементы выбора (picker) ────────────────────────────────────────────── */ +.picker-item { + display: flex; + align-items: center; + justify-content: space-between; + padding: 3px 6px; + border-radius: 4px; + cursor: pointer; + margin-bottom: 2px; + font-size: 12px; + transition: background 0.1s; +} + +.picker-item:hover { + background: #e9ecef; +} + +.picker-item .item-label { + flex: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.picker-item .item-badge { + font-size: 10px; + margin-left: 4px; + flex-shrink: 0; +} + +.picker-item .item-action { + flex-shrink: 0; + margin-left: 4px; + color: #6c757d; + font-size: 14px; + line-height: 1; +} + +.picker-item .item-action:hover { + color: #0d6efd; +} + +.picker-item-selected { + background: #cfe2ff; +} + +.picker-item-selected:hover { + background: #b6d4fe; +} + +.picker-item-selected .item-action:hover { + color: #dc3545; +} + +/* ─── Кнопки действий в таблице ───────────────────────────────────────────── */ +.btn-action { + padding: 2px 6px; + font-size: 12px; +} + +/* ─── Теги в ячейках таблицы ─────────────────────────────────────────────── */ +.cell-tags { + display: flex; + flex-wrap: wrap; + gap: 2px; +} + +.cell-tag { + background: #e9ecef; + border-radius: 3px; + padding: 1px 5px; + font-size: 11px; + white-space: nowrap; +} + +.cell-tag-group { + background: #d1ecf1; + color: #0c5460; +} + +.cell-tag-server { + background: #d4edda; + color: #155724; +} + +.cell-tag-net { + background: #fff3cd; + color: #856404; +} + +.cell-tag-svc { + background: #f8d7da; + color: #721c24; +} + +.cell-tag-sgr { + background: #e2d9f3; + color: #432874; +} + +/* ─── Модальные окна ──────────────────────────────────────────────────────── */ +.modal-header { + padding: 10px 16px; +} + +.modal-body { + padding: 16px; +} + +.modal-footer { + padding: 8px 16px; +} + +/* ─── Toast ───────────────────────────────────────────────────────────────── */ +.toast { + min-width: 260px; +} + +/* ─── Скроллбар ───────────────────────────────────────────────────────────── */ +::-webkit-scrollbar { + width: 6px; + height: 6px; +} +::-webkit-scrollbar-track { + background: #f1f1f1; +} +::-webkit-scrollbar-thumb { + background: #adb5bd; + border-radius: 3px; +} +::-webkit-scrollbar-thumb:hover { + background: #6c757d; +} + +/* ─── Drag handle для правил ──────────────────────────────────────────────── */ +.drag-handle { + cursor: grab; + color: #adb5bd; + font-size: 16px; + padding: 0 4px; +} +.drag-handle:active { + cursor: grabbing; +} + +tr.dragging { + opacity: 0.5; + background: #cfe2ff !important; +} + +tr.drag-over { + border-top: 2px solid #0d6efd; +} diff --git a/app/templates/index.html b/app/templates/index.html new file mode 100644 index 0000000..70c14f9 --- /dev/null +++ b/app/templates/index.html @@ -0,0 +1,548 @@ + + + + + + Firewall Rules Builder + + + + + + + + + + +
+ + +
+ + + + +
+
+
Объекты (хосты и сети)
+ +
+
+ + +
+
+ + + + + + + +
КлючТипIPПрефиксШлюзДоменОписаниеAffinity
+
+
+ + + + +
+
+
Группы объектов
+ +
+
+ +
+
+ + + + + +
КлючИмяЭлементы
+
+
+ + + + +
+
+
Сервисы
+ +
+
+ + +
+
+ + + + + +
КлючИмяПротоколПорт источникаПорт назначения
+
+
+ + + + +
+
+
Группы сервисов
+ +
+
+ + + + + +
КлючИмяСервисы
+
+
+ + + + +
+
+
Правила Firewall
+
+ + +
+
+
+ + + + + + + + + + + + + + + + + + +
#ПорядокИмяОписаниеИсточникНазначениеСервисыДействиеЛогIDPAffinity
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + diff --git a/fw_report.py b/fw_report.py new file mode 100644 index 0000000..a335b8b --- /dev/null +++ b/fw_report.py @@ -0,0 +1,330 @@ +""" +fw_report.py — генератор Excel-отчёта по правилам firewall. +Читает данные из fw_settings.py и создаёт файл fw_report.xlsx со страницами: + - Правила (Rules) + - Объекты (Objects) + - Группы объектов (Groups) + - Сервисы (Services) + - Группы сервисов (Service Groups) +""" + +import sys +import os + +# Добавляем текущую директорию в путь, чтобы импортировать fw_settings +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +from fw_settings import servers, nets, groups, services, service_groups, rules + +from openpyxl import Workbook +from openpyxl.styles import ( + Font, PatternFill, Alignment, Border, Side, GradientFill +) +from openpyxl.utils import get_column_letter + +# ─── Цветовая палитра ──────────────────────────────────────────────────────── +CLR_HEADER_BG = "1F4E79" # тёмно-синий — заголовки таблиц +CLR_HEADER_FG = "FFFFFF" # белый текст +CLR_SPAN_BG = "D6E4F0" # голубой — строки-разделители (span) +CLR_SPAN_FG = "1F4E79" # тёмно-синий текст +CLR_ALLOW_BG = "E2EFDA" # светло-зелёный — allow +CLR_DENY_BG = "FCE4D6" # светло-красный — deny +CLR_ALT_BG = "F2F2F2" # светло-серый — чётные строки +CLR_WHITE = "FFFFFF" + +THIN = Side(style="thin", color="BFBFBF") +BORDER = Border(left=THIN, right=THIN, top=THIN, bottom=THIN) + + +def make_fill(hex_color: str) -> PatternFill: + return PatternFill("solid", fgColor=hex_color) + + +def header_font(bold=True) -> Font: + return Font(name="Calibri", bold=bold, color=CLR_HEADER_FG, size=11) + + +def cell_font(bold=False, color="000000") -> Font: + return Font(name="Calibri", bold=bold, color=color, size=10) + + +def apply_header_row(ws, row: int, headers: list[str]): + """Записывает строку заголовков с форматированием.""" + for col, text in enumerate(headers, start=1): + c = ws.cell(row=row, column=col, value=text) + c.font = header_font() + c.fill = make_fill(CLR_HEADER_BG) + c.alignment = Alignment(horizontal="center", vertical="center", wrap_text=True) + c.border = BORDER + + +def set_col_widths(ws, widths: list[int]): + for i, w in enumerate(widths, start=1): + ws.column_dimensions[get_column_letter(i)].width = w + + +def fmt_cell(ws, row, col, value, bold=False, bg=None, align="left", wrap=True, color="000000"): + c = ws.cell(row=row, column=col, value=value) + c.font = cell_font(bold=bold, color=color) + c.alignment = Alignment(horizontal=align, vertical="center", wrap_text=wrap) + c.border = BORDER + if bg: + c.fill = make_fill(bg) + return c + + +# ─── Вспомогательные функции для извлечения имён ──────────────────────────── + +def obj_display(obj: dict) -> str: + """Возвращает строку вида 'hostname (ip/prefix)' или 'name' для группы.""" + if "ip" in obj and "prefix" in obj: + return f"{obj.get('hostname', '')} ({obj['ip']}/{obj['prefix']})" + if "hostname" in obj: + return obj["hostname"] + if "name" in obj: + return obj["name"] + return str(obj) + + +def group_display(grp: dict) -> str: + return grp.get("name", "") + + +def svc_display(svc: dict) -> str: + return svc.get("name", "") + + +def list_to_str(items, fn) -> str: + if not items: + return "—" + return "\n".join(fn(i) for i in items) + + +# ─── Лист: Объекты ─────────────────────────────────────────────────────────── + +def build_objects_sheet(wb: Workbook): + ws = wb.create_sheet("Объекты") + ws.freeze_panes = "A2" + + headers = ["Имя", "Тип", "IP-адрес", "Префикс", "Шлюз", "Домен", "Описание"] + apply_header_row(ws, 1, headers) + set_col_widths(ws, [20, 10, 16, 8, 16, 16, 40]) + + all_objects = {} + for k, v in servers.items(): + all_objects[k] = v + for k, v in nets.items(): + all_objects[k] = v + + for row_idx, (key, obj) in enumerate(all_objects.items(), start=2): + bg = CLR_WHITE if row_idx % 2 == 0 else CLR_ALT_BG + fmt_cell(ws, row_idx, 1, obj.get("hostname", key), bg=bg) + fmt_cell(ws, row_idx, 2, obj.get("type", ""), bg=bg, align="center") + fmt_cell(ws, row_idx, 3, obj.get("ip", ""), bg=bg, align="center") + fmt_cell(ws, row_idx, 4, str(obj.get("prefix", "")), bg=bg, align="center") + fmt_cell(ws, row_idx, 5, obj.get("gw", ""), bg=bg, align="center") + fmt_cell(ws, row_idx, 6, obj.get("domain", ""), bg=bg) + fmt_cell(ws, row_idx, 7, obj.get("description", ""), bg=bg) + + ws.row_dimensions[1].height = 30 + + +# ─── Лист: Группы объектов ─────────────────────────────────────────────────── + +def build_groups_sheet(wb: Workbook): + ws = wb.create_sheet("Группы объектов") + ws.freeze_panes = "A2" + + headers = ["Имя группы", "Элементы группы"] + apply_header_row(ws, 1, headers) + set_col_widths(ws, [35, 60]) + + for row_idx, (key, grp) in enumerate(groups.items(), start=2): + bg = CLR_WHITE if row_idx % 2 == 0 else CLR_ALT_BG + items = grp.get("items", []) + items_str = "\n".join(i.get("hostname", str(i)) for i in items) if items else "—" + fmt_cell(ws, row_idx, 1, grp.get("name", key), bold=True, bg=bg) + c = fmt_cell(ws, row_idx, 2, items_str, bg=bg) + # авто-высота строки по числу элементов + ws.row_dimensions[row_idx].height = max(15, 15 * max(1, len(items))) + + ws.row_dimensions[1].height = 30 + + +# ─── Лист: Сервисы ─────────────────────────────────────────────────────────── + +def build_services_sheet(wb: Workbook): + ws = wb.create_sheet("Сервисы") + ws.freeze_panes = "A2" + + headers = ["Ключ", "Имя сервиса", "Протокол", "Порт источника", "Порт назначения"] + apply_header_row(ws, 1, headers) + set_col_widths(ws, [28, 38, 14, 18, 18]) + + for row_idx, (key, svc) in enumerate(services.items(), start=2): + bg = CLR_WHITE if row_idx % 2 == 0 else CLR_ALT_BG + fmt_cell(ws, row_idx, 1, key, bg=bg) + fmt_cell(ws, row_idx, 2, svc.get("name", ""), bg=bg) + fmt_cell(ws, row_idx, 3, svc.get("proto", ""), bg=bg, align="center") + fmt_cell(ws, row_idx, 4, svc.get("sport", ""), bg=bg, align="center") + fmt_cell(ws, row_idx, 5, svc.get("dport", ""), bg=bg, align="center") + + ws.row_dimensions[1].height = 30 + + +# ─── Лист: Группы сервисов ─────────────────────────────────────────────────── + +def build_service_groups_sheet(wb: Workbook): + ws = wb.create_sheet("Группы сервисов") + ws.freeze_panes = "A2" + + headers = ["Ключ", "Имя группы", "Сервисы (имя | протокол | dport)"] + apply_header_row(ws, 1, headers) + set_col_widths(ws, [25, 35, 60]) + + for row_idx, (key, sg) in enumerate(service_groups.items(), start=2): + bg = CLR_WHITE if row_idx % 2 == 0 else CLR_ALT_BG + items = sg.get("items", []) + lines = [] + for svc in items: + lines.append( + f"{svc.get('name','')} | {svc.get('proto','')} | {svc.get('dport','')}" + ) + items_str = "\n".join(lines) if lines else "—" + fmt_cell(ws, row_idx, 1, key, bg=bg) + fmt_cell(ws, row_idx, 2, sg.get("name", key), bold=True, bg=bg) + fmt_cell(ws, row_idx, 3, items_str, bg=bg) + ws.row_dimensions[row_idx].height = max(15, 15 * max(1, len(items))) + + ws.row_dimensions[1].height = 30 + + +# ─── Лист: Правила ─────────────────────────────────────────────────────────── + +def _extract_src_dst(lst) -> str: + """Преобразует список src/dst (может содержать dict серверов или групп) в строку.""" + if not lst: + return "—" + parts = [] + for item in lst: + if not isinstance(item, dict): + parts.append(str(item)) + continue + # Это группа (есть ключ 'items') или сервер/сеть (есть ключ 'ip') + if "items" in item: + parts.append(item.get("name", "?")) + elif "ip" in item: + hostname = item.get("hostname", "") + ip = item.get("ip", "") + prefix = item.get("prefix", "") + parts.append(f"{hostname} ({ip}/{prefix})") + else: + parts.append(item.get("hostname") or item.get("name") or str(item)) + return "\n".join(parts) + + +def _extract_services(svc_list, sg_list) -> str: + parts = [] + if svc_list: + for s in svc_list: + if isinstance(s, dict): + parts.append(s.get("name", str(s))) + if sg_list: + for sg in sg_list: + if isinstance(sg, dict): + parts.append(f"[{sg.get('name', str(sg))}]") + return "\n".join(parts) if parts else "—" + + +def build_rules_sheet(wb: Workbook): + ws = wb.create_sheet("Правила", 0) # первый лист + ws.freeze_panes = "A3" + + headers = [ + "№", "Порядок", "Имя правила", "Описание", + "Источник", "Назначение", "Сервисы", + "Действие", "Лог", "IDP", "Affinity" + ] + apply_header_row(ws, 1, headers) + set_col_widths(ws, [5, 8, 28, 40, 30, 30, 35, 10, 6, 6, 25]) + + rule_num = 0 + for row_idx, rule in enumerate(rules, start=2): + rtype = rule.get("type", "rule") + + if rtype == "span": + # Строка-разделитель (заголовок секции) + ws.merge_cells( + start_row=row_idx, start_column=1, + end_row=row_idx, end_column=len(headers) + ) + c = ws.cell(row=row_idx, column=1, value=rule.get("name", "")) + c.font = Font(name="Calibri", bold=True, color=CLR_SPAN_FG, size=11) + c.fill = make_fill(CLR_SPAN_BG) + c.alignment = Alignment(horizontal="center", vertical="center") + c.border = BORDER + ws.row_dimensions[row_idx].height = 22 + continue + + # Обычное правило + rule_num += 1 + action = rule.get("action", "") + if action == "allow": + bg = CLR_ALLOW_BG + elif action in ("deny", "drop", "reject"): + bg = CLR_DENY_BG + else: + bg = CLR_WHITE if row_idx % 2 == 0 else CLR_ALT_BG + + src_str = _extract_src_dst(rule.get("src_list")) + dst_str = _extract_src_dst(rule.get("dst_list")) + svc_str = _extract_services( + rule.get("service_list"), rule.get("service_group_list") + ) + affinity_str = ", ".join(rule.get("affinity", [])) + + fmt_cell(ws, row_idx, 1, rule_num, bg=bg, align="center") + fmt_cell(ws, row_idx, 2, rule.get("order", ""), bg=bg, align="center") + fmt_cell(ws, row_idx, 3, rule.get("name", ""), bg=bg, bold=True) + fmt_cell(ws, row_idx, 4, rule.get("description", ""), bg=bg) + fmt_cell(ws, row_idx, 5, src_str, bg=bg) + fmt_cell(ws, row_idx, 6, dst_str, bg=bg) + fmt_cell(ws, row_idx, 7, svc_str, bg=bg) + fmt_cell(ws, row_idx, 8, action, bg=bg, align="center", bold=True) + fmt_cell(ws, row_idx, 9, rule.get("log", ""), bg=bg, align="center") + fmt_cell(ws, row_idx, 10, rule.get("idp", ""), bg=bg, align="center") + fmt_cell(ws, row_idx, 11, affinity_str, bg=bg) + + # Высота строки — по числу строк в самом длинном поле + max_lines = max( + len(src_str.split("\n")), + len(dst_str.split("\n")), + len(svc_str.split("\n")), + 1 + ) + ws.row_dimensions[row_idx].height = max(18, 15 * max_lines) + + ws.row_dimensions[1].height = 30 + + +# ─── Главная функция ───────────────────────────────────────────────────────── + +def main(): + output_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), "fw_report.xlsx") + + wb = Workbook() + # Удаляем дефолтный лист + wb.remove(wb.active) + + build_rules_sheet(wb) + build_objects_sheet(wb) + build_groups_sheet(wb) + build_services_sheet(wb) + build_service_groups_sheet(wb) + + wb.save(output_file) + print(f"Отчёт сохранён: {output_file}") + + +if __name__ == "__main__": + main() diff --git a/fw_report.xlsx b/fw_report.xlsx new file mode 100644 index 0000000..f8a35a2 Binary files /dev/null and b/fw_report.xlsx differ diff --git a/fw_settings.py b/fw_settings.py new file mode 100644 index 0000000..d9fcc7f --- /dev/null +++ b/fw_settings.py @@ -0,0 +1,1322 @@ +servers = { + "cr": { + "hostname": "cr", + "ip": "172.19.20.2", + "prefix": "24", + "gw": "172.19.20.1", + "domain": "avndr.ru", + "description": "ЦР ПУЦ + TLS", + "type": "host", + "affinity": ["fw_cr", "fw_cr_ca"], + }, + "cs": { + "hostname": "cs", + "ip": "172.19.20.3", + "prefix": "24", + "gw": "172.19.20.1", + "domain": "avndr.ru", + "description": "ЦС ПУЦ + TLS", + "type": "host", + "affinity": ["fw_cr", "fw_cr_ca"], + }, + "rk-uc": { + "hostname": "rk-uc", + "ip": "172.19.40.3", + "prefix": "24", + "gw": "172.19.40.1", + "domain": "avndr.ru", + "description": "Сервер РК", + "type": "host", + "affinity": ["fw_cr", "fw_cr_ca"], + }, + "ntp": { + "hostname": "ntp", + "ip": "172.19.40.4", + "prefix": "24", + "gw": "172.19.40.1", + "domain": "avndr.ru", + "description": "Сервер точного времени-1", + "type": "host", + "affinity": ["fw_cr", "fw_cr_ca"], + }, + "pki": { + "hostname": "pki", + "ip": "172.19.100.4", + "prefix": "24", + "gw": "172.19.100.1", + "domain": "avndr.ru", + "description": "PKI-кластер", + "type": "host", + "affinity": ["fw_cr", "fw_cr_ca"], + }, + "mps": { + "hostname": "mps", + "ip": "172.19.100.5", + "prefix": "24", + "gw": "172.19.100.1", + "domain": "avndr.ru", + "description": "МПС", + "type": "host", + "affinity": ["fw_cr", "fw_cr_ca"], + }, + "esia": { + "hostname": "esia", + "ip": "172.19.150.4", + "prefix": "24", + "gw": "172.19.150.1", + "domain": "avndr.ru", + "description": "ТР-ЕСИА", + "type": "host", + "affinity": ["fw_cr", "fw_cr_ca"], + }, + "ko-app": { + "hostname": "ko-app", + "ip": "172.19.110.4", + "prefix": "24", + "gw": "172.19.110.1", + "domain": "avndr.ru", + "description": "Сервер КО", + "type": "host", + "affinity": ["fw_cr", "fw_cr_ca"], + }, + "ko-db": { + "hostname": "ko-db", + "ip": "172.19.110.5", + "prefix": "24", + "gw": "172.19.110.1", + "domain": "avndr.ru", + "description": "Сервер КО СУБД", + "type": "host", + "affinity": ["fw_cr", "fw_cr_ca"], + }, + "ko-csp": { + "hostname": "ko-csp", + "ip": "172.19.110.6", + "prefix": "24", + "gw": "172.19.110.1", + "domain": "avndr.ru", + "description": "Сервер КО СКЗИ", + "type": "host", + "affinity": ["fw_cr", "fw_cr_ca"], + }, + "kk-app": { + "hostname": "kk-app", + "ip": "172.19.120.4", + "prefix": "24", + "gw": "172.19.120.1", + "domain": "avndr.ru", + "description": "Сервер КК", + "type": "host", + "affinity": ["fw_cr", "fw_cr_ca"], + }, + "kk-db": { + "hostname": "kk-db", + "ip": "172.19.120.5", + "prefix": "24", + "gw": "172.19.120.1", + "domain": "avndr.ru", + "description": "Сервер КК СУБД", + "type": "host", + "affinity": ["fw_cr", "fw_cr_ca"], + }, + "kk-csp": { + "hostname": "kk-csp", + "ip": "172.19.120.6", + "prefix": "24", + "gw": "172.19.120.1", + "domain": "avndr.ru", + "description": "Сервер КК СКЗИ", + "type": "host", + "affinity": ["fw_cr", "fw_cr_ca"], + }, + "core": { + "hostname": "core", + "ip": "172.19.130.4", + "prefix": "24", + "gw": "172.19.130.1", + "domain": "avndr.ru", + "description": "Ядро ВВС", + "type": "host", + "affinity": ["fw_cr", "fw_cr_ca"], + }, + "core-db": { + "hostname": "core-db", + "ip": "172.19.130.5", + "prefix": "24", + "gw": "172.19.130.1", + "domain": "avndr.ru", + "description": "СУБД Ядро ВВС", + "type": "host", + "affinity": ["fw_cr", "fw_cr_ca"], + }, + "arch": { + "hostname": "arch", + "ip": "172.19.130.6", + "prefix": "24", + "gw": "172.19.130.1", + "domain": "avndr.ru", + "description": "Модуль архивирования", + "type": "host", + "affinity": ["fw_cr", "fw_cr_ca"], + }, + "arch-db": { + "hostname": "arch-db", + "ip": "172.19.130.7", + "prefix": "24", + "gw": "172.19.130.1", + "domain": "avndr.ru", + "description": "СУБД Модуль архивирования", + "type": "host", + "affinity": ["fw_cr", "fw_cr_ca"], + }, + "web-apps": { + "hostname": "web-apps", + "ip": "172.19.130.8", + "prefix": "24", + "gw": "172.19.130.1", + "domain": "avndr.ru", + "description": "Сервер веб-приложений СС", + "type": "host", + "affinity": ["fw_cr", "fw_cr_ca"], + }, + "aldp": { + "hostname": "aldp", + "ip": "172.19.140.4", + "prefix": "24", + "gw": "172.19.140.1", + "domain": "avndr.ru", + "description": "Сервер ИБ-1 (ALD Pro)", + "type": "host", + "affinity": ["fw_cr", "fw_cr_ca"], + }, + "alds": { + "hostname": "alds", + "ip": "172.19.140.5", + "prefix": "24", + "gw": "172.19.140.1", + "domain": "avndr.ru", + "description": "Сервер ИБ-2 (ALD Pro)", + "type": "host", + "affinity": ["fw_cr", "fw_cr_ca"], + }, + "rk": { + "hostname": "rk", + "ip": "172.19.140.6", + "prefix": "24", + "gw": "172.19.140.1", + "domain": "avndr.ru", + "description": "Сервер РК", + "type": "host", + "affinity": ["fw_cr", "fw_cr_ca"], + }, + "ksc": { + "hostname": "ksc", + "ip": "172.19.140.7", + "prefix": "24", + "gw": "172.19.140.1", + "domain": "avndr.ru", + "description": "ВМ Kaspersky Security Center", + "type": "host", + "affinity": ["fw_cr", "fw_cr_ca"], + }, + "log": { + "hostname": "log", + "ip": "172.19.140.8", + "prefix": "24", + "gw": "172.19.140.1", + "domain": "avndr.ru", + "description": "ВМ Сервер журналирования", + "type": "host", + "affinity": ["fw_cr", "fw_cr_ca"], + }, + "repo": { + "hostname": "repo", + "ip": "172.19.140.9", + "prefix": "24", + "gw": "172.19.140.1", + "domain": "avndr.ru", + "description": "ВМ Сервер репозиторий ПО", + "type": "host", + "affinity": ["fw_cr", "fw_cr_ca"], + }, + "zbx": { + "hostname": "zbx", + "ip": "172.19.140.10", + "prefix": "24", + "gw": "172.19.140.1", + "domain": "avndr.ru", + "description": "ВМ Сервер мониторинга (ZbxProxy)", + "type": "host", + "affinity": ["fw_cr", "fw_cr_ca"], + }, + "arm-cont4": { + "hostname": "arm-cont4", + "ip": "172.19.210.2", + "prefix": "24", + "gw": "172.19.210.1", + "domain": "avndr.ru", + "description": "АРМ ЦУС Континент 4", + "type": "host", + "affinity": ["fw_cr", "fw_cr_ca"], + }, + "arm-web-oper": { + "hostname": "arm-web-oper", + "ip": "172.19.220.2", + "prefix": "24", + "gw": "172.19.220.1", + "domain": "avndr.ru", + "description": "ВВС АРМ WEB (1)", + "type": "host", + "affinity": ["fw_cr", "fw_cr_ca"], + }, + "arm-web-adm": { + "hostname": "arm-web-adm", + "ip": "172.19.230.2", + "prefix": "24", + "gw": "172.19.230.1", + "domain": "avndr.ru", + "description": "ВВС АРМ WEB (2)", + "type": "host", + "affinity": ["fw_cr", "fw_cr_ca"], + }, + "arm-web-pki": { + "hostname": "arm-web-pki", + "ip": "172.19.230.2", + "prefix": "24", + "gw": "172.19.230.1", + "domain": "avndr.ru", + "description": "АРМ адм САВС", + "type": "host", + "affinity": ["fw_cr", "fw_cr_ca"], + }, +} + +# networks +nets = { + "net_any": { + "hostname": "net_any", + "description": "Any", + "domain": "avndr.ru", + "ip": "0.0.0.0", + "prefix": 0, + "type": "network", + "affinity": ["fw_cr", "fw_cr_ca"], + }, + "net_uc_srv": { + "hostname": "net_uc_srv", + "description": "Сегмент УЦ ПУЦ+TLS", + "domain": "avndr.ru", + "ip": "172.19.20.0", + "prefix": 24, + "type": "network", + "affinity": ["fw_cr", "fw_cr_ca"], + }, + "net_uc_adm_srv": { + "hostname": "net_uc_adm_srv", + "description": "Административный сегмент УЦ", + "domain": "avndr.ru", + "ip": "172.19.40.0", + "prefix": 24, + "type": "network", + "affinity": ["fw_cr", "fw_cr_ca"], + }, + "net_dr_pki": { + "hostname": "net_dr_pki", + "description": "Сегмент САВС", + "domain": "avndr.ru", + "ip": "172.19.100.0", + "prefix": 24, + "type": "network", + "affinity": ["fw_cr", "fw_cr_ca"], + }, + "net_dr_ko": { + "hostname": "net_dr_ko", + "description": "Сегмент КО", + "domain": "avndr.ru", + "ip": "172.19.110.0", + "prefix": 24, + "type": "network", + "affinity": ["fw_cr", "fw_cr_ca"], + }, + "net_dr_kk": { + "hostname": "net_dr_kk", + "description": "Сегмент КК", + "domain": "avndr.ru", + "ip": "172.19.120.0", + "prefix": 24, + "type": "network", + "affinity": ["fw_cr", "fw_cr_ca"], + }, + "net_dr_core_srv": { + "hostname": "net_dr_core_srv", + "description": "Сегмент интеграции", + "domain": "avndr.ru", + "ip": "172.19.130.0", + "prefix": 24, + "type": "network", + "affinity": ["fw_cr", "fw_cr_ca"], + }, + "net_dr_adm_srv": { + "hostname": "net_dr_adm_srv", + "description": "Административный сегмент", + "domain": "avndr.ru", + "ip": "172.19.140.0", + "prefix": 24, + "type": "network", + "affinity": ["fw_cr", "fw_cr_ca"], + }, + "net_dr_gis_esia": { + "hostname": "net_dr_gis_esia", + "description": "Сегмент ГИС ЕСИА", + "domain": "avndr.ru", + "ip": "172.19.150.0", + "prefix": 24, + "type": "network", + "affinity": ["fw_cr", "fw_cr_ca"], + }, + "net_dr_adm_arm_cont4": { + "hostname": "net_dr_adm_arm_cont4", + "description": "Сегмент администраторов ЦР", + "domain": "avndr.ru", + "ip": "172.19.210.0", + "prefix": 24, + "type": "network", + "affinity": ["fw_cr", "fw_cr_ca"], + }, + "net_dr_adm_arm_web1": { + "hostname": "net_dr_adm_arm_web1", + "description": "Сегмент администраторов ЦР", + "domain": "avndr.ru", + "ip": "172.19.220.0", + "prefix": 24, + "type": "network", + "affinity": ["fw_cr", "fw_cr_ca"], + }, + "net_dr_adm_arm_web2": { + "hostname": "net_dr_adm_arm_web2", + "description": "Сегмент администраторов ЦР", + "domain": "avndr.ru", + "ip": "172.19.230.0", + "prefix": 24, + "type": "network", + "affinity": ["fw_cr", "fw_cr_ca"], + }, + "net_dr_adm_arm_pki": { + "hostname": "net_dr_adm_arm_pki", + "description": "Сегмент администраторов ЦР", + "domain": "avndr.ru", + "ip": "172.19.230.0", + "prefix": 24, + "type": "network", + "affinity": ["fw_cr", "fw_cr_ca"], + }, + "net_dr_adm_arm_ngate": { + "hostname": "net_dr_adm_arm_ngate", + "description": "Сегмент администраторов ЦР", + "domain": "avndr.ru", + "ip": "172.19.250.0", + "prefix": 24, + "type": "network", + "affinity": ["fw_cr", "fw_cr_ca"], + }, +} + + +groups = { + "net_any": {"name": "net_any", "items": [{"hostname": "0.0.0.0/0"}]}, + "prot_set_uc_adm": { + "name": "prot_set_uc_adm", + "items": [{"hostname": "arm-cont3"}, {"hostname": "arm-cr"}], + }, + "prot_set_uc_arm_reg_dr": { + "name": "prot_set_uc_arm_reg_dr", + "items": [{"hostname": "arm-cr"}], + }, + "prot_set_uc_arm_reg_tls": { + "name": "prot_set_uc_arm_reg_tls", + "items": [{"hostname": "arm-cr"}], + }, + "prot_set_uc_reg_dr": {"name": "prot_set_uc_reg_dr", "items": [{"hostname": "cr"}]}, + "prot_set_uc_reg_tls": { + "name": "prot_set_uc_reg_tls", + "items": [{"hostname": "cr"}], + }, + "set_abs": {"name": "set_abs", "items": []}, + "set_cdp": {"name": "set_cdp", "items": []}, + "set_dbo": {"name": "set_dbo", "items": []}, + "set_dns": {"name": "set_dns", "items": []}, + "set_dr": { + "name": "set_dr", + "items": [ + {"hostname": "net_dr_pki"}, + {"hostname": "net_dr_gis_esia"}, + {"hostname": "net_dr_ko"}, + {"hostname": "net_dr_kk"}, + {"hostname": "net_dr_core_srv"}, + {"hostname": "net_dr_adm_arm_cont4"}, + {"hostname": "net_dr_adm_arm_web1"}, + {"hostname": "net_dr_adm_arm_web2"}, + {"hostname": "net_dr_adm_arm_pki"}, + {"hostname": "net_dr_adm_arm_ngate"}, + ], + }, + "set_dr_adm_pki_cl": { + "name": "set_dr_adm_pki_cl", + "items": [{"hostname": "pki.avndr.ru"}], + }, + "set_dr_adm_web_adm": { + "name": "set_dr_adm_web_adm", + "items": [{"hostname": "arm-web-adm.avndr.ru"}], + }, + "set_dr_adm_web_oper": { + "name": "set_dr_adm_web_oper", + "items": [{"hostname": "arm-web-oper.avndr.ru"}], + }, + "set_dr_arm_ngate": { + "name": "set_dr_arm_ngate", + "items": [{"hostname": "arm-ngate.avndr.ru"}], + }, + "set_dr_esia_tr": { + "name": "set_dr_esia_tr", + "items": [{"hostname": "esia.avndr.ru"}], + }, + "set_dr_gateout": { + "name": "set_dr_gateout", + "items": [{"hostname": "core.avndr.ru"}], + }, + "set_dr_kk_be": { + "name": "set_dr_kk_be", + "items": [{"hostname": "kk-app.avndr.ru"}], + }, + "set_dr_kk_crypto": { + "name": "set_dr_kk_crypto", + "items": [{"hostname": "kk-csp.avnd.ru"}], + }, + "set_dr_kk_db": {"name": "set_dr_kk_db", "items": [{"hostname": "kk-db.avndr.ru"}]}, + "set_dr_ko_be": { + "name": "set_dr_ko_be", + "items": [{"hostname": "ko-app.avndr.ru"}], + }, + "set_dr_ko_crypto": { + "name": "set_dr_ko_crypto", + "items": [{"hostname": "ko-csp.avndr.ru"}], + }, + "set_dr_ko_db": {"name": "set_dr_ko_db", "items": [{"hostname": "ko-db.avndr.ru"}]}, + "set_dr_ngate": { + "name": "set_dr_ngate", + "items": [ + {"hostname": "ngate-mgmt"}, + {"hostname": "ngate-node01"}, + {"hostname": "ngate-node02"}, + ], + }, + "set_dr_ngate_mgmt": { + "name": "set_dr_ngate_mgmt", + "items": [{"hostname": "ngate-mgmt"}], + }, + "set_dr_ngate_nodes": { + "name": "set_dr_ngate_nodes", + "items": [{"hostname": "ngate-node01"}, {"hostname": "ngate-node02"}], + }, + "set_dr_pki_cluster": { + "name": "set_dr_pki_cluster", + "items": [{"hostname": "pki.avndr.ru"}], + }, + "set_dr_plcr": { + "name": "set_dr_plcr", + "items": [ + {"hostname": "cbr_cd-tuz01"}, + {"hostname": "cbr_cd-tuz02"}, + {"hostname": "cbr_cd-tuz03"}, + {"hostname": "cbr_cd-tuz04"}, + ], + }, + "set_dr_savs_mps": { + "name": "set_dr_savs_mps", + "items": [{"hostname": "mps.avndr.ru"}], + }, + "set_dr_savs_mps_be": { + "name": "set_dr_savs_mps_be", + "items": [{"hostname": "mps.avndr.ru"}], + }, + "set_dr_savs_mps_crypto": { + "name": "set_dr_savs_mps_crypto", + "items": [{"hostname": "mps.avndr.ru"}], + }, + "set_dr_savs_mps_db": { + "name": "set_dr_savs_mps_db", + "items": [{"hostname": "mps.avndr.ru"}], + }, + "set_dr_ss_arch_be": { + "name": "set_dr_ss_arch_be", + "items": [{"hostname": "core.avndr.ru"}], + }, + "set_dr_ss_arch_db": { + "name": "set_dr_ss_arch_db", + "items": [{"hostname": "arch-db.avndr.ru"}], + }, + "set_dr_ss_core_bbs": { + "name": "set_dr_ss_core_bbs", + "items": [{"hostname": "core.avndr.ru"}], + }, + "set_dr_ss_core_bbs_db": { + "name": "set_dr_ss_core_bbs_db", + "items": [{"hostname": "core-db.avndr.ru"}], + }, + "set_dr_ss_integr_be": { + "name": "set_dr_ss_integr_be", + "items": [{"hostname": "core.avndr.ru"}], + }, + "set_dr_ss_keycloak": { + "name": "set_dr_ss_keycloak", + "items": [{"hostname": "core.avndr.ru"}], + }, + "set_dr_ss_nginx": { + "name": "set_dr_ss_nginx", + "items": [{"hostname": "core.avndr.ru"}], + }, + "set_dr_tech_server": {"name": "set_dr_tech_server", "items": [{"hostname": "-"}]}, + "set_hsm": {"name": "set_hsm", "items": [{"hostname": "-"}]}, + "set_ksc": {"name": "set_ksc", "items": [{"hostname": "ksc.avndr.ru"}]}, + "set_ntp": {"name": "set_ntp", "items": []}, + "set_rubackup_servers": { + "name": "set_rubackup_servers", + "items": [{"hostname": "rk.avndr.ru"}], + }, + "set_siem": {"name": "set_siem", "items": []}, + "set_uc": { + "name": "set_uc", + "items": [ + {"hostname": "net_uc_srv"}, + {"hostname": "net_uc_adm_srv"}, + {"hostname": "net_uc_cus_adm"}, + {"hostname": "net_uc_arm_ra"}, + ], + }, + "set_uc_adm_arm_reg": { + "name": "set_uc_adm_arm_reg", + "items": [{"hostname": "arm-cr"}], + }, + "set_uc_arm_hsm": {"name": "set_uc_arm_hsm", "items": [{"hostname": "arm-hsm"}]}, + "set_uc_cgw_ncc3": { + "name": "set_uc_cgw_ncc3", + "items": [{"hostname": "gw-uc"}, {"hostname": "ncc-uc"}], + }, + "set_uc_cgw_ncc4": { + "name": "set_uc_cgw_ncc4", + "items": [ + {"hostname": "ncc.avndr.ru"}, + {"hostname": "gw.avndr.ru"}, + {"hostname": "gw02.avndr.ru"}, + {"hostname": "gw.avndr.ru"}, + ], + }, + "set_uc_cgw3": {"name": "set_uc_cgw3", "items": [{"hostname": "gw-uc"}]}, + "set_uc_cgw4": { + "name": "set_uc_cgw4", + "items": [ + {"hostname": "gw.avndr.ru"}, + {"hostname": "gw02.avndr.ru"}, + {"hostname": "gw.avndr.ru"}, + ], + }, + "set_uc_ncc3": {"name": "set_uc_ncc3", "items": [{"hostname": "ncc-uc"}]}, + "set_uc_ncc4": {"name": "set_uc_ncc4", "items": [{"hostname": "ncc.avndr.ru"}]}, + "set_uc_ntp": {"name": "set_uc_ntp", "items": [{"hostname": "ntp"}]}, + "set_uc_ntp_prot": {"name": "set_uc_ntp_prot", "items": [{"hostname": "ntp"}]}, + "set_uc_reg_dr": {"name": "set_uc_reg_dr", "items": [{"hostname": "cs"}]}, + "set_uc_reg_tls": {"name": "set_uc_reg_tls", "items": [{"hostname": "cr"}]}, + "set_uc_rubackup_servers": { + "name": "set_uc_rubackup_servers", + "items": [{"hostname": "rk-uc"}], + }, + "set_zabbix": {"name": "set_zabbix", "items": []}, + "set_uc_cert_tls": {"name": "set_uc_cert_tls", "items": [{"hostname": "cs"}]}, + "set_uc_dr": {"name": "set_uc_dr", "items": [{"hostname": "cs"}]}, + "grp_web_servers": { + "name": "grp_web_servers", + "items": [ + {"hostname": "web01"}, + {"hostname": "web02"}, + {"hostname": "net_dmz"}, + ], + }, +} + + +# services +services = { + "cyberbackup-7780": { + "name": "cyberbackup-7780-tcp", + "sport": "any", + "dport": "7780", + "proto": "tcp", + }, + "cyberbackup-9862": { + "name": "cyberbackup-9862-tcp", + "sport": "any", + "dport": "9862", + "proto": "tcp", + }, + "cyberbackup-9877": { + "name": "cyberbackup-9877-tcp", + "sport": "any", + "dport": "9877", + "proto": "tcp", + }, + "cyberbackup-data-9852": { + "name": "cyberbackup-data-9852-tcp", + "sport": "any", + "dport": "9852", + "proto": "tcp", + }, + "cyberbackup-data-9876": { + "name": "cyberbackup-data-9876-tcp", + "sport": "any", + "dport": "9876", + "proto": "tcp", + }, + "dc-locator": { + "name": "dc-locator-389-udp", + "sport": "any", + "dport": "389", + "proto": "udp", + }, + "dns-tcp": {"name": "dns-53-tcp", "sport": "any", "dport": "53", "proto": "tcp"}, + "dns-udp": {"name": "dns-53-udp", "sport": "any", "dport": "53", "proto": "udp"}, + "globalcatalog-tcp": { + "name": "globalcatalog-3268-tcp", + "sport": "any", + "dport": "3268", + "proto": "tcp", + }, + "globalcatalog-udp": { + "name": "globalcatalog-3268-udp", + "sport": "any", + "dport": "3268", + "proto": "udp", + }, + "ngate-webcon": { + "name": "ngate-webcon-8000-tcp", + "sport": "any", + "dport": "8000", + "proto": "tcp", + }, + "icmp": {"name": "icmp-echo", "sport": "-", "dport": "-", "proto": "icmp-request"}, + "syslog-tcp": { + "name": "syslog-514-tcp", + "sport": "any", + "dport": "514", + "proto": "tcp", + }, + "syslog-udp": { + "name": "syslog-514-udp", + "sport": "any", + "dport": "514", + "proto": "udp", + }, + "syslog-10514-udp": { + "name": "syslog-10514-udp", + "sport": "any", + "dport": "10514", + "proto": "udp", + }, + "ssh": {"name": "ssh-22-tcp", "sport": "any", "dport": "22", "proto": "tcp"}, + "smtp": {"name": "smtp-25-tcp", "sport": "any", "dport": "25", "proto": "tcp"}, + "smtp-tls": { + "name": "smtp-tls-587-tcp", + "sport": "any", + "dport": "587", + "proto": "tcp", + }, + "smtp-ssl": { + "name": "smtp-ssl-465-tcp", + "sport": "any", + "dport": "465", + "proto": "tcp", + }, + "smb": {"name": "smb-445-tcp", "sport": "any", "dport": "445", "proto": "tcp"}, + "sn-tls": { + "name": "sn-tls-443-tcp", + "sport": "any", + "dport": "443", + "proto": "tcp", + }, + "sn-pwd-change-tcp": { + "name": "sn-pwd-change-42464-tcp", + "sport": "any", + "dport": "42464", + "proto": "tcp", + }, + "sn-pwd-change-udp": { + "name": "sn-pwd-change-42464-udp", + "sport": "any", + "dport": "42464", + "proto": "udp", + }, + "sn-lds-tls": { + "name": "sn-lds-tls-50001-tcp", + "sport": "any", + "dport": "30001", + "proto": "tcp", + }, + "sn-lds": { + "name": "sn-lds-50000-tcp", + "sport": "any", + "dport": "30000", + "proto": "tcp", + }, + "sn-kerberos-tcp": { + "name": "sn-kerberos-42088-tcp", + "sport": "any", + "dport": "42088", + "proto": "tcp", + }, + "sn-kerberos-udp": { + "name": "sn-kerberos-42088-udp", + "sport": "any", + "dport": "42088", + "proto": "udp", + }, + "sn-gc-lds-tls": { + "name": "sn-gc-lds-tls-50003-tcp", + "sport": "any", + "dport": "30003", + "proto": "tcp", + }, + "sn-gc-lds": { + "name": "sn-gc-lds-50002-tcp", + "sport": "any", + "dport": "30002", + "proto": "tcp", + }, + "snmp-trap-162-udp": { + "name": "snmp-trap-162-udp", + "sport": "any", + "dport": "162", + "proto": "udp", + }, + "snmp-161-udp": { + "name": "snmp-161-udp", + "sport": "any", + "dport": "161", + "proto": "udp", + }, + "tls-pcr-processing-ul": { + "name": "tls-pcr-processing-ul-443-tcp (change)", + "sport": "any", + "dport": "443", + "proto": "tcp", + }, + "tls-pcr-processing-fl": { + "name": "tls-pcr-processing-fl-443-tcl (change)", + "sport": "any", + "dport": "443", + "proto": "tcp", + }, + "tls-pcr-processing-fp": { + "name": "tls-pcr-processing-fp-443-tcp (change)", + "sport": "any", + "dport": "443", + "proto": "tcp", + }, + "rdp-tcp": { + "name": "rdp-3389-tcp", + "sport": "any", + "dport": "3389", + "proto": "tcp", + }, + "rdp-udp": { + "name": "rdp-3389-udp", + "sport": "any", + "dport": "3389", + "proto": "udp", + }, + "psql-tcp": { + "name": "psql-5432-tcp", + "sport": "any", + "dport": "5432", + "proto": "tcp", + }, + "ntp": {"name": "ntp-123-udp", "sport": "any", "dport": "123", "proto": "udp"}, + "netbios-137-udp": { + "name": "netbios-137-udp", + "sport": "any", + "dport": "137", + "proto": "udp", + }, + "netbios-138-udp": { + "name": "netbios-138-udp", + "sport": "any", + "dport": "138", + "proto": "udp", + }, + "netbios-139-tcp": { + "name": "netbios-139-tcp", + "sport": "any", + "dport": "139", + "proto": "tcp", + }, + "ldaps": {"name": "ldaps-636-tcp", "sport": "any", "dport": "636", "proto": "tcp"}, + "ldap": {"name": "ldap-389-tcp", "sport": "any", "dport": "389", "proto": "tcp"}, + "ksc-klserver-13000-udp": { + "name": "ksc-klserver-13000-udp", + "sport": "any", + "dport": "13000", + "proto": "udp", + }, + "ksc-klserver-13000-tcp": { + "name": "ksc-klserver-13000-tcp", + "sport": "any", + "dport": "13000", + "proto": "tcp", + }, + "ksc-klnagent-14000-tcp": { + "name": "ksc-klnagent-14000-tcp", + "sport": "any", + "dport": "14000", + "proto": "tcp", + }, + "ksc-distribution-tls": { + "name": "ksc-distribution-tls-8061-tcp", + "sport": "any", + "dport": "8061", + "proto": "tcp", + }, + "ksc-distribution": { + "name": "ksc-distribution-8060-tcp", + "sport": "any", + "dport": "8060", + "proto": "tcp", + }, + "ksc-webcon": { + "name": "ksc-webcon-8080-tcp", + "sport": "any", + "dport": "8080", + "proto": "tcp", + }, + "klnagent": { + "name": "klnagent-15000-udp", + "sport": "any", + "dport": "15000", + "proto": "udp", + }, + "krb-password-tcp": { + "name": "krb-password-464-tcp", + "sport": "any", + "dport": "464", + "proto": "tcp", + }, + "krb-password-udp": { + "name": "krb-password-464-udp", + "sport": "any", + "dport": "464", + "proto": "udp", + }, + "krb-88-udp": {"name": "krb-88-udp", "sport": "any", "dport": "88", "proto": "udp"}, + "krb-88-tcp": {"name": "krb-88-tcp", "sport": "any", "dport": "88", "proto": "tcp"}, + "k3-vpn": { + "name": "k3-vpn-10000-10031-udp", + "sport": "10000-10031", + "dport": "10000-10031", + "proto": "udp", + }, + "k3-sd-to-ap": { + "name": "k3-sd-to-ap-7500-udp", + "sport": "any", + "dport": "7500", + "proto": "udp", + }, + "k3-filetransfer-5103": { + "name": "k3-filetransfer-5103-tcp", + "sport": "any", + "dport": "5103", + "proto": "tcp", + }, + "k3-messages-5100": { + "name": "k3-messages-5100-udp", + "sport": "any", + "dport": "5100", + "proto": "udp", + }, + "k3-messages-5106-5107": { + "name": "k3-messages-5106-5107-udp", + "sport": "any", + "dport": "5106,5107", + "proto": "udp", + }, + "k3-messages-5109": { + "name": "k3-messages-5109-udp", + "sport": "5100", + "dport": "5109", + "proto": "udp", + }, + "k3-messages-5109-tcp": { + "name": "k3-messages-5109-tcp", + "sport": "5100", + "dport": "5109", + "proto": "tcp", + }, + "zabbix-agent-active": { + "name": "zabbix-agent(active)-10051-tcp", + "sport": "any", + "dport": "10051", + "proto": "tcp", + }, + "zabbix-agent": { + "name": "zabbix-agent-10050-tcp", + "sport": "any", + "dport": "10050", + "proto": "tcp", + }, + "http": {"name": "http-80-tcp", "sport": "any", "dport": "80", "proto": "tcp"}, + "TLS": {"name": "TLS", "sport": "any", "dport": "443", "proto": "tcp"}, + "nats-tech-4223": { + "name": "nats-tech-4223-tcp", + "sport": "any", + "dport": "4223", + "proto": "tcp", + }, + "nats-digrub-4222": { + "name": "nats-digrub-4222-tcp", + "sport": "any", + "dport": "4222", + "proto": "tcp", + }, + "nats-tls-4224": { + "name": "nats-tls-4224-tcp", + "sport": "any", + "dport": "4224", + "proto": "tcp", + }, + "ra-tech-1443": { + "name": "ra-tech-442-tcp", + "sport": "any", + "dport": "1443", + "proto": "tcp", + }, + "ra-digrub-443": { + "name": "ra-digrub-443-tcp", + "sport": "any", + "dport": "443", + "proto": "tcp", + }, + "ra-tls-2443": { + "name": "ra-tls-444-tcp", + "sport": "any", + "dport": "2443", + "proto": "tcp", + }, + "drweb-ess-2193-tcp": { + "name": "drweb-ess-2193-tcp", + "sport": "any", + "dport": "2193", + "proto": "tcp", + }, +} + +# service groups +service_groups = { + "sg_dns": {"name": "sg_dns", "items": [services["dns-tcp"], services["dns-udp"]]}, + "sn-in": { + "name": "SecretNet-In", + "items": [ + services["sn-pwd-change-tcp"], + services["sn-pwd-change-udp"], + services["sn-lds-tls"], + services["sn-lds"], + services["sn-kerberos-tcp"], + services["sn-kerberos-udp"], + services["sn-gc-lds-tls"], + services["sn-gc-lds"], + ], + }, + "ad-ds-in": { + "name": "ADDS-In", + "items": [ + services["dns-tcp"], + services["dns-udp"], + services["globalcatalog-tcp"], + services["globalcatalog-udp"], + services["ntp"], + services["netbios-137-udp"], + services["netbios-138-udp"], + services["netbios-139-tcp"], + services["ldaps"], + services["ldap"], + services["krb-password-tcp"], + services["krb-password-udp"], + services["krb-88-udp"], + services["krb-88-tcp"], + services["dc-locator"], + services["smb"], + ], + }, + "ksc-in": { + "name": "KasperskySecurityCenter-In", + "items": [ + services["ksc-klserver-13000-udp"], + services["ksc-klserver-13000-tcp"], + services["ksc-klnagent-14000-tcp"], + services["ksc-distribution-tls"], + services["ksc-distribution"], + ], + }, + "klnagent-in": { + "name": "KasperskyLabsNetworkAgent-In", + "items": [services["klnagent"]], + }, + "cyberbackup-in": { + "name": "Cyberbackup-In", + "items": [ + services["cyberbackup-7780"], + # services['cyberbackup-9862'], + services["cyberbackup-9877"], + # services['cyberbackup-data-9852'], + # services['cyberbackup-data-9876'], + services["smb"], + ], + }, +} + +# rules +rules = [ + { + "name": "Инфраструктурные правила", + "order": 1000, + "type": "span", + "affinity": ['fw_cr'], + }, + { + "name": "ICMP Echo", + "order": 1010, + "description": "Разрешить ICMP", + "src_list": [groups["set_dr"]], + "dst_list": [groups["net_any"]], + "service_list": [services["icmp"]], + "service_group_list": None, + "action": "allow", + "log": "false", + "idp": "false", + "affinity": ["fw_ca_cgw", "fw_core"], + "type": "rule", + }, + { + "name": "ICMP Echo-ext", + "order": 1020, + "description": "Разрешить ICMP", + "src_list": [groups["net_any"]], + "dst_list": [groups["set_dr"]], + "service_list": [services["icmp"]], + "service_group_list": None, + "action": "allow", + "log": "false", + "idp": "false", + "affinity": ["fw_ca_cgw", "fw_core"], + "type": "rule", + }, + { + "name": "to_dns", + "order": 1030, + "description": "Разрешить доступ к DNS", + "src_list": [groups["set_dr"]], + "dst_list": [groups["set_dns"]], + "service_list": [services["ssh"]], + "service_group_list": None, + "action": "allow", + "log": "false", + "idp": "false", + "affinity": ["fw_ca_cgw", "fw_core"], + "type": "rule", + }, + { + "name": "to_syslog", + "order": 1040, + "description": "Разрешить доступ к Syslog", + "src_list": [groups["set_dr"]], + "dst_list": [groups["set_siem"]], + "service_list": [services["syslog-tcp"]], + "service_group_list": None, + "action": "allow", + "log": "false", + "idp": "false", + "affinity": ["fw_ca_cgw", "fw_core"], + "type": "rule", + }, + { + "name": "to_ksc", + "order": 1050, + "description": "Разрешить доступ к Kaspersky Security Center", + "src_list": [groups["set_dr"]], + "dst_list": [groups["set_ksc"]], + "service_list": [services["ssh"]], + "service_group_list": None, + "action": "allow", + "log": "false", + "idp": "false", + "affinity": ["fw_ca_cgw", "fw_core"], + "type": "rule", + }, + { + "name": "to_kaspersky_updates", + "order": 1060, + "description": "Разрешить доступ к папке обновлений Kaspersky", + "src_list": [groups["set_dr"]], + "dst_list": [groups["set_ksc"]], + "service_list": [services["ssh"]], + "service_group_list": None, + "action": "allow", + "log": "false", + "idp": "false", + "affinity": ["fw_ca_cgw", "fw_core"], + "type": "rule", + }, + { + "name": "to_zabbix", + "order": 1070, + "description": "Разрешить доступ к серверам Zabbix", + "src_list": [groups["set_dr"]], + "dst_list": [groups["set_zabbix"]], + "service_list": [services["ssh"]], + "service_group_list": None, + "action": "allow", + "log": "false", + "idp": "false", + "affinity": ["fw_ca_cgw", "fw_core"], + "type": "rule", + }, + { + "name": "Взаимодействие в УЦ", + "order": 1080, + "type": "span", + "affinity": ['fw_cr'], + }, + { + "name": "pki_cluster_tls", + "order": 1090, + "description": "Разрешить обращения PKI-кластер к Центру регистрации УЦ TLS", + "src_list": [ + servers["pki"], + groups["set_dr_pki_cluster"] + ], + "dst_list": [groups["set_uc_reg_tls"]], + "service_list": [services["ssh"]], + "service_group_list": None, + "action": "allow", + "log": "false", + "idp": "false", + "affinity": ["fw_ca_cgw"], + "type": "rule", + }, + { + "name": "pki_cluster_dr", + "order": 1100, + "description": "Разрешить обращения PKI-кластер к Центру регистрации УЦ УНЭП", + "src_list": [groups["set_dr_pki_cluster"]], + "dst_list": [groups["set_uc_reg_dr"]], + "service_list": [services["ssh"]], + "service_group_list": None, + "action": "allow", + "log": "false", + "idp": "false", + "affinity": ["fw_ca_cgw"], + "type": "rule", + }, + { + "name": "crl_request_tls_external", + "order": 1110, + "description": "Разрешить доступ к CRL из сети предприятия", + "src_list": [groups["net_any"]], + "dst_list": [groups["set_uc_reg_tls"]], + "service_list": [services["ssh"]], + "service_group_list": None, + "action": "allow", + "log": "false", + "idp": "false", + "affinity": ["fw_ca_cgw"], + "type": "rule", + }, + { + "name": "crl_request_dr_external", + "order": 1120, + "description": "Разрешить доступ к CRL из сети предприятия", + "src_list": [groups["net_any"]], + "dst_list": [groups["set_uc_reg_dr"]], + "service_list": [services["ssh"]], + "service_group_list": None, + "action": "allow", + "log": "false", + "idp": "false", + "affinity": ["fw_ca_cgw"], + "type": "rule", + }, + { + "name": "rubackup-cmd", + "order": 1130, + "description": "Управление операциями на клиенте резервного копирования", + "src_list": [groups["set_dr"]], + "dst_list": [groups["set_rubackup_servers"]], + "service_list": [services["ssh"]], + "service_group_list": None, + "action": "allow", + "log": "false", + "idp": "false", + "affinity": ["fw_ca_cgw"], + "type": "rule", + }, + { + "name": "rubackup-media", + "order": 1140, + "description": "Передача данных между медиасервером и клиентом", + "src_list": [groups["set_dr"]], + "dst_list": [groups["set_rubackup_servers"]], + "service_list": [services["ssh"]], + "service_group_list": None, + "action": "allow", + "log": "false", + "idp": "false", + "affinity": ["fw_ca_cgw"], + "type": "rule", + }, + { + "name": "rubackup-api", + "order": 1150, + "description": "Управление операциями RuBackup через REST API", + "src_list": [groups["set_dr"]], + "dst_list": [groups["set_rubackup_servers"]], + "service_list": [services["ssh"]], + "service_group_list": None, + "action": "allow", + "log": "false", + "idp": "false", + "affinity": ["fw_ca_cgw"], + "type": "rule", + }, + { + "name": "repo", + "order": 1160, + "description": "Внутренний репозиторий", + "src_list": [groups["set_dr"]], + "dst_list": [groups["set_dr_tech_server"]], + "service_list": [services["ssh"]], + "service_group_list": None, + "action": "allow", + "log": "false", + "idp": "false", + "affinity": ["fw_cr"], + "type": "rule", + }, + { + "name": "CC", + "order": 1170, + "type": "span", + "affinity": ['fw_cr'], + }, + { + "name": "ngate", + "order": 1190, + "type": "span", + "affinity": ['fw_cr'], + }, + +]