owned.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. package firewall
  2. import (
  3. "fmt"
  4. "regexp"
  5. "strconv"
  6. "strings"
  7. )
  8. // ownedPayload confines DebTik rules to dedicated chains. The host's chains,
  9. // policies and rules are left intact; only legacy DebTik ID rules are migrated.
  10. func ownedPayload(payload, live string) string {
  11. liveTables := map[string][]string{}
  12. counts := map[string][2]uint64{}
  13. oldChains := map[string][]string{}
  14. counted := map[string]bool{}
  15. counterPattern := regexp.MustCompile(`^\[(\d+):(\d+)\] (.*)$`)
  16. commentPattern := regexp.MustCompile(`--comment (?:"id:([^"]+)"|id:([^ ]+))`)
  17. table := ""
  18. for _, line := range strings.Split(live, "\n") {
  19. var pkts, bytes uint64
  20. if m := counterPattern.FindStringSubmatch(line); m != nil {
  21. pkts, _ = strconv.ParseUint(m[1], 10, 64)
  22. bytes, _ = strconv.ParseUint(m[2], 10, 64)
  23. line = m[3]
  24. }
  25. if m := commentPattern.FindStringSubmatch(line); m != nil {
  26. id := m[1] + m[2]
  27. key := table + ":" + id
  28. c := counts[key]
  29. counts[key] = [2]uint64{c[0] + pkts, c[1] + bytes}
  30. }
  31. if strings.HasPrefix(line, ":DT_") {
  32. oldChains[table] = append(oldChains[table], strings.Fields(line)[0][1:])
  33. }
  34. if strings.HasPrefix(line, "*") {
  35. table = line[1:]
  36. } else if strings.HasPrefix(line, "-A ") {
  37. liveTables[table] = append(liveTables[table], line)
  38. }
  39. }
  40. var out strings.Builder
  41. for _, block := range strings.Split(payload, "COMMIT") {
  42. lines := strings.Split(strings.TrimSpace(block), "\n")
  43. if len(lines) == 0 || !strings.HasPrefix(lines[0], "*") {
  44. continue
  45. }
  46. table = lines[0][1:]
  47. names := map[string]string{}
  48. var chains []string
  49. for _, line := range lines[1:] {
  50. if strings.HasPrefix(line, ":") {
  51. name := strings.Fields(line)[0][1:]
  52. names[name] = "DT_" + table + "_" + name
  53. chains = append(chains, name)
  54. }
  55. }
  56. fmt.Fprintf(&out, "*%s\n", table)
  57. for _, chain := range chains {
  58. fmt.Fprintf(&out, ":%s - [0:0]\n-F %s\n", names[chain], names[chain])
  59. }
  60. for _, line := range liveTables[table] {
  61. if !strings.HasPrefix(strings.Fields(line)[1], "DT_") && strings.Contains(line, "--comment") && (strings.Contains(line, "id:filter-") || strings.Contains(line, "id:nat-") || strings.Contains(line, "id:mangle-")) {
  62. out.WriteString("-D " + strings.TrimPrefix(line, "-A ") + "\n")
  63. }
  64. }
  65. for _, line := range lines[1:] {
  66. fields := strings.Fields(line)
  67. if len(fields) < 2 || fields[0] != "-A" {
  68. continue
  69. }
  70. line = strings.Replace(line, "-A "+fields[1]+" ", "-A "+names[fields[1]]+" ", 1)
  71. for original, owned := range names {
  72. line = strings.ReplaceAll(line, "-j "+original+" ", "-j "+owned+" ")
  73. line = strings.ReplaceAll(line, "-g "+original+" ", "-g "+owned+" ")
  74. if strings.HasSuffix(line, "-g "+original) {
  75. line = strings.TrimSuffix(line, "-g "+original) + "-g " + owned
  76. }
  77. if strings.HasSuffix(line, "-j "+original) {
  78. line = strings.TrimSuffix(line, "-j "+original) + "-j " + owned
  79. }
  80. }
  81. if m := commentPattern.FindStringSubmatch(line); m != nil {
  82. key := table + ":" + m[1] + m[2]
  83. if !counted[key] {
  84. c := counts[key]
  85. if c[0] != 0 || c[1] != 0 {
  86. line = fmt.Sprintf("[%d:%d] %s", c[0], c[1], line)
  87. }
  88. counted[key] = true
  89. }
  90. }
  91. out.WriteString(line + "\n")
  92. }
  93. wanted := map[string]bool{}
  94. for _, name := range names {
  95. wanted[name] = true
  96. }
  97. var obsolete []string
  98. for _, old := range oldChains[table] {
  99. if wanted[old] {
  100. continue
  101. }
  102. foreign := false
  103. for _, rule := range liveTables[table] {
  104. source := strings.Fields(rule)[1]
  105. if !strings.HasPrefix(source, "DT_") && (strings.HasSuffix(rule, "-j "+old) || strings.HasSuffix(rule, "-g "+old)) {
  106. foreign = true
  107. }
  108. }
  109. if !foreign {
  110. obsolete = append(obsolete, old)
  111. }
  112. }
  113. for _, old := range obsolete {
  114. fmt.Fprintf(&out, "-F %s\n", old)
  115. }
  116. for _, old := range obsolete {
  117. fmt.Fprintf(&out, "-X %s\n", old)
  118. }
  119. for _, chain := range chains {
  120. builtin := chain == "INPUT" || chain == "OUTPUT" || chain == "FORWARD" || chain == "PREROUTING" || chain == "POSTROUTING"
  121. if !builtin {
  122. continue
  123. }
  124. exists := false
  125. for _, line := range liveTables[table] {
  126. if strings.HasPrefix(line, "-A "+chain+" ") && strings.Contains(line, "debtik:hook") && strings.HasSuffix(line, "-j "+names[chain]) {
  127. exists = true
  128. break
  129. }
  130. }
  131. if !exists {
  132. fmt.Fprintf(&out, "-I %s 1 -m comment --comment debtik:hook -j %s\n", chain, names[chain])
  133. }
  134. }
  135. out.WriteString("COMMIT\n")
  136. }
  137. return out.String()
  138. }