feat: add netplan configuration models and backend API for applying netplan
All checks were successful
Build and Deploy MITM Webserver / build (push) Successful in 8s
All checks were successful
Build and Deploy MITM Webserver / build (push) Successful in 8s
This commit is contained in:
36
backend/src/Models/netplan.py
Normal file
36
backend/src/Models/netplan.py
Normal file
@@ -0,0 +1,36 @@
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import List, Dict, Optional
|
||||
|
||||
|
||||
class Nameservers(BaseModel):
|
||||
addresses: List[str] = Field(default_factory=list)
|
||||
search: List[str] = Field(default_factory=list)
|
||||
|
||||
|
||||
class EthernetConfig(BaseModel):
|
||||
dhcp4: Optional[bool] = None
|
||||
dhcp6: Optional[bool] = None
|
||||
addresses: Optional[List[str]] = None
|
||||
gateway4: Optional[str] = None
|
||||
gateway6: Optional[str] = None
|
||||
nameservers: Optional[Nameservers] = None
|
||||
optional: Optional[bool] = None
|
||||
|
||||
|
||||
class BridgeConfig(BaseModel):
|
||||
interfaces: List[str] = Field(default_factory=list) # ["eth1", "eth2"]
|
||||
dhcp4: Optional[bool] = None
|
||||
dhcp6: Optional[bool] = None
|
||||
addresses: Optional[List[str]] = None
|
||||
gateway4: Optional[str] = None
|
||||
gateway6: Optional[str] = None
|
||||
nameservers: Optional[Nameservers] = None
|
||||
parameters: Optional[dict] = None # allows spanning-tree, port-priority, forward-delay, etc.
|
||||
optional: Optional[bool] = None
|
||||
|
||||
|
||||
class NetworkConfig(BaseModel):
|
||||
version: int = 2
|
||||
renderer: Optional[str] = "networkd"
|
||||
ethernets: Dict[str, EthernetConfig] = Field(default_factory=dict)
|
||||
bridges: Dict[str, BridgeConfig] = Field(default_factory=dict)
|
||||
@@ -1,3 +1,6 @@
|
||||
from asyncio import subprocess
|
||||
from http.client import HTTPException
|
||||
import src.Models.netplan as netplan
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
import os
|
||||
@@ -25,4 +28,33 @@ def read_root():
|
||||
@app.get("/api/nft/ruleset")
|
||||
def read_root():
|
||||
message = os.popen("sudo nft --json list ruleset").read().strip()
|
||||
return {"message": message}
|
||||
return {"message": message}
|
||||
|
||||
|
||||
NETPLAN_FILE = "/etc/netplan/99-app.yaml"
|
||||
|
||||
@app.post("/netplan/apply")
|
||||
def apply_netplan(config: netplan.NetworkConfig):
|
||||
try:
|
||||
data = {"network": config.dict(exclude_none=True)}
|
||||
|
||||
with open(NETPLAN_FILE, "w") as f:
|
||||
yaml.dump(data, f, sort_keys=False)
|
||||
|
||||
# Safety: rollback if config breaks connectivity
|
||||
result = subprocess.run(
|
||||
["netplan", "try", "--timeout", "10"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
|
||||
if result.returncode != 0:
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f"Netplan failed: {result.stderr}"
|
||||
)
|
||||
|
||||
return {"status": "ok", "output": result.stdout}
|
||||
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
92
start_dev.sh
Executable file
92
start_dev.sh
Executable file
@@ -0,0 +1,92 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
# Kill children on exit
|
||||
cleanup() {
|
||||
echo
|
||||
echo "🛑 Stopping dev environment…"
|
||||
kill 0 2>/dev/null
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
START_BE=true
|
||||
START_FE=true
|
||||
|
||||
case "$1" in
|
||||
backend|be)
|
||||
START_FE=false
|
||||
;;
|
||||
frontend|fe)
|
||||
START_BE=false
|
||||
;;
|
||||
"" )
|
||||
# default → both
|
||||
;;
|
||||
* )
|
||||
echo "❌ Unknown argument: $1"
|
||||
echo "Usage:"
|
||||
echo " ./dev.sh → start both"
|
||||
echo " ./dev.sh be → start backend only"
|
||||
echo " ./dev.sh fe → start frontend only"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
|
||||
###############################
|
||||
# Backend
|
||||
###############################
|
||||
if [ "$START_BE" = true ]; then
|
||||
(
|
||||
echo "🚀 Starting backend…"
|
||||
cd backend || { echo "❌ backend folder missing"; exit 1; }
|
||||
|
||||
if [ ! -d venv ]; then
|
||||
echo "⚙️ Creating virtual environment…"
|
||||
python3 -m venv venv
|
||||
./venv/bin/pip install -r requirements.txt
|
||||
fi
|
||||
|
||||
echo "📡 Backend at http://localhost:8000"
|
||||
./venv/bin/uvicorn src.main:app --reload --host 0.0.0.0 --port 8000
|
||||
) &
|
||||
BE_PID=$!
|
||||
else
|
||||
echo "⏭ Skipping backend"
|
||||
fi
|
||||
|
||||
|
||||
###############################
|
||||
# Frontend
|
||||
###############################
|
||||
if [ "$START_FE" = true ]; then
|
||||
(
|
||||
echo "🚀 Starting frontend…"
|
||||
cd frontend || { echo "❌ frontend folder missing"; exit 1; }
|
||||
|
||||
if [ ! -d node_modules ]; then
|
||||
echo "📦 Installing frontend dependencies…"
|
||||
npm install
|
||||
fi
|
||||
|
||||
echo "🌍 Frontend at http://localhost:5173"
|
||||
npm run dev-local
|
||||
) &
|
||||
FE_PID=$!
|
||||
else
|
||||
echo "⏭ Skipping frontend"
|
||||
fi
|
||||
|
||||
|
||||
###############################
|
||||
# Wait for both
|
||||
###############################
|
||||
echo
|
||||
echo "💡 Dev environment running."
|
||||
[ "$START_BE" = true ] && echo " Backend PID: $BE_PID"
|
||||
[ "$START_FE" = true ] && echo " Frontend PID: $FE_PID"
|
||||
echo " Press CTRL+C to stop everything."
|
||||
echo
|
||||
|
||||
wait
|
||||
Reference in New Issue
Block a user