manager.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886
  1. package firewall
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "fmt"
  6. "log"
  7. "os"
  8. "os/exec"
  9. "strings"
  10. "sync"
  11. "time"
  12. )
  13. const (
  14. FilterStorePath = "/etc/proxmox-router/firewall/filter.json"
  15. NATStorePath = "/etc/proxmox-router/firewall/nat.json"
  16. MangleStorePath = "/etc/proxmox-router/firewall/mangle.json"
  17. AddressListsStorePath = "/etc/proxmox-router/firewall/address_lists.json"
  18. )
  19. type FirewallManager struct {
  20. mu sync.Mutex
  21. committed []byte
  22. lastError string
  23. applied bool
  24. filterRules []FilterRule
  25. natRules []NATRule
  26. mangleRules []MangleRule
  27. addressLists []AddressListEntry
  28. geoIPMgr *GeoIPManager
  29. }
  30. func NewFirewallManager() *FirewallManager {
  31. m := &FirewallManager{
  32. filterRules: make([]FilterRule, 0),
  33. natRules: make([]NATRule, 0),
  34. mangleRules: make([]MangleRule, 0),
  35. addressLists: make([]AddressListEntry, 0),
  36. geoIPMgr: NewGeoIPManager(),
  37. }
  38. m.geoIPMgr.referenced = m.geoReferenced
  39. loadErr := m.load()
  40. m.remember()
  41. if loadErr != nil {
  42. m.lastError = loadErr.Error()
  43. log.Printf("Firewall configuration not loaded: %v", loadErr)
  44. return m
  45. }
  46. // Defaults are installed only once, never because a user intentionally saved
  47. // an empty list or deleted all rules.
  48. fresh := true
  49. for _, path := range []string{FilterStorePath, NATStorePath, MangleStorePath, AddressListsStorePath} {
  50. if _, err := os.Stat(path); !os.IsNotExist(err) {
  51. fresh = false
  52. }
  53. }
  54. if fresh {
  55. if err := m.initDefaultRules(); err != nil {
  56. m.lastError = err.Error()
  57. return m
  58. }
  59. }
  60. if err := m.Apply(); err != nil {
  61. log.Printf("Firewall not applied: %v", err)
  62. }
  63. m.remember()
  64. return m
  65. }
  66. func (m *FirewallManager) initDefaultRules() error {
  67. if len(m.natRules) == 0 {
  68. // Default Masquerade NAT only on WAN
  69. m.natRules = append(m.natRules, NATRule{
  70. ID: fmt.Sprintf("nat-%d", time.Now().UnixNano()),
  71. Order: 1,
  72. Chain: "postrouting",
  73. Action: "masquerade",
  74. OutInterfaceList: "WAN",
  75. Comment: "defconf: masquerade outgoing traffic to WAN",
  76. Disabled: false,
  77. })
  78. }
  79. if len(m.filterRules) == 0 {
  80. // Default Fasttrack / Established,Related Filter
  81. m.filterRules = append(m.filterRules, FilterRule{
  82. ID: fmt.Sprintf("filter-%d-1", time.Now().UnixNano()),
  83. Order: 1,
  84. Chain: "input",
  85. Action: "accept",
  86. ConnectionState: []string{"established", "related"},
  87. Comment: "defconf: accept established,related",
  88. Disabled: false,
  89. })
  90. m.filterRules = append(m.filterRules, FilterRule{
  91. ID: fmt.Sprintf("filter-%d-2", time.Now().UnixNano()),
  92. Order: 2,
  93. Chain: "forward",
  94. Action: "accept",
  95. ConnectionState: []string{"established", "related"},
  96. Comment: "defconf: accept established,related forward",
  97. Disabled: false,
  98. })
  99. }
  100. return m.save()
  101. }
  102. func (m *FirewallManager) load() error {
  103. var failures []error
  104. for path, target := range map[string]any{FilterStorePath: &m.filterRules, NATStorePath: &m.natRules, MangleStorePath: &m.mangleRules, AddressListsStorePath: &m.addressLists} {
  105. data, err := os.ReadFile(path)
  106. if os.IsNotExist(err) {
  107. continue
  108. }
  109. if err == nil {
  110. err = json.Unmarshal(data, target)
  111. }
  112. if err != nil {
  113. failures = append(failures, fmt.Errorf("%s: %w", path, err))
  114. }
  115. }
  116. if m.filterRules == nil {
  117. m.filterRules = []FilterRule{}
  118. }
  119. // Older releases implicitly compiled port matches with protocol=all as TCP.
  120. // Keep the existing kernel behavior and expose the actual protocol on edit.
  121. for i := range m.natRules {
  122. r := &m.natRules[i]
  123. if (r.Protocol == "" || r.Protocol == "all") && (r.SrcPort != "" || r.DstPort != "" || r.AnyPort != "") {
  124. r.Protocol = "tcp"
  125. log.Printf("NAT rule %s: migrated legacy implicit TCP port match", r.ID)
  126. }
  127. }
  128. if m.natRules == nil {
  129. m.natRules = []NATRule{}
  130. }
  131. if m.mangleRules == nil {
  132. m.mangleRules = []MangleRule{}
  133. }
  134. if m.addressLists == nil {
  135. m.addressLists = []AddressListEntry{}
  136. }
  137. return errors.Join(failures...)
  138. }
  139. func (m *FirewallManager) snapshot() []byte {
  140. data, _ := json.Marshal([]any{m.filterRules, m.natRules, m.mangleRules, m.addressLists})
  141. return data
  142. }
  143. func (m *FirewallManager) remember() { m.committed = m.snapshot() }
  144. func (m *FirewallManager) restore(data []byte) {
  145. var fields []json.RawMessage
  146. if json.Unmarshal(data, &fields) == nil && len(fields) == 4 {
  147. _ = json.Unmarshal(fields[0], &m.filterRules)
  148. _ = json.Unmarshal(fields[1], &m.natRules)
  149. _ = json.Unmarshal(fields[2], &m.mangleRules)
  150. _ = json.Unmarshal(fields[3], &m.addressLists)
  151. }
  152. }
  153. func (m *FirewallManager) save() error {
  154. if err := os.MkdirAll("/etc/proxmox-router/firewall", 0755); err != nil {
  155. return err
  156. }
  157. files := map[string][]byte{}
  158. for path, value := range map[string]any{FilterStorePath: m.filterRules, NATStorePath: m.natRules, MangleStorePath: m.mangleRules, AddressListsStorePath: m.addressLists} {
  159. data, err := json.MarshalIndent(value, "", " ")
  160. if err != nil {
  161. return err
  162. }
  163. files[path] = data
  164. }
  165. return saveFileGroup(files)
  166. }
  167. func (m *FirewallManager) commitChange() error {
  168. before := append([]byte(nil), m.committed...)
  169. err := m.Apply()
  170. if err == nil {
  171. err = m.save()
  172. }
  173. if err != nil {
  174. m.restore(before)
  175. rollbackErr := m.Apply()
  176. saveErr := m.save()
  177. if rollbackErr != nil || saveErr != nil {
  178. m.lastError = fmt.Sprintf("firewall change failed: %v; rollback apply: %v; rollback save: %v", err, rollbackErr, saveErr)
  179. m.applied = false
  180. return errors.New(m.lastError)
  181. }
  182. return err
  183. }
  184. m.remember()
  185. return nil
  186. }
  187. func (m *FirewallManager) Reapply() error { m.mu.Lock(); defer m.mu.Unlock(); return m.Apply() }
  188. // Apply re-synchronizes AddressLists & GeoIP sets with ipset and applies iptables
  189. func (m *FirewallManager) Apply() (result error) {
  190. defer func() {
  191. m.applied = result == nil
  192. m.lastError = ""
  193. if result != nil {
  194. m.lastError = result.Error()
  195. }
  196. }()
  197. for _, r := range m.filterRules {
  198. if err := validateRule("filter", r); err != nil {
  199. return err
  200. }
  201. }
  202. for _, r := range m.natRules {
  203. if err := validateRule("nat", r); err != nil {
  204. return err
  205. }
  206. }
  207. for _, r := range m.mangleRules {
  208. if err := validateRule("mangle", r); err != nil {
  209. return err
  210. }
  211. }
  212. // Sync GeoIP country sets
  213. if m.geoIPMgr != nil {
  214. // Also ensure any GeoIP lists explicitly referenced in active rules are loaded into ipset
  215. for _, r := range m.filterRules {
  216. if !r.Disabled {
  217. if r.SrcGeoIP != "" {
  218. if err := m.geoIPMgr.EnsureCountryOrPreset(r.SrcGeoIP); err != nil {
  219. return err
  220. }
  221. }
  222. if r.DstGeoIP != "" {
  223. if err := m.geoIPMgr.EnsureCountryOrPreset(r.DstGeoIP); err != nil {
  224. return err
  225. }
  226. }
  227. }
  228. }
  229. for _, r := range m.natRules {
  230. if !r.Disabled {
  231. if r.SrcGeoIP != "" {
  232. if err := m.geoIPMgr.EnsureCountryOrPreset(r.SrcGeoIP); err != nil {
  233. return err
  234. }
  235. }
  236. if r.DstGeoIP != "" {
  237. if err := m.geoIPMgr.EnsureCountryOrPreset(r.DstGeoIP); err != nil {
  238. return err
  239. }
  240. }
  241. }
  242. }
  243. for _, r := range m.mangleRules {
  244. if !r.Disabled {
  245. if r.SrcGeoIP != "" {
  246. if err := m.geoIPMgr.EnsureCountryOrPreset(r.SrcGeoIP); err != nil {
  247. return err
  248. }
  249. }
  250. if r.DstGeoIP != "" {
  251. if err := m.geoIPMgr.EnsureCountryOrPreset(r.DstGeoIP); err != nil {
  252. return err
  253. }
  254. }
  255. }
  256. }
  257. }
  258. // Collect any custom address lists referenced in active rules
  259. var refLists []string
  260. for _, r := range m.filterRules {
  261. if !r.Disabled {
  262. if r.SrcAddressList != "" {
  263. refLists = append(refLists, r.SrcAddressList)
  264. }
  265. if r.DstAddressList != "" {
  266. refLists = append(refLists, r.DstAddressList)
  267. }
  268. if r.AddressList != "" {
  269. refLists = append(refLists, r.AddressList)
  270. }
  271. }
  272. }
  273. for _, r := range m.natRules {
  274. if !r.Disabled {
  275. if r.SrcAddressList != "" {
  276. refLists = append(refLists, r.SrcAddressList)
  277. }
  278. if r.DstAddressList != "" {
  279. refLists = append(refLists, r.DstAddressList)
  280. }
  281. if r.AddressList != "" {
  282. refLists = append(refLists, r.AddressList)
  283. }
  284. }
  285. }
  286. for _, r := range m.mangleRules {
  287. if !r.Disabled {
  288. if r.SrcAddressList != "" {
  289. refLists = append(refLists, r.SrcAddressList)
  290. }
  291. if r.DstAddressList != "" {
  292. refLists = append(refLists, r.DstAddressList)
  293. }
  294. if r.AddressList != "" {
  295. refLists = append(refLists, r.AddressList)
  296. }
  297. }
  298. }
  299. // Sync custom ipset address lists
  300. if err := SyncAddressLists(m.addressLists, refLists...); err != nil {
  301. return err
  302. }
  303. if err := ApplyDualStack(m.filterRules, m.natRules, m.mangleRules); err != nil {
  304. return err
  305. }
  306. collectUnusedAddressLists(m.addressLists, refLists)
  307. return nil
  308. }
  309. func (m *FirewallManager) GeoIP() *GeoIPManager {
  310. return m.geoIPMgr
  311. }
  312. // ==================== Filter Rules ====================
  313. func (m *FirewallManager) ListFilterRules() []FilterRule {
  314. m.mu.Lock()
  315. defer m.mu.Unlock()
  316. counters, _ := ReadRuleCounters(m.filterRules, m.natRules, m.mangleRules)
  317. res := make([]FilterRule, len(m.filterRules))
  318. for i, r := range m.filterRules {
  319. res[i] = r
  320. if c, ok := counters[r.ID]; ok {
  321. res[i].Packets = c[0]
  322. res[i].Bytes = c[1]
  323. }
  324. }
  325. return res
  326. }
  327. func (m *FirewallManager) SaveFilterRule(r FilterRule) error {
  328. m.mu.Lock()
  329. defer m.mu.Unlock()
  330. r = normalizeNativeRule("filter", r).(FilterRule)
  331. if err := validateNewPortProtocol(r); err != nil {
  332. return err
  333. }
  334. if err := validateChainPlacement("filter", r); err != nil {
  335. return err
  336. }
  337. if r.ID == "" {
  338. r.ID = fmt.Sprintf("filter-%d", time.Now().UnixNano())
  339. }
  340. found := false
  341. for i, existing := range m.filterRules {
  342. if existing.ID == r.ID {
  343. r.Order = existing.Order
  344. m.filterRules[i] = r
  345. found = true
  346. break
  347. }
  348. }
  349. if !found {
  350. r.Order = len(m.filterRules) + 1
  351. m.filterRules = append(m.filterRules, r)
  352. }
  353. return m.commitChange()
  354. }
  355. func (m *FirewallManager) DeleteFilterRule(id string) error {
  356. m.mu.Lock()
  357. defer m.mu.Unlock()
  358. var updated []FilterRule
  359. for _, r := range m.filterRules {
  360. if r.ID != id {
  361. updated = append(updated, r)
  362. }
  363. }
  364. for i := range updated {
  365. updated[i].Order = i + 1
  366. }
  367. m.filterRules = updated
  368. return m.commitChange()
  369. }
  370. func (m *FirewallManager) MoveFilterRule(id string, dir string) error {
  371. m.mu.Lock()
  372. defer m.mu.Unlock()
  373. idx := -1
  374. for i, r := range m.filterRules {
  375. if r.ID == id {
  376. idx = i
  377. break
  378. }
  379. }
  380. if idx == -1 {
  381. return fmt.Errorf("rule not found")
  382. }
  383. chains := make([]string, len(m.filterRules))
  384. for i, rule := range m.filterRules {
  385. chains[i] = rule.Chain
  386. }
  387. other, err := neighboringChainRule(chains, idx, dir, false)
  388. if err != nil {
  389. return err
  390. }
  391. if other >= 0 {
  392. m.filterRules[idx], m.filterRules[other] = m.filterRules[other], m.filterRules[idx]
  393. }
  394. for i := range m.filterRules {
  395. m.filterRules[i].Order = i + 1
  396. }
  397. return m.commitChange()
  398. }
  399. func (m *FirewallManager) SetFilterRuleState(id string, disabled bool) error {
  400. m.mu.Lock()
  401. defer m.mu.Unlock()
  402. for i := range m.filterRules {
  403. if m.filterRules[i].ID == id {
  404. m.filterRules[i].Disabled = disabled
  405. return m.commitChange()
  406. }
  407. }
  408. return fmt.Errorf("rule not found")
  409. }
  410. // ==================== NAT Rules ====================
  411. func (m *FirewallManager) ListNATRules() []NATRule {
  412. m.mu.Lock()
  413. defer m.mu.Unlock()
  414. counters, _ := ReadRuleCounters(m.filterRules, m.natRules, m.mangleRules)
  415. res := make([]NATRule, len(m.natRules))
  416. for i, r := range m.natRules {
  417. res[i] = r
  418. if c, ok := counters[r.ID]; ok {
  419. res[i].Packets = c[0]
  420. res[i].Bytes = c[1]
  421. }
  422. }
  423. return res
  424. }
  425. func (m *FirewallManager) SaveNATRule(r NATRule) error {
  426. m.mu.Lock()
  427. defer m.mu.Unlock()
  428. r = normalizeNativeRule("nat", r).(NATRule)
  429. if err := validateNewPortProtocol(r); err != nil {
  430. return err
  431. }
  432. if err := validateChainPlacement("nat", r); err != nil {
  433. return err
  434. }
  435. if r.ID == "" {
  436. r.ID = fmt.Sprintf("nat-%d", time.Now().UnixNano())
  437. }
  438. found := false
  439. for i, existing := range m.natRules {
  440. if existing.ID == r.ID {
  441. r.Order = existing.Order
  442. m.natRules[i] = r
  443. found = true
  444. break
  445. }
  446. }
  447. if !found {
  448. r.Order = len(m.natRules) + 1
  449. m.natRules = append(m.natRules, r)
  450. }
  451. return m.commitChange()
  452. }
  453. func (m *FirewallManager) DeleteNATRule(id string) error {
  454. m.mu.Lock()
  455. defer m.mu.Unlock()
  456. var updated []NATRule
  457. for _, r := range m.natRules {
  458. if r.ID != id {
  459. updated = append(updated, r)
  460. }
  461. }
  462. for i := range updated {
  463. updated[i].Order = i + 1
  464. }
  465. m.natRules = updated
  466. return m.commitChange()
  467. }
  468. func (m *FirewallManager) MoveNATRule(id string, dir string) error {
  469. m.mu.Lock()
  470. defer m.mu.Unlock()
  471. idx := -1
  472. for i, r := range m.natRules {
  473. if r.ID == id {
  474. idx = i
  475. break
  476. }
  477. }
  478. if idx == -1 {
  479. return fmt.Errorf("rule not found")
  480. }
  481. chains := make([]string, len(m.natRules))
  482. for i, rule := range m.natRules {
  483. chains[i] = rule.Chain
  484. }
  485. other, err := neighboringChainRule(chains, idx, dir, true)
  486. if err != nil {
  487. return err
  488. }
  489. if other >= 0 {
  490. m.natRules[idx], m.natRules[other] = m.natRules[other], m.natRules[idx]
  491. }
  492. for i := range m.natRules {
  493. m.natRules[i].Order = i + 1
  494. }
  495. return m.commitChange()
  496. }
  497. func (m *FirewallManager) SetNATRuleState(id string, disabled bool) error {
  498. m.mu.Lock()
  499. defer m.mu.Unlock()
  500. for i := range m.natRules {
  501. if m.natRules[i].ID == id {
  502. m.natRules[i].Disabled = disabled
  503. return m.commitChange()
  504. }
  505. }
  506. return fmt.Errorf("rule not found")
  507. }
  508. // ==================== Mangle Rules ====================
  509. func (m *FirewallManager) ListMangleRules() []MangleRule {
  510. m.mu.Lock()
  511. defer m.mu.Unlock()
  512. counters, _ := ReadRuleCounters(m.filterRules, m.natRules, m.mangleRules)
  513. res := make([]MangleRule, len(m.mangleRules))
  514. for i, r := range m.mangleRules {
  515. res[i] = r
  516. if c, ok := counters[r.ID]; ok {
  517. res[i].Packets = c[0]
  518. res[i].Bytes = c[1]
  519. }
  520. }
  521. return res
  522. }
  523. func (m *FirewallManager) SaveMangleRule(r MangleRule) error {
  524. m.mu.Lock()
  525. defer m.mu.Unlock()
  526. r = normalizeNativeRule("mangle", r).(MangleRule)
  527. if err := validateNewPortProtocol(r); err != nil {
  528. return err
  529. }
  530. if err := validateChainPlacement("mangle", r); err != nil {
  531. return err
  532. }
  533. if r.ID == "" {
  534. r.ID = fmt.Sprintf("mangle-%d", time.Now().UnixNano())
  535. }
  536. found := false
  537. for i, existing := range m.mangleRules {
  538. if existing.ID == r.ID {
  539. r.Order = existing.Order
  540. m.mangleRules[i] = r
  541. found = true
  542. break
  543. }
  544. }
  545. if !found {
  546. r.Order = len(m.mangleRules) + 1
  547. m.mangleRules = append(m.mangleRules, r)
  548. }
  549. return m.commitChange()
  550. }
  551. func (m *FirewallManager) DeleteMangleRule(id string) error {
  552. m.mu.Lock()
  553. defer m.mu.Unlock()
  554. var updated []MangleRule
  555. for _, r := range m.mangleRules {
  556. if r.ID != id {
  557. updated = append(updated, r)
  558. }
  559. }
  560. for i := range updated {
  561. updated[i].Order = i + 1
  562. }
  563. m.mangleRules = updated
  564. return m.commitChange()
  565. }
  566. func (m *FirewallManager) MoveMangleRule(id string, dir string) error {
  567. m.mu.Lock()
  568. defer m.mu.Unlock()
  569. idx := -1
  570. for i, r := range m.mangleRules {
  571. if r.ID == id {
  572. idx = i
  573. break
  574. }
  575. }
  576. if idx == -1 {
  577. return fmt.Errorf("rule not found")
  578. }
  579. chains := make([]string, len(m.mangleRules))
  580. for i, rule := range m.mangleRules {
  581. chains[i] = rule.Chain
  582. }
  583. other, err := neighboringChainRule(chains, idx, dir, false)
  584. if err != nil {
  585. return err
  586. }
  587. if other >= 0 {
  588. m.mangleRules[idx], m.mangleRules[other] = m.mangleRules[other], m.mangleRules[idx]
  589. }
  590. for i := range m.mangleRules {
  591. m.mangleRules[i].Order = i + 1
  592. }
  593. return m.commitChange()
  594. }
  595. func (m *FirewallManager) SetMangleRuleState(id string, disabled bool) error {
  596. m.mu.Lock()
  597. defer m.mu.Unlock()
  598. for i := range m.mangleRules {
  599. if m.mangleRules[i].ID == id {
  600. m.mangleRules[i].Disabled = disabled
  601. return m.commitChange()
  602. }
  603. }
  604. return fmt.Errorf("rule not found")
  605. }
  606. // ==================== Address Lists ====================
  607. func (m *FirewallManager) ListAddressLists() []AddressListEntry {
  608. m.mu.Lock()
  609. defer m.mu.Unlock()
  610. res := make([]AddressListEntry, len(m.addressLists))
  611. copy(res, m.addressLists)
  612. return append(res, m.dynamicEntries()...)
  613. }
  614. func (m *FirewallManager) SaveAddressListEntry(e AddressListEntry) error {
  615. if e.Dynamic || e.Timeout != 0 {
  616. return fmt.Errorf("dynamic entries are managed by packet rules")
  617. }
  618. m.mu.Lock()
  619. defer m.mu.Unlock()
  620. if e.ID == "" {
  621. e.ID = fmt.Sprintf("addrlist-%d", time.Now().UnixNano())
  622. }
  623. found := false
  624. for i, existing := range m.addressLists {
  625. if existing.ID == e.ID {
  626. m.addressLists[i] = e
  627. found = true
  628. break
  629. }
  630. }
  631. if !found {
  632. m.addressLists = append(m.addressLists, e)
  633. }
  634. return m.commitChange()
  635. }
  636. func (m *FirewallManager) DeleteAddressListEntry(id string) error {
  637. if strings.HasPrefix(id, "dynamic-") {
  638. m.mu.Lock()
  639. defer m.mu.Unlock()
  640. return deleteDynamicEntry(id)
  641. }
  642. m.mu.Lock()
  643. defer m.mu.Unlock()
  644. var updated []AddressListEntry
  645. for _, e := range m.addressLists {
  646. if e.ID != id {
  647. updated = append(updated, e)
  648. }
  649. }
  650. m.addressLists = updated
  651. return m.commitChange()
  652. }
  653. func (m *FirewallManager) SetAddressListEntryState(id string, disabled bool) error {
  654. m.mu.Lock()
  655. defer m.mu.Unlock()
  656. for i := range m.addressLists {
  657. if m.addressLists[i].ID == id {
  658. m.addressLists[i].Disabled = disabled
  659. return m.commitChange()
  660. }
  661. }
  662. return fmt.Errorf("address list entry not found")
  663. }
  664. func (m *FirewallManager) ResetCounters() error {
  665. m.mu.Lock()
  666. defer m.mu.Unlock()
  667. for _, binary := range []string{"iptables", "ip6tables"} {
  668. out, err := exec.Command(binary + "-save").Output()
  669. if err != nil {
  670. if binary == "iptables" {
  671. return err
  672. }
  673. continue
  674. }
  675. table := ""
  676. for _, line := range strings.Split(string(out), "\n") {
  677. if strings.HasPrefix(line, "*") {
  678. table = line[1:]
  679. }
  680. if strings.HasPrefix(line, ":DT_") {
  681. chain := strings.Fields(line)[0][1:]
  682. if err := exec.Command(binary, "-t", table, "-Z", chain).Run(); err != nil {
  683. return err
  684. }
  685. }
  686. }
  687. }
  688. return nil
  689. }
  690. func (m *FirewallManager) GetSummary() FirewallSummary {
  691. m.mu.Lock()
  692. defer m.mu.Unlock()
  693. conns, connErr := ListActiveConnections()
  694. connectionError := ""
  695. if connErr != nil {
  696. connectionError = connErr.Error()
  697. }
  698. geoCount := 0
  699. geoSubnets := 0
  700. if m.geoIPMgr != nil {
  701. status := m.geoIPMgr.GetStatus()
  702. if c, ok := status["enabled_count"].(int); ok {
  703. geoCount = c
  704. }
  705. if s, ok := status["total_subnets"].(int); ok {
  706. geoSubnets = s
  707. }
  708. }
  709. return FirewallSummary{
  710. FilterRulesCount: len(m.filterRules),
  711. NATRulesCount: len(m.natRules),
  712. MangleRulesCount: len(m.mangleRules),
  713. AddressListsCount: len(m.addressLists),
  714. GeoCountriesCount: geoCount,
  715. GeoSubnetsCount: geoSubnets,
  716. ActiveConnections: len(conns),
  717. ConnectionsError: connectionError,
  718. Applied: m.applied, LastError: m.lastError,
  719. }
  720. }
  721. // RenameInterface updates firewall rule interface names when an interface is renamed.
  722. func (m *FirewallManager) RenameInterface(oldName, newName string) {
  723. m.mu.Lock()
  724. defer m.mu.Unlock()
  725. changed := false
  726. for i := range m.filterRules {
  727. if m.filterRules[i].InInterface == oldName {
  728. m.filterRules[i].InInterface = newName
  729. changed = true
  730. }
  731. if m.filterRules[i].OutInterface == oldName {
  732. m.filterRules[i].OutInterface = newName
  733. changed = true
  734. }
  735. }
  736. for i := range m.natRules {
  737. if m.natRules[i].InInterface == oldName {
  738. m.natRules[i].InInterface = newName
  739. changed = true
  740. }
  741. if m.natRules[i].OutInterface == oldName {
  742. m.natRules[i].OutInterface = newName
  743. changed = true
  744. }
  745. }
  746. for i := range m.mangleRules {
  747. if m.mangleRules[i].InInterface == oldName {
  748. m.mangleRules[i].InInterface = newName
  749. changed = true
  750. }
  751. if m.mangleRules[i].OutInterface == oldName {
  752. m.mangleRules[i].OutInterface = newName
  753. changed = true
  754. }
  755. }
  756. if changed {
  757. if err := m.commitChange(); err != nil {
  758. m.lastError = err.Error()
  759. log.Printf("Firewall rename failed: %v", err)
  760. }
  761. }
  762. }
  763. // Rules in different kernel chains never run in the same sequence. Move to the
  764. // adjacent rule in the same chain even when the stored list interleaves chains.
  765. func neighboringChainRule(chains []string, index int, direction string, nat bool) (int, error) {
  766. step := 0
  767. switch direction {
  768. case "up":
  769. step = -1
  770. case "down":
  771. step = 1
  772. default:
  773. return -1, fmt.Errorf("direction must be up or down")
  774. }
  775. if index < 0 || index >= len(chains) {
  776. return -1, fmt.Errorf("rule not found")
  777. }
  778. canonical := func(s string) string {
  779. s = strings.ToLower(s)
  780. if nat && s == "dstnat" {
  781. return "prerouting"
  782. }
  783. if nat && s == "srcnat" {
  784. return "postrouting"
  785. }
  786. return s
  787. }
  788. target := canonical(chains[index])
  789. for i := index + step; i >= 0 && i < len(chains); i += step {
  790. if canonical(chains[i]) == target {
  791. return i, nil
  792. }
  793. }
  794. return -1, nil
  795. }