Files
IPWhiteList/docs/plan-gpt54.md
T

529 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Полный план реализации — IP WhiteList Microservice
> Автор: GitHub Copilot (GPT-5.4)
> Дата: 2026-05-29
> Статус: рабочий план для реализации
## 1. Цель системы
Нужно реализовать внутренний веб-сервис, в котором клиенты облачного провайдера смогут самостоятельно управлять доверенными IPv4-адресами и подсетями. Эти записи должны исключаться из блокировки во время DDoS-митигции.
Сервис должен решать 3 задачи:
1. Дать клиенту self-service интерфейс для управления whitelist.
2. Дать администраторам и сетевым инженерам централизованный просмотр и контроль.
3. Отдавать агрегированный экспорт всех активных записей в текстовом формате для внешних систем фильтрации.
## 2. Что именно должно быть в первой рабочей версии
Первая версия должна включать:
1. Авторизацию через Keycloak OIDC.
2. Разделение прав client/admin.
3. Поддержку одной или нескольких компаний у пользователя.
4. Таблицу whitelist-записей.
5. Создание, редактирование и soft delete записей.
6. Проверку лимитов по компаниям.
7. Аудит всех изменяющих действий.
8. Экспорт агрегированного списка активных CIDR в text/plain.
9. Серверную валидацию IPv4 и CIDR по правилам ТЗ.
10. Клиентскую валидацию формы для UX.
## 3. Обязательные уточнения до начала интеграции с Keycloak
До кодирования OIDC-части нужно получить точные ответы на 3 вопроса:
1. Какой claim или role означает администратора.
Сейчас в ТЗ сказано: clientId = WZ01112 и отдельный чек-бокс. Нужно точно знать, во что это превращается в токене.
2. Как кодируется принадлежность к нескольким компаниям.
В ТЗ упомянут мультикомпанейный сценарий, но в claims перечислен только clientID. Нужно уточнить, это строка, массив, groups или другой формат.
3. Где именно ограничивается внешний экспортный endpoint по IP.
Нужно решить, это делает приложение, Nginx/Ingress, или оба уровня сразу.
Без этих 3 ответов можно делать каркас, БД, валидацию, CRUD и UI, но нельзя окончательно зафиксировать auth-слой.
## 4. Рекомендуемый стек
### Бэкенд
- Python 3.11+
- FastAPI
- Uvicorn
Причина: Python понятен команде, FastAPI даёт простой роутинг, типизацию, dependency injection и удобную основу для API и HTML-эндпоинтов.
### База данных
- PostgreSQL
- psycopg2-binary
- Alembic для миграций
Причина: нужны надёжные транзакции, аудит, индексы и понятный деплой. Здесь нет выгоды от тяжёлой ORM-магии, поэтому лучше простой и читаемый SQL.
### UI
- Jinja2
- обычные HTML-формы
- HTMX по желанию, только если реально упрощает частичные обновления
- минимальный JS для inline-валидации и уведомлений
Причина: задача не требует SPA. Простая серверная отрисовка снизит сложность и упростит поддержку.
### Авторизация
- Keycloak OIDC
- JWT-проверка по JWKS
- отдельный dev-режим без Keycloak только для локальной разработки
### Работа с IP
- стандартный модуль ipaddress
- netaddr только если стандартной библиотеки окажется недостаточно для агрегирования
Примечание: начать можно вообще без netaddr. Для суммаризации сначала стоит проверить, хватает ли ipaddress.collapse_addresses.
## 5. Архитектурные принципы
1. Синхронный код по умолчанию.
Для этой системы async не нужен. Он только повысит стоимость поддержки.
2. Серверная валидация является источником истины.
Клиентская валидация только помогает пользователю.
3. Аудит append-only.
Записи аудита нельзя изменять и удалять.
4. Все проверки прав и лимитов выполняются на сервере внутри транзакций.
5. Soft delete обязателен для whitelist-записей.
6. Значение лимита по умолчанию должно меняться через конфиг без пересборки.
7. Dev-заглушка авторизации должна быть жёстко отключаемой в production.
## 6. Предлагаемая структура проекта
```text
IPWhiteList/
├── app/
│ ├── main.py
│ ├── config.py
│ ├── db.py
│ ├── security.py
│ ├── validators.py
│ ├── cidr_utils.py
│ ├── services/
│ │ ├── entries.py
│ │ ├── companies.py
│ │ ├── audit.py
│ │ └── export.py
│ ├── repositories/
│ │ ├── entries.py
│ │ ├── companies.py
│ │ └── audit.py
│ ├── routers/
│ │ ├── ui.py
│ │ ├── admin.py
│ │ └── export.py
│ ├── auth/
│ │ ├── oidc.py
│ │ ├── dev_stub.py
│ │ └── deps.py
│ └── templates/
│ ├── base.html
│ ├── index.html
│ ├── entry_form.html
│ ├── login_error.html
│ └── admin/
│ ├── audit.html
│ └── limits.html
├── static/
│ └── style.css
├── migrations/
│ └── versions/
├── tests/
│ ├── test_validators.py
│ ├── test_entries_service.py
│ ├── test_export.py
│ └── test_auth_mapping.py
├── docs/
│ ├── plan.md
│ ├── plan-v2.md
│ └── plan-gpt54.md
├── requirements.txt
├── .env.example
├── alembic.ini
├── docker-compose.yml
├── Dockerfile
└── README.md
```
## 7. Модель данных
### Таблица companies
Назначение: хранение компаний и переопределённых лимитов.
Поля:
1. id
2. client_id
3. name
4. custom_limit
5. created_at
6. updated_at
Правила:
1. client_id уникален.
2. custom_limit может быть null, тогда используется глобальный лимит.
### Таблица whitelist_entries
Назначение: активные и удалённые whitelist-записи.
Поля:
1. id
2. company_id
3. value_cidr
4. comment
5. created_by
6. created_at
7. updated_by
8. updated_at
9. deleted_by
10. deleted_at
Правила:
1. value_cidr хранится только в нормализованном виде.
2. deleted_at is null означает активную запись.
3. comment ограничен 255 символами.
### Таблица audit_log
Назначение: неизменяемый журнал действий.
Поля:
1. id
2. user_email
3. company_id
4. action
5. old_value
6. new_value
7. created_at
Дополнительно желательно хранить:
1. target_entry_id
2. request_id
3. actor_role
Это не противоречит ТЗ и упростит разбор инцидентов.
## 8. Правила авторизации и ролей
### Клиент
1. Видит только записи своей активной компании.
2. Может создавать, редактировать и удалять записи только в допустимом контексте компании.
3. Может переключать активную компанию, если в токене действительно есть доступ к нескольким компаниям.
### Администратор
1. Видит записи всех компаний.
2. Может менять записи всех компаний.
3. Может видеть удалённые записи.
4. Может смотреть аудит.
5. Может менять custom_limit для компании.
### Что нужно реализовать в коде
1. Унифицированную модель текущего пользователя.
2. Отдельную функцию маппинга claims в внутреннюю роль.
3. Жёсткие проверки прав на уровне service-слоя, не только роутеров.
## 9. Валидация IPv4 и CIDR
Эта часть критична. Её нужно делать одной из первых и сразу покрывать тестами.
### Поддерживаемый ввод
1. Одиночный IPv4 адрес, который трактуется как /32.
2. IPv4 подсеть в CIDR нотации.
### Запрещённый ввод
1. IPv6.
2. Доменное имя.
3. Маска шире допустимой.
4. Любые private или special ranges из приложения А.
### Правила маски
Допустимы только /22 ... /32.
### Нормализация
Если пользователь ввёл адрес с host-битами, сервис должен:
1. Нормализовать значение до адреса сети.
2. Сохранить нормализованное значение.
3. Вернуть пользователю явное сообщение, что адрес был нормализован.
### Проверки в пределах компании
1. Запрет полного дубликата активной записи.
2. Запрет любого пересечения активной записи с существующими активными записями той же компании.
3. Между разными компаниями пересечения допускаются.
### Список запрещённых диапазонов
Нужно захардкодить как конфигурацию приложения и покрыть тестами:
1. 10.0.0.0/8
2. 172.16.0.0/12
3. 192.168.0.0/16
4. 100.64.0.0/10
5. 127.0.0.0/8
6. 169.254.0.0/16
7. 192.0.0.0/24
8. 192.0.2.0/24
9. 198.51.100.0/24
10. 203.0.113.0/24
11. 198.18.0.0/15
12. 224.0.0.0/4
13. 240.0.0.0/4
14. 255.255.255.255/32
## 10. Лимиты и конкурентность
Это важное место, которого обычно недооценивают.
### Правила лимитов
1. Есть глобальный DEFAULT_LIMIT, по умолчанию 15.
2. Для компании может быть custom_limit.
3. При снижении лимита ниже текущего количества записей существующие записи не удаляются.
4. Пока число активных записей больше лимита, новые записи создавать нельзя.
### Риск гонок
Если два запроса одновременно создают записи в одной компании, возможны:
1. Пробитие лимита.
2. Пропуск пересечения.
3. Пропуск дубликата.
### Что делать
Операцию создания и обновления записи нужно делать в транзакции с сериализацией логики на уровне компании. Практически это можно решить так:
1. Брать advisory lock по company_id перед проверками и записью.
2. Либо делать SELECT ... FOR UPDATE по строке компании, если этого достаточно для вашей схемы доступа.
Для первой версии я бы выбрал advisory lock по company_id. Это проще и надёжнее для бизнес-ограничений, которые нельзя полностью выразить обычным unique index.
## 11. Экспорт агрегированного списка
### Требования
1. В экспорт попадают только активные записи.
2. Данные берутся по всем компаниям.
3. Пересечения между компаниями допустимы на уровне хранения, но в export должны агрегироваться в минимальный набор CIDR.
4. Формат ответа: text/plain.
5. Одна строка = один CIDR.
### Что нужно зафиксировать реализационно
1. Результат должен быть отсортирован для стабильности.
2. В ответе должен быть завершающий перевод строки.
3. Content-Type должен быть text/plain; charset=utf-8.
4. Желательно отдавать Content-Disposition с понятным именем файла.
### Защита endpoint
Если endpoint на старте работает без auth, то доступ надо ограничить минимум одним из способов:
1. Проверка client IP в приложении.
2. Ограничение на reverse proxy.
3. Оба сразу.
## 12. UI-потоки
### Экран клиента
Должны быть:
1. Селектор активной компании, если компаний несколько.
2. Таблица записей.
3. Индикатор использовано X из N.
4. Форма создания записи.
5. Возможность редактирования.
6. Возможность soft delete.
### Экран администратора
Должны быть:
1. Таблица по всем компаниям.
2. Фильтр по компании.
3. Фильтр показа удалённых записей.
4. Просмотр журнала аудита.
5. Управление лимитами компании.
### UX-детали, которые обязательно сделать
1. Понятные сообщения об ошибках валидации.
2. Явное сообщение о нормализации адреса.
3. Явное сообщение о превышении лимита.
4. Явное сообщение о пересечении с существующей записью.
## 13. Пошаговый план реализации
### Этап 1. Каркас проекта
1. Создать структуру каталогов.
2. Подготовить requirements.txt.
3. Подготовить .env.example.
4. Подключить FastAPI, Jinja2, static.
5. Подготовить docker-compose.yml с PostgreSQL.
Результат этапа: приложение стартует, открывается базовая страница, есть подключение к БД.
### Этап 2. Схема БД и миграции
1. Настроить Alembic.
2. Создать initial migration.
3. Поднять таблицы companies, whitelist_entries, audit_log.
4. Добавить нужные индексы.
Результат этапа: схема БД фиксирована и воспроизводима.
### Этап 3. Валидатор CIDR
1. Реализовать разбор IPv4 и CIDR.
2. Реализовать проверку маски.
3. Реализовать нормализацию.
4. Реализовать проверку запрещённых диапазонов.
5. Написать тесты на валидатор.
Результат этапа: независимый, протестированный модуль бизнес-валидации.
### Этап 4. Сервисный слой для записей
1. Реализовать list.
2. Реализовать create.
3. Реализовать update.
4. Реализовать soft delete.
5. Реализовать проверки лимитов, дубликатов и пересечений.
6. Добавить транзакционную защиту от гонок.
Результат этапа: бизнес-операции работают без UI.
### Этап 5. Аудит
1. Добавить запись CREATE.
2. Добавить запись UPDATE со старым и новым состоянием.
3. Добавить запись DELETE.
4. Добавить интерфейс чтения для admin.
Результат этапа: все изменяющие действия фиксируются.
### Этап 6. Авторизация
1. Реализовать dev-заглушку.
2. Реализовать чтение и валидацию JWT из Keycloak.
3. Реализовать преобразование claims в current user.
4. Реализовать проверки client/admin.
5. Реализовать переключение компании.
Результат этапа: права и контекст пользователя работают сквозным образом.
### Этап 7. HTML-интерфейс
1. Реализовать страницу списка.
2. Реализовать формы создания и редактирования.
3. Реализовать soft delete из UI.
4. Реализовать админские экраны.
Результат этапа: сервис пригоден для ручной эксплуатации.
### Этап 8. Экспорт
1. Реализовать сбор всех активных CIDR.
2. Реализовать агрегацию.
3. Реализовать endpoint export.
4. Реализовать сетевое ограничение.
Результат этапа: внешняя система может забирать текстовый агрегированный whitelist.
### Этап 9. Финализация
1. Написать README.
2. Подготовить Dockerfile.
3. Подготовить пример systemd unit при необходимости.
4. Прогнать ручной smoke-test.
Результат этапа: сервис можно разворачивать и передавать коллегам.
## 14. Тестовая стратегия
Минимально обязательные тесты:
1. Валидный одиночный IPv4 превращается в /32.
2. Валидная подсеть принимается.
3. Host-биты нормализуются.
4. Маски шире допустимой границы отклоняются.
5. IPv6 отклоняется.
6. Все запрещённые диапазоны отклоняются.
7. Дубликат в одной компании запрещён.
8. Пересечение в одной компании запрещено.
9. Тот же CIDR в другой компании разрешён.
10. Soft delete освобождает лимит.
11. Export не включает soft-deleted записи.
12. Export агрегирует CIDR корректно.
13. Client не видит чужие компании.
14. Admin видит все компании.
15. Аудит создаётся для create, update, delete.
## 15. Что можно отложить после первой версии
Это не нужно тащить в MVP:
1. Полноценный SPA.
2. Сложная ORM.
3. WebSocket.
4. Фоновая очередь.
5. Исторические версии записей кроме audit log.
6. Автоматическое уведомление по email.
## 16. Главные риски проекта
1. Неясный формат claims из Keycloak.
2. Гонки при одновременном создании записей.
3. Ошибки в трактовке пересечений CIDR.
4. Неправильная нормализация адресов без понятного сообщения пользователю.
5. Слишком раннее усложнение фронтенда.
## 17. Что я бы делал первым
Если начинать реализацию прямо сейчас, порядок такой:
1. Каркас проекта.
2. Схема БД.
3. Валидатор и тесты.
4. Сервис create/update/delete с транзакциями.
5. Только потом UI и Keycloak.
Это самый безопасный путь: сначала фиксируется ядро бизнес-логики, потом уже внешний слой.
## 18. Итоговое решение
За основу реализации стоит брать простой Python/FastAPI сервис с PostgreSQL, синхронной серверной логикой, жёсткой серверной валидацией, транзакционной защитой от гонок и минималистичным HTML UI.
Главная мысль: сложность здесь не во фронтенде и не в фреймворке, а в корректной реализации правил CIDR, лимитов, ролей и аудита. План должен защищать именно эти части, а не раздувать стек.