Files
alpine-router/handlers/firewall.go
2026-04-15 11:38:26 +03:00

101 lines
2.3 KiB
Go

package handlers
import (
"encoding/json"
"fmt"
"math/rand"
"net/http"
"nano-router/config"
"nano-router/nat"
"nano-router/network"
)
func HandleFirewall(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
handleFirewallGet(w, r)
case http.MethodPost:
handleFirewallSave(w, r)
default:
fail(w, http.StatusMethodNotAllowed, "method not allowed")
}
}
func handleFirewallGet(w http.ResponseWriter, r *http.Request) {
cfg, err := config.Load()
if err != nil {
fail(w, http.StatusInternalServerError, err.Error())
return
}
// Collect interface names for the UI dropdowns.
names, _ := network.GetInterfaces()
ok(w, map[string]interface{}{
"installed": nat.IsInstalled(),
"rules": cfg.Firewall.Rules,
"vlan_isolation": cfg.Firewall.VLANIsolation,
"interfaces": names,
})
}
func handleFirewallSave(w http.ResponseWriter, r *http.Request) {
var body struct {
Rules []config.FirewallRule `json:"rules"`
VLANIsolation bool `json:"vlan_isolation"`
}
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
fail(w, http.StatusBadRequest, "invalid json: "+err.Error())
return
}
// Assign IDs to new rules that lack one.
for i := range body.Rules {
if body.Rules[i].ID == "" {
body.Rules[i].ID = fmt.Sprintf("%x", rand.Int63())
}
}
cfg, err := config.Load()
if err != nil {
fail(w, http.StatusInternalServerError, "load config: "+err.Error())
return
}
cfg.Firewall.Rules = body.Rules
cfg.Firewall.VLANIsolation = body.VLANIsolation
if err := config.Save(cfg); err != nil {
fail(w, http.StatusInternalServerError, "save config: "+err.Error())
return
}
ok(w, map[string]string{"message": "saved"})
}
func HandleFirewallApply(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
fail(w, http.StatusMethodNotAllowed, "method not allowed")
return
}
if !nat.IsInstalled() {
fail(w, http.StatusServiceUnavailable, "nftables (nft) не установлен — выполните: apk add nftables")
return
}
cfg, err := config.Load()
if err != nil {
fail(w, http.StatusInternalServerError, "load config: "+err.Error())
return
}
if err := applyAllRules(cfg); err != nil {
fail(w, http.StatusInternalServerError, "apply: "+err.Error())
return
}
ok(w, map[string]string{"message": "firewall applied"})
}