iptables.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542
  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. "io"
  18. "net"
  19. "os"
  20. "sync"
  21. "time"
  22. "github.com/coreos/go-iptables/iptables"
  23. "github.com/go-kit/kit/log"
  24. "github.com/go-kit/kit/log/level"
  25. "github.com/prometheus/client_golang/prometheus"
  26. )
  27. const ipv6ModuleDisabledPath = "/sys/module/ipv6/parameters/disable"
  28. func ipv6Disabled() (bool, error) {
  29. f, err := os.Open(ipv6ModuleDisabledPath)
  30. if err != nil {
  31. return false, err
  32. }
  33. defer f.Close()
  34. disabled := make([]byte, 1)
  35. if _, err = io.ReadFull(f, disabled); err != nil {
  36. return false, err
  37. }
  38. return disabled[0] == '1', nil
  39. }
  40. // Protocol represents an IP protocol.
  41. type Protocol byte
  42. type RuleSet struct {
  43. appendRules []Rule // Rules to append to the chain - order matters.
  44. prependRules []Rule // Rules to prepend to the chain - order does not matter.
  45. }
  46. const (
  47. // ProtocolIPv4 represents the IPv4 protocol.
  48. ProtocolIPv4 Protocol = iota
  49. // ProtocolIPv6 represents the IPv6 protocol.
  50. ProtocolIPv6
  51. )
  52. func (rs *RuleSet) AddToAppend(rule Rule) {
  53. rs.appendRules = append(rs.appendRules, rule)
  54. }
  55. func (rs *RuleSet) AddToPrepend(rule Rule) {
  56. rs.prependRules = append(rs.prependRules, rule)
  57. }
  58. func (rs *RuleSet) AppendRuleSet(other RuleSet) RuleSet {
  59. return RuleSet{
  60. appendRules: append(rs.appendRules, other.appendRules...),
  61. prependRules: append(rs.prependRules, other.prependRules...),
  62. }
  63. }
  64. // GetProtocol will return a protocol from the length of an IP address.
  65. func GetProtocol(ip net.IP) Protocol {
  66. if len(ip) == net.IPv4len || ip.To4() != nil {
  67. return ProtocolIPv4
  68. }
  69. return ProtocolIPv6
  70. }
  71. // Client represents any type that can administer iptables rules.
  72. type Client interface {
  73. AppendUnique(table string, chain string, rule ...string) error
  74. InsertUnique(table, chain string, pos int, rule ...string) error
  75. Delete(table string, chain string, rule ...string) error
  76. Exists(table string, chain string, rule ...string) (bool, error)
  77. List(table string, chain string) ([]string, error)
  78. ClearChain(table string, chain string) error
  79. DeleteChain(table string, chain string) error
  80. NewChain(table string, chain string) error
  81. ListChains(table string) ([]string, error)
  82. }
  83. // Rule is an interface for interacting with iptables objects.
  84. type Rule interface {
  85. Append(Client) error
  86. Prepend(Client) error
  87. Delete(Client) error
  88. Exists(Client) (bool, error)
  89. String() string
  90. Proto() Protocol
  91. }
  92. // rule represents an iptables rule.
  93. type rule struct {
  94. table string
  95. chain string
  96. spec []string
  97. proto Protocol
  98. }
  99. // NewRule creates a new iptables or ip6tables rule in the given table and chain
  100. // depending on the given protocol.
  101. func NewRule(proto Protocol, table, chain string, spec ...string) Rule {
  102. return &rule{table, chain, spec, proto}
  103. }
  104. // NewIPv4Rule creates a new iptables rule in the given table and chain.
  105. func NewIPv4Rule(table, chain string, spec ...string) Rule {
  106. return &rule{table, chain, spec, ProtocolIPv4}
  107. }
  108. // NewIPv6Rule creates a new ip6tables rule in the given table and chain.
  109. func NewIPv6Rule(table, chain string, spec ...string) Rule {
  110. return &rule{table, chain, spec, ProtocolIPv6}
  111. }
  112. func (r *rule) Prepend(client Client) error {
  113. if err := client.InsertUnique(r.table, r.chain, 1, r.spec...); err != nil {
  114. return fmt.Errorf("failed to add iptables rule: %v", err)
  115. }
  116. return nil
  117. }
  118. func (r *rule) Append(client Client) error {
  119. if err := client.AppendUnique(r.table, r.chain, r.spec...); err != nil {
  120. return fmt.Errorf("failed to add iptables rule: %v", err)
  121. }
  122. return nil
  123. }
  124. func (r *rule) Delete(client Client) error {
  125. // Ignore the returned error as an error likely means
  126. // that the rule doesn't exist, which is fine.
  127. client.Delete(r.table, r.chain, r.spec...)
  128. return nil
  129. }
  130. func (r *rule) Exists(client Client) (bool, error) {
  131. return client.Exists(r.table, r.chain, r.spec...)
  132. }
  133. func (r *rule) String() string {
  134. if r == nil {
  135. return ""
  136. }
  137. spec := r.table + " -A " + r.chain
  138. for i, s := range r.spec {
  139. spec += " "
  140. // If this is the content of a comment, wrap the value in quotes.
  141. if i > 0 && r.spec[i-1] == "--comment" {
  142. spec += `"` + s + `"`
  143. } else {
  144. spec += s
  145. }
  146. }
  147. return spec
  148. }
  149. func (r *rule) Proto() Protocol {
  150. return r.proto
  151. }
  152. // chain represents an iptables chain.
  153. type chain struct {
  154. table string
  155. chain string
  156. proto Protocol
  157. }
  158. // NewIPv4Chain creates a new iptables chain in the given table.
  159. func NewIPv4Chain(table, name string) Rule {
  160. return &chain{table, name, ProtocolIPv4}
  161. }
  162. // NewIPv6Chain creates a new ip6tables chain in the given table.
  163. func NewIPv6Chain(table, name string) Rule {
  164. return &chain{table, name, ProtocolIPv6}
  165. }
  166. func (c *chain) Prepend(client Client) error {
  167. return c.Append(client)
  168. }
  169. func (c *chain) Append(client Client) error {
  170. // Note: `ClearChain` creates a chain if it does not exist.
  171. if err := client.ClearChain(c.table, c.chain); err != nil {
  172. return fmt.Errorf("failed to add iptables chain: %v", err)
  173. }
  174. return nil
  175. }
  176. func (c *chain) Delete(client Client) error {
  177. // The chain must be empty before it can be deleted.
  178. if err := client.ClearChain(c.table, c.chain); err != nil {
  179. return fmt.Errorf("failed to clear iptables chain: %v", err)
  180. }
  181. // Ignore the returned error as an error likely means
  182. // that the chain doesn't exist, which is fine.
  183. client.DeleteChain(c.table, c.chain)
  184. return nil
  185. }
  186. func (c *chain) Exists(client Client) (bool, error) {
  187. // The code for "chain already exists".
  188. existsErr := 1
  189. err := client.NewChain(c.table, c.chain)
  190. se, ok := err.(statusExiter)
  191. switch {
  192. case err == nil:
  193. // If there was no error adding a new chain, then it did not exist.
  194. // Delete it and return false.
  195. client.DeleteChain(c.table, c.chain)
  196. return false, nil
  197. case ok && se.ExitStatus() == existsErr:
  198. return true, nil
  199. default:
  200. return false, err
  201. }
  202. }
  203. func (c *chain) String() string {
  204. if c == nil {
  205. return ""
  206. }
  207. return chainToString(c.table, c.chain)
  208. }
  209. func (c *chain) Proto() Protocol {
  210. return c.proto
  211. }
  212. func chainToString(table, chain string) string {
  213. return fmt.Sprintf("%s -N %s", table, chain)
  214. }
  215. // Controller is able to reconcile a given set of iptables rules.
  216. type Controller struct {
  217. v4 Client
  218. v6 Client
  219. errors chan error
  220. logger log.Logger
  221. resyncPeriod time.Duration
  222. registerer prometheus.Registerer
  223. sync.Mutex
  224. appendRules []Rule
  225. prependRules []Rule
  226. subscribed bool
  227. }
  228. // ControllerOption modifies the controller's configuration.
  229. type ControllerOption func(h *Controller)
  230. // WithLogger adds a logger to the controller.
  231. func WithLogger(logger log.Logger) ControllerOption {
  232. return func(c *Controller) {
  233. c.logger = logger
  234. }
  235. }
  236. // WithResyncPeriod modifies how often the controller reconciles.
  237. func WithResyncPeriod(resyncPeriod time.Duration) ControllerOption {
  238. return func(c *Controller) {
  239. c.resyncPeriod = resyncPeriod
  240. }
  241. }
  242. // WithClients adds iptables clients to the controller.
  243. func WithClients(v4, v6 Client) ControllerOption {
  244. return func(c *Controller) {
  245. c.v4 = v4
  246. c.v6 = v6
  247. }
  248. }
  249. func WithRegisterer(registerer prometheus.Registerer) ControllerOption {
  250. return func(c *Controller) {
  251. c.registerer = registerer
  252. }
  253. }
  254. // New generates a new iptables rules controller.
  255. // If no options are given, IPv4 and IPv6 clients
  256. // will be instantiated using the regular iptables backend.
  257. func New(opts ...ControllerOption) (*Controller, error) {
  258. c := &Controller{
  259. errors: make(chan error),
  260. logger: log.NewNopLogger(),
  261. }
  262. for _, o := range opts {
  263. o(c)
  264. }
  265. if c.v4 == nil {
  266. v4, err := iptables.NewWithProtocol(iptables.ProtocolIPv4)
  267. if err != nil {
  268. return nil, fmt.Errorf("failed to create iptables IPv4 client: %v", err)
  269. }
  270. c.v4 = wrapWithMetrics(v4, "IPv4", c.registerer)
  271. }
  272. if c.v6 == nil {
  273. disabled, err := ipv6Disabled()
  274. if err != nil {
  275. return nil, fmt.Errorf("failed to check IPv6 status: %v", err)
  276. }
  277. if disabled {
  278. level.Info(c.logger).Log("msg", "IPv6 is disabled in the kernel; disabling the IPv6 iptables controller")
  279. c.v6 = &fakeClient{}
  280. } else {
  281. v6, err := iptables.NewWithProtocol(iptables.ProtocolIPv6)
  282. if err != nil {
  283. return nil, fmt.Errorf("failed to create iptables IPv6 client: %v", err)
  284. }
  285. c.v6 = wrapWithMetrics(v6, "IPv6", c.registerer)
  286. }
  287. }
  288. return c, nil
  289. }
  290. // Run watches for changes to iptables rules and reconciles
  291. // the rules against the desired state.
  292. func (c *Controller) Run(stop <-chan struct{}) (<-chan error, error) {
  293. c.Lock()
  294. if c.subscribed {
  295. c.Unlock()
  296. return c.errors, nil
  297. }
  298. // Ensure a given instance only subscribes once.
  299. c.subscribed = true
  300. c.Unlock()
  301. go func() {
  302. t := time.NewTimer(c.resyncPeriod)
  303. defer close(c.errors)
  304. for {
  305. select {
  306. case <-t.C:
  307. if err := c.reconcile(); err != nil {
  308. nonBlockingSend(c.errors, fmt.Errorf("failed to reconcile rules: %v", err))
  309. }
  310. t.Reset(c.resyncPeriod)
  311. case <-stop:
  312. return
  313. }
  314. }
  315. }()
  316. return c.errors, nil
  317. }
  318. // reconcile makes sure that every rule is still in the backend.
  319. // It does not ensure that the order in the backend is correct.
  320. // If any rule is missing, that rule and all following rules are
  321. // re-added.
  322. func (c *Controller) reconcile() error {
  323. c.Lock()
  324. defer c.Unlock()
  325. var rc ruleCache
  326. if err := c.reconcileAppendRules(rc); err != nil {
  327. return err
  328. }
  329. return c.reconcilePrependRules(rc)
  330. }
  331. func (c *Controller) reconcileAppendRules(rc ruleCache) error {
  332. for i, r := range c.appendRules {
  333. ok, err := rc.exists(c.client(r.Proto()), r)
  334. if err != nil {
  335. return fmt.Errorf("failed to check if rule exists: %v", err)
  336. }
  337. if !ok {
  338. level.Info(c.logger).Log("msg", fmt.Sprintf("applying %d iptables rules", len(c.appendRules)-i))
  339. if err := c.resetFromIndex(i, c.appendRules); err != nil {
  340. return fmt.Errorf("failed to add rule: %v", err)
  341. }
  342. break
  343. }
  344. }
  345. return nil
  346. }
  347. func (c *Controller) reconcilePrependRules(rc ruleCache) error {
  348. for _, r := range c.prependRules {
  349. ok, err := rc.exists(c.client(r.Proto()), r)
  350. if err != nil {
  351. return fmt.Errorf("failed to check if rule exists: %v", err)
  352. }
  353. if !ok {
  354. level.Info(c.logger).Log("msg", "prepending iptables rule")
  355. if err := r.Prepend(c.client(r.Proto())); err != nil {
  356. return fmt.Errorf("failed to prepend rule: %v", err)
  357. }
  358. }
  359. }
  360. return nil
  361. }
  362. // resetFromIndex re-adds all rules starting from the given index.
  363. func (c *Controller) resetFromIndex(i int, rules []Rule) error {
  364. if i >= len(rules) {
  365. return nil
  366. }
  367. for j := i; j < len(rules); j++ {
  368. if err := rules[j].Delete(c.client(rules[j].Proto())); err != nil {
  369. return fmt.Errorf("failed to delete rule: %v", err)
  370. }
  371. if err := rules[j].Append(c.client(rules[j].Proto())); err != nil {
  372. return fmt.Errorf("failed to add rule: %v", err)
  373. }
  374. }
  375. return nil
  376. }
  377. // deleteFromIndex deletes all rules starting from the given index.
  378. func (c *Controller) deleteFromIndex(i int, rules *[]Rule) error {
  379. if i >= len(*rules) {
  380. return nil
  381. }
  382. for j := i; j < len(*rules); j++ {
  383. if err := (*rules)[j].Delete(c.client((*rules)[j].Proto())); err != nil {
  384. *rules = append((*rules)[:i], (*rules)[j:]...)
  385. return fmt.Errorf("failed to delete rule: %v", err)
  386. }
  387. (*rules)[j] = nil
  388. }
  389. *rules = (*rules)[:i]
  390. return nil
  391. }
  392. // Set idempotently overwrites any iptables rules previously defined
  393. // for the controller with the given set of rules.
  394. func (c *Controller) Set(rules RuleSet) error {
  395. c.Lock()
  396. defer c.Unlock()
  397. if err := c.setAppendRules(rules.appendRules); err != nil {
  398. return err
  399. }
  400. return c.setPrependRules(rules.prependRules)
  401. }
  402. func (c *Controller) setAppendRules(appendRules []Rule) error {
  403. var i int
  404. for ; i < len(appendRules); i++ {
  405. if i < len(c.appendRules) {
  406. if appendRules[i].String() != c.appendRules[i].String() {
  407. if err := c.deleteFromIndex(i, &c.appendRules); err != nil {
  408. return err
  409. }
  410. }
  411. }
  412. if i >= len(c.appendRules) {
  413. if err := appendRules[i].Append(c.client(appendRules[i].Proto())); err != nil {
  414. return fmt.Errorf("failed to add rule: %v", err)
  415. }
  416. c.appendRules = append(c.appendRules, appendRules[i])
  417. }
  418. }
  419. err := c.deleteFromIndex(i, &c.appendRules)
  420. if err != nil {
  421. return fmt.Errorf("failed to delete rule: %v", err)
  422. }
  423. return nil
  424. }
  425. func (c *Controller) setPrependRules(prependRules []Rule) error {
  426. for _, prependRule := range prependRules {
  427. if !containsRule(c.prependRules, prependRule) {
  428. if err := prependRule.Prepend(c.client(prependRule.Proto())); err != nil {
  429. return fmt.Errorf("failed to add rule: %v", err)
  430. }
  431. c.prependRules = append(c.prependRules, prependRule)
  432. }
  433. }
  434. for _, existingRule := range c.prependRules {
  435. if !containsRule(prependRules, existingRule) {
  436. if err := existingRule.Delete(c.client(existingRule.Proto())); err != nil {
  437. return fmt.Errorf("failed to delete rule: %v", err)
  438. }
  439. c.prependRules = removeRule(c.prependRules, existingRule)
  440. }
  441. }
  442. return nil
  443. }
  444. func removeRule(rules []Rule, toRemove Rule) []Rule {
  445. ret := make([]Rule, 0, len(rules))
  446. for _, rule := range rules {
  447. if rule.String() != toRemove.String() {
  448. ret = append(ret, rule)
  449. }
  450. }
  451. return ret
  452. }
  453. func containsRule(haystack []Rule, needle Rule) bool {
  454. for _, element := range haystack {
  455. if element.String() == needle.String() {
  456. return true
  457. }
  458. }
  459. return false
  460. }
  461. // CleanUp will clean up any rules created by the controller.
  462. func (c *Controller) CleanUp() error {
  463. c.Lock()
  464. defer c.Unlock()
  465. err := c.deleteFromIndex(0, &c.prependRules)
  466. if err != nil {
  467. return err
  468. }
  469. return c.deleteFromIndex(0, &c.appendRules)
  470. }
  471. func (c *Controller) client(p Protocol) Client {
  472. switch p {
  473. case ProtocolIPv4:
  474. return c.v4
  475. case ProtocolIPv6:
  476. return c.v6
  477. default:
  478. panic("unknown protocol")
  479. }
  480. }
  481. func nonBlockingSend(errors chan<- error, err error) {
  482. select {
  483. case errors <- err:
  484. default:
  485. }
  486. }