setup_ip_tables.go 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. package bridge
  2. import (
  3. "fmt"
  4. "net"
  5. "github.com/Sirupsen/logrus"
  6. "github.com/docker/libnetwork/iptables"
  7. "github.com/docker/libnetwork/netutils"
  8. )
  9. // DockerChain: DOCKER iptable chain name
  10. const (
  11. DockerChain = "DOCKER"
  12. IsolationChain = "DOCKER-ISOLATION"
  13. )
  14. func setupIPChains(config *configuration) (*iptables.ChainInfo, *iptables.ChainInfo, *iptables.ChainInfo, error) {
  15. // Sanity check.
  16. if config.EnableIPTables == false {
  17. return nil, nil, nil, fmt.Errorf("cannot create new chains, EnableIPTable is disabled")
  18. }
  19. hairpinMode := !config.EnableUserlandProxy
  20. natChain, err := iptables.NewChain(DockerChain, iptables.Nat, hairpinMode)
  21. if err != nil {
  22. return nil, nil, nil, fmt.Errorf("failed to create NAT chain: %v", err)
  23. }
  24. defer func() {
  25. if err != nil {
  26. if err := iptables.RemoveExistingChain(DockerChain, iptables.Nat); err != nil {
  27. logrus.Warnf("failed on removing iptables NAT chain on cleanup: %v", err)
  28. }
  29. }
  30. }()
  31. filterChain, err := iptables.NewChain(DockerChain, iptables.Filter, false)
  32. if err != nil {
  33. return nil, nil, nil, fmt.Errorf("failed to create FILTER chain: %v", err)
  34. }
  35. defer func() {
  36. if err != nil {
  37. if err := iptables.RemoveExistingChain(DockerChain, iptables.Filter); err != nil {
  38. logrus.Warnf("failed on removing iptables FILTER chain on cleanup: %v", err)
  39. }
  40. }
  41. }()
  42. isolationChain, err := iptables.NewChain(IsolationChain, iptables.Filter, false)
  43. if err != nil {
  44. return nil, nil, nil, fmt.Errorf("failed to create FILTER isolation chain: %v", err)
  45. }
  46. if err := addReturnRule(IsolationChain); err != nil {
  47. return nil, nil, nil, err
  48. }
  49. return natChain, filterChain, isolationChain, nil
  50. }
  51. func (n *bridgeNetwork) setupIPTables(config *networkConfiguration, i *bridgeInterface) error {
  52. d := n.driver
  53. d.Lock()
  54. driverConfig := d.config
  55. d.Unlock()
  56. // Sanity check.
  57. if driverConfig.EnableIPTables == false {
  58. return fmt.Errorf("Cannot program chains, EnableIPTable is disabled")
  59. }
  60. // Pickup this configuraton option from driver
  61. hairpinMode := !driverConfig.EnableUserlandProxy
  62. addrv4, _, err := netutils.GetIfaceAddr(config.BridgeName)
  63. if err != nil {
  64. return fmt.Errorf("Failed to setup IP tables, cannot acquire Interface address: %s", err.Error())
  65. }
  66. ipnet := addrv4.(*net.IPNet)
  67. maskedAddrv4 := &net.IPNet{
  68. IP: ipnet.IP.Mask(ipnet.Mask),
  69. Mask: ipnet.Mask,
  70. }
  71. if err = setupIPTablesInternal(config.BridgeName, maskedAddrv4, config.EnableICC, config.EnableIPMasquerade, hairpinMode, true); err != nil {
  72. return fmt.Errorf("Failed to Setup IP tables: %s", err.Error())
  73. }
  74. n.registerIptCleanFunc(func() error {
  75. return setupIPTablesInternal(config.BridgeName, maskedAddrv4, config.EnableICC, config.EnableIPMasquerade, hairpinMode, false)
  76. })
  77. natChain, filterChain, _, err := n.getDriverChains()
  78. if err != nil {
  79. return fmt.Errorf("Failed to setup IP tables, cannot acquire chain info %s", err.Error())
  80. }
  81. err = iptables.ProgramChain(natChain, config.BridgeName, hairpinMode, true)
  82. if err != nil {
  83. return fmt.Errorf("Failed to program NAT chain: %s", err.Error())
  84. }
  85. err = iptables.ProgramChain(filterChain, config.BridgeName, hairpinMode, true)
  86. if err != nil {
  87. return fmt.Errorf("Failed to program FILTER chain: %s", err.Error())
  88. }
  89. if err := ensureJumpRule("FORWARD", IsolationChain); err != nil {
  90. return err
  91. }
  92. n.registerIptCleanFunc(func() error {
  93. return iptables.ProgramChain(filterChain, config.BridgeName, hairpinMode, false)
  94. })
  95. n.portMapper.SetIptablesChain(filterChain, n.getNetworkBridgeName())
  96. return nil
  97. }
  98. type iptRule struct {
  99. table iptables.Table
  100. chain string
  101. preArgs []string
  102. args []string
  103. }
  104. func setupIPTablesInternal(bridgeIface string, addr net.Addr, icc, ipmasq, hairpin, enable bool) error {
  105. var (
  106. address = addr.String()
  107. natRule = iptRule{table: iptables.Nat, chain: "POSTROUTING", preArgs: []string{"-t", "nat"}, args: []string{"-s", address, "!", "-o", bridgeIface, "-j", "MASQUERADE"}}
  108. hpNatRule = iptRule{table: iptables.Nat, chain: "POSTROUTING", preArgs: []string{"-t", "nat"}, args: []string{"-m", "addrtype", "--src-type", "LOCAL", "-o", bridgeIface, "-j", "MASQUERADE"}}
  109. outRule = iptRule{table: iptables.Filter, chain: "FORWARD", args: []string{"-i", bridgeIface, "!", "-o", bridgeIface, "-j", "ACCEPT"}}
  110. inRule = iptRule{table: iptables.Filter, chain: "FORWARD", args: []string{"-o", bridgeIface, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"}}
  111. )
  112. // Set NAT.
  113. if ipmasq {
  114. if err := programChainRule(natRule, "NAT", enable); err != nil {
  115. return err
  116. }
  117. }
  118. // In hairpin mode, masquerade traffic from localhost
  119. if hairpin {
  120. if err := programChainRule(hpNatRule, "MASQ LOCAL HOST", enable); err != nil {
  121. return err
  122. }
  123. }
  124. // Set Inter Container Communication.
  125. if err := setIcc(bridgeIface, icc, enable); err != nil {
  126. return err
  127. }
  128. // Set Accept on all non-intercontainer outgoing packets.
  129. if err := programChainRule(outRule, "ACCEPT NON_ICC OUTGOING", enable); err != nil {
  130. return err
  131. }
  132. // Set Accept on incoming packets for existing connections.
  133. if err := programChainRule(inRule, "ACCEPT INCOMING", enable); err != nil {
  134. return err
  135. }
  136. return nil
  137. }
  138. func programChainRule(rule iptRule, ruleDescr string, insert bool) error {
  139. var (
  140. prefix []string
  141. operation string
  142. condition bool
  143. doesExist = iptables.Exists(rule.table, rule.chain, rule.args...)
  144. )
  145. if insert {
  146. condition = !doesExist
  147. prefix = []string{"-I", rule.chain}
  148. operation = "enable"
  149. } else {
  150. condition = doesExist
  151. prefix = []string{"-D", rule.chain}
  152. operation = "disable"
  153. }
  154. if rule.preArgs != nil {
  155. prefix = append(rule.preArgs, prefix...)
  156. }
  157. if condition {
  158. if err := iptables.RawCombinedOutput(append(prefix, rule.args...)...); err != nil {
  159. return fmt.Errorf("Unable to %s %s rule: %s", operation, ruleDescr, err.Error())
  160. }
  161. }
  162. return nil
  163. }
  164. func setIcc(bridgeIface string, iccEnable, insert bool) error {
  165. var (
  166. table = iptables.Filter
  167. chain = "FORWARD"
  168. args = []string{"-i", bridgeIface, "-o", bridgeIface, "-j"}
  169. acceptArgs = append(args, "ACCEPT")
  170. dropArgs = append(args, "DROP")
  171. )
  172. if insert {
  173. if !iccEnable {
  174. iptables.Raw(append([]string{"-D", chain}, acceptArgs...)...)
  175. if !iptables.Exists(table, chain, dropArgs...) {
  176. if err := iptables.RawCombinedOutput(append([]string{"-A", chain}, dropArgs...)...); err != nil {
  177. return fmt.Errorf("Unable to prevent intercontainer communication: %s", err.Error())
  178. }
  179. }
  180. } else {
  181. iptables.Raw(append([]string{"-D", chain}, dropArgs...)...)
  182. if !iptables.Exists(table, chain, acceptArgs...) {
  183. if err := iptables.RawCombinedOutput(append([]string{"-I", chain}, acceptArgs...)...); err != nil {
  184. return fmt.Errorf("Unable to allow intercontainer communication: %s", err.Error())
  185. }
  186. }
  187. }
  188. } else {
  189. // Remove any ICC rule.
  190. if !iccEnable {
  191. if iptables.Exists(table, chain, dropArgs...) {
  192. iptables.Raw(append([]string{"-D", chain}, dropArgs...)...)
  193. }
  194. } else {
  195. if iptables.Exists(table, chain, acceptArgs...) {
  196. iptables.Raw(append([]string{"-D", chain}, acceptArgs...)...)
  197. }
  198. }
  199. }
  200. return nil
  201. }
  202. // Control Inter Network Communication. Install/remove only if it is not/is present.
  203. func setINC(iface1, iface2 string, enable bool) error {
  204. var (
  205. table = iptables.Filter
  206. chain = IsolationChain
  207. args = [2][]string{{"-i", iface1, "-o", iface2, "-j", "DROP"}, {"-i", iface2, "-o", iface1, "-j", "DROP"}}
  208. )
  209. if enable {
  210. for i := 0; i < 2; i++ {
  211. if iptables.Exists(table, chain, args[i]...) {
  212. continue
  213. }
  214. if err := iptables.RawCombinedOutput(append([]string{"-I", chain}, args[i]...)...); err != nil {
  215. return fmt.Errorf("unable to add inter-network communication rule: %v", err)
  216. }
  217. }
  218. } else {
  219. for i := 0; i < 2; i++ {
  220. if !iptables.Exists(table, chain, args[i]...) {
  221. continue
  222. }
  223. if err := iptables.RawCombinedOutput(append([]string{"-D", chain}, args[i]...)...); err != nil {
  224. return fmt.Errorf("unable to remove inter-network communication rule: %v", err)
  225. }
  226. }
  227. }
  228. return nil
  229. }
  230. func addReturnRule(chain string) error {
  231. var (
  232. table = iptables.Filter
  233. args = []string{"-j", "RETURN"}
  234. )
  235. if iptables.Exists(table, chain, args...) {
  236. return nil
  237. }
  238. err := iptables.RawCombinedOutput(append([]string{"-I", chain}, args...)...)
  239. if err != nil {
  240. return fmt.Errorf("unable to add return rule in %s chain: %s", chain, err.Error())
  241. }
  242. return nil
  243. }
  244. // Ensure the jump rule is on top
  245. func ensureJumpRule(fromChain, toChain string) error {
  246. var (
  247. table = iptables.Filter
  248. args = []string{"-j", toChain}
  249. )
  250. if iptables.Exists(table, fromChain, args...) {
  251. err := iptables.RawCombinedOutput(append([]string{"-D", fromChain}, args...)...)
  252. if err != nil {
  253. return fmt.Errorf("unable to remove jump to %s rule in %s chain: %s", toChain, fromChain, err.Error())
  254. }
  255. }
  256. err := iptables.RawCombinedOutput(append([]string{"-I", fromChain}, args...)...)
  257. if err != nil {
  258. return fmt.Errorf("unable to insert jump to %s rule in %s chain: %s", toChain, fromChain, err.Error())
  259. }
  260. return nil
  261. }