From 9133480863a33db14be5eb048831dbb3bde471a3 Mon Sep 17 00:00:00 2001 From: Alexander gritsenko Date: Sun, 19 Apr 2026 19:55:21 +0300 Subject: [PATCH] yeah, baby! --- README.md | 1 - __pycache__/fw_settings.cpython-313.pyc | Bin 0 -> 21798 bytes app/app.py | 664 ++++++++ app/data.json | 2080 +++++++++++++++++++++++ app/static/app.js | 1073 ++++++++++++ app/static/style.css | 239 +++ app/templates/index.html | 548 ++++++ fw_report.py | 330 ++++ fw_report.xlsx | Bin 0 -> 16066 bytes fw_settings.py | 1322 ++++++++++++++ 10 files changed, 6256 insertions(+), 1 deletion(-) delete mode 100644 README.md create mode 100644 __pycache__/fw_settings.cpython-313.pyc create mode 100644 app/app.py create mode 100644 app/data.json create mode 100644 app/static/app.js create mode 100644 app/static/style.css create mode 100644 app/templates/index.html create mode 100644 fw_report.py create mode 100644 fw_report.xlsx create mode 100644 fw_settings.py 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 0000000000000000000000000000000000000000..0affbcb567e6ed1da5858e8ed3edb871a8d7e618 GIT binary patch literal 21798 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', 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 0000000000000000000000000000000000000000..f8a35a25040f78a85e45c8dec01107f1577f2e2a GIT binary patch literal 16066 zcmZ|019W9e(>@;Cwr!l))(r5m$(cG}yeUD1!4c~|w9K-@b0?@uZt0kX6W;o9A-KOf3c?NM)r>P`i zms)N`ioRWS0Cx=~Vc9w!4APN1Ie$2+3|BHO_(!&d~F~J3Wj3#)T7KtQ=v}y-&`UdoqAvIB5H-;UG zgetaq%WF0wfN?}!zdY7d)N@0flk$rZGJOp;XItGQ&+QAs5A_3*d=619WI<&J_8_dt zdyRmElI|)471mLKS0^(@qfQABQ;#?+2e>N+MK4c*Yi+wM8jB06erRLiD$91`Z4FekPHc%b*BZ9H1h0|%7x3QbdQ z2kFEm-#=KpOd07c$SQ82y*jJ0?$_T#c@+ST;I3U-j)x3M18!Q6SR zJ~iN6jxZ2eJ<`(6C^o>mOM)F~ED8QRPPR3j7mV)YWl~Jy=$O;(_JZNtaZpIYjjR3& z>|=POlMEl)iPpa3~p9w6R6)Z7m!|(#2o( zYa6No=9alw5=Q!7x^!lO)5?mPHhM($2ZXGT=wr%}#`(agYC;q(A@w&;mIJS=oBYO8 zC*1ffzSc~*>O|g?HpTmLnjt&Nz5U(aO$AgfhGu3Zef&ne62M!f9`fhVp;+K~xHHu! zojo7=&$A;&R~-RXINN*IuAGfyv*EUQVB3mqS-qKemR$ux(DnJ|(?-h=tCg`uKO>do z=YD%_ou->*z(i`v&sn%^%teI`R!qCXcV5c}_f>`tmQ6dJnp04|Raz9BV3-Mu@144% zpf?-{&Jh8r+d40f&w?iDQa?K-;H==cZ3t;!sb6q~BEJs~fGwKOVkZH_?K5M*5r8=Q zXTB(SRtYtRZTH5_YR4>2Q8y^yK&1`)-b?rq53EQX3VTjHJj51~vrJt+!JNwRni8re zRqISm?Po>UleTR6q{3(MC1}_#DAH)>5PMkQvyzPphiYzLvq8FJ~ znEx9#JC1z=hHs-?X@I^a%HWKv+-yDat429nJiq?%*+i_<-zf>}>E&&**4Atx>hKH1^;;EOpvb)dT%=oje&!1Pw0;U;dA0UWs&XCq>874|^^WQ% zU|B_)>}7s6CUiDXa7y3RjN8f)jKT4k^2Xsey5!)qy*%hR?~@iXDJ6?E;|OQSuDzvNBA9}2n2BfiKDN}1{t z33l_)OEFCxQ~;n^S(cO0i8tvtFJ052Z5!#+=Ro>bDT3U5RnceG8U&?{$@BaYA$Dwg zl&r0{HxswFR%F~@T}ADJZAJ2xtJgdQ)wFDM)3~Nb1%FCK>!!N%J6}Dz+|;{9#c(zp zvK$61ilkuzW|4H>Ew)nU5v^3e3TKxXYa>cBR{P%Jke!@TGm3K~A_z-M8&|2jF|%pW z%@#czG@Vw62Fk9i&iTSodPb!?iSu)^OOO7|o9&1M;sMW*-kpv}<&TGry=Uuq9?35m zZUe}`j$$CBiFgngm@NAW7jZEVsK65mBcfLzenv%jJ}J+z9XD_-)aa8H{9N8*bpAnd zy0hdcXkpcG;KZ2rdzY8--(4b>p6Pt80+d;d7BELeLVNgF?M0TA$!{xHDPRNFc9kdE za5^dUW%}H$GwCTm)^z9j>M{AmZ}U^P3F?9!(%qYT#mYa_7YUwNwzzE9xHmy{4^inv z3V!r>g@ySbmwoGOt&)Et&)})|xb`JVe_dQFc-Yv6^PDf!EHt7;7LbATB#eVFjL^CR z=tZM_t0=0e7+htBbMsNZh08VJ+zJ*;!(m+rFXxJcu@PDjJP=r$hc|nG2jInm>oTV{ zvghy1!SJrXU&2@S_DAiUhj#)0JAhc}4GASfeI^F1KtNdk1P~V+dkY6MV`E1L#=rmm zjv@o;8`f*X=tGyVclZ#k-+fni6}Eh>h+^@Fj2oHGtYGiNb4p{sq>!}iQ#JmkAzWiD z@bMK>M5Agnuj$@Iw?%Dxps0C+=~Fw7cQlctW#P)m9fZ9bOe(n*$Pjs-49O5^u*VXG zyyh3i!jt6{(5*N(#N#hnGBhQWJ#*!Pij^0@2A}C4yt4vS4jBZT5o5}>x0+*}KleMo zt**jsHR4xPfGM4|@UKP*@@9THJAOpI@#|Z_Dc`VOwi|j$u_>vl2zbCcCwU!O;M)s5fST*7`?29`U#Cmj<5xm$C+Hc(|G2EO%E*PEWsD(#2iq z%(#PR=iK*-*?U0ciWvCQ;kiDNl5Z+Xtw%;K5 zA)5Qay7BvWdr}iQ>GD^jM_j(dOR(1LF(<9lHIP_xOg_9w?X7_!mqe%0J2*>L@HN0# z5Bn4Uxcy*dToso7S+#p{p(Pf?_Gd zkO|;O71M{E_!m~*Yf4!6g_lwTWF)ok;+0n!Vpj<|z9BpHzP3<}iZ^1CDv}&tPny7TI&OkTd zs#F<2yF8y?Q!0_4H6FLrgcM277Fhve5s4Q$0A&Wa0W=l>cJ-O=43UE>#F&jR0k5aK z=qSpwesb!=MZ#E@jH!nx&cet^^n7$$?&`lj`{WC2u%c?X!XUUb zZlDq2BhDIatQBbNiRaOyE^6}9X>^fPxEWK-5Uf-Yt^+`xDOHd@u{@nwSq`W1_sb{I8l__-yd~Mcyo2A?dOM&$yuy%s5aX%w7%gppszJ zokd1>TbSziJk@WQ9A|qfOwyd0k^FU~+}Fa6rXzyT$McQkt$ne^D96bqc7DIDgmVj# z&MJG&h1S_;c63eZYCb-F5BKhW1_^jfHC%;OpH93H?18N%2TeOJ+7XARtxXteSWB>z zd}~Bp!PpKPLo+Yk0!Ii6;QmxOk~g|a!D|Z{B?0N12mBBa@10~2dy%Wet4H)|$lXIp zslT?WnK$roA&zF__s_!?I4ReW!E|Ndso82AN z0_QO{&6JexRQEJT_(65v)1!UTdtmNnon-V2js(EKWu5*LY_@W|AGYZLp97m)|qKRk9eA{dULEvgc)o$KFbEO&YlOV_~f3h=c57lINN5pYZ%u+W_U;~*2^g_ z!t455wlt%}*1YKFS5MxQI;=e42RJNP7+w_biyxM{tx(Q7gdItA)+}QY)u5#`ZVst* zu#HDAgZeVYh@RrTOBRUqotV4%eO#_rU#h^~hk0^!DtNP1u%mim=x&U#L0kw>Eg6MS;UhyqkyZ}F3}$--7; z|J6Zi`W-{EjvX209YHXPrz zo)z?mUr;5zsICD}$4+Gn|6G( z&wZaK?X;C}x6@l5Os}-h))1dx+5__ZUGzU9`l#7P<#c0>@}z%?$P}Hk&X)#k9bAcI z6Bw%OCcc%*zsm!1cGBud1IdHimhB(BrX`%_^j7dm1=t$W^-Ft4nFpl&Lj_Q)PRi-# z8s*9Tq_R!US=S9ypzRHL<|Z9I=~4CxrAwf(W?HOd zijS6GXeQ;Ok{Cjc>7MyCprvAkk#!tW_1WL|6>{2D-*~DLlmFLDw0m##7sCgc=;vbK zf-bbD@u&CPG;IG?Yp#LfPi}C|IcKG<*y}iMTV9Q?hyJ=*QlR714!69tP?aDa8hQ0IG$k0 zD#USkHHjKZ&sc36t;}fr#@)?yp3^ECIvi=EMfb6m3Z>lq@op8w4np`|M`8jok*DMhPV9hG z&y+K^Sw)nvfabP8>p`w+p7#FBy0GOi=NAktKwLl?&^u z`=L{99xoc#qJ~~hXcuByiVtJ4aKpioC(hg(LVz!A*V9sY8x5;gzx(9e2 zJMY)PK)mT1b4d$K_598e+7%IGoUbSl$CzYe>bSRXp!&1oF#DEcL94PfI-!gH?e~bs zM{`N#>lKdy&PM2qo+Ho7yQWpkp<~|0RZg}S0pUCEPnty<_T{J-^Xo1L_0DCB#f%3b znl1UnBzVNuz8h0l0Chu>tt@nvv?~Wncu;^I`Rhu5Eisa^Z+B~`YNDA2B}Q5Qt|bYR z*Hs}+jWP?Zg*H+vuF1~xcN9Wysq34J0D+D1gvi&{hFdi>J)fJkM%7b<+s^7l@|!B1 zmL5x;jF9kaJvH1^OET}GnM!+H(F?uZFYVy=E?4Uv6Rn)+ThWAJ9D5RiuM9J1btF$b zE06N}@1!IjHg^2);g86YKfaK4A*<~Qw<9MDKA@7kRJhVk4o2@&b|qZCPTbQ2$i}D@ zY(^(&*;|a3jd*-h5>t&uOa$auL$I>!liR^vjC(8?T#*=g&%lvL*C2GDP<4~9daC2; z$RLr(c&T+mkD^>Dbi>ppAtxCxY={$HsmZ&R55q#g!YI14=Eh9==GLSty#~H4^3=0Z8<9+^AV;Xl2I7UtDnx4L3+aAgkbmAmvBFCvRIH zgYhus`watTM-ygY!rCyG`jDGXHFuoIN!Ux%Er`VW4U`|O{K~Ij49c(e@7)GbujzZo ztbQiip}s*#e#^wSHC{Kmrz0ssN5p#x;e;FSltsc)B#yvB?(iLtooYwZ3%6l%PFt`o z-CUIG*;EBTy0BK3qdg!Ai5|v%Txo&yO>CY$Xrrgcm1p_U)3kt6q^x&JbJ-H!+ zL{3MWWra%?T^F2cmBk{^b3&TYa9!>lSE=nASUQMHHRv|NL8K{0Mkwa%l!$U%x>iqkm9GX=-!r+T0K}T010y*KE6C$k4sUKk!5S#h0Nf?pkfs@)zCC_4y~A{6J#CLn0^P z#|m=sUhv*lI4hTX)&bGM)ULwnS?t&;U80~Jv<#6(+B5M~9ujj!xeU>lD2dM=i;r+n ziz?suxtRWR3=ik=?K7j)6Y{y4HKxI&DNc07?jMa~aHxmzKd`_garcrY^$z)19MF0i zPvuM&^rk|@3i&u-#kZWlPl&&U+MB-gK~;_}HVRoTs4dzP5M8&uT5FYkyyDOsL7F)Q z$oDTfEwwnkyz7{Xgtylu2FCsVucUz+gy3QlGPX17l<*~+Io*Fw^frZ-jtN#!h%cuSc zIjeGSAWR-AdZ!5p|TT?8V!;t&}QzTA06X_OYzMFwjHkl_pv$rTmm z+H2aW3Fgqruu7CMeeEnH&f|#1D6VWzB=Sx`y1msTNMfBeJysN!=fXowhSR&i^A&V^ z4+=R6wDRNS0x^ve7fSQ=6%qACa3_}1{Z7U>;Icd4a(|jh$~DBrEpEy+dhVLAhCRP_ z&E)^HVTTzUF)=;k%SoDCy)kswUz)!GM?^-CKA@A>^&;0jEOy`@>5(qyn&o)1k@Klh zwjRn~pUlZ>^Kk{ViB#I&fFUBMCoH%w0wk`Mo1pXeVF^eyzpE6JJP%0VPSRtZhmP*L z24s{R2WA38MN`{<+H686G@;D?_zVqP45NJr70`4a(6jXNV?cBX5RTmAo!e$mh~i^N z^NzObDf>?R&eLaG4q{g6x}$m6B{@3OAFmUIou2_MNU;$gAzH{j06cc)6@@c~0NIKG z?L*LB8I8;o3VrEYqV1z}P{?Lh#ikRAbo*m7!LSrwebd}NFcfGL4i@a1g`GYg9=IWt zIp8raFyzodip!wA!~|;qo$Mfxj1UR))lBVmTRI1X+(lhnJz%oiY$Y|OJ0RQzIfl3p z*dv(M3##XalW;yvYDeS8*S3t?Y>fE+C6w*#eQxX5V+cjK{8wcbPs z@oBjrfGijn$RN_}<25=ga^|@$1`p-ISI=GP927-FG#dU~Dkjk+=RP*(Z(Qg~()s;O z6rzqvC_f+?s@6lItF|Zuy8R*et?`)zojZG*=XN9=w9lNTtNcJ~NNqL39t)6@(PF&|%Y!4k}~K}d{E0@mLsGxnRb zIn|S&e@LfZ!e}s9Jz`4~3X2ymuvP?;l}bc<2y{GD30*GSmD8PjC@o(fni6KMomYHmCz1Xu9uuYeMm zQ9FL8-o54N3+$w=wTn8Yl~V+zV#4!L)pS^yinREICOBF`qwA99SQR&smKy{^#pTer zSMZz5HoFX2!u=+S^vy<_w~v2kq)+#9NKlYKK<*6xXGY5UcSf2fYqQ3V7kml+j1RYk zuT>=%#@W4Uuy(vFFXvhM!C$B z3xmznZWWE@riLdqV2@;SbZ5c1?wq`Aw+mG9fg9T@H$DkMZs@el- zcHBX=WT$qcs?^s`Rq**=j|%c>=;de{DhQlf<3W0yQ(3`3lnS`^gCnd4W^M$VOmtN& zI&NgSapi(qZ{pysb(h>0v*cCpn=Fa-s(IDeTKj}DhR@PEl!GUNZlyN3&!CZuHW^&szwXSos5(ZBV!1?+76@_o9R|`_n=ExF&@cC{^TY9;_<4?W=E{9^SuC z^?x62thMPm6FL)KZ0bZ6a_4XI$+b#nY0*3x{{DSYwc&C9GPN<~avAb#5aw0zvhhxC zTK=+;@`A@<#0V{!gJQpNzrJ{Tzx)YX9*TF0pmJ%qh0nCqjhr+Wi$e;nI1sw7VkfWI zF9kEdfTr;GomAa0wDwk3k$kb6-&lA^opFLaYJ+@`g&6!?H;+;4RkISLigdap^VJl(F*$8@H&EKZn+t<45;aA7I(e+ zXpz2hRb8=s>wz%^2}SK=wSmqCVq@&>I(g~xrU@WZKjOy0br z3b@Qx<66nUV6oqZx z8Aui(*mpP@062?<@MS)zGay>7z$>jGmbjaa)<%VqW$JTW3G^&Q{_ACQD|5)&tKFwC z7f?R?+F$Hpl8&M3_t367Ntbe@W%*DeJdo^OkYGv zOu7Vt!!0Y{)Quq81Qyn&Oa?H%dkO-&v#NCCzE|ecEgUePKGd;+ zuyVhJbNs$aJ3EM0I~2%^&xNOgi|_`E@d55$8H%P06a6+1;cceTFJWS=F7_?Lr}|5A zG%_MC!kBewtn7}b`UOcAL1X@$+6Ku8qa@@k$($)Hw?N3pvV_58WPU69=h9Zh=21xP z0ghWv4Vzz;pg4?g{1vpFLA_vK4nT`guvuNiSLKrc#Sb(7i0bBs7m-QV8ZTLwS3h&T zxV&mBQ|=mH-k#AdzOX@EB_cllG(6Eg!NHX{9eq#3yGlTG{V7v;`BjWZT8W>=?K#Zz z8{q_`Pv%{4%euI6oac=(5z zc()O-N$vXxzpwE)IhCNGJ}?E-_r(O`7%{n25OG!FLxz24<^ue|rMqMvu+R2(<(e!1 zRD?+Pl~Pe%fKPz1f^fuG=M+zYnCQbW5|(ygN}&7U;0!|Ej%OQ*NVslNyY-=}LS~F= z1WiTu?5Xz)H1|M7sJka%L~Uy1o#Ak-9L+z2UU?wCuJk*E&C?pX#zHH#zcYzrUGq1) z(Pn;~CEH+%Ks_&3a95dZOaE+u6diZ8KvaX3@^2@~W^6Hc)*y2R>4%ldUAtiv|-8wKS_ z<&`wG=Y3{$8hWLtM> zR>Sebb7#!aYptH~OC{r&EB)5!VO1eYNpZYLhH-1GTPDU+NnBak^<~GDomVi(T$jypZ-sx~t%E!eU;G`+4U zCfYvs5Ti??(eS=LdIWwb??4Oo{D%2aBjFiJ%abAtMh{bMZ$c&bTW?v z_}tf}3va}&NP_p8my?~-jV>2~vg{xA0ja14KNNm#5p=G+HNI3m{|+bbrKRzk^?tjW zkVF)MbboaIeR^SvKFOZ3h=*Nv6 zahX`gx|IxuSY2u4OEpxvlr-)G!zf(HH_VP+F}wE}aWu?KQ#X((!tQJKak1!nMw46{ zP!2i}u*&dV+2B}GWgu>0r>tXW9y@(X_vG;bx-iSLATgh1=;XXUF(58&$E*YB=4~K` zV;vS5r%ja>$i-0yC{SX!jL~QXU?k`Tw?;~|KG1<`m+P~+XklEjU?!x2=|UtsE08EtQP_NM z-KW+>JuO%7Pvs8TEms#YW}|}m-G{Ag_#y}z9-#pI3YADeA$~P%>S{KSVSZ~f$hBD-5!KN2 z)jzoD!@Ra;36I{8rG>scBjmjUUF~K=buls#E7w$2zfZKdIl}nrDyr*U-LSY-cefi= zKah(HTo_yhtMY4pe+YKf_Kwawzxna}qD*Z<5Rb`wV+ zYnR)Pkd|PV6C@r=5y6sGyKiR^lvh`Sos~8f&64Q1LcdRtJGdP6Td{$q4-FsYSmnC= zc%I$omSMw_m<50oxio-kJR5d{4k)HU#%2-+O(opVj6Bhx^U zRFYuhux_Kic}~_E!xOZ74bUk(EP=vKTUGkR37tV9H7ls;Oa(I4rJ+E|XC9@o(mZm#=6O;tXn;Vo-?%5i_H%;?UKlftp{Dx9;;Ub8z9F##* zsCC-?T8)Cy0(V}&s1Z|%6xj9`$YEp}GiIj0tfm<|o7gs5zc#4+ZK95JJTQGI#|VjQ z)X?_qmk$$x2$D=RLa4}=JI7QM6Q-OJP5=yu1Sfoy7cA5s0PF&()>vDU5%O-PCdMDO zATZtc6HEWsgLk-FXMk;9omN#f>m!KaQ*GO-dd>$Sv;&(kdz(RgU2a{RdM|1B(^)KF-=4BHJqm~e z&Sk+b(N1Rza{=X<1RDiJ0~(DJ8}U#a;=Q!eqZ&VzsGRZ07UJi=KX^ANGWa5Iv~K5v(VN6oQn=@czVKBfn}S@ zeM2NzM-x8s8VY*p3gDen;g-sDJ3ZowM1w>^ErN~ng1o!AQ|4MDDNsGx>D)G^bt4PHKNr5O+>B*9K>5gc|6uqNw=A7F;29e%&TAaaGKa7?2iRz~92UWh`? zH$9UI;eqRrEl9+nH@sT-IX#j3Ia4tXz9=Y5c<}8&>@n)KH^kX1PKY#FWvoBg^{PDn zteJL=LUMt3Tc0nF9ic~kpF6C zOe{x0H9@MZ<2!Axq~?e$DKUVl+r`eEf#>=)>XWK|*(q)tUJix&4MKB1WC-8QE{N#o z(Dpk$PUv{pqf60oP+ZjYql#7>|2sceWwIvJ6=!6ICupu;%FfSQJo!}YHKZN*kJ{E` z*dlGz+YZ?cPBC{`3R%->G)G!GM5P;Q!AA zo$ap#JyT6KdQB9oQ)1M++SAYf^paDHJhoATme+_rS>c5#nMJ2iU;&fDP6en&Q1&V!ezorO*;yvJ7Ny%2G3)D0&FHJHm}rkJVk~aTAY;Ua2AUJ$!dyzVnW764jg-TMylJ zZ1RUzUP?ktgR5JHywwy&r-kLYby}^$4ySq6HhA`4Em*LxRc-|4)D_!wk+U=>Wg=Fv zDAz6!J(ZiU1v*+X)^Ln$eobPsznbD)(P^B9&5*-zVUF{ucU=FEK;Yu^oib{3n^Mb8817|?+ZZuxB zUfe%!--|=Xh$Fh!&-9;;3C8r+ydS#M&$D0F-lEUf9?stjdq!RfD7%`x&u!KOR@GiE ze=}A@ud2yEOAc&-E3k{j#JwPGR0Keu)_$p91&ZtP9qcunY z86QI8J9BX6r+E9e+%6mOcX52Lo5-{b{6+4ZM}#AmMuCEl@MXRYrz1a!%fS^|*gfmW zfuuZCDaP*#+$jnpo+BROXilEl?j4!yox64rf{YW+T;)chc>eA@ze(4wGB+j3k`cD!=) zUw{zi$B3gSJB`r9yqfwfzFN39y0j-I$E6mm(Od`U;V&iQU+2_~Mt;tR=Bdjwgn6uT zt6F9DZ|LCvw&t-&a=LR|?^^Q;Qun9Uy3}7=oA$drY(7kJu77%yDwhd5VYS^3FSI;H zZGps2aqr7uw%`O=T)!qIW#wkR^|R9~z_o*mPoG)FJxMPFg7@@sIs!RMZJya-U0}7# z->leCPIr3{f8&`of5^&7=%JLsvtumexqbE0XZT^6!Q=a27E48+M?fF9`99)&RU@U? zxCy|wCwrUV6>!kU*zQA>jH}s%2f;&_G{@W{n=u1G#!Oz+G6qRpa2`=w@l`@Rw)VP&cqyWkPzRRs7Kn0xdfNCTRsTTVVBb{>QSK2wwGmf2tw( zaaHyd0vQ^EF?dalDEdR@R@>3-fE_p0LfTw0c;TpqF)w5Ag>dO^-BDs4_=Mz>6dGVD zOL;hgY9rC1ijIt23OXmk<3};7JGMa!56=FWWnAfOYN&B#koAyL`wS7fn^mh_g-|p( zwv#RtVJq{FB~2>+7nZ@L#S7fAFSf+^g$vWUYiva~3kb)mIx(7d#&npo>U_9G^vm^m z=KZm^rHdP>DW?k6@n&uDn~&_zMCSRCUU~~ZbTQo(kK)SP-XlGD^UyNLksJv~PwsJ+Q8fsA?QCYbI8K)dt7ZkSY+wWyXF zHqA4JuE_4}tO1-x%Zdt1xsghxOh?k&dxk<_A4ulkzD^Nyyk*Pe9_JTOYo)%^RG{nM zZKQ45%v$hJ!*q7;S!S8e%Y7V&ag+Ex#^lYKMWliQRuC%4?KQPeXGzP_!>#LNsp|LKcW@!z; z@C&;O{K6IHM+7%>H+XG;&79W96d(|QqymKnzz}_z8*f4rlv!z4!ab&GN`mJ;NJ0@S zUXOFLCFTPAf<_GPGYA6LN@XVI3q^*Jfa3=PqHd~8{G8I%oCoD36ic#z8>F5m+#Gu^ zOqfR$LedW|Cn}DV!{Dl~rCkDtPTjFYhjVTKc|LNV6+QrZu~T)tc_5(3*CdX%5qA8Xiu_Yk?< zP4u~O@R5~8Uxf(BamI64E3_PB>epUTWvc+HwV>9|vddGaVUrQ0&jrLx-!CK49o~`; z)Av~~Hhpn;mAZq8J#BTUH+F$Ry9a)(>`Nn+7SUX>^I?_wm{^~JnNQ2CDnENIX1gjH zzr81l$&(PXrMrtQ_!91%P{p6M(OGa?X!xz(b?f-!cDe+O@2M-3sW5bQNer68ruqr| z-+~Si=?mq3)|gp*x*NoQT+Lsn;lJ6~e4gI1vW$$IkXvU$8qy=b0R@cJhWCfrQs;>Y zDq zz?)51h-QhiLVYV9Fb~57SgAfj7{(in!x3Sx%g4TVVAW2MKlX!u&}qt=EqdpZ=?9)Q z%7^Jd!HZ#V>x#&RcJby5rM(nE-#x8eKEZg2o_4b?^9M4=MQ+quE*+(!U?@x&>F%0( zPRFo0_1?_5J|~Wi<=m^OaC_l;4flMzEB3iPqss@#ztssMEVe%UsYa2{6cy(mb^c?z z|68eloAE@#7=j-Yl9+el2_)z6SU(gkhb6qg^!8QAG&D;@a;Q0KVUr zMa8fWVLbVz=X9Y*(-xyfW0uiC$_<=UtJvZ0?WK{pXwfrl`YKLJ@WNPN)XnliG z6g%nOg#k_*Dz5AcUV;;P-B(N(;`AgkiZn|^rYKo&EEFkGZ`NcoirTCvVW+wi5VV9>ZoJg!yg%2>VS;E$)`856WhNG>~04kWp*N< zQ0ieP<`Z&sl}idntjszvu5 zUF7aI7gv>+2n`kuN$BcIHDt9qVkrNYIZC9IKpsuF~OYCtCup2B73q{BP!M zs8gayl9_J%L?i3@dQeuo;AmawN6=ku3NEQR$Boy9?`gN)-leev$`RV~1H%)_5$o^8 zu^dOgy57TA`Pja$^!OaVzb<^9H3bGi1Ny(~YCjF^?~l*ti2whqZ2v_6SfFGN3)hFw*jzgqtfzlWXc literal 0 HcmV?d00001 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'], + }, + +]