setup_ip_tables.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  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 config.Internal {
  72. if err = setupInternalNetworkRules(config.BridgeName, maskedAddrv4, true); err != nil {
  73. return fmt.Errorf("Failed to Setup IP tables: %s", err.Error())
  74. }
  75. n.registerIptCleanFunc(func() error {
  76. return setupInternalNetworkRules(config.BridgeName, maskedAddrv4, false)
  77. })
  78. } else {
  79. if err = setupIPTablesInternal(config.BridgeName, maskedAddrv4, config.EnableICC, config.EnableIPMasquerade, hairpinMode, true); err != nil {
  80. return fmt.Errorf("Failed to Setup IP tables: %s", err.Error())
  81. }
  82. n.registerIptCleanFunc(func() error {
  83. return setupIPTablesInternal(config.BridgeName, maskedAddrv4, config.EnableICC, config.EnableIPMasquerade, hairpinMode, false)
  84. })
  85. natChain, filterChain, _, err := n.getDriverChains()
  86. if err != nil {
  87. return fmt.Errorf("Failed to setup IP tables, cannot acquire chain info %s", err.Error())
  88. }
  89. err = iptables.ProgramChain(natChain, config.BridgeName, hairpinMode, true)
  90. if err != nil {
  91. return fmt.Errorf("Failed to program NAT chain: %s", err.Error())
  92. }
  93. err = iptables.ProgramChain(filterChain, config.BridgeName, hairpinMode, true)
  94. if err != nil {
  95. return fmt.Errorf("Failed to program FILTER chain: %s", err.Error())
  96. }
  97. n.registerIptCleanFunc(func() error {
  98. return iptables.ProgramChain(filterChain, config.BridgeName, hairpinMode, false)
  99. })
  100. n.portMapper.SetIptablesChain(filterChain, n.getNetworkBridgeName())
  101. }
  102. if err := ensureJumpRule("FORWARD", IsolationChain); err != nil {
  103. return err
  104. }
  105. return nil
  106. }
  107. type iptRule struct {
  108. table iptables.Table
  109. chain string
  110. preArgs []string
  111. args []string
  112. }
  113. func setupIPTablesInternal(bridgeIface string, addr net.Addr, icc, ipmasq, hairpin, enable bool) error {
  114. var (
  115. address = addr.String()
  116. natRule = iptRule{table: iptables.Nat, chain: "POSTROUTING", preArgs: []string{"-t", "nat"}, args: []string{"-s", address, "!", "-o", bridgeIface, "-j", "MASQUERADE"}}
  117. hpNatRule = iptRule{table: iptables.Nat, chain: "POSTROUTING", preArgs: []string{"-t", "nat"}, args: []string{"-m", "addrtype", "--src-type", "LOCAL", "-o", bridgeIface, "-j", "MASQUERADE"}}
  118. outRule = iptRule{table: iptables.Filter, chain: "FORWARD", args: []string{"-i", bridgeIface, "!", "-o", bridgeIface, "-j", "ACCEPT"}}
  119. inRule = iptRule{table: iptables.Filter, chain: "FORWARD", args: []string{"-o", bridgeIface, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"}}
  120. )
  121. // Set NAT.
  122. if ipmasq {
  123. if err := programChainRule(natRule, "NAT", enable); err != nil {
  124. return err
  125. }
  126. }
  127. // In hairpin mode, masquerade traffic from localhost
  128. if hairpin {
  129. if err := programChainRule(hpNatRule, "MASQ LOCAL HOST", enable); err != nil {
  130. return err
  131. }
  132. }
  133. // Set Inter Container Communication.
  134. if err := setIcc(bridgeIface, icc, enable); err != nil {
  135. return err
  136. }
  137. // Set Accept on all non-intercontainer outgoing packets.
  138. if err := programChainRule(outRule, "ACCEPT NON_ICC OUTGOING", enable); err != nil {
  139. return err
  140. }
  141. // Set Accept on incoming packets for existing connections.
  142. if err := programChainRule(inRule, "ACCEPT INCOMING", enable); err != nil {
  143. return err
  144. }
  145. return nil
  146. }
  147. func programChainRule(rule iptRule, ruleDescr string, insert bool) error {
  148. var (
  149. prefix []string
  150. operation string
  151. condition bool
  152. doesExist = iptables.Exists(rule.table, rule.chain, rule.args...)
  153. )
  154. if insert {
  155. condition = !doesExist
  156. prefix = []string{"-I", rule.chain}
  157. operation = "enable"
  158. } else {
  159. condition = doesExist
  160. prefix = []string{"-D", rule.chain}
  161. operation = "disable"
  162. }
  163. if rule.preArgs != nil {
  164. prefix = append(rule.preArgs, prefix...)
  165. }
  166. if condition {
  167. if err := iptables.RawCombinedOutput(append(prefix, rule.args...)...); err != nil {
  168. return fmt.Errorf("Unable to %s %s rule: %s", operation, ruleDescr, err.Error())
  169. }
  170. }
  171. return nil
  172. }
  173. func setIcc(bridgeIface string, iccEnable, insert bool) error {
  174. var (
  175. table = iptables.Filter
  176. chain = "FORWARD"
  177. args = []string{"-i", bridgeIface, "-o", bridgeIface, "-j"}
  178. acceptArgs = append(args, "ACCEPT")
  179. dropArgs = append(args, "DROP")
  180. )
  181. if insert {
  182. if !iccEnable {
  183. iptables.Raw(append([]string{"-D", chain}, acceptArgs...)...)
  184. if !iptables.Exists(table, chain, dropArgs...) {
  185. if err := iptables.RawCombinedOutput(append([]string{"-A", chain}, dropArgs...)...); err != nil {
  186. return fmt.Errorf("Unable to prevent intercontainer communication: %s", err.Error())
  187. }
  188. }
  189. } else {
  190. iptables.Raw(append([]string{"-D", chain}, dropArgs...)...)
  191. if !iptables.Exists(table, chain, acceptArgs...) {
  192. if err := iptables.RawCombinedOutput(append([]string{"-I", chain}, acceptArgs...)...); err != nil {
  193. return fmt.Errorf("Unable to allow intercontainer communication: %s", err.Error())
  194. }
  195. }
  196. }
  197. } else {
  198. // Remove any ICC rule.
  199. if !iccEnable {
  200. if iptables.Exists(table, chain, dropArgs...) {
  201. iptables.Raw(append([]string{"-D", chain}, dropArgs...)...)
  202. }
  203. } else {
  204. if iptables.Exists(table, chain, acceptArgs...) {
  205. iptables.Raw(append([]string{"-D", chain}, acceptArgs...)...)
  206. }
  207. }
  208. }
  209. return nil
  210. }
  211. // Control Inter Network Communication. Install/remove only if it is not/is present.
  212. func setINC(iface1, iface2 string, enable bool) error {
  213. var (
  214. table = iptables.Filter
  215. chain = IsolationChain
  216. args = [2][]string{{"-i", iface1, "-o", iface2, "-j", "DROP"}, {"-i", iface2, "-o", iface1, "-j", "DROP"}}
  217. )
  218. if enable {
  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{"-I", chain}, args[i]...)...); err != nil {
  224. return fmt.Errorf("unable to add inter-network communication rule: %v", err)
  225. }
  226. }
  227. } else {
  228. for i := 0; i < 2; i++ {
  229. if !iptables.Exists(table, chain, args[i]...) {
  230. continue
  231. }
  232. if err := iptables.RawCombinedOutput(append([]string{"-D", chain}, args[i]...)...); err != nil {
  233. return fmt.Errorf("unable to remove inter-network communication rule: %v", err)
  234. }
  235. }
  236. }
  237. return nil
  238. }
  239. func addReturnRule(chain string) error {
  240. var (
  241. table = iptables.Filter
  242. args = []string{"-j", "RETURN"}
  243. )
  244. if iptables.Exists(table, chain, args...) {
  245. return nil
  246. }
  247. err := iptables.RawCombinedOutput(append([]string{"-I", chain}, args...)...)
  248. if err != nil {
  249. return fmt.Errorf("unable to add return rule in %s chain: %s", chain, err.Error())
  250. }
  251. return nil
  252. }
  253. // Ensure the jump rule is on top
  254. func ensureJumpRule(fromChain, toChain string) error {
  255. var (
  256. table = iptables.Filter
  257. args = []string{"-j", toChain}
  258. )
  259. if iptables.Exists(table, fromChain, args...) {
  260. err := iptables.RawCombinedOutput(append([]string{"-D", fromChain}, args...)...)
  261. if err != nil {
  262. return fmt.Errorf("unable to remove jump to %s rule in %s chain: %s", toChain, fromChain, err.Error())
  263. }
  264. }
  265. err := iptables.RawCombinedOutput(append([]string{"-I", fromChain}, args...)...)
  266. if err != nil {
  267. return fmt.Errorf("unable to insert jump to %s rule in %s chain: %s", toChain, fromChain, err.Error())
  268. }
  269. return nil
  270. }
  271. func removeIPChains() {
  272. for _, chainInfo := range []iptables.ChainInfo{
  273. {Name: DockerChain, Table: iptables.Nat},
  274. {Name: DockerChain, Table: iptables.Filter},
  275. {Name: IsolationChain, Table: iptables.Filter},
  276. } {
  277. if err := chainInfo.Remove(); err != nil {
  278. logrus.Warnf("Failed to remove existing iptables entries in table %s chain %s : %v", chainInfo.Table, chainInfo.Name, err)
  279. }
  280. }
  281. }
  282. func setupInternalNetworkRules(bridgeIface string, addr net.Addr, insert bool) error {
  283. var (
  284. inDropRule = iptRule{table: iptables.Filter, chain: IsolationChain, args: []string{"-i", bridgeIface, "!", "-d", addr.String(), "-j", "DROP"}}
  285. outDropRule = iptRule{table: iptables.Filter, chain: IsolationChain, args: []string{"-o", bridgeIface, "!", "-s", addr.String(), "-j", "DROP"}}
  286. )
  287. if err := programChainRule(inDropRule, "DROP INCOMING", insert); err != nil {
  288. return err
  289. }
  290. if err := programChainRule(outDropRule, "DROP OUTGOING", insert); err != nil {
  291. return err
  292. }
  293. return nil
  294. }