setup_ip_tables_linux_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. package bridge
  2. import (
  3. "net"
  4. "testing"
  5. "github.com/docker/docker/internal/testutils/netnsutils"
  6. "github.com/docker/docker/libnetwork/driverapi"
  7. "github.com/docker/docker/libnetwork/iptables"
  8. "github.com/docker/docker/libnetwork/netlabel"
  9. "github.com/docker/docker/libnetwork/portmapper"
  10. "github.com/vishvananda/netlink"
  11. "gotest.tools/v3/assert"
  12. )
  13. const (
  14. iptablesTestBridgeIP = "192.168.42.1"
  15. )
  16. // A testRegisterer implements the driverapi.Registerer interface.
  17. type testRegisterer struct {
  18. t *testing.T
  19. d *driver
  20. }
  21. func (r *testRegisterer) RegisterDriver(name string, di driverapi.Driver, _ driverapi.Capability) error {
  22. if got, want := name, "bridge"; got != want {
  23. r.t.Fatalf("got driver name %s, want %s", got, want)
  24. }
  25. d, ok := di.(*driver)
  26. if !ok {
  27. r.t.Fatalf("got driver type %T, want %T", di, &driver{})
  28. }
  29. r.d = d
  30. return nil
  31. }
  32. func TestProgramIPTable(t *testing.T) {
  33. // Create a test bridge with a basic bridge configuration (name + IPv4).
  34. defer netnsutils.SetupTestOSContext(t)()
  35. nh, err := netlink.NewHandle()
  36. if err != nil {
  37. t.Fatal(err)
  38. }
  39. createTestBridge(getBasicTestConfig(), &bridgeInterface{nlh: nh}, t)
  40. // Store various iptables chain rules we care for.
  41. rules := []struct {
  42. rule iptRule
  43. descr string
  44. }{
  45. {iptRule{ipv: iptables.IPv4, table: iptables.Filter, chain: "FORWARD", args: []string{"-d", "127.1.2.3", "-i", "lo", "-o", "lo", "-j", "DROP"}}, "Test Loopback"},
  46. {iptRule{ipv: iptables.IPv4, table: iptables.Nat, chain: "POSTROUTING", args: []string{"-s", iptablesTestBridgeIP, "!", "-o", DefaultBridgeName, "-j", "MASQUERADE"}}, "NAT Test"},
  47. {iptRule{ipv: iptables.IPv4, table: iptables.Filter, chain: "FORWARD", args: []string{"-o", DefaultBridgeName, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"}}, "Test ACCEPT INCOMING"},
  48. {iptRule{ipv: iptables.IPv4, table: iptables.Filter, chain: "FORWARD", args: []string{"-i", DefaultBridgeName, "!", "-o", DefaultBridgeName, "-j", "ACCEPT"}}, "Test ACCEPT NON_ICC OUTGOING"},
  49. {iptRule{ipv: iptables.IPv4, table: iptables.Filter, chain: "FORWARD", args: []string{"-i", DefaultBridgeName, "-o", DefaultBridgeName, "-j", "ACCEPT"}}, "Test enable ICC"},
  50. {iptRule{ipv: iptables.IPv4, table: iptables.Filter, chain: "FORWARD", args: []string{"-i", DefaultBridgeName, "-o", DefaultBridgeName, "-j", "DROP"}}, "Test disable ICC"},
  51. }
  52. // Assert the chain rules' insertion and removal.
  53. for _, c := range rules {
  54. assertIPTableChainProgramming(c.rule, c.descr, t)
  55. }
  56. }
  57. func TestSetupIPChains(t *testing.T) {
  58. // Create a test bridge with a basic bridge configuration (name + IPv4).
  59. defer netnsutils.SetupTestOSContext(t)()
  60. nh, err := netlink.NewHandle()
  61. if err != nil {
  62. t.Fatal(err)
  63. }
  64. driverconfig := configuration{
  65. EnableIPTables: true,
  66. }
  67. d := &driver{
  68. config: driverconfig,
  69. }
  70. assertChainConfig(d, t)
  71. config := getBasicTestConfig()
  72. br := &bridgeInterface{nlh: nh}
  73. createTestBridge(config, br, t)
  74. assertBridgeConfig(config, br, d, t)
  75. config.EnableIPMasquerade = true
  76. assertBridgeConfig(config, br, d, t)
  77. config.EnableICC = true
  78. assertBridgeConfig(config, br, d, t)
  79. config.EnableIPMasquerade = false
  80. assertBridgeConfig(config, br, d, t)
  81. }
  82. func getBasicTestConfig() *networkConfiguration {
  83. config := &networkConfiguration{
  84. BridgeName: DefaultBridgeName,
  85. AddressIPv4: &net.IPNet{IP: net.ParseIP(iptablesTestBridgeIP), Mask: net.CIDRMask(16, 32)},
  86. }
  87. return config
  88. }
  89. func createTestBridge(config *networkConfiguration, br *bridgeInterface, t *testing.T) {
  90. if err := setupDevice(config, br); err != nil {
  91. t.Fatalf("Failed to create the testing Bridge: %s", err.Error())
  92. }
  93. if err := setupBridgeIPv4(config, br); err != nil {
  94. t.Fatalf("Failed to bring up the testing Bridge: %s", err.Error())
  95. }
  96. if config.EnableIPv6 {
  97. if err := setupBridgeIPv6(config, br); err != nil {
  98. t.Fatalf("Failed to bring up the testing Bridge: %s", err.Error())
  99. }
  100. }
  101. }
  102. // Assert base function which pushes iptables chain rules on insertion and removal.
  103. func assertIPTableChainProgramming(rule iptRule, descr string, t *testing.T) {
  104. // Add
  105. if err := programChainRule(rule, descr, true); err != nil {
  106. t.Fatalf("Failed to program iptable rule %s: %s", descr, err.Error())
  107. }
  108. if !rule.Exists() {
  109. t.Fatalf("Failed to effectively program iptable rule: %s", descr)
  110. }
  111. // Remove
  112. if err := programChainRule(rule, descr, false); err != nil {
  113. t.Fatalf("Failed to remove iptable rule %s: %s", descr, err.Error())
  114. }
  115. if rule.Exists() {
  116. t.Fatalf("Failed to effectively remove iptable rule: %s", descr)
  117. }
  118. }
  119. // Assert function which create chains.
  120. func assertChainConfig(d *driver, t *testing.T) {
  121. var err error
  122. d.natChain, d.filterChain, d.isolationChain1, d.isolationChain2, err = setupIPChains(d.config, iptables.IPv4)
  123. if err != nil {
  124. t.Fatal(err)
  125. }
  126. if d.config.EnableIP6Tables {
  127. d.natChainV6, d.filterChainV6, d.isolationChain1V6, d.isolationChain2V6, err = setupIPChains(d.config, iptables.IPv6)
  128. if err != nil {
  129. t.Fatal(err)
  130. }
  131. }
  132. }
  133. // Assert function which pushes chains based on bridge config parameters.
  134. func assertBridgeConfig(config *networkConfiguration, br *bridgeInterface, d *driver, t *testing.T) {
  135. nw := bridgeNetwork{
  136. portMapper: portmapper.New(),
  137. portMapperV6: portmapper.New(),
  138. config: config,
  139. }
  140. nw.driver = d
  141. // Attempt programming of ip tables.
  142. err := nw.setupIP4Tables(config, br)
  143. if err != nil {
  144. t.Fatalf("%v", err)
  145. }
  146. if d.config.EnableIP6Tables {
  147. if err := nw.setupIP6Tables(config, br); err != nil {
  148. t.Fatalf("%v", err)
  149. }
  150. }
  151. }
  152. // Regression test for https://github.com/moby/moby/issues/46445
  153. func TestSetupIP6TablesWithHostIPv4(t *testing.T) {
  154. defer netnsutils.SetupTestOSContext(t)()
  155. d := newDriver()
  156. dc := &configuration{
  157. EnableIPTables: true,
  158. EnableIP6Tables: true,
  159. }
  160. if err := d.configure(map[string]interface{}{netlabel.GenericData: dc}); err != nil {
  161. t.Fatal(err)
  162. }
  163. nc := &networkConfiguration{
  164. BridgeName: DefaultBridgeName,
  165. AddressIPv4: &net.IPNet{IP: net.ParseIP(iptablesTestBridgeIP), Mask: net.CIDRMask(16, 32)},
  166. EnableIPMasquerade: true,
  167. EnableIPv6: true,
  168. AddressIPv6: &net.IPNet{IP: net.ParseIP("2001:db8::1"), Mask: net.CIDRMask(64, 128)},
  169. HostIPv4: net.ParseIP("192.0.2.2"),
  170. }
  171. nh, err := netlink.NewHandle()
  172. if err != nil {
  173. t.Fatal(err)
  174. }
  175. br := &bridgeInterface{nlh: nh}
  176. createTestBridge(nc, br, t)
  177. assertBridgeConfig(nc, br, d, t)
  178. }
  179. func TestOutgoingNATRules(t *testing.T) {
  180. br := "br-nattest"
  181. brIPv4 := &net.IPNet{IP: net.ParseIP(iptablesTestBridgeIP), Mask: net.CIDRMask(16, 32)}
  182. brIPv6 := &net.IPNet{IP: net.ParseIP("2001:db8::1"), Mask: net.CIDRMask(64, 128)}
  183. maskedBrIPv4 := &net.IPNet{IP: brIPv4.IP.Mask(brIPv4.Mask), Mask: brIPv4.Mask}
  184. maskedBrIPv6 := &net.IPNet{IP: brIPv6.IP.Mask(brIPv6.Mask), Mask: brIPv6.Mask}
  185. hostIPv4 := net.ParseIP("192.0.2.2")
  186. hostIPv6 := net.ParseIP("2001:db8:1::1")
  187. for _, tc := range []struct {
  188. desc string
  189. enableIPTables bool
  190. enableIP6Tables bool
  191. enableIPv6 bool
  192. enableIPMasquerade bool
  193. hostIPv4 net.IP
  194. hostIPv6 net.IP
  195. // Hairpin NAT rules are not tested here because they are orthogonal to outgoing NAT. They
  196. // exist to support the port forwarding DNAT rules: without any port forwarding there would be
  197. // no need for any hairpin NAT rules, and when there is port forwarding then hairpin NAT rules
  198. // are needed even if outgoing NAT is disabled. Hairpin NAT tests belong with the port
  199. // forwarding DNAT tests.
  200. wantIPv4Masq bool
  201. wantIPv4Snat bool
  202. wantIPv6Masq bool
  203. wantIPv6Snat bool
  204. }{
  205. {
  206. desc: "everything disabled",
  207. },
  208. {
  209. desc: "iptables/ip6tables disabled",
  210. enableIPv6: true,
  211. enableIPMasquerade: true,
  212. },
  213. {
  214. desc: "host IP with iptables/ip6tables disabled",
  215. enableIPv6: true,
  216. enableIPMasquerade: true,
  217. hostIPv4: hostIPv4,
  218. hostIPv6: hostIPv6,
  219. },
  220. {
  221. desc: "masquerade disabled, no host IP",
  222. enableIPTables: true,
  223. enableIP6Tables: true,
  224. enableIPv6: true,
  225. },
  226. {
  227. desc: "masquerade disabled, with host IP",
  228. enableIPTables: true,
  229. enableIP6Tables: true,
  230. enableIPv6: true,
  231. hostIPv4: hostIPv4,
  232. hostIPv6: hostIPv6,
  233. },
  234. {
  235. desc: "IPv4 masquerade, IPv6 disabled",
  236. enableIPTables: true,
  237. enableIPMasquerade: true,
  238. wantIPv4Masq: true,
  239. },
  240. {
  241. desc: "IPv4 SNAT, IPv6 disabled",
  242. enableIPTables: true,
  243. enableIPMasquerade: true,
  244. hostIPv4: hostIPv4,
  245. wantIPv4Snat: true,
  246. },
  247. {
  248. desc: "IPv4 masquerade, IPv6 masquerade",
  249. enableIPTables: true,
  250. enableIP6Tables: true,
  251. enableIPv6: true,
  252. enableIPMasquerade: true,
  253. wantIPv4Masq: true,
  254. wantIPv6Masq: true,
  255. },
  256. {
  257. desc: "IPv4 masquerade, IPv6 SNAT",
  258. enableIPTables: true,
  259. enableIP6Tables: true,
  260. enableIPv6: true,
  261. enableIPMasquerade: true,
  262. hostIPv6: hostIPv6,
  263. wantIPv4Masq: true,
  264. wantIPv6Snat: true,
  265. },
  266. {
  267. desc: "IPv4 SNAT, IPv6 masquerade",
  268. enableIPTables: true,
  269. enableIP6Tables: true,
  270. enableIPv6: true,
  271. enableIPMasquerade: true,
  272. hostIPv4: hostIPv4,
  273. wantIPv4Snat: true,
  274. wantIPv6Masq: true,
  275. },
  276. {
  277. desc: "IPv4 SNAT, IPv6 SNAT",
  278. enableIPTables: true,
  279. enableIP6Tables: true,
  280. enableIPv6: true,
  281. enableIPMasquerade: true,
  282. hostIPv4: hostIPv4,
  283. hostIPv6: hostIPv6,
  284. wantIPv4Snat: true,
  285. wantIPv6Snat: true,
  286. },
  287. } {
  288. t.Run(tc.desc, func(t *testing.T) {
  289. defer netnsutils.SetupTestOSContext(t)()
  290. dc := &configuration{
  291. EnableIPTables: tc.enableIPTables,
  292. EnableIP6Tables: tc.enableIP6Tables,
  293. }
  294. r := &testRegisterer{t: t}
  295. if err := Register(r, map[string]interface{}{netlabel.GenericData: dc}); err != nil {
  296. t.Fatal(err)
  297. }
  298. if r.d == nil {
  299. t.Fatal("testRegisterer.RegisterDriver never called")
  300. }
  301. nc := &networkConfiguration{
  302. BridgeName: br,
  303. AddressIPv4: brIPv4,
  304. AddressIPv6: brIPv6,
  305. EnableIPv6: tc.enableIPv6,
  306. EnableIPMasquerade: tc.enableIPMasquerade,
  307. HostIPv4: tc.hostIPv4,
  308. HostIPv6: tc.hostIPv6,
  309. }
  310. ipv4Data := []driverapi.IPAMData{{Pool: maskedBrIPv4, Gateway: brIPv4}}
  311. ipv6Data := []driverapi.IPAMData{{Pool: maskedBrIPv6, Gateway: brIPv6}}
  312. if !nc.EnableIPv6 {
  313. nc.AddressIPv6 = nil
  314. ipv6Data = nil
  315. }
  316. if err := r.d.CreateNetwork("nattest", map[string]interface{}{netlabel.GenericData: nc}, nil, ipv4Data, ipv6Data); err != nil {
  317. t.Fatal(err)
  318. }
  319. defer func() {
  320. if err := r.d.DeleteNetwork("nattest"); err != nil {
  321. t.Fatal(err)
  322. }
  323. }()
  324. // Log the contents of all chains to aid troubleshooting.
  325. for _, ipv := range []iptables.IPVersion{iptables.IPv4, iptables.IPv6} {
  326. ipt := iptables.GetIptable(ipv)
  327. for _, table := range []iptables.Table{iptables.Nat, iptables.Filter, iptables.Mangle} {
  328. out, err := ipt.Raw("-t", string(table), "-S")
  329. if err != nil {
  330. t.Error(err)
  331. }
  332. t.Logf("%s: %s %s table rules:\n%s", tc.desc, ipv, table, string(out))
  333. }
  334. }
  335. for _, rc := range []struct {
  336. want bool
  337. rule iptRule
  338. }{
  339. // Rule order doesn't matter: At most one of the following IPv4 rules will exist, and the
  340. // same goes for the IPv6 rules.
  341. {tc.wantIPv4Masq, iptRule{iptables.IPv4, iptables.Nat, "POSTROUTING", []string{"-s", maskedBrIPv4.String(), "!", "-o", br, "-j", "MASQUERADE"}}},
  342. {tc.wantIPv4Snat, iptRule{iptables.IPv4, iptables.Nat, "POSTROUTING", []string{"-s", maskedBrIPv4.String(), "!", "-o", br, "-j", "SNAT", "--to-source", hostIPv4.String()}}},
  343. {tc.wantIPv6Masq, iptRule{iptables.IPv6, iptables.Nat, "POSTROUTING", []string{"-s", maskedBrIPv6.String(), "!", "-o", br, "-j", "MASQUERADE"}}},
  344. {tc.wantIPv6Snat, iptRule{iptables.IPv6, iptables.Nat, "POSTROUTING", []string{"-s", maskedBrIPv6.String(), "!", "-o", br, "-j", "SNAT", "--to-source", hostIPv6.String()}}},
  345. } {
  346. assert.Equal(t, rc.rule.Exists(), rc.want)
  347. }
  348. })
  349. }
  350. }