firewall_handlers.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. package api
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "net/http"
  6. "time"
  7. "debtik/internal/firewall"
  8. "debtik/internal/routing"
  9. )
  10. // ======================== Firewall Summary ========================
  11. func (s *Server) handleFirewallSummary(w http.ResponseWriter, r *http.Request) {
  12. writeJSON(w, http.StatusOK, s.fwMgr.GetSummary())
  13. }
  14. func (s *Server) handleResetFirewallCounters(w http.ResponseWriter, r *http.Request) {
  15. if err := s.fwMgr.ResetCounters(); err != nil {
  16. writeError(w, http.StatusInternalServerError, err.Error())
  17. return
  18. }
  19. writeJSON(w, http.StatusOK, map[string]any{"success": true})
  20. }
  21. // ======================== Filter Rules ========================
  22. func (s *Server) handleListFilterRules(w http.ResponseWriter, r *http.Request) {
  23. rules := s.fwMgr.ListFilterRules()
  24. writeJSON(w, http.StatusOK, rules)
  25. }
  26. func (s *Server) handleSaveFilterRule(w http.ResponseWriter, r *http.Request) {
  27. var rule firewall.FilterRule
  28. decoder := json.NewDecoder(r.Body)
  29. decoder.DisallowUnknownFields()
  30. if err := decoder.Decode(&rule); err != nil {
  31. writeError(w, http.StatusBadRequest, "invalid request body")
  32. return
  33. }
  34. if rule.ID == "" {
  35. rule.ID = fmt.Sprintf("filter-%d", time.Now().UnixNano())
  36. }
  37. if err := firewall.ValidateRule("filter", rule); err != nil {
  38. writeError(w, http.StatusUnprocessableEntity, err.Error())
  39. return
  40. }
  41. if err := s.fwMgr.SaveFilterRule(rule); err != nil {
  42. writeError(w, http.StatusInternalServerError, err.Error())
  43. return
  44. }
  45. writeJSON(w, http.StatusOK, map[string]any{"success": true, "id": rule.ID})
  46. }
  47. func (s *Server) handleDeleteFilterRule(w http.ResponseWriter, r *http.Request) {
  48. id := r.PathValue("id")
  49. if err := s.fwMgr.DeleteFilterRule(id); err != nil {
  50. writeError(w, http.StatusInternalServerError, err.Error())
  51. return
  52. }
  53. writeJSON(w, http.StatusOK, map[string]any{"success": true, "id": id})
  54. }
  55. func (s *Server) handleMoveFilterRule(w http.ResponseWriter, r *http.Request) {
  56. id := r.PathValue("id")
  57. var req struct {
  58. Direction string `json:"direction"` // "up" or "down"
  59. }
  60. _ = json.NewDecoder(r.Body).Decode(&req)
  61. if err := s.fwMgr.MoveFilterRule(id, req.Direction); err != nil {
  62. writeError(w, http.StatusInternalServerError, err.Error())
  63. return
  64. }
  65. writeJSON(w, http.StatusOK, map[string]any{"success": true, "id": id})
  66. }
  67. func (s *Server) handleSetFilterRuleState(w http.ResponseWriter, r *http.Request) {
  68. id := r.PathValue("id")
  69. var req struct {
  70. Disabled bool `json:"disabled"`
  71. }
  72. _ = json.NewDecoder(r.Body).Decode(&req)
  73. if err := s.fwMgr.SetFilterRuleState(id, req.Disabled); err != nil {
  74. writeError(w, http.StatusInternalServerError, err.Error())
  75. return
  76. }
  77. writeJSON(w, http.StatusOK, map[string]any{"success": true, "id": id, "disabled": req.Disabled})
  78. }
  79. // ======================== NAT Rules ========================
  80. func (s *Server) handleListNATRules(w http.ResponseWriter, r *http.Request) {
  81. rules := s.fwMgr.ListNATRules()
  82. writeJSON(w, http.StatusOK, rules)
  83. }
  84. func (s *Server) handleSaveNATRule(w http.ResponseWriter, r *http.Request) {
  85. var rule firewall.NATRule
  86. decoder := json.NewDecoder(r.Body)
  87. decoder.DisallowUnknownFields()
  88. if err := decoder.Decode(&rule); err != nil {
  89. writeError(w, http.StatusBadRequest, "invalid request body")
  90. return
  91. }
  92. if rule.ID == "" {
  93. rule.ID = fmt.Sprintf("nat-%d", time.Now().UnixNano())
  94. }
  95. if err := firewall.ValidateRule("nat", rule); err != nil {
  96. writeError(w, http.StatusUnprocessableEntity, err.Error())
  97. return
  98. }
  99. if err := s.fwMgr.SaveNATRule(rule); err != nil {
  100. writeError(w, http.StatusInternalServerError, err.Error())
  101. return
  102. }
  103. writeJSON(w, http.StatusOK, map[string]any{"success": true, "id": rule.ID})
  104. }
  105. func (s *Server) handleDeleteNATRule(w http.ResponseWriter, r *http.Request) {
  106. id := r.PathValue("id")
  107. if err := s.fwMgr.DeleteNATRule(id); err != nil {
  108. writeError(w, http.StatusInternalServerError, err.Error())
  109. return
  110. }
  111. writeJSON(w, http.StatusOK, map[string]any{"success": true, "id": id})
  112. }
  113. func (s *Server) handleMoveNATRule(w http.ResponseWriter, r *http.Request) {
  114. id := r.PathValue("id")
  115. var req struct {
  116. Direction string `json:"direction"`
  117. }
  118. _ = json.NewDecoder(r.Body).Decode(&req)
  119. if err := s.fwMgr.MoveNATRule(id, req.Direction); err != nil {
  120. writeError(w, http.StatusInternalServerError, err.Error())
  121. return
  122. }
  123. writeJSON(w, http.StatusOK, map[string]any{"success": true, "id": id})
  124. }
  125. func (s *Server) handleSetNATRuleState(w http.ResponseWriter, r *http.Request) {
  126. id := r.PathValue("id")
  127. var req struct {
  128. Disabled bool `json:"disabled"`
  129. }
  130. _ = json.NewDecoder(r.Body).Decode(&req)
  131. if err := s.fwMgr.SetNATRuleState(id, req.Disabled); err != nil {
  132. writeError(w, http.StatusInternalServerError, err.Error())
  133. return
  134. }
  135. writeJSON(w, http.StatusOK, map[string]any{"success": true, "id": id, "disabled": req.Disabled})
  136. }
  137. // ======================== Mangle Rules ========================
  138. func (s *Server) handleListMangleRules(w http.ResponseWriter, r *http.Request) {
  139. rules := s.fwMgr.ListMangleRules()
  140. writeJSON(w, http.StatusOK, rules)
  141. }
  142. func (s *Server) handleSaveMangleRule(w http.ResponseWriter, r *http.Request) {
  143. var rule firewall.MangleRule
  144. decoder := json.NewDecoder(r.Body)
  145. decoder.DisallowUnknownFields()
  146. if err := decoder.Decode(&rule); err != nil {
  147. writeError(w, http.StatusBadRequest, "invalid request body")
  148. return
  149. }
  150. if rule.ID == "" {
  151. rule.ID = fmt.Sprintf("mangle-%d", time.Now().UnixNano())
  152. }
  153. if err := firewall.ValidateRule("mangle", rule); err != nil {
  154. writeError(w, http.StatusUnprocessableEntity, err.Error())
  155. return
  156. }
  157. if err := s.fwMgr.SaveMangleRule(rule); err != nil {
  158. writeError(w, http.StatusInternalServerError, err.Error())
  159. return
  160. }
  161. writeJSON(w, http.StatusOK, map[string]any{"success": true, "id": rule.ID})
  162. }
  163. func (s *Server) handleDeleteMangleRule(w http.ResponseWriter, r *http.Request) {
  164. id := r.PathValue("id")
  165. if err := s.fwMgr.DeleteMangleRule(id); err != nil {
  166. writeError(w, http.StatusInternalServerError, err.Error())
  167. return
  168. }
  169. writeJSON(w, http.StatusOK, map[string]any{"success": true, "id": id})
  170. }
  171. func (s *Server) handleMoveMangleRule(w http.ResponseWriter, r *http.Request) {
  172. id := r.PathValue("id")
  173. var req struct {
  174. Direction string `json:"direction"`
  175. }
  176. _ = json.NewDecoder(r.Body).Decode(&req)
  177. if err := s.fwMgr.MoveMangleRule(id, req.Direction); err != nil {
  178. writeError(w, http.StatusInternalServerError, err.Error())
  179. return
  180. }
  181. writeJSON(w, http.StatusOK, map[string]any{"success": true, "id": id})
  182. }
  183. func (s *Server) handleSetMangleRuleState(w http.ResponseWriter, r *http.Request) {
  184. id := r.PathValue("id")
  185. var req struct {
  186. Disabled bool `json:"disabled"`
  187. }
  188. _ = json.NewDecoder(r.Body).Decode(&req)
  189. if err := s.fwMgr.SetMangleRuleState(id, req.Disabled); err != nil {
  190. writeError(w, http.StatusInternalServerError, err.Error())
  191. return
  192. }
  193. writeJSON(w, http.StatusOK, map[string]any{"success": true, "id": id, "disabled": req.Disabled})
  194. }
  195. // ======================== Address Lists ========================
  196. func (s *Server) handleListAddressLists(w http.ResponseWriter, r *http.Request) {
  197. lists := s.fwMgr.ListAddressLists()
  198. writeJSON(w, http.StatusOK, lists)
  199. }
  200. func (s *Server) handleSaveAddressListEntry(w http.ResponseWriter, r *http.Request) {
  201. var entry firewall.AddressListEntry
  202. if err := json.NewDecoder(r.Body).Decode(&entry); err != nil || entry.Name == "" || entry.Address == "" {
  203. writeError(w, http.StatusBadRequest, "name and address are required")
  204. return
  205. }
  206. if err := s.fwMgr.SaveAddressListEntry(entry); err != nil {
  207. writeError(w, http.StatusInternalServerError, err.Error())
  208. return
  209. }
  210. writeJSON(w, http.StatusOK, map[string]any{"success": true, "id": entry.ID})
  211. }
  212. func (s *Server) handleDeleteAddressListEntry(w http.ResponseWriter, r *http.Request) {
  213. id := r.PathValue("id")
  214. if err := s.fwMgr.DeleteAddressListEntry(id); err != nil {
  215. writeError(w, http.StatusInternalServerError, err.Error())
  216. return
  217. }
  218. writeJSON(w, http.StatusOK, map[string]any{"success": true, "id": id})
  219. }
  220. func (s *Server) handleSetAddressListEntryState(w http.ResponseWriter, r *http.Request) {
  221. id := r.PathValue("id")
  222. var req struct {
  223. Disabled bool `json:"disabled"`
  224. }
  225. _ = json.NewDecoder(r.Body).Decode(&req)
  226. if err := s.fwMgr.SetAddressListEntryState(id, req.Disabled); err != nil {
  227. writeError(w, http.StatusInternalServerError, err.Error())
  228. return
  229. }
  230. writeJSON(w, http.StatusOK, map[string]any{"success": true, "id": id, "disabled": req.Disabled})
  231. }
  232. // ======================== Geo-Location ========================
  233. func (s *Server) handleGeoIPStatus(w http.ResponseWriter, r *http.Request) {
  234. status := s.fwMgr.GeoIP().GetStatus()
  235. writeJSON(w, http.StatusOK, status)
  236. }
  237. func (s *Server) handleGeoIPConfig(w http.ResponseWriter, r *http.Request) {
  238. var req struct {
  239. AutoUpdate bool `json:"auto_update"`
  240. IntervalHours int `json:"interval_hours"`
  241. }
  242. if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
  243. writeError(w, http.StatusBadRequest, "invalid request body")
  244. return
  245. }
  246. if err := s.fwMgr.GeoIP().SetConfig(req.AutoUpdate, req.IntervalHours); err != nil {
  247. writeError(w, http.StatusInternalServerError, err.Error())
  248. return
  249. }
  250. writeJSON(w, http.StatusOK, map[string]any{"success": true, "config": req})
  251. }
  252. func (s *Server) handleListGeoCountries(w http.ResponseWriter, r *http.Request) {
  253. countries := s.fwMgr.GeoIP().ListCountries()
  254. writeJSON(w, http.StatusOK, countries)
  255. }
  256. func (s *Server) handleToggleGeoCountry(w http.ResponseWriter, r *http.Request) {
  257. code := r.PathValue("code")
  258. var req struct {
  259. Enabled bool `json:"enabled"`
  260. }
  261. _ = json.NewDecoder(r.Body).Decode(&req)
  262. if err := s.fwMgr.GeoIP().ToggleCountry(code, req.Enabled); err != nil {
  263. writeError(w, http.StatusInternalServerError, err.Error())
  264. return
  265. }
  266. writeJSON(w, http.StatusOK, map[string]any{"success": true, "code": code, "enabled": req.Enabled})
  267. }
  268. func (s *Server) handleUpdateGeoCountry(w http.ResponseWriter, r *http.Request) {
  269. code := r.PathValue("code")
  270. count, err := s.fwMgr.GeoIP().SyncCountryZone(code)
  271. if err != nil {
  272. writeError(w, http.StatusInternalServerError, err.Error())
  273. return
  274. }
  275. writeJSON(w, http.StatusOK, map[string]any{"success": true, "code": code, "subnets": count})
  276. }
  277. func (s *Server) handleGeoIPUpdateNow(w http.ResponseWriter, r *http.Request) {
  278. if err := s.fwMgr.GeoIP().UpdateNow(); err != nil {
  279. writeError(w, http.StatusConflict, err.Error())
  280. return
  281. }
  282. writeJSON(w, http.StatusOK, map[string]any{"success": true, "message": "update started"})
  283. }
  284. func (s *Server) handleGeoIPBatchToggle(w http.ResponseWriter, r *http.Request) {
  285. var req struct {
  286. Preset string `json:"preset"`
  287. Codes []string `json:"codes"`
  288. Enabled bool `json:"enabled"`
  289. }
  290. if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
  291. writeError(w, http.StatusBadRequest, "invalid request body")
  292. return
  293. }
  294. if err := s.fwMgr.GeoIP().BatchToggle(req.Preset, req.Enabled, req.Codes); err != nil {
  295. writeError(w, http.StatusInternalServerError, err.Error())
  296. return
  297. }
  298. writeJSON(w, http.StatusOK, map[string]any{"success": true, "preset": req.Preset, "enabled": req.Enabled})
  299. }
  300. func (s *Server) handleGeoCountrySubnets(w http.ResponseWriter, r *http.Request) {
  301. code := r.PathValue("code")
  302. subnets := s.fwMgr.GeoIP().GetCountrySubnets(code, 100)
  303. writeJSON(w, http.StatusOK, map[string]any{"code": code, "subnets": subnets})
  304. }
  305. // ======================== Active Connections ========================
  306. func (s *Server) handleListConnections(w http.ResponseWriter, r *http.Request) {
  307. conns, err := firewall.ListActiveConnections()
  308. if err != nil {
  309. writeError(w, http.StatusInternalServerError, err.Error())
  310. return
  311. }
  312. writeJSON(w, http.StatusOK, conns)
  313. }
  314. func (s *Server) handleKillConnection(w http.ResponseWriter, r *http.Request) {
  315. var req firewall.ConnectionSelector
  316. if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
  317. writeError(w, http.StatusBadRequest, "invalid request body")
  318. return
  319. }
  320. if err := firewall.ValidateConnectionSelector(req); err != nil {
  321. writeError(w, http.StatusUnprocessableEntity, err.Error())
  322. return
  323. }
  324. if err := firewall.KillFlow(req); err != nil {
  325. writeError(w, http.StatusInternalServerError, err.Error())
  326. return
  327. }
  328. writeJSON(w, http.StatusOK, map[string]any{"success": true})
  329. }
  330. // ======================== Routes & Policy Routing ========================
  331. func (s *Server) handleListRoutes(w http.ResponseWriter, r *http.Request) {
  332. routes := s.routingMgr.ListRoutes()
  333. writeJSON(w, http.StatusOK, routes)
  334. }
  335. func (s *Server) handleSaveRoute(w http.ResponseWriter, r *http.Request) {
  336. var route routing.RouteItem
  337. if err := json.NewDecoder(r.Body).Decode(&route); err != nil {
  338. writeError(w, http.StatusBadRequest, "invalid request body")
  339. return
  340. }
  341. if err := s.routingMgr.SaveRoute(route); err != nil {
  342. writeError(w, http.StatusInternalServerError, err.Error())
  343. return
  344. }
  345. writeJSON(w, http.StatusOK, map[string]any{"success": true, "id": route.ID})
  346. }
  347. func (s *Server) handleDeleteRoute(w http.ResponseWriter, r *http.Request) {
  348. id := r.PathValue("id")
  349. if err := s.routingMgr.DeleteRoute(id); err != nil {
  350. writeError(w, http.StatusInternalServerError, err.Error())
  351. return
  352. }
  353. writeJSON(w, http.StatusOK, map[string]any{"success": true, "id": id})
  354. }
  355. func (s *Server) handleSetRouteState(w http.ResponseWriter, r *http.Request) {
  356. id := r.PathValue("id")
  357. var req struct {
  358. Disabled bool `json:"disabled"`
  359. }
  360. _ = json.NewDecoder(r.Body).Decode(&req)
  361. if err := s.routingMgr.SetRouteState(id, req.Disabled); err != nil {
  362. writeError(w, http.StatusInternalServerError, err.Error())
  363. return
  364. }
  365. writeJSON(w, http.StatusOK, map[string]any{"success": true, "id": id, "disabled": req.Disabled})
  366. }
  367. func (s *Server) handleListPolicyRules(w http.ResponseWriter, r *http.Request) {
  368. rules := s.routingMgr.ListPolicyRules()
  369. writeJSON(w, http.StatusOK, rules)
  370. }
  371. func (s *Server) handleSavePolicyRule(w http.ResponseWriter, r *http.Request) {
  372. var rule routing.PolicyRuleItem
  373. if err := json.NewDecoder(r.Body).Decode(&rule); err != nil {
  374. writeError(w, http.StatusBadRequest, "invalid request body")
  375. return
  376. }
  377. if err := s.routingMgr.SavePolicyRule(rule); err != nil {
  378. writeError(w, http.StatusInternalServerError, err.Error())
  379. return
  380. }
  381. writeJSON(w, http.StatusOK, map[string]any{"success": true, "id": rule.ID})
  382. }
  383. func (s *Server) handleDeletePolicyRule(w http.ResponseWriter, r *http.Request) {
  384. id := r.PathValue("id")
  385. if err := s.routingMgr.DeletePolicyRule(id); err != nil {
  386. writeError(w, http.StatusInternalServerError, err.Error())
  387. return
  388. }
  389. writeJSON(w, http.StatusOK, map[string]any{"success": true, "id": id})
  390. }
  391. func (s *Server) handleListRoutingTables(w http.ResponseWriter, r *http.Request) {
  392. tables := s.routingMgr.ListTables()
  393. writeJSON(w, http.StatusOK, tables)
  394. }
  395. func (s *Server) handleSaveRoutingTable(w http.ResponseWriter, r *http.Request) {
  396. var table routing.RoutingTableItem
  397. if err := json.NewDecoder(r.Body).Decode(&table); err != nil || table.Name == "" {
  398. writeError(w, http.StatusBadRequest, "table name is required")
  399. return
  400. }
  401. if err := s.routingMgr.SaveTable(table); err != nil {
  402. writeError(w, http.StatusInternalServerError, err.Error())
  403. return
  404. }
  405. writeJSON(w, http.StatusOK, map[string]any{"success": true, "name": table.Name})
  406. }
  407. func (s *Server) handleFirewallCapabilities(w http.ResponseWriter, r *http.Request) {
  408. writeJSON(w, 200, firewall.Capabilities())
  409. }
  410. func (s *Server) handleResetRuleCounters(w http.ResponseWriter, r *http.Request) {
  411. if err := s.fwMgr.ResetRuleCounters(r.PathValue("kind"), r.PathValue("id")); err != nil {
  412. writeError(w, 400, err.Error())
  413. return
  414. }
  415. writeJSON(w, 200, map[string]any{"success": true})
  416. }