manager.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  1. package firewall
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "os"
  6. "os/exec"
  7. "sync"
  8. "time"
  9. )
  10. const (
  11. FilterStorePath = "/etc/proxmox-router/firewall/filter.json"
  12. NATStorePath = "/etc/proxmox-router/firewall/nat.json"
  13. MangleStorePath = "/etc/proxmox-router/firewall/mangle.json"
  14. AddressListsStorePath = "/etc/proxmox-router/firewall/address_lists.json"
  15. )
  16. type FirewallManager struct {
  17. mu sync.Mutex
  18. filterRules []FilterRule
  19. natRules []NATRule
  20. mangleRules []MangleRule
  21. addressLists []AddressListEntry
  22. geoIPMgr *GeoIPManager
  23. }
  24. func NewFirewallManager() *FirewallManager {
  25. m := &FirewallManager{
  26. filterRules: make([]FilterRule, 0),
  27. natRules: make([]NATRule, 0),
  28. mangleRules: make([]MangleRule, 0),
  29. addressLists: make([]AddressListEntry, 0),
  30. geoIPMgr: NewGeoIPManager(),
  31. }
  32. m.load()
  33. // If clean installation and no rules exist, initialize default sensible router rules (like RouterOS default firewall)
  34. if len(m.filterRules) == 0 && len(m.natRules) == 0 {
  35. m.initDefaultRules()
  36. }
  37. _ = m.Apply()
  38. return m
  39. }
  40. func (m *FirewallManager) initDefaultRules() {
  41. // Default Masquerade NAT
  42. m.natRules = append(m.natRules, NATRule{
  43. ID: fmt.Sprintf("nat-%d", time.Now().UnixNano()),
  44. Order: 1,
  45. Chain: "srcnat",
  46. Action: "masquerade",
  47. Comment: "defconf: masquerade outgoing traffic",
  48. Disabled: false,
  49. })
  50. // Default Fasttrack / Established,Related Filter
  51. m.filterRules = append(m.filterRules, FilterRule{
  52. ID: fmt.Sprintf("filter-%d-1", time.Now().UnixNano()),
  53. Order: 1,
  54. Chain: "input",
  55. Action: "accept",
  56. ConnectionState: []string{"established", "related"},
  57. Comment: "defconf: accept established,related",
  58. Disabled: false,
  59. })
  60. m.filterRules = append(m.filterRules, FilterRule{
  61. ID: fmt.Sprintf("filter-%d-2", time.Now().UnixNano()),
  62. Order: 2,
  63. Chain: "forward",
  64. Action: "accept",
  65. ConnectionState: []string{"established", "related"},
  66. Comment: "defconf: accept established,related forward",
  67. Disabled: false,
  68. })
  69. _ = m.save()
  70. }
  71. func (m *FirewallManager) load() {
  72. if data, err := os.ReadFile(FilterStorePath); err == nil {
  73. _ = json.Unmarshal(data, &m.filterRules)
  74. }
  75. if data, err := os.ReadFile(NATStorePath); err == nil {
  76. _ = json.Unmarshal(data, &m.natRules)
  77. }
  78. if data, err := os.ReadFile(MangleStorePath); err == nil {
  79. _ = json.Unmarshal(data, &m.mangleRules)
  80. }
  81. if data, err := os.ReadFile(AddressListsStorePath); err == nil {
  82. _ = json.Unmarshal(data, &m.addressLists)
  83. }
  84. }
  85. func (m *FirewallManager) save() error {
  86. _ = os.MkdirAll("/etc/proxmox-router/firewall", 0755)
  87. if d, err := json.MarshalIndent(m.filterRules, "", " "); err == nil {
  88. _ = os.WriteFile(FilterStorePath, d, 0600)
  89. }
  90. if d, err := json.MarshalIndent(m.natRules, "", " "); err == nil {
  91. _ = os.WriteFile(NATStorePath, d, 0600)
  92. }
  93. if d, err := json.MarshalIndent(m.mangleRules, "", " "); err == nil {
  94. _ = os.WriteFile(MangleStorePath, d, 0600)
  95. }
  96. if d, err := json.MarshalIndent(m.addressLists, "", " "); err == nil {
  97. _ = os.WriteFile(AddressListsStorePath, d, 0600)
  98. }
  99. return nil
  100. }
  101. // Apply re-synchronizes AddressLists & GeoIP sets with ipset and applies iptables
  102. func (m *FirewallManager) Apply() error {
  103. // Sync GeoIP country sets
  104. if m.geoIPMgr != nil {
  105. _ = m.geoIPMgr.ApplyAllEnabledSets()
  106. }
  107. // Sync custom ipset address lists
  108. _ = SyncAddressLists(m.addressLists)
  109. payload := BuildIPTablesRestore(m.filterRules, m.natRules, m.mangleRules)
  110. return ApplyIPTables(payload)
  111. }
  112. func (m *FirewallManager) GeoIP() *GeoIPManager {
  113. return m.geoIPMgr
  114. }
  115. // ==================== Filter Rules ====================
  116. func (m *FirewallManager) ListFilterRules() []FilterRule {
  117. m.mu.Lock()
  118. defer m.mu.Unlock()
  119. counters, _ := ReadRuleCounters(m.filterRules, m.natRules, m.mangleRules)
  120. res := make([]FilterRule, len(m.filterRules))
  121. for i, r := range m.filterRules {
  122. res[i] = r
  123. if c, ok := counters[r.ID]; ok {
  124. res[i].Packets = c[0]
  125. res[i].Bytes = c[1]
  126. }
  127. }
  128. return res
  129. }
  130. func (m *FirewallManager) SaveFilterRule(r FilterRule) error {
  131. m.mu.Lock()
  132. defer m.mu.Unlock()
  133. if r.ID == "" {
  134. r.ID = fmt.Sprintf("filter-%d", time.Now().UnixNano())
  135. }
  136. found := false
  137. for i, existing := range m.filterRules {
  138. if existing.ID == r.ID {
  139. r.Order = existing.Order
  140. m.filterRules[i] = r
  141. found = true
  142. break
  143. }
  144. }
  145. if !found {
  146. r.Order = len(m.filterRules) + 1
  147. m.filterRules = append(m.filterRules, r)
  148. }
  149. _ = m.save()
  150. return m.Apply()
  151. }
  152. func (m *FirewallManager) DeleteFilterRule(id string) error {
  153. m.mu.Lock()
  154. defer m.mu.Unlock()
  155. var updated []FilterRule
  156. for _, r := range m.filterRules {
  157. if r.ID != id {
  158. updated = append(updated, r)
  159. }
  160. }
  161. for i := range updated {
  162. updated[i].Order = i + 1
  163. }
  164. m.filterRules = updated
  165. _ = m.save()
  166. return m.Apply()
  167. }
  168. func (m *FirewallManager) MoveFilterRule(id string, dir string) error {
  169. m.mu.Lock()
  170. defer m.mu.Unlock()
  171. idx := -1
  172. for i, r := range m.filterRules {
  173. if r.ID == id {
  174. idx = i
  175. break
  176. }
  177. }
  178. if idx == -1 {
  179. return fmt.Errorf("rule not found")
  180. }
  181. if dir == "up" && idx > 0 {
  182. m.filterRules[idx], m.filterRules[idx-1] = m.filterRules[idx-1], m.filterRules[idx]
  183. } else if dir == "down" && idx < len(m.filterRules)-1 {
  184. m.filterRules[idx], m.filterRules[idx+1] = m.filterRules[idx+1], m.filterRules[idx]
  185. }
  186. for i := range m.filterRules {
  187. m.filterRules[i].Order = i + 1
  188. }
  189. _ = m.save()
  190. return m.Apply()
  191. }
  192. func (m *FirewallManager) SetFilterRuleState(id string, disabled bool) error {
  193. m.mu.Lock()
  194. defer m.mu.Unlock()
  195. for i := range m.filterRules {
  196. if m.filterRules[i].ID == id {
  197. m.filterRules[i].Disabled = disabled
  198. _ = m.save()
  199. return m.Apply()
  200. }
  201. }
  202. return fmt.Errorf("rule not found")
  203. }
  204. // ==================== NAT Rules ====================
  205. func (m *FirewallManager) ListNATRules() []NATRule {
  206. m.mu.Lock()
  207. defer m.mu.Unlock()
  208. counters, _ := ReadRuleCounters(m.filterRules, m.natRules, m.mangleRules)
  209. res := make([]NATRule, len(m.natRules))
  210. for i, r := range m.natRules {
  211. res[i] = r
  212. if c, ok := counters[r.ID]; ok {
  213. res[i].Packets = c[0]
  214. res[i].Bytes = c[1]
  215. }
  216. }
  217. return res
  218. }
  219. func (m *FirewallManager) SaveNATRule(r NATRule) error {
  220. m.mu.Lock()
  221. defer m.mu.Unlock()
  222. if r.ID == "" {
  223. r.ID = fmt.Sprintf("nat-%d", time.Now().UnixNano())
  224. }
  225. found := false
  226. for i, existing := range m.natRules {
  227. if existing.ID == r.ID {
  228. r.Order = existing.Order
  229. m.natRules[i] = r
  230. found = true
  231. break
  232. }
  233. }
  234. if !found {
  235. r.Order = len(m.natRules) + 1
  236. m.natRules = append(m.natRules, r)
  237. }
  238. _ = m.save()
  239. return m.Apply()
  240. }
  241. func (m *FirewallManager) DeleteNATRule(id string) error {
  242. m.mu.Lock()
  243. defer m.mu.Unlock()
  244. var updated []NATRule
  245. for _, r := range m.natRules {
  246. if r.ID != id {
  247. updated = append(updated, r)
  248. }
  249. }
  250. for i := range updated {
  251. updated[i].Order = i + 1
  252. }
  253. m.natRules = updated
  254. _ = m.save()
  255. return m.Apply()
  256. }
  257. func (m *FirewallManager) MoveNATRule(id string, dir string) error {
  258. m.mu.Lock()
  259. defer m.mu.Unlock()
  260. idx := -1
  261. for i, r := range m.natRules {
  262. if r.ID == id {
  263. idx = i
  264. break
  265. }
  266. }
  267. if idx == -1 {
  268. return fmt.Errorf("rule not found")
  269. }
  270. if dir == "up" && idx > 0 {
  271. m.natRules[idx], m.natRules[idx-1] = m.natRules[idx-1], m.natRules[idx]
  272. } else if dir == "down" && idx < len(m.natRules)-1 {
  273. m.natRules[idx], m.natRules[idx+1] = m.natRules[idx+1], m.natRules[idx]
  274. }
  275. for i := range m.natRules {
  276. m.natRules[i].Order = i + 1
  277. }
  278. _ = m.save()
  279. return m.Apply()
  280. }
  281. func (m *FirewallManager) SetNATRuleState(id string, disabled bool) error {
  282. m.mu.Lock()
  283. defer m.mu.Unlock()
  284. for i := range m.natRules {
  285. if m.natRules[i].ID == id {
  286. m.natRules[i].Disabled = disabled
  287. _ = m.save()
  288. return m.Apply()
  289. }
  290. }
  291. return fmt.Errorf("rule not found")
  292. }
  293. // ==================== Mangle Rules ====================
  294. func (m *FirewallManager) ListMangleRules() []MangleRule {
  295. m.mu.Lock()
  296. defer m.mu.Unlock()
  297. counters, _ := ReadRuleCounters(m.filterRules, m.natRules, m.mangleRules)
  298. res := make([]MangleRule, len(m.mangleRules))
  299. for i, r := range m.mangleRules {
  300. res[i] = r
  301. if c, ok := counters[r.ID]; ok {
  302. res[i].Packets = c[0]
  303. res[i].Bytes = c[1]
  304. }
  305. }
  306. return res
  307. }
  308. func (m *FirewallManager) SaveMangleRule(r MangleRule) error {
  309. m.mu.Lock()
  310. defer m.mu.Unlock()
  311. if r.ID == "" {
  312. r.ID = fmt.Sprintf("mangle-%d", time.Now().UnixNano())
  313. }
  314. found := false
  315. for i, existing := range m.mangleRules {
  316. if existing.ID == r.ID {
  317. r.Order = existing.Order
  318. m.mangleRules[i] = r
  319. found = true
  320. break
  321. }
  322. }
  323. if !found {
  324. r.Order = len(m.mangleRules) + 1
  325. m.mangleRules = append(m.mangleRules, r)
  326. }
  327. _ = m.save()
  328. return m.Apply()
  329. }
  330. func (m *FirewallManager) DeleteMangleRule(id string) error {
  331. m.mu.Lock()
  332. defer m.mu.Unlock()
  333. var updated []MangleRule
  334. for _, r := range m.mangleRules {
  335. if r.ID != id {
  336. updated = append(updated, r)
  337. }
  338. }
  339. for i := range updated {
  340. updated[i].Order = i + 1
  341. }
  342. m.mangleRules = updated
  343. _ = m.save()
  344. return m.Apply()
  345. }
  346. func (m *FirewallManager) MoveMangleRule(id string, dir string) error {
  347. m.mu.Lock()
  348. defer m.mu.Unlock()
  349. idx := -1
  350. for i, r := range m.mangleRules {
  351. if r.ID == id {
  352. idx = i
  353. break
  354. }
  355. }
  356. if idx == -1 {
  357. return fmt.Errorf("rule not found")
  358. }
  359. if dir == "up" && idx > 0 {
  360. m.mangleRules[idx], m.mangleRules[idx-1] = m.mangleRules[idx-1], m.mangleRules[idx]
  361. } else if dir == "down" && idx < len(m.mangleRules)-1 {
  362. m.mangleRules[idx], m.mangleRules[idx+1] = m.mangleRules[idx+1], m.mangleRules[idx]
  363. }
  364. for i := range m.mangleRules {
  365. m.mangleRules[i].Order = i + 1
  366. }
  367. _ = m.save()
  368. return m.Apply()
  369. }
  370. func (m *FirewallManager) SetMangleRuleState(id string, disabled bool) error {
  371. m.mu.Lock()
  372. defer m.mu.Unlock()
  373. for i := range m.mangleRules {
  374. if m.mangleRules[i].ID == id {
  375. m.mangleRules[i].Disabled = disabled
  376. _ = m.save()
  377. return m.Apply()
  378. }
  379. }
  380. return fmt.Errorf("rule not found")
  381. }
  382. // ==================== Address Lists ====================
  383. func (m *FirewallManager) ListAddressLists() []AddressListEntry {
  384. m.mu.Lock()
  385. defer m.mu.Unlock()
  386. res := make([]AddressListEntry, len(m.addressLists))
  387. copy(res, m.addressLists)
  388. return res
  389. }
  390. func (m *FirewallManager) SaveAddressListEntry(e AddressListEntry) error {
  391. m.mu.Lock()
  392. defer m.mu.Unlock()
  393. if e.ID == "" {
  394. e.ID = fmt.Sprintf("addrlist-%d", time.Now().UnixNano())
  395. }
  396. found := false
  397. for i, existing := range m.addressLists {
  398. if existing.ID == e.ID {
  399. m.addressLists[i] = e
  400. found = true
  401. break
  402. }
  403. }
  404. if !found {
  405. m.addressLists = append(m.addressLists, e)
  406. }
  407. _ = m.save()
  408. return m.Apply()
  409. }
  410. func (m *FirewallManager) DeleteAddressListEntry(id string) error {
  411. m.mu.Lock()
  412. defer m.mu.Unlock()
  413. var updated []AddressListEntry
  414. for _, e := range m.addressLists {
  415. if e.ID != id {
  416. updated = append(updated, e)
  417. }
  418. }
  419. m.addressLists = updated
  420. _ = m.save()
  421. return m.Apply()
  422. }
  423. func (m *FirewallManager) SetAddressListEntryState(id string, disabled bool) error {
  424. m.mu.Lock()
  425. defer m.mu.Unlock()
  426. for i := range m.addressLists {
  427. if m.addressLists[i].ID == id {
  428. m.addressLists[i].Disabled = disabled
  429. _ = m.save()
  430. return m.Apply()
  431. }
  432. }
  433. return fmt.Errorf("address list entry not found")
  434. }
  435. func (m *FirewallManager) ResetCounters() error {
  436. _ = exec.Command("iptables", "-Z").Run()
  437. return nil
  438. }
  439. func (m *FirewallManager) GetSummary() FirewallSummary {
  440. m.mu.Lock()
  441. defer m.mu.Unlock()
  442. conns, _ := ListActiveConnections()
  443. geoCount := 0
  444. geoSubnets := 0
  445. if m.geoIPMgr != nil {
  446. status := m.geoIPMgr.GetStatus()
  447. if c, ok := status["enabled_count"].(int); ok {
  448. geoCount = c
  449. }
  450. if s, ok := status["total_subnets"].(int); ok {
  451. geoSubnets = s
  452. }
  453. }
  454. return FirewallSummary{
  455. FilterRulesCount: len(m.filterRules),
  456. NATRulesCount: len(m.natRules),
  457. MangleRulesCount: len(m.mangleRules),
  458. AddressListsCount: len(m.addressLists),
  459. GeoCountriesCount: geoCount,
  460. GeoSubnetsCount: geoSubnets,
  461. ActiveConnections: len(conns),
  462. }
  463. }