From 4f6f00e1916a8c58e67c8118d015988d86718d19 Mon Sep 17 00:00:00 2001 From: Alessandro Boch Date: Wed, 21 Oct 2015 09:08:19 -0700 Subject: [PATCH] Do not update etc/hosts for every container - Only user named containers will be published into other containers' etc/hosts file. - Also block linking to containers which are not connected to the default network Signed-off-by: Alessandro Boch --- daemon/container_unix.go | 15 +++- daemon/daemon.go | 7 +- daemon/network/settings.go | 1 + .../docker_cli_network_unix_test.go | 85 ++++++++++++++++++- 4 files changed, 102 insertions(+), 6 deletions(-) diff --git a/daemon/container_unix.go b/daemon/container_unix.go index 3b22081e50..d5ea7afd97 100644 --- a/daemon/container_unix.go +++ b/daemon/container_unix.go @@ -532,6 +532,9 @@ func (container *Container) buildSandboxOptions(n libnetwork.Network) ([]libnetw } for linkAlias, child := range children { + if !isLinkable(child) { + return nil, fmt.Errorf("Cannot link to %s, as it does not belong to the default network", child.Name) + } _, alias := path.Split(linkAlias) // allow access to the linked container via the alias, real name, and container hostname aliasList := alias + " " + child.Config.Hostname @@ -578,6 +581,16 @@ func (container *Container) buildSandboxOptions(n libnetwork.Network) ([]libnetw return sboxOptions, nil } +func isLinkable(child *Container) bool { + // A container is linkable only if it belongs to the default network + for _, nw := range child.NetworkSettings.Networks { + if nw == "bridge" { + return true + } + } + return false +} + func (container *Container) getEndpointInNetwork(n libnetwork.Network) (libnetwork.Endpoint, error) { endpointName := strings.TrimPrefix(container.Name, "/") return n.EndpointByName(endpointName) @@ -862,7 +875,7 @@ func (container *Container) buildCreateEndpointOptions(n libnetwork.Network) ([] createOptions = append(createOptions, libnetwork.EndpointOptionGeneric(genericOption)) } - if n.Name() == "bridge" { + if n.Name() == "bridge" || container.NetworkSettings.IsAnonymousEndpoint { createOptions = append(createOptions, libnetwork.CreateOptionAnonymous()) } diff --git a/daemon/daemon.go b/daemon/daemon.go index 83b15a302e..f8fc788281 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -477,8 +477,9 @@ func (daemon *Daemon) getEntrypointAndArgs(configEntrypoint *stringutils.StrSlic func (daemon *Daemon) newContainer(name string, config *runconfig.Config, imgID string) (*Container, error) { var ( - id string - err error + id string + err error + noExplicitName = name == "" ) id, name, err = daemon.generateIDAndName(name) if err != nil { @@ -495,7 +496,7 @@ func (daemon *Daemon) newContainer(name string, config *runconfig.Config, imgID base.Config = config base.hostConfig = &runconfig.HostConfig{} base.ImageID = imgID - base.NetworkSettings = &network.Settings{} + base.NetworkSettings = &network.Settings{IsAnonymousEndpoint: noExplicitName} base.Name = name base.Driver = daemon.driver.String() base.ExecDriver = daemon.execDriver.Name() diff --git a/daemon/network/settings.go b/daemon/network/settings.go index d59c369db9..4ade435413 100644 --- a/daemon/network/settings.go +++ b/daemon/network/settings.go @@ -43,4 +43,5 @@ type Settings struct { SandboxKey string SecondaryIPAddresses []Address SecondaryIPv6Addresses []Address + IsAnonymousEndpoint bool } diff --git a/integration-cli/docker_cli_network_unix_test.go b/integration-cli/docker_cli_network_unix_test.go index 8bf5659233..fca94b219e 100644 --- a/integration-cli/docker_cli_network_unix_test.go +++ b/integration-cli/docker_cli_network_unix_test.go @@ -431,11 +431,11 @@ func (s *DockerDaemonSuite) TestDockerNetworkNoDiscoveryDefaultBridgeNetwork(c * hosts, err := s.d.Cmd("exec", cid1, "cat", hostsFile) c.Assert(err, checker.IsNil) - out, err = s.d.Cmd("run", "-d", "busybox", "top") + out, err = s.d.Cmd("run", "-d", "--name", "container2", "busybox", "top") c.Assert(err, check.IsNil) cid2 := strings.TrimSpace(out) - // verify first container's etc/hosts file has not changed after spawning second container + // verify first container's etc/hosts file has not changed after spawning the second named container hostsPost, err := s.d.Cmd("exec", cid1, "cat", hostsFile) c.Assert(err, checker.IsNil) c.Assert(string(hosts), checker.Equals, string(hostsPost), @@ -485,3 +485,84 @@ func (s *DockerDaemonSuite) TestDockerNetworkNoDiscoveryDefaultBridgeNetwork(c * c.Assert(string(hosts), checker.Equals, string(hostsPost), check.Commentf("Unexpected %s content after disconnecting from second network", hostsFile)) } + +func (s *DockerNetworkSuite) TestDockerNetworkAnonymousEndpoint(c *check.C) { + hostsFile := "/etc/hosts" + cstmBridgeNw := "custom-bridge-nw" + + dockerCmd(c, "network", "create", "-d", "bridge", cstmBridgeNw) + assertNwIsAvailable(c, cstmBridgeNw) + + // run two anonymous containers and store their etc/hosts content + out, _ := dockerCmd(c, "run", "-d", "--net", cstmBridgeNw, "busybox", "top") + cid1 := strings.TrimSpace(out) + + hosts1, err := readContainerFileWithExec(cid1, hostsFile) + c.Assert(err, checker.IsNil) + + out, _ = dockerCmd(c, "run", "-d", "--net", cstmBridgeNw, "busybox", "top") + cid2 := strings.TrimSpace(out) + + hosts2, err := readContainerFileWithExec(cid2, hostsFile) + c.Assert(err, checker.IsNil) + + // verify first container etc/hosts file has not changed + hosts1post, err := readContainerFileWithExec(cid1, hostsFile) + c.Assert(err, checker.IsNil) + c.Assert(string(hosts1), checker.Equals, string(hosts1post), + check.Commentf("Unexpected %s change on anonymous container creation", hostsFile)) + + // start a named container + cName := "AnyName" + out, _ = dockerCmd(c, "run", "-d", "--net", cstmBridgeNw, "--name", cName, "busybox", "top") + cid3 := strings.TrimSpace(out) + + // verify etc/hosts file for first two containers contains the named container entry + hosts1post, err = readContainerFileWithExec(cid1, hostsFile) + c.Assert(err, checker.IsNil) + c.Assert(string(hosts1post), checker.Contains, cName, + check.Commentf("Container 1 %s file does not contain entries for named container %q: %s", hostsFile, cName, string(hosts1post))) + + hosts2post, err := readContainerFileWithExec(cid2, hostsFile) + c.Assert(err, checker.IsNil) + c.Assert(string(hosts2post), checker.Contains, cName, + check.Commentf("Container 2 %s file does not contain entries for named container %q: %s", hostsFile, cName, string(hosts2post))) + + // Stop named container and verify first two containers' etc/hosts entries are back to original + dockerCmd(c, "stop", cid3) + hosts1post, err = readContainerFileWithExec(cid1, hostsFile) + c.Assert(err, checker.IsNil) + c.Assert(string(hosts1), checker.Equals, string(hosts1post), + check.Commentf("Unexpected %s change on anonymous container creation", hostsFile)) + + hosts2post, err = readContainerFileWithExec(cid2, hostsFile) + c.Assert(err, checker.IsNil) + c.Assert(string(hosts2), checker.Equals, string(hosts2post), + check.Commentf("Unexpected %s change on anonymous container creation", hostsFile)) +} + +func (s *DockerNetworkSuite) TestDockerNetworkLinkOndefaultNetworkOnly(c *check.C) { + // Link feature must work only on default network, and not across networks + cnt1 := "container1" + cnt2 := "container2" + network := "anotherbridge" + + // Run first container on default network + dockerCmd(c, "run", "-d", "--name", cnt1, "busybox", "top") + + // Create another network and run the second container on it + dockerCmd(c, "network", "create", network) + assertNwIsAvailable(c, network) + dockerCmd(c, "run", "-d", "--net", network, "--name", cnt2, "busybox", "top") + + // Try launching a container on default network, linking to the first container. Must succeed + dockerCmd(c, "run", "-d", "--link", fmt.Sprintf("%s:%s", cnt1, cnt1), "busybox", "top") + + // Try launching a container on default network, linking to the second container. Must fail + _, _, err := dockerCmdWithError("run", "-d", "--link", fmt.Sprintf("%s:%s", cnt2, cnt2), "busybox", "top") + c.Assert(err, checker.NotNil) + + // Connect second container to default network. Now a container on default network can link to it + dockerCmd(c, "network", "connect", "bridge", cnt2) + dockerCmd(c, "run", "-d", "--link", fmt.Sprintf("%s:%s", cnt2, cnt2), "busybox", "top") +}