Initial YakPanel commit
This commit is contained in:
83
YakPanel-server/backend/app/api/firewall.py
Normal file
83
YakPanel-server/backend/app/api/firewall.py
Normal file
@@ -0,0 +1,83 @@
|
||||
"""YakPanel - Firewall API"""
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select
|
||||
from pydantic import BaseModel
|
||||
|
||||
from app.core.database import get_db
|
||||
from app.core.utils import exec_shell_sync
|
||||
from app.api.auth import get_current_user
|
||||
from app.models.user import User
|
||||
from app.models.firewall import FirewallRule
|
||||
|
||||
router = APIRouter(prefix="/firewall", tags=["firewall"])
|
||||
|
||||
|
||||
class CreateFirewallRuleRequest(BaseModel):
|
||||
port: str
|
||||
protocol: str = "tcp"
|
||||
action: str = "accept"
|
||||
ps: str = ""
|
||||
|
||||
|
||||
@router.get("/list")
|
||||
async def firewall_list(
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""List firewall rules"""
|
||||
result = await db.execute(select(FirewallRule).order_by(FirewallRule.id))
|
||||
rows = result.scalars().all()
|
||||
return [{"id": r.id, "port": r.port, "protocol": r.protocol, "action": r.action, "ps": r.ps} for r in rows]
|
||||
|
||||
|
||||
@router.post("/create")
|
||||
async def firewall_create(
|
||||
body: CreateFirewallRuleRequest,
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""Add firewall rule (stored in panel; use Apply to UFW to sync)"""
|
||||
if not body.port or len(body.port) > 32:
|
||||
raise HTTPException(status_code=400, detail="Invalid port")
|
||||
rule = FirewallRule(port=body.port, protocol=body.protocol, action=body.action, ps=body.ps)
|
||||
db.add(rule)
|
||||
await db.commit()
|
||||
return {"status": True, "msg": "Rule added", "id": rule.id}
|
||||
|
||||
|
||||
@router.delete("/{rule_id}")
|
||||
async def firewall_delete(
|
||||
rule_id: int,
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""Delete firewall rule"""
|
||||
result = await db.execute(select(FirewallRule).where(FirewallRule.id == rule_id))
|
||||
rule = result.scalar_one_or_none()
|
||||
if not rule:
|
||||
raise HTTPException(status_code=404, detail="Rule not found")
|
||||
await db.delete(rule)
|
||||
await db.commit()
|
||||
return {"status": True, "msg": "Rule deleted"}
|
||||
|
||||
|
||||
@router.post("/apply")
|
||||
async def firewall_apply(
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""Apply firewall rules to ufw (runs ufw allow/deny for each rule)"""
|
||||
result = await db.execute(select(FirewallRule).order_by(FirewallRule.id))
|
||||
rules = result.scalars().all()
|
||||
errors = []
|
||||
for r in rules:
|
||||
port_proto = f"{r.port}/{r.protocol}"
|
||||
action = "allow" if r.action == "accept" else "deny"
|
||||
cmd = f"ufw {action} {port_proto}"
|
||||
out, err = exec_shell_sync(cmd, timeout=10)
|
||||
if err and "error" in err.lower() and "already" not in err.lower():
|
||||
errors.append(f"{port_proto}: {err.strip()}")
|
||||
if errors:
|
||||
raise HTTPException(status_code=500, detail="; ".join(errors))
|
||||
return {"status": True, "msg": "Rules applied", "count": len(rules)}
|
||||
Reference in New Issue
Block a user