iptables.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. // Copyright 2019 the Kilo authors
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package iptables
  15. import (
  16. "fmt"
  17. "net"
  18. "strings"
  19. "sync"
  20. "time"
  21. "github.com/coreos/go-iptables/iptables"
  22. )
  23. type iptablesClient interface {
  24. AppendUnique(string, string, ...string) error
  25. Delete(string, string, ...string) error
  26. Exists(string, string, ...string) (bool, error)
  27. ClearChain(string, string) error
  28. DeleteChain(string, string) error
  29. NewChain(string, string) error
  30. }
  31. // rule represents an iptables rule.
  32. type rule struct {
  33. table string
  34. chain string
  35. spec []string
  36. client iptablesClient
  37. }
  38. func (r *rule) Add() error {
  39. if err := r.client.AppendUnique(r.table, r.chain, r.spec...); err != nil {
  40. return fmt.Errorf("failed to add iptables rule: %v", err)
  41. }
  42. return nil
  43. }
  44. func (r *rule) Delete() error {
  45. // Ignore the returned error as an error likely means
  46. // that the rule doesn't exist, which is fine.
  47. r.client.Delete(r.table, r.chain, r.spec...)
  48. return nil
  49. }
  50. func (r *rule) Exists() (bool, error) {
  51. return r.client.Exists(r.table, r.chain, r.spec...)
  52. }
  53. func (r *rule) String() string {
  54. if r == nil {
  55. return ""
  56. }
  57. return fmt.Sprintf("%s_%s_%s", r.table, r.chain, strings.Join(r.spec, "_"))
  58. }
  59. // chain represents an iptables chain.
  60. type chain struct {
  61. table string
  62. chain string
  63. client iptablesClient
  64. }
  65. func (c *chain) Add() error {
  66. if err := c.client.ClearChain(c.table, c.chain); err != nil {
  67. return fmt.Errorf("failed to add iptables chain: %v", err)
  68. }
  69. return nil
  70. }
  71. func (c *chain) Delete() error {
  72. // The chain must be empty before it can be deleted.
  73. if err := c.client.ClearChain(c.table, c.chain); err != nil {
  74. return fmt.Errorf("failed to clear iptables chain: %v", err)
  75. }
  76. // Ignore the returned error as an error likely means
  77. // that the chain doesn't exist, which is fine.
  78. c.client.DeleteChain(c.table, c.chain)
  79. return nil
  80. }
  81. func (c *chain) Exists() (bool, error) {
  82. // The code for "chain already exists".
  83. existsErr := 1
  84. err := c.client.NewChain(c.table, c.chain)
  85. se, ok := err.(statusExiter)
  86. switch {
  87. case err == nil:
  88. // If there was no error adding a new chain, then it did not exist.
  89. // Delete it and return false.
  90. c.client.DeleteChain(c.table, c.chain)
  91. return false, nil
  92. case ok && se.ExitStatus() == existsErr:
  93. return true, nil
  94. default:
  95. return false, err
  96. }
  97. }
  98. func (c *chain) String() string {
  99. if c == nil {
  100. return ""
  101. }
  102. return fmt.Sprintf("%s_%s", c.table, c.chain)
  103. }
  104. // Rule is an interface for interacting with iptables objects.
  105. type Rule interface {
  106. Add() error
  107. Delete() error
  108. Exists() (bool, error)
  109. String() string
  110. }
  111. // Controller is able to reconcile a given set of iptables rules.
  112. type Controller struct {
  113. client iptablesClient
  114. errors chan error
  115. sync.Mutex
  116. rules []Rule
  117. subscribed bool
  118. }
  119. // New generates a new iptables rules controller.
  120. // It expects an IP address length to determine
  121. // whether to operate in IPv4 or IPv6 mode.
  122. func New(ipLength int) (*Controller, error) {
  123. p := iptables.ProtocolIPv4
  124. if ipLength == net.IPv6len {
  125. p = iptables.ProtocolIPv6
  126. }
  127. client, err := iptables.NewWithProtocol(p)
  128. if err != nil {
  129. return nil, fmt.Errorf("failed to create iptables client: %v", err)
  130. }
  131. return &Controller{
  132. client: client,
  133. errors: make(chan error),
  134. }, nil
  135. }
  136. // Run watches for changes to iptables rules and reconciles
  137. // the rules against the desired state.
  138. func (c *Controller) Run(stop <-chan struct{}) (<-chan error, error) {
  139. c.Lock()
  140. if c.subscribed {
  141. c.Unlock()
  142. return c.errors, nil
  143. }
  144. // Ensure a given instance only subscribes once.
  145. c.subscribed = true
  146. c.Unlock()
  147. go func() {
  148. defer close(c.errors)
  149. for {
  150. select {
  151. case <-time.After(5 * time.Second):
  152. case <-stop:
  153. return
  154. }
  155. if err := c.reconcile(); err != nil {
  156. nonBlockingSend(c.errors, fmt.Errorf("failed to reconcile rules: %v", err))
  157. }
  158. }
  159. }()
  160. return c.errors, nil
  161. }
  162. // reconcile makes sure that every rule is still in the backend.
  163. // It does not ensure that the order in the backend is correct.
  164. // If any rule is missing, that rule and all following rules are
  165. // re-added.
  166. func (c *Controller) reconcile() error {
  167. c.Lock()
  168. defer c.Unlock()
  169. for i, r := range c.rules {
  170. ok, err := r.Exists()
  171. if err != nil {
  172. return fmt.Errorf("failed to check if rule exists: %v", err)
  173. }
  174. if !ok {
  175. if err := resetFromIndex(i, c.rules); err != nil {
  176. return fmt.Errorf("failed to add rule: %v", err)
  177. }
  178. break
  179. }
  180. }
  181. return nil
  182. }
  183. // resetFromIndex re-adds all rules starting from the given index.
  184. func resetFromIndex(i int, rules []Rule) error {
  185. if i >= len(rules) {
  186. return nil
  187. }
  188. for j := i; j < len(rules); j++ {
  189. if err := rules[j].Delete(); err != nil {
  190. return fmt.Errorf("failed to delete rule: %v", err)
  191. }
  192. if err := rules[j].Add(); err != nil {
  193. return fmt.Errorf("failed to add rule: %v", err)
  194. }
  195. }
  196. return nil
  197. }
  198. // deleteFromIndex deletes all rules starting from the given index.
  199. func deleteFromIndex(i int, rules *[]Rule) error {
  200. if i >= len(*rules) {
  201. return nil
  202. }
  203. for j := i; j < len(*rules); j++ {
  204. if err := (*rules)[j].Delete(); err != nil {
  205. return fmt.Errorf("failed to delete rule: %v", err)
  206. }
  207. (*rules)[j] = nil
  208. }
  209. *rules = (*rules)[:i]
  210. return nil
  211. }
  212. // Set idempotently overwrites any iptables rules previously defined
  213. // for the controller with the given set of rules.
  214. func (c *Controller) Set(rules []Rule) error {
  215. c.Lock()
  216. defer c.Unlock()
  217. var i int
  218. for ; i < len(rules); i++ {
  219. if i < len(c.rules) {
  220. if rules[i].String() != c.rules[i].String() {
  221. if err := deleteFromIndex(i, &c.rules); err != nil {
  222. return err
  223. }
  224. }
  225. }
  226. if i >= len(c.rules) {
  227. setRuleClient(rules[i], c.client)
  228. if err := rules[i].Add(); err != nil {
  229. return fmt.Errorf("failed to add rule: %v", err)
  230. }
  231. c.rules = append(c.rules, rules[i])
  232. }
  233. }
  234. return deleteFromIndex(i, &c.rules)
  235. }
  236. // CleanUp will clean up any rules created by the controller.
  237. func (c *Controller) CleanUp() error {
  238. c.Lock()
  239. defer c.Unlock()
  240. return deleteFromIndex(0, &c.rules)
  241. }
  242. // IPIPRules returns a set of iptables rules that are necessary
  243. // when traffic between nodes must be encapsulated with IPIP.
  244. func IPIPRules(nodes []*net.IPNet) []Rule {
  245. var rules []Rule
  246. rules = append(rules, &chain{"filter", "KILO-IPIP", nil})
  247. rules = append(rules, &rule{"filter", "INPUT", []string{"-m", "comment", "--comment", "Kilo: jump to IPIP chain", "-p", "4", "-j", "KILO-IPIP"}, nil})
  248. for _, n := range nodes {
  249. // Accept encapsulated traffic from peers.
  250. rules = append(rules, &rule{"filter", "KILO-IPIP", []string{"-m", "comment", "--comment", "Kilo: allow IPIP traffic", "-s", n.IP.String(), "-j", "ACCEPT"}, nil})
  251. }
  252. // Drop all other IPIP traffic.
  253. rules = append(rules, &rule{"filter", "INPUT", []string{"-m", "comment", "--comment", "Kilo: reject other IPIP traffic", "-p", "4", "-j", "DROP"}, nil})
  254. return rules
  255. }
  256. // ForwardRules returns a set of iptables rules that are necessary
  257. // when traffic must be forwarded for the overlay.
  258. func ForwardRules(subnets ...*net.IPNet) []Rule {
  259. var rules []Rule
  260. for _, subnet := range subnets {
  261. s := subnet.String()
  262. rules = append(rules, []Rule{
  263. // Forward traffic to and from the overlay.
  264. &rule{"filter", "FORWARD", []string{"-s", s, "-j", "ACCEPT"}, nil},
  265. &rule{"filter", "FORWARD", []string{"-d", s, "-j", "ACCEPT"}, nil},
  266. }...)
  267. }
  268. return rules
  269. }
  270. // MasqueradeRules returns a set of iptables rules that are necessary
  271. // to NAT traffic from the local Pod subnet to the Internet and out of the Kilo interface.
  272. func MasqueradeRules(kilo, private, localPodSubnet *net.IPNet, remotePodSubnet, peers []*net.IPNet) []Rule {
  273. var rules []Rule
  274. rules = append(rules, &chain{"nat", "KILO-NAT", nil})
  275. // NAT packets from Kilo interface.
  276. rules = append(rules, &rule{"mangle", "PREROUTING", []string{"-m", "comment", "--comment", "Kilo: jump to mark chain", "-i", "kilo+", "-j", "MARK", "--set-xmark", "0x1107/0x1107"}, nil})
  277. rules = append(rules, &rule{"nat", "POSTROUTING", []string{"-m", "comment", "--comment", "Kilo: NAT packets from Kilo interface", "-m", "mark", "--mark", "0x1107/0x1107", "-j", "KILO-NAT"}, nil})
  278. // NAT packets from pod subnet.
  279. rules = append(rules, &rule{"nat", "POSTROUTING", []string{"-m", "comment", "--comment", "Kilo: jump to NAT chain", "-s", localPodSubnet.String(), "-j", "KILO-NAT"}, nil})
  280. rules = append(rules, &rule{"nat", "KILO-NAT", []string{"-m", "comment", "--comment", "Kilo: do not NAT packets destined for the local Pod subnet", "-d", localPodSubnet.String(), "-j", "RETURN"}, nil})
  281. rules = append(rules, &rule{"nat", "KILO-NAT", []string{"-m", "comment", "--comment", "Kilo: do not NAT packets destined for the Kilo subnet", "-d", kilo.String(), "-j", "RETURN"}, nil})
  282. rules = append(rules, &rule{"nat", "KILO-NAT", []string{"-m", "comment", "--comment", "Kilo: do not NAT packets destined for the local private IP", "-d", private.String(), "-j", "RETURN"}, nil})
  283. for _, r := range remotePodSubnet {
  284. rules = append(rules, &rule{"nat", "KILO-NAT", []string{"-m", "comment", "--comment", "Kilo: do not NAT packets from local pod subnet to remote pod subnets", "-s", localPodSubnet.String(), "-d", r.String(), "-j", "RETURN"}, nil})
  285. }
  286. for _, p := range peers {
  287. rules = append(rules, &rule{"nat", "KILO-NAT", []string{"-m", "comment", "--comment", "Kilo: do not NAT packets from local pod subnet to peers", "-s", localPodSubnet.String(), "-d", p.String(), "-j", "RETURN"}, nil})
  288. }
  289. rules = append(rules, &rule{"nat", "KILO-NAT", []string{"-m", "comment", "--comment", "Kilo: NAT remaining packets", "-j", "MASQUERADE"}, nil})
  290. return rules
  291. }
  292. func nonBlockingSend(errors chan<- error, err error) {
  293. select {
  294. case errors <- err:
  295. default:
  296. }
  297. }
  298. // setRuleClient is a helper to set the iptables client on different kinds of rules.
  299. func setRuleClient(r Rule, c iptablesClient) {
  300. switch v := r.(type) {
  301. case *rule:
  302. v.client = c
  303. case *chain:
  304. v.client = c
  305. }
  306. }