2025-06-09 10:17:12 +05:00
|
|
|
|
from fastapi import APIRouter, HTTPException
|
|
|
|
|
|
from typing import List
|
|
|
|
|
|
from pydantic import BaseModel, Field
|
|
|
|
|
|
|
|
|
|
|
|
from models.ufw import UFWRulesResponse, UFWStatus, UFWRule
|
|
|
|
|
|
from services.ufw_service import UFWService
|
|
|
|
|
|
|
|
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
|
2025-06-12 16:32:05 +05:00
|
|
|
|
|
2025-06-09 10:17:12 +05:00
|
|
|
|
class RuleNumbers(BaseModel):
|
2025-10-21 10:51:32 +05:00
|
|
|
|
"""Модель запроса на удаление UFW правил.
|
|
|
|
|
|
|
|
|
|
|
|
Атрибуты:
|
|
|
|
|
|
|
|
|
|
|
|
- ``numbers`` (List[int]): Список номеров правил для удаления.
|
|
|
|
|
|
"""
|
2025-06-12 16:47:27 +05:00
|
|
|
|
numbers: List[int] = Field(
|
|
|
|
|
|
..., description="Список номеров правил для удаления")
|
2025-06-09 10:17:12 +05:00
|
|
|
|
|
2025-06-12 16:32:05 +05:00
|
|
|
|
|
2025-06-09 10:17:12 +05:00
|
|
|
|
@router.get("/rules", response_model=UFWRulesResponse)
|
|
|
|
|
|
async def get_ufw_rules():
|
2025-10-14 11:42:57 +05:00
|
|
|
|
"""Возвращает все правила UFW.
|
|
|
|
|
|
|
|
|
|
|
|
Осуществляет обращение к сервису `UFWService` для чтения текущего списка
|
|
|
|
|
|
правил брандмауэра и возвращает результат в сериализованном виде для клиента.
|
|
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
UFWRulesResponse: Структура с коллекцией правил и сопутствующей
|
|
|
|
|
|
информацией о состоянии.
|
|
|
|
|
|
|
|
|
|
|
|
Raises:
|
|
|
|
|
|
HTTPException: В случае ошибок взаимодействия с UFW (status_code=500).
|
2025-06-09 10:17:12 +05:00
|
|
|
|
"""
|
|
|
|
|
|
ufw_service = UFWService()
|
|
|
|
|
|
try:
|
|
|
|
|
|
return ufw_service.get_ufw_rules()
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
raise HTTPException(status_code=500, detail=f"Error: {str(e)}")
|
|
|
|
|
|
|
2025-06-12 16:32:05 +05:00
|
|
|
|
|
2025-06-09 10:17:12 +05:00
|
|
|
|
@router.get("/status", response_model=UFWStatus)
|
|
|
|
|
|
async def get_ufw_status():
|
2025-10-14 11:42:57 +05:00
|
|
|
|
"""Возвращает текущий статус UFW.
|
|
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
UFWStatus: Объект со сведениями о включенности UFW и деталями статуса.
|
|
|
|
|
|
|
|
|
|
|
|
Raises:
|
|
|
|
|
|
HTTPException: Если не удалось получить статус (status_code=500).
|
2025-06-09 10:17:12 +05:00
|
|
|
|
"""
|
|
|
|
|
|
ufw_service = UFWService()
|
|
|
|
|
|
try:
|
|
|
|
|
|
return ufw_service.get_ufw_status()
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
raise HTTPException(status_code=500, detail=f"Error: {str(e)}")
|
|
|
|
|
|
|
2025-06-12 16:32:05 +05:00
|
|
|
|
|
2025-06-09 10:17:12 +05:00
|
|
|
|
@router.post("/enable")
|
|
|
|
|
|
async def enable_ufw():
|
2025-10-14 11:42:57 +05:00
|
|
|
|
"""Включает UFW.
|
|
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
dict: Словарь с ключами `status` и `message` при успешном выполнении.
|
|
|
|
|
|
|
|
|
|
|
|
Raises:
|
|
|
|
|
|
HTTPException: Если включение завершилось неуспешно (status_code=500).
|
2025-06-09 10:17:12 +05:00
|
|
|
|
"""
|
|
|
|
|
|
ufw_service = UFWService()
|
|
|
|
|
|
try:
|
|
|
|
|
|
success = ufw_service.enable_ufw()
|
|
|
|
|
|
if success:
|
|
|
|
|
|
return {"status": "success", "message": "UFW успешно включен"}
|
|
|
|
|
|
else:
|
2025-06-12 16:47:27 +05:00
|
|
|
|
raise HTTPException(status_code=500,
|
|
|
|
|
|
detail="Не удалось включить UFW")
|
2025-06-09 10:17:12 +05:00
|
|
|
|
except Exception as e:
|
|
|
|
|
|
raise HTTPException(status_code=500, detail=f"Error: {str(e)}")
|
|
|
|
|
|
|
2025-06-12 16:32:05 +05:00
|
|
|
|
|
2025-06-09 10:17:12 +05:00
|
|
|
|
@router.post("/disable")
|
|
|
|
|
|
async def disable_ufw():
|
2025-10-14 11:42:57 +05:00
|
|
|
|
"""Выключает UFW.
|
|
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
dict: Словарь с ключами `status` и `message` при успешном выполнении.
|
|
|
|
|
|
|
|
|
|
|
|
Raises:
|
|
|
|
|
|
HTTPException: Если выключение завершилось неуспешно (status_code=500).
|
2025-06-09 10:17:12 +05:00
|
|
|
|
"""
|
|
|
|
|
|
ufw_service = UFWService()
|
|
|
|
|
|
try:
|
|
|
|
|
|
success = ufw_service.disable_ufw()
|
|
|
|
|
|
if success:
|
|
|
|
|
|
return {"status": "success", "message": "UFW успешно выключен"}
|
|
|
|
|
|
else:
|
2025-06-12 16:47:27 +05:00
|
|
|
|
raise HTTPException(status_code=500,
|
|
|
|
|
|
detail="Не удалось выключить UFW")
|
2025-06-09 10:17:12 +05:00
|
|
|
|
except Exception as e:
|
|
|
|
|
|
raise HTTPException(status_code=500, detail=f"Error: {str(e)}")
|
|
|
|
|
|
|
2025-06-12 16:32:05 +05:00
|
|
|
|
|
2025-06-09 10:17:12 +05:00
|
|
|
|
@router.post("/rules/in")
|
|
|
|
|
|
async def add_ufw_in_rules(rules: List[UFWRule]):
|
2025-10-14 11:42:57 +05:00
|
|
|
|
"""Добавляет входящие (in) правила UFW.
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
rules (List[UFWRule]): Список правил, определяющих направления,
|
|
|
|
|
|
порты/диапазоны/протоколы и действия (allow/deny/reject).
|
|
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
dict: Итог выполнения с количеством успешно добавленных правил и деталями
|
|
|
|
|
|
по каждому правилу.
|
|
|
|
|
|
|
|
|
|
|
|
Raises:
|
|
|
|
|
|
HTTPException: Если UFW отключен (status_code=400) или при внутренних
|
|
|
|
|
|
ошибках (status_code=500).
|
2025-06-09 10:17:12 +05:00
|
|
|
|
"""
|
|
|
|
|
|
ufw_service = UFWService()
|
|
|
|
|
|
try:
|
|
|
|
|
|
# Проверяем статус UFW
|
|
|
|
|
|
status = ufw_service.get_ufw_status()
|
|
|
|
|
|
if not status.enabled:
|
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
|
status_code=400,
|
2025-06-12 16:32:05 +05:00
|
|
|
|
detail="UFW отключен. Включите UFW перед добавлением правил.",
|
2025-06-09 10:17:12 +05:00
|
|
|
|
)
|
2025-06-12 16:32:05 +05:00
|
|
|
|
|
2025-06-09 10:17:12 +05:00
|
|
|
|
result = ufw_service.add_rules(rules)
|
|
|
|
|
|
if result["success"]:
|
2025-06-12 16:47:27 +05:00
|
|
|
|
successful_rules = len(
|
|
|
|
|
|
[r for r in result["results"] if r["success"]])
|
2025-06-09 10:17:12 +05:00
|
|
|
|
total_rules = len(result["results"])
|
|
|
|
|
|
return {
|
|
|
|
|
|
"status": "success",
|
2025-06-12 16:47:27 +05:00
|
|
|
|
"message":
|
|
|
|
|
|
f"Добавлено {successful_rules} из {total_rules} правил",
|
2025-06-12 16:32:05 +05:00
|
|
|
|
"details": result,
|
2025-06-09 10:17:12 +05:00
|
|
|
|
}
|
|
|
|
|
|
else:
|
2025-06-12 16:32:05 +05:00
|
|
|
|
raise HTTPException(
|
|
|
|
|
|
status_code=500,
|
|
|
|
|
|
detail=result.get("error", "Не удалось добавить правила"),
|
|
|
|
|
|
)
|
2025-06-09 10:17:12 +05:00
|
|
|
|
except Exception as e:
|
|
|
|
|
|
raise HTTPException(status_code=500, detail=f"Error: {str(e)}")
|
|
|
|
|
|
|
2025-06-12 16:32:05 +05:00
|
|
|
|
|
2025-06-09 10:17:12 +05:00
|
|
|
|
@router.post("/rules/out")
|
|
|
|
|
|
async def add_ufw_out_rules(rules: List[UFWRule]):
|
2025-10-14 11:42:57 +05:00
|
|
|
|
"""Добавляет исходящие (out) правила UFW.
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
rules (List[UFWRule]): Список правил исходящего трафика для добавления.
|
|
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
dict: Объект статуса с сообщением об успешном добавлении.
|
|
|
|
|
|
|
|
|
|
|
|
Raises:
|
|
|
|
|
|
HTTPException: Если UFW отключен (status_code=400) или при внутренних
|
|
|
|
|
|
ошибках (status_code=500).
|
2025-06-09 10:17:12 +05:00
|
|
|
|
"""
|
|
|
|
|
|
ufw_service = UFWService()
|
|
|
|
|
|
try:
|
|
|
|
|
|
# Проверяем статус UFW
|
|
|
|
|
|
status = ufw_service.get_ufw_status()
|
|
|
|
|
|
if not status.enabled:
|
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
|
status_code=400,
|
2025-06-12 16:32:05 +05:00
|
|
|
|
detail="UFW отключен. Включите UFW перед добавлением правил.",
|
2025-06-09 10:17:12 +05:00
|
|
|
|
)
|
2025-06-12 16:32:05 +05:00
|
|
|
|
|
2025-06-09 10:17:12 +05:00
|
|
|
|
success = ufw_service.add_ufw_out_rules(rules)
|
|
|
|
|
|
if success:
|
2025-06-12 16:32:05 +05:00
|
|
|
|
return {
|
|
|
|
|
|
"status": "success",
|
|
|
|
|
|
"message": "Исходящие правила UFW успешно добавлены",
|
|
|
|
|
|
}
|
2025-06-09 10:17:12 +05:00
|
|
|
|
else:
|
2025-06-12 16:32:05 +05:00
|
|
|
|
raise HTTPException(
|
2025-06-12 16:47:27 +05:00
|
|
|
|
status_code=500,
|
|
|
|
|
|
detail="Не удалось добавить исходящие правила UFW")
|
2025-06-09 10:17:12 +05:00
|
|
|
|
except Exception as e:
|
|
|
|
|
|
raise HTTPException(status_code=500, detail=f"Error: {str(e)}")
|
|
|
|
|
|
|
2025-06-12 16:32:05 +05:00
|
|
|
|
|
2025-06-09 10:17:12 +05:00
|
|
|
|
@router.delete("/rules")
|
|
|
|
|
|
async def delete_ufw_rules(rule_numbers: RuleNumbers):
|
2025-10-14 11:42:57 +05:00
|
|
|
|
"""Удаляет правила UFW по их номерам.
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
rule_numbers (RuleNumbers): Номера правил, подлежащих удалению (как в
|
|
|
|
|
|
выводе `ufw status numbered`).
|
|
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
dict: Информация об общем количестве удаляемых правил, числе успешных и
|
|
|
|
|
|
возможной ошибке.
|
|
|
|
|
|
|
|
|
|
|
|
Raises:
|
|
|
|
|
|
HTTPException: При ошибках удаления (status_code=500).
|
2025-06-09 10:17:12 +05:00
|
|
|
|
"""
|
|
|
|
|
|
ufw_service = UFWService()
|
|
|
|
|
|
try:
|
|
|
|
|
|
result = ufw_service.delete_rules(rule_numbers.numbers)
|
|
|
|
|
|
if result["success"]:
|
|
|
|
|
|
return {
|
|
|
|
|
|
"status": "success",
|
2025-06-12 16:47:27 +05:00
|
|
|
|
"message":
|
|
|
|
|
|
f"Удалено {result['successful']} из {result['total']} правил",
|
2025-06-12 16:32:05 +05:00
|
|
|
|
"details": result,
|
2025-06-09 10:17:12 +05:00
|
|
|
|
}
|
|
|
|
|
|
else:
|
|
|
|
|
|
raise HTTPException(status_code=500, detail=result["error"])
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
raise HTTPException(status_code=500, detail=f"Error: {str(e)}")
|