manager.go 11 KB

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