Телеком-инвентаризация - переход от Excel к PostgreSQL, FastAPI и ZTP




В жизни любого сетевого инженера наступает момент, когда количество обслуживаемых маршрутизаторов, стыков трансмиссии и протокольных параметров перешагивает критическую отметку. Обычно этот рубеж встречают во всеоружии: десятком таблиц Excel с говорящими названиями вроде Nodes_Final_v3_исправлено_ЮГ(копия).xlsx. Какое-то время эта система даже кажется рабочей, пока масштабы сети и требования к защите данных не заставляют взглянуть правде в глаза.

Реальность такова, что когда база сетевого оборудования разрастается до тысячи узлов, хранить её в Excel становится не просто неудобно, а откровенно небезопасно. Десятки разрозненных файлов, разбросанных по разным макрорегионам, пересылаемые в чатах таблицы и общие сетевые папки — прямой путь к утечке конфиденциальной топологии всей сети. Оставлять данные критической инфраструктуры в открытом доступе или доверять их неконтролируемым локальным копиям на десятках рабочих станций больше не представлялось возможным.

Excel — отличный инструмент для быстрых расчётов, но когда речь заходит о корпоративном телекоме, всегда можно сделать лучше. Возникла острая необходимость собрать единую точку правды и переехать на полноценную реляционную базу данных. Теперь она крутится на выделенном локальном сервере прямо у меня под столом: с жестким разграничением доступа, изоляцией сетевого контура и постоянным аудитом действий. И только сейчас, когда критические данные закрыты от посторонних глаз и защищены строгими декларативными правилами, появилось настоящее ощущение безопасности :)

Помимо закрытия вопросов безопасности, задача стояла сугубо практическая: создать централизованный инструмент технического учёта оборудования с инлайн-редактированием, валидацией адресного пространства, автоматической привязкой сетевых профилей и мгновенной генерацией боевых конфигураций.

Архитектура решения

Система построена по классической трехзвенной схеме с акцентом на производительность и минимальные задержки:

  • Фронтенд & UI: SPA на базе Tabulator 6.2. Позволяет отображать и фильтровать 47 параметров оборудования в реальном времени, поддерживает inline-редактирование ячеек, автоподстановку фамилий инженеров, контекстное меню для быстрых действий (генерация .cfg, очистка, вызов CLI) и переключение темы (☀️ / 🌙).
  • Транспорт & Интеграции: Прямые переходы по протоколу ssh:// сразу в сессии SecureCRT/PuTTY, интеграция с внешними скриптами автоматизации через REST API.
  • Веб-сервер: Nginx в роли реверс-прокси и SSL-терминатора, отдающий статику (CSS, JS вендоров, SVG Favicon) и пересылающий запросы в локальный Unix-сокет приложения.
  • Бэкенд: FastAPI + Uvicorn. Асинхронное ядро с пулом соединений через SQLAlchemy к СУБД.
  • Хранилище данных: PostgreSQL со строгой схемой данных, внешними ключами и таблицами аудита.

Логическая схема взаимодействия

+----------------------------------------------------------------------------------------------------------------------+
|                                            ВХОДНЫЕ КАНАЛЫ И ПОЛЬЗОВАТЕЛИ                                             |
+----------------------------------------------------------------------------------------------------------------------+
         |                                                 |                                                  |
 [ Инженеры / Web UI / exe для Windows ]         [ Внешние интеграции / CLI ]                        [ SecureCRT / SSH ]
 (Браузер, Рабочие места, Приложение)             (Python / cURL / REST API)                      (Прямой переход ssh://)
         |                                                 |                                                  |
         +-------------------------------------------------+--------------------------------------------------+
                                                           | (HTTP / HTTPS / 443)
                                                           v
+----------------------------------------------------------------------------------------------------------------------+
|                                            ВЕБ-СЕРВЕР И ПРОКСИ (Nginx)                                               |
|  • SSL Termination + HTTP Reverse Proxy                                                                              |
|  • Проксирование запросов на ASGI-сервер Uvicorn через сокет (/var/www/html/app/environment_inventory.sock)          |
|  • Статическая отдача CSS, SVG Favicon и JS вендоров                                                                 |
+----------------------------------------------------------------------------------------------------------------------+
                                                           |
                                                           v
+----------------------------------------------------------------------------------------------------------------------+
|                                         ЯДРО ПРИЛОЖЕНИЯ (FastAPI / Uvicorn / Python)                                 |
|                                                                                                                      |
|  +---------------------------------------+  +---------------------------------------------------------------------+  |
|  |     ФРОНТЕНД & UI (Tabulator 6.2)     |  |                         API & BACKEND ENDPOINTS                     |  |
|  |---------------------------------------|  |---------------------------------------------------------------------|  |
|  | • 47 параметров оборудования          |  | • /                   - Главный SPA-интерфейс (index.html)          |  |
|  | • Inline-редактирование ячеек         |  | • /api/v1/me          - Авторизация и профиль пользователя          |  |
|  | • Автоподстановка Фамилий инженеров   |  | • /api/v1/nodes       - CRUD узлов, фильтрация по макрорегионам     |  |
|  | • Контекстное меню (CLI, .cfg, Clear) |  | • /api/v1/config/...  - Генерация / скачивание .cfg конфигов узла   |  |
|  | • Фильтры 5 округов (Юг, Поволжье..)  |  | • /api/v1/import/...  - Парсинг и импорт Excel данных               |  |
|  | • Переключение Темы (☀️ / 🌙)        |  | • /api/v1/export/...  - Выгрузка таблицы в .xlsx файл               |  |
|  +---------------------------------------+  +---------------------------------------------------------------------+  |
|                                                                                                                      |
|  ------------------------------------------------------------------------------------------------------------------  |
|                                         МОДУЛЬ БИЗНЕС-ЛОГИКИ И БЕЗОПАСНОСТИ                                          |
|                                                                                                                      |
|  • Аутентификация и RBAC: HTTP Basic Auth (Bcrypt), роли (admin / engineer / viewer с ограниченным списком           | 
|     редактируемых колонок VIEWER_ALLOWED_FIELDS: СМР, ПНР, Миграция, BMC) в связке с мультирегиональным доступом     | 
|	 (allowed_macro)                                                                                                 | 
|  • Контроль уникальности: декларативная валидация уникальности Hostname_env и Loopback IP в базе данных              |          
|  • Авторасчет статусов узла: В плане -> СМР -> ПНР -> МИГР (на основе заполнения цепочки дат)                        |
|  • L1/L3 авто-связывание: автоматическое заполнение L1 портов на основе данных трансмиссии                           |
|  • Региональные профили: автоматическая привязка параметров (BGP AS, ISIS Area, MTU, Lo pairs, RR1..RR4, TimeZone)   |
|  • Сквозной аудит: перехват всех изменений и автоматическая запись в таблицу audit_logs                              |
|  ------------------------------------------------------------------------------------------------------------------  |
                                                           |
                                                           | (SQLAlchemy / asyncpg Connection Pool)
                                                           v
+----------------------------------------------------------------------------------------------------------------------+
|                                                БАЗА ДАННЫХ (PostgreSQL 17)                                           |
|                                                                                                                      |
|  [ 1. Таблица узлов: environment_nodes (47 полей) ]                                                                  |
|  • Идентификация: seq_num, hostname_env, site_name, address, model_env, loopback_ip, ptp_address, node_status        |
|  • Трансмиссия & L1/L3: cross_environment_*, l1_*, l3_*, rrl_*                                                       |
|  • Интеграция & Сеть: integration_*, isis_area, bgp_as, lo_pairs, bgp_rr1..rr4                                       |
|  • СМР / ПНР: cmr_date, pnr_date, init_version, executor, cmr_pnr_note                                               |
|  • Миграция & BMC: migration_date, migrator, migration_note, bmc_version, bmc_update_date, bmc_engineer              |
|  • Региональные параметры: region, macro, power_type, timezone                                                       |
|                                                                                                                      |
|  [ 2. Справочник регионов: network_region_profiles ]   [ 3. Справочник пользователей: users ]                        |
|  • Шаблоны BGP/ISIS/MTU/RR по регионам                • id, username, password_hash, full_name, role,                |
|                                                         allowed_macro (text[]), is_active, created_at                |
|                                                                                                                      |
|  [ 4. Таблица аудита: audit_logs ]                                                                                   |
|  • id, node_id, field_name, old_val, new_val, author, created_at                                                     |
+----------------------------------------------------------------------------------------------------------------------+

Ключевые преимущества перед таблицей Excel

  • Персональная ответственность и гибкий RBAC: Каждый инженер работает под собственной учётной записью с хешированием паролей через bcrypt. Реализована гранулярная матрица прав (admin, engineer, viewer) с возможностью привязки пользователя сразу к нескольким макрорегионам (мультирегиональный доступ) для фильтрации зоны видимости узлов.
  • Исключение адресных коллизий: База данных на уровне ограничений целостности (UNIQUE) блокирует дублирование Hostname environment и Loopback IP.
  • Нестираемый аудит каждого действия (Audit Trail): Автоматическая фиксация каждого изменения поля в таблице audit_logs с записью автора, даты, старого и нового значений.
  • Автоматизация бизнес-логики и автоподстановки:
    • Автоматический расчёт жизненного цикла узла (В планеСМРПНРМИГР) по цепочке заполнения дат.
    • Автоподстановка фамилии авторизованного инженера при установке дат СМР, ПНР, Миграции и BMC.
    • Автоматическая привязка L1-портов на основе данных трансмиссии подключения.
  • Динамические региональные профили: Автоподстановка параметров маршрутизации (BGP AS, ISIS Area, BGP RR1..RR4, Lo pairs, MTU, TimeZone) из справочников без необходимости ручного ввода.
  • Прямой мост к автоконфигурации (ZTP): Мгновенная генерация и скачивание готовых файлов конфигурации .cfg (с разделением шаблонов под разные модели оборудования), а также предпросмотр CLI-команд прямо из интерфейса.
  • Интеграция с рабочими местами инженеров: Запуск SSH-сессий в терминальном клиенте (SecureCRT / PuTTY) в один клик по Loopback IP через URI-схему ssh://.
  • Двусторонний обмен с Excel: Возможность гибкой пакетной загрузки оборудования через импорт и выгрузки актуальных отчётов в .xlsx с фильтрацией по макрорегионам.
  • Защита периметра и изоляция клиентов: Защита API и веб-интерфейса сервисом Fail2ban (отслеживание автоматических сканеров и бан IP-адресов при повторных 401 Unauthorized ошибках подбора паролей) и выделенный порт 57218 для безопасного подключения десктопного тонкого клиента.

Архитектурный доклад: Принципы функционирования платформы


Уровень 1. Входные каналы и пользователи

Уровень обеспечивает взаимодействие различных типов клиентов с системой через независимые сценарии:

  • Инженеры (Web UI / SPA): Пользователи могут работать через современный веб-интерфейс в браузере. Интерфейс построен по принципу Single Page Application (SPA), позволяя выполнять инлайн-редактирование ячеек таблицы на лету без перезагрузки всей страницы.
  • Десктопный тонкий клиент (Windows GUI / pywebview): Автономное приложение в виде единого исполняемого файла exe, работающее через выделенный сетевой порт 57218 без необходимости открывать браузер общего назначения.
  • Внешние интеграции и CLI (REST API): Сторонние Python-скрипты, модули автоматизации и сетевые утилиты обращаются напрямую к JSON API для пакетного импорта, экспорта данных и генерации конфигурационных файлов через вызовы cURL.
  • Терминальные клиенты (SecureCRT / PuTTY): В ячейках с Loopback IP реализована интеграция с операционной системой клиента через URI-схему ssh://. Клик по IP-адресу инициирует открытие преднастроенного SSH-клиента на рабочей станции инженера без необходимости ручного копирования адреса.

Уровень 2. Веб-сервер, обратный прокси и безопасность (Nginx + Fail2ban + Iptables)

Выступает первой точкой входа сетевого трафика, обеспечивая производительность, проксирование и многоуровневую защиту периметра хоста:

  • Межсетевой экран ядра (Iptables): Базовый рубеж фильтрации. Отсекает паразитный трафик до уровня сокетов приложений, пропуская соединения строго на целевые порты (SSH, 443 для веб-интерфейса и 57218 для выделенного клиента), а также динамически применяет правила блокировки атакующих хостов от Fail2ban.
  • SSL Termination & Маршрутизация: Nginx расшифровывает защищённые HTTPS-сессии (порт 443), снимая нагрузку с ядра приложения, и проксирует HTTP-запросы на внутренний ASGI-сервер Uvicorn через высокоскоростной сокет /var/www/html/app/environment_inventory.sock.
  • Выделенный шлюз для десктопного клиента: Отдельный server-блок Nginx на порту 57218 осуществляет изолированное проксирование трафика от приложения с корректной передачей заголовков авторизации (Authorization, WWW-Authenticate).
  • Двухуровневая активная защита (Fail2ban):
    • nginx-auth-fail — непрерывно мониторит access.log, отслеживая повторные коды 401 Unauthorized и блокируя попытки подбора паролей к API и интерфейсу.
    • nginx-custom-bad — выявляет автоматизированные сканеры, вредоносные ботнеты, попытки обращения к скрытым путям (.env, wp-login, phpmyadmin) и некорректные HTTP-запросы (коды 400, 403, 404, 444), отправляя нарушителей в бан еще до того, как они создадут паразитную нагрузку.
  • Статический контент: Отдача вспомогательных файлов (CSS-стили, JS-библиотеки, SVG-иконки) осуществляется напрямую через Nginx в обход Python, что гарантирует мгновенную загрузку UI-компонентов.

Уровень 3. Ядро приложения (FastAPI / Uvicorn / Python Core)

Центральный слой платформы, объединяющий пользовательский интерфейс, API-эндпоинты и строгую бизнес-логику.

1. Фронтенд & Табличный движок (Tabulator 6.2)

  • Управляет отображением 47 параметров сетевого оборудования, сгруппированных по 10 тематическим блокам.
  • Перехватывает события редактирования ячеек (cellEdited), проверяет права доступа текущей роли инженера и инициирует асинхронную синхронизацию с бэкендом.
  • Предоставляет контекстное меню для просмотра CLI-команд, скачивания файлов .cfg и очистки дат.
  • Адаптивная смена темы (☀️ / 🌙): Реализовано бесшовное переключение между светлой и тёмной темами оформления с динамической сменой CSS-переменных и стилей табличного ядра Tabulator (tabulator_midnight.min.css / tabulator_simple.min.css), а также автоматическим сохранением выбранного режима в localStorage браузера.

2. API & Backend Endpoints

  • / — Рендеринг основного SPA-шаблона.
  • /api/v1/me — Идентификация пользователя, передача профиля, ролевых ограничений и списка доступных макрорегионов.
  • /api/v1/nodes — Основной CRUD-маршрут для получения и сохранения параметров узлов с серверной фильтрацией по закрепленным за пользователем округам.
  • /api/v1/config/view/{id} и /download/{id} — Движок ZTP: рендерит шаблоны Jinja2 (start_config_model.j2 и start_config_environment.j2) в готовые конфигурационные файлы на основе параметров узла.
  • /api/v1/import/excel и /export/excel — Модули пакетной обработки данных с использованием библиотек pandas и openpyxl.

3. Модуль бизнес-логики и безопасности

  • Ролевая модель (RBAC) и мультирегиональность: Авторизация через HTTP Basic Auth с валидацией паролей функцией get_password_hash (Bcrypt). Поддерживается закрепление инженера как за одним, так и за группой макрорегионов (например, одновременно Юг и Поволжье). Для роли viewer доступно редактирование только строго определённого набора полей (блоки СМР, ПНР, Миграция, BMC).
  • Авторасчёт жизненного цикла: Статус узла (node_status) пересчитывается автоматически: при наличии даты СМР статус становится СМР, при наличии СМР + ПНР — ПНР, при наличии всех трёх дат (СМР + ПНР + Миграция) — МИГР.
  • Автоподстановка исполнителей: При внесении или изменении дат система извлекает фамилию авторизованного пользователя и автоматически заполняет поля executor, migrator или bmc_engineer.
  • Авто-связывание L1/L3: При указании узла или порта в блоках трансмиссии для различных вендоров, поля уровня L1 Node name и L1 Port заполняются автоматически.
  • Региональные профили: При выборе региона система автоматически подставляет параметры BGP AS, ISIS Area, MTU, TimeZone, а для специализированных регионов распределяет пары используемых адресов BGP RR1..RR4.

Уровень 4. База данных (PostgreSQL)

Слой хранения данных, обеспечивающий целостность, уникальность, аудит и разграничение прав доступа.

  • Таблица environment_nodes (47 полей): Хранит полную цифровую модель каждого узла. Декларативные ограничения целостности на уровне СУБД (UNIQUE) для полей hostname_environment и loopback_ip исключают появление дубликатов и коллизий в сети.
  • Таблица network_region_profiles: Служит нормализованным справочником параметров для всех регионов и макрорегионов (Юг, Поволжье, Центр, Северо-Запад, Москва).
  • Таблица users (Справочник пользователей и мультирегиональный RBAC): Хранит данные инженеров (username, password_hash, full_name), их роли (admin, engineer, viewer) и перечень закрепленных макрорегионов (allowed_macro в виде массива или нормализованного списка), что позволяет гибко комбинировать зоны ответственности сотрудников.
  • Таблица audit_logs (Audit Trail): Нестираемый журнал аудита. Любое изменение любого параметра через веб-интерфейс или API фиксируется отдельной записью с указанием node_id, имени поля, старого значения, нового значения, логина автора и точной метки времени.

Реализованный стек устраняет риски потери информации, блокирует коллизии IP-адресации на этапе ввода, гарантирует персональную ответственность сотрудников при мультирегиональной работе, надежно защищает сетевой периметр от атак перебора паролей и подозрительных бот-сканеров, сокращает рутинные операции благодаря сквозной автоматизации и автогенерации ZTP-конфигураций.

От концептуальной схемы к реализации

Красивая архитектура на диаграмме не имеет ценности без предсказуемой и легко масштабируемой реализации. Чтобы система выдерживала параллельную работу инженеров, мгновенно реагировала на инлайн-редактирование и не создавала накладных расходов на серверное железо, кодовая база была выстроена по модульному принципу.

Ниже представлена внутренняя механика ядра: файловая структура каталога приложения, специфика работы асинхронных сессий SQLAlchemy поверх драйвера asyncpg и привязка сервиса к системному демону systemd.

Внутреннее устройство и кодовая база проекта

Окружение: Каталог /var/www/html/app, системный Python (без venv), кэш байткода вынесен в /home/user/.pycache, ASGI-сервер Uvicorn с сокетом /var/www/html/app/environment_inventory.sock (плюс проксирование через Nginx на порт 57218 для Windows тонкого клиента), защита периметра через Iptables и Fail2ban (nginx-auth-fail / nginx-custom-bad), СУБД net_inventory (PostgreSQL 17).

Инженерная ремарка: В целях информационной безопасности реальный боевой код модулей, токены, реквизиты авторизации и топологические структуры в статье опущены. Ключевые фрагменты логики приведены в виде абстрактных концептуальных сниппетов и базовых интерфейсов.

1. Дерево файлов и каталогов проекта

/var/www/html/app/
│
├── static/                         # Статические ресурсы веб-интерфейса
│   ├── css/                        # Таблицы стилей оформления
│   ├── js/                         # JavaScript-модули и вендорные библиотеки
│   └── favicon.svg                 # Векторная иконка сайта (Favicon)
│
├── templates/                      # HTML-страницы и шаблоны конфигураций Jinja2
│   ├── index.html                  # Главная страница SPA-интерфейса (47 полей + Tabulator 6.2 + мультирегиональный RBAC)
│   ├── start_config_environment.j2 # Эталонный Jinja2-шаблон генерации стартовых конфигураций environment
│   └── start_config_model.j2       # Jinja2-шаблон генерации конфигураций для линейки моделей
│
├── scripts/                        # Вспомогательные консольные утилиты
│   └── import_excel.py             # Асинхронная пакетная загрузка и валидация данных из Excel (.xlsx)
│
├── auth.py                         # Модуль аутентификации (HTTP Basic Auth, функция get_password_hash на bcrypt, RBAC)
├── database.py                     # Подключение asyncpg и пул асинхронных соединений PostgreSQL 17
├── models.py                       # Декларативные модели SQLAlchemy 2.0 (User, EnvironmentNode, NetworkRegionProfile, AuditLog)
├── rules.py                        # Движок авторасчёта бизнес-правил (BGP AS, /30 gateway, дефолты MTU/Speed)
├── main.py                         # Точка входа FastAPI (API-эндпоинты, мультирегиональная фильтрация, ZTP, экспорт XLSX)
└── environment_inventory.sock      # Активный UNIX-сокет межпроцессного взаимодействия Nginx и Uvicorn

2. Назначение и состав ключевых файлов


1. auth.py — Аутентификация, хеширование паролей и гибкий RBAC

Обеспечивает безопасность приложения и разграничение зон ответственности:

  • Хеширование и валидация паролей с использованием bcrypt через функцию get_password_hash и метод verify_password.
  • Асинхронная зависимость get_current_user для валидации учетных данных HTTP Basic Auth, с обязательной проверкой флага активности учетной записи (is_active) без необходимости физического удаления пользователей из базы.
  • Поддержка ролей admin, engineer (полный доступ к сетевым параметрам) и viewer: для наблюдателей действует белый список VIEWER_ALLOWED_FIELDS, разрешающий редактировать только фактические даты и отметки этапов (СМР, ПНР, Миграция, BMC) при полном запрете на изменение сетевой топологии и добавление узлов.
  • Поддержка мультирегиональности: разбор и валидация массива разрешенных пользователю макрорегионов (allowed_macro, тип text[]), позволяющая гибко закреплять инженера сразу за несколькими округами с разной ролью (например, одновременно engineer-Юг и viewer-Поволжье).

Концепт реализации хеширования и валидации пользователя:

from passlib.context import CryptContext
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBasic, HTTPBasicCredentials
from database import get_db

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
security = HTTPBasic()

def verify_password(plain_password: str, hashed_password: str) -> bool:
    return pwd_context.verify(plain_password, hashed_password)

async def get_current_user(credentials: HTTPBasicCredentials = Depends(security), db = Depends(get_db)):
    user = await get_user_by_username(db, credentials.username)
    if not user or not verify_password(credentials.password, user.password_hash):
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, headers={"WWW-Authenticate": "Basic"})
    if not user.is_active:
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Учетная запись отключена")
    return user

2. database.py — Асинхронный пул соединений с БД

Инициализирует асинхронный движок SQLAlchemy 2.0 поверх драйвера asyncpg:

from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker

DATABASE_URL = "postgresql+asyncpg://netadmin:SECURE_PASS@127.0.0.1:5432/net_inventory"
engine = create_async_engine(DATABASE_URL, echo=False, pool_size=10, max_overflow=5)
AsyncSessionLocal = async_sessionmaker(bind=engine, expire_on_commit=False)

async def get_db():
    async with AsyncSessionLocal() as session:
        yield session

3. models.py — Модели данных SQLAlchemy 2.0

Содержит декларативные модели с методом to_dict(), безопасно сериализующим даты и IP-адреса в JSON-формат:

  • User: хранит системные идентификаторы (id), учетные записи (username, password_hash), ФИО инженера (full_name) для автоподстановки в этапы работ, системную роль (role: admin, engineer, viewer), массив разрешенных округов (allowed_macro: text[]), статус активности (is_active: bool) для мгновенного отзыва доступа без удаления аккаунта и таймстамп регистрации (created_at).
  • EnvironmentNode: цифровая модель узла (47 атрибутов). Включает ограничения целостности (UNIQUE) на поля hostname_environment и loopback_ip для исключения адресных коллизий на уровне СУБД.
  • NetworkRegionProfile: эталонные региональные параметры (BGP AS, ISIS Area, Route Reflectors RR1..RR4, пары Lo pairs, MTU, TimeZone).
  • AuditLog: нестираемый журнал аудита изменений (node_id, field_name, old_val, new_val, author, created_at).

Концепт структуры модели узла:

from sqlalchemy import Column, Integer, String, Date, UniqueConstraint
from sqlalchemy.orm import DeclarativeBase

class Base(DeclarativeBase):
    pass

class EnvironmentNode(Base):
	__tablename__ = "environment_nodes"

	id = Column(Integer, primary_key=True)
	hostname_environment = Column(String(64), unique=True, nullable=False, index=True)
	loopback_ip = Column(String(45), unique=True, nullable=False, index=True)
	node_status = Column(String(32), default="В плане")
	macro_region = Column(String(64), nullable=False)
    # Поля жизненного цикла, трансмиссии и BGP...

4. rules.py — Движок авторасчёта и бизнес-правил

Асинхронная функция apply_business_rules() пересчёта параметров перед сохранением и генерацией конфигураций:

  • Автоопределение порта интеграции и скорости по модели оборудования.
  • Динамический поиск и подстановка эталонного сетевого профиля региона из базы данных PostgreSQL 17 (BGP AS, ISIS Area, Route Reflectors RR1..RR4, Lo pairs, MTU).
  • Автоматический расчет IP-шлюза соседа (gateway_ip) в подсети /30 для команд ip route и ping.
  • Автоматический расчёт жизненного цикла узла (node_status: «В плане», «СМР», «ПНР», «МИГР») по цепочке дат.
  • Автоподстановка фамилии авторизованного инженера в поля исполнителей (executor, migrator, bmc_engineer) при фиксации дат.
  • Автоматическая трансляция параметров оптической трансмиссии (заполненная в соответствующей ячейке различных вендоров) на уровень L1 (L1 Node name, L1 Port).
  • Валидация и нормализация IP-адресов через ipaddress.

Концепт расчета жизненного цикла узла:

def resolve_lifecycle_status(cmr_date, pnr_date, migration_date) -> str:
    if migration_date:
        return "МИГР"
    if pnr_date:
        return "ПНР"
    if cmr_date:
        return "СМР"
    return "В плане"

5. main.py — REST API, ZTP-генератор, Экспорт/Импорт и Контроль прав

  • GET /: Отдаёт интерфейс операторской таблицы Tabulator.
  • GET /api/v1/me: Получение данных текущего пользователя, его роли и списка всех разрешённых ему макрорегионов.
  • GET /api/docs: Интерактивная Swagger-документация с тестированием всех методов.
  • GET /api/v1/meta/regions: Выдача списка доступных регионов и сетевых эталонов.
  • GET /api/v1/nodes: Быстрая асинхронная отдача записей таблицы в формате JSON с фильтрацией: пользователи видят только узлы, входящие в назначенные им макрорегионы (мультивыборка по allowed_macro).
  • POST /api/v1/nodes: Сохранение inline-правок с валидацией региона, проверкой прав полей (RBAC), автоподстановкой ответственных инженеров и транзакционной записью каждого изменения в audit_logs. При ошибке аутентификации отдает HTTP 401, попадающий под мониторинг Fail2ban.
  • GET /api/v1/config/view/{node_id}: Предпросмотр CLI-конфигурации узла на базе шаблонов Jinja2.
  • GET /api/v1/config/download/{node_id}: Скачивание файла .cfg с корректным именем хоста под целевую аппаратную платформу.
  • GET /api/v1/export/excel: Потоковая выгрузка актуальной базы в XLSX с помощью pandas и openpyxl с учетом региональных прав пользователя.
  • POST /api/v1/import/excel: Пакетный импорт данных из Excel с проверкой прав доступа, нормализацией полей и валидацией бизнес-правил.

Концепт фильтрации узлов по мультирегиональному профилю:

from fastapi import APIRouter, Depends
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

@router.get("/api/v1/nodes")
async def get_nodes(user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db)):
    stmt = select(EnvironmentNode)
    if user.role != "admin":
        stmt = stmt.where(EnvironmentNode.macro_region.in_(user.allowed_macro))
    result = await db.execute(stmt)
    return [node.to_dict() for node in result.scalars().all()]

6. templates/ — Шаблоны конфигураций (ZTP Jinja2)

  • index.html: дашборд оператора на Tabulator.js для просмотра, фильтрации и ручного управления сетевым оборудованием.
  • start_config_environment.j2: эталонный шаблон для базовой линейки используемого оборудования.
  • start_config_model.j2: специализированный шаблон начальной конфигурации для оборудования определенной линейки.

3. Шлюз Nginx, порт 57218 и активная эшелонированная защита (Fail2ban + Iptables)

Для подключения автономного десктопного клиента на Windows (сборка на базе pywebview в один .exe-файл) и защиты API от атак перебора паролей и сетевого сканирования развернута связка Nginx, Fail2ban и правил ядра Linux (Iptables):

  • Выделенный порт для десктоп-клиента: В Nginx поднят отдельный блок server { listen 57218; }, который проксирует вызовы напрямую в сокет /var/www/html/app/environment_inventory.sock с обязательным пробросом заголовков Authorization и WWW-Authenticate.
  • Джейл авторизации (nginx-auth-fail): Непрерывно анализирует /var/log/nginx/access.log, отслеживая попытки несанкционированного доступа по коду ответа 401 Unauthorized. При превышении порога неудачных попыток атакующий IP-адрес автоматически изолируется на транспортном уровне сетевым фильтром ядра (через динамические сеты nftables или цепочки iptables).
  • Джейл поведенческой фильтрации (nginx-custom-bad): Анализирует лог обращений на предмет вредоносной активности (поиск скрытых файлов .env, .git, админок, зондирование уязвимостей, malformed-запросы с кодами 400, 403, 404, 444) и превентивно отправляет в бан сетевые сканеры и ботнеты.

Конфигурация блока Nginx для тонкого клиента:

server {
listen 57218;
server_name БЕЛЫЙ_IP_СЕРВЕРА;

location / {
	proxy_pass http://unix:/var/www/html/app/environment_inventory.sock;
	proxy_http_version 1.1;
	proxy_set_header Connection "";
	proxy_set_header Host $host;
	proxy_set_header X-Real-IP $remote_addr;
	proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
	proxy_set_header Authorization $http_authorization;
	proxy_pass_header Authorization;
	proxy_pass_header WWW-Authenticate;
    }
}

Фильтры Fail2ban (/etc/fail2ban/filter.d/):

# 1. Отлов брутфорса и 401 Unauthorized (nginx-auth-fail.conf)
[Definition]
failregex = ^<HOST> - .* "(GET|POST).*" 401
ignoreregex =

# 2. Отлов паразитных сканеров, ботов и аномалий (nginx-custom-bad.conf)
[Definition]
failregex = ^<HOST> - .* "(GET|POST|HEAD) .*(/wp-.*|/admin.*|\.env|\.git|phpmyadmin).*" (400|403|404|444)
		^<HOST> - .* "(GET|POST).*" 444
ignoreregex =

Блокировка нарушителей на уровне сетевого стека ядра (nftables / iptables):

При срабатывании любого из фильтров Fail2ban блокирует атакующий IP на уровне ядра Linux. В современных дистрибутивах (Debian 12/13) сервис по умолчанию взаимодействует с подсистемой nftables через быстродействующие хэш-множества (sets), отсекая паразитные пакеты до того, как они нагрузят веб-сервер:

# Проверка заблокированных IP в таблице ядра (nftables)
sudo nft list table inet f2b-table

# Мониторинг состояния "тюрем" через клиент Fail2ban
sudo fail2ban-client status nginx-custom-bad
sudo fail2ban-client status nginx-auth-fail

# Для систем со старым бэкендом iptables-multiport (если настроен явно)
sudo iptables -L -n -v | grep f2b-nginx

4. Запуск через Systemd (/etc/systemd/system/environment-inventory.service)

Конфигурация сервиса для работы с Unix-сокетом и изоляцией байткода в /home/user/.pycache:

[Unit]
Description=Environment Telecom Inventory (FastAPI Core)
After=network.target postgresql.service

[Service]
User=user
Group=www-data
WorkingDirectory=/var/www/html/app
Environment=PYTHONPYCACHEPREFIX=/home/user/.pycache
ExecStart=/usr/bin/uvicorn main:app --uds /var/www/html/app/environment_inventory.sock --workers 2 --umask 007
Restart=always
RestartSec=3

[Install]
WantedBy=multi-user.target

5. Сценарий масштабирования: вынос базы данных на удалённый сервер СУБД

Архитектура сервиса позволяет в любой момент вынести СУБД PostgreSQL 17 на выделенный хост (физический сервер или отдельную ВМ) для изоляции нагрузки, масштабирования и централизованного резервного копирования.

Шаг 1. Перенос существующей базы на новый сервер

  • Создание дампа на текущем хосте:
    pg_dump -U netadmin -h 127.0.0.1 -d net_inventory -F c -b -v -f /tmp/net_inventory_backup.dump
  • Передача файла дампа на сервер СУБД:
    scp /tmp/net_inventory_backup.dump user@192.168.1.100:/tmp/
  • Создание роли, пустой БД и восстановление данных на новом сервере:
    # Создание пользователя и базы в PostgreSQL 17
    sudo -u postgres psql -c "CREATE USER netadmin WITH PASSWORD 'SECURE_DB_PASSWORD';"
    sudo -u postgres psql -c "CREATE DATABASE net_inventory OWNER netadmin;"
    
    # Развертывание дампа
    pg_restore -U netadmin -d net_inventory -v /tmp/net_inventory_backup.dump

Шаг 2. Настройка сетевого доступа на удалённом сервере PostgreSQL 17

  • Разрешить приём входящих сетевых соединений (postgresql.conf):
    В конфигурационном файле /etc/postgresql/17/main/postgresql.conf задаем директиву:
    listen_addresses = '*'
  • Разрешить авторизацию с IP веб-сервера FastAPI (pg_hba.conf):
    В конец файла /etc/postgresql/17/main/pg_hba.conf добавить правило доступа для пользователя netadmin (например, с адреса 192.168.1.50):
    # TYPE  DATABASE        USER        ADDRESS          METHOD
    host    net_inventory   netadmin    192.168.1.50/32  scram-sha-256
  • Ограничение подключений через межсетевой экран (iptables):
    sudo iptables -A INPUT -p tcp -s 192.168.1.50 --dport 5432 -j ACCEPT
  • Перезапуск службы СУБД:
    sudo systemctl restart postgresql

Шаг 3. Переключение FastAPI-бэкенда на удалённую базу данных

  • Обновление строки подключения в database.py:
    Изменить IP-адрес удаленного сервера PostgreSQL (например, 192.168.1.100):
    DATABASE_URL = "postgresql+asyncpg://netadmin:SECURE_DB_PASSWORD@192.168.1.100:5432/net_inventory"
  • Корректировка Systemd-юнита (environment-inventory.service):
    Исключить локальную зависимость postgresql.service из директивы After:
    [Unit]
    Description=Environment Telecom Inventory (FastAPI Core)
    After=network.target
  • Применение изменений и перезапуск сервиса:
    sudo systemctl daemon-reload
    sudo systemctl restart environment-inventory.service

Шаг 4. Проверка статуса и журналов сервиса

Убедиться в успешной инициализации пула подключений к удалённой СУБД:

systemctl status environment-inventory.service
journalctl -u environment-inventory.service -n 30 --no-pager

Главный нюанс — Безопасность (Важно!)

При выносе базы данных за пределы локальной подсети трафик между серверами будет ходить через открытый интернет; передавать пароли и сетевые топологии в чистом виде небезопасно.

Для работы через интернет обязательно:

  • Использовать SSL-шифрование в PostgreSQL: Настроить сертификаты (параметр ?ssl=require в строке DATABASE_URL), чтобы весь сетевой трафик между FastAPI и базой данных шифровался.
  • Использовать защищенный канал (VPN / Tailscale / WireGuard / IPsec): Самый надежный вариант для распределенной архитектуры — поднять между серверами частную виртуальную сеть (VPN). В таком случае серверы будут видеть друг друга по безопасным внутренним IP (например, 10.8.0.x), а порт СУБД 5432 будет полностью изолирован от публичного сканирования.

Итоги внедрения и эксплуатационные выводы

Отказ от ручного ведения учета кардинально изменил рабочий процесс учета, эксплуатации и ввода оборудования:

  • Единый источник правды (Single Source of Truth): Полностью ушли от десятков разрозненных региональных Excel-файлов, пересылаемых в чатах и сетевых папках. База данных централизована на выделенном сервере, что гарантирует абсолютную консистентность данных и исключает работу с неактуальными версиями топологии.
  • Ликвидация сетевых коллизий: Ограничения уникальности на уровне СУБД (UNIQUE) полностью исключили дублирование Loopback IP и хостнеймов — человеческий фактор при заведении нового оборудования сведен к нулю.
  • Ускорение подготовки узлов (ZTP): Время генерации эталонной конфигурации узла сократилось с 15–20 минут ручной подстановки параметров по текстовым файлам до 2 секунд (один клик для экспорта выверенного .cfg).
  • Прозрачность этапов строительства: Благодаря автоматическому расчёту статусов (В плане → СМР → ПНР → МИГР) и сквозному журналу audit_logs отпала необходимость в бесконечных статус-митингах и выяснениях, кто и когда изменил стыковочные порты.
  • Защищенный периметр: Комбинация изоляции прав по макрорегионам (allowed_macro), выделенного порта для тонкого клиента и активной блокировки сканеров через Fail2ban обеспечила стабильную работу сервиса без риска компрометации критической инфраструктуры.

Вектор дальнейшего развития: Следующий логичный шаг архитектуры — трансформация платформы из учетной системы (Source of Truth) в активный оркестратор: добавление модуля пуша конфигураций прямо на узлы через Scrapli / Netmiko и интеграция со сквозным SNMP/Telemetry-мониторингом состояния портов в реальном времени. Ну а дальше... Реализация и интеграция будущих идей, которые пока еще не успели сформироваться в мысль.

Здесь я попытался показать техническую реализацию своей идеи — так, как вижу её сам. Статья не претендует на универсальное руководство к действию, поэтому здесь и отсутствуют длинные "портянки" рабочего кода: главное — передать архитектурную суть.


Пользовательский SPA-интерфейс инвентарной платформы (Tabulator 6.2)