diff --git a/container.go b/container.go index f4a3c762ab..0042919e4d 100644 --- a/container.go +++ b/container.go @@ -58,25 +58,26 @@ type Container struct { } type Config struct { - Hostname string - User string - Memory int64 // Memory limit (in bytes) - MemorySwap int64 // Total memory usage (memory + swap); set `-1' to disable swap - CpuShares int64 // CPU shares (relative weight vs. other containers) - AttachStdin bool - AttachStdout bool - AttachStderr bool - PortSpecs []string - Tty bool // Attach standard streams to a tty, including stdin if it is not closed. - OpenStdin bool // Open stdin - StdinOnce bool // If true, close stdin after the 1 attached client disconnects. - Env []string - Cmd []string - Dns []string - Image string // Name of the image as it was passed by the operator (eg. could be symbolic) - Volumes map[string]struct{} - VolumesFrom string - Entrypoint []string + Hostname string + User string + Memory int64 // Memory limit (in bytes) + MemorySwap int64 // Total memory usage (memory + swap); set `-1' to disable swap + CpuShares int64 // CPU shares (relative weight vs. other containers) + AttachStdin bool + AttachStdout bool + AttachStderr bool + PortSpecs []string + Tty bool // Attach standard streams to a tty, including stdin if it is not closed. + OpenStdin bool // Open stdin + StdinOnce bool // If true, close stdin after the 1 attached client disconnects. + Env []string + Cmd []string + Dns []string + Image string // Name of the image as it was passed by the operator (eg. could be symbolic) + Volumes map[string]struct{} + VolumesFrom string + Entrypoint []string + NetworkDisabled bool } type HostConfig struct { @@ -106,6 +107,7 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, flTty := cmd.Bool("t", false, "Allocate a pseudo-tty") flMemory := cmd.Int64("m", 0, "Memory limit (in bytes)") flContainerIDFile := cmd.String("cidfile", "", "Write the container ID to the file") + flNetwork := cmd.Bool("n", true, "Enable networking for this container") if capabilities != nil && *flMemory > 0 && !capabilities.MemoryLimit { //fmt.Fprintf(stdout, "WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.\n") @@ -174,23 +176,24 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, } config := &Config{ - Hostname: *flHostname, - PortSpecs: flPorts, - User: *flUser, - Tty: *flTty, - OpenStdin: *flStdin, - Memory: *flMemory, - CpuShares: *flCpuShares, - AttachStdin: flAttach.Get("stdin"), - AttachStdout: flAttach.Get("stdout"), - AttachStderr: flAttach.Get("stderr"), - Env: flEnv, - Cmd: runCmd, - Dns: flDns, - Image: image, - Volumes: flVolumes, - VolumesFrom: *flVolumesFrom, - Entrypoint: entrypoint, + Hostname: *flHostname, + PortSpecs: flPorts, + User: *flUser, + Tty: *flTty, + NetworkDisabled: !*flNetwork, + OpenStdin: *flStdin, + Memory: *flMemory, + CpuShares: *flCpuShares, + AttachStdin: flAttach.Get("stdin"), + AttachStdout: flAttach.Get("stdout"), + AttachStderr: flAttach.Get("stderr"), + Env: flEnv, + Cmd: runCmd, + Dns: flDns, + Image: image, + Volumes: flVolumes, + VolumesFrom: *flVolumesFrom, + Entrypoint: entrypoint, } hostConfig := &HostConfig{ Binds: binds, @@ -511,8 +514,12 @@ func (container *Container) Start(hostConfig *HostConfig) error { if err := container.EnsureMounted(); err != nil { return err } - if err := container.allocateNetwork(); err != nil { - return err + if container.runtime.networkManager.disabled { + container.Config.NetworkDisabled = true + } else { + if err := container.allocateNetwork(); err != nil { + return err + } } // Make sure the config is compatible with the current kernel @@ -626,7 +633,9 @@ func (container *Container) Start(hostConfig *HostConfig) error { } // Networking - params = append(params, "-g", container.network.Gateway.String()) + if !container.Config.NetworkDisabled { + params = append(params, "-g", container.network.Gateway.String()) + } // User if container.Config.User != "" { @@ -728,6 +737,10 @@ func (container *Container) StderrPipe() (io.ReadCloser, error) { } func (container *Container) allocateNetwork() error { + if container.Config.NetworkDisabled { + return nil + } + iface, err := container.runtime.networkManager.Allocate() if err != nil { return err @@ -754,6 +767,9 @@ func (container *Container) allocateNetwork() error { } func (container *Container) releaseNetwork() { + if container.Config.NetworkDisabled { + return + } container.network.Release() container.network = nil container.NetworkSettings = &NetworkSettings{} diff --git a/container_test.go b/container_test.go index a90018decc..a1ac0bd33a 100644 --- a/container_test.go +++ b/container_test.go @@ -1252,3 +1252,41 @@ func TestRestartWithVolumes(t *testing.T) { t.Fatalf("Expected volume path: %s Actual path: %s", expected, actual) } } + +func TestOnlyLoopbackExistsWhenUsingDisableNetworkOption(t *testing.T) { + runtime := mkRuntime(t) + defer nuke(runtime) + + config, hc, _, err := ParseRun([]string{"-n=false", GetTestImage(runtime).ID, "ip", "addr", "show"}, nil) + if err != nil { + t.Fatal(err) + } + c, err := NewBuilder(runtime).Create(config) + if err != nil { + t.Fatal(err) + } + stdout, err := c.StdoutPipe() + if err != nil { + t.Fatal(err) + } + + defer runtime.Destroy(c) + if err := c.Start(hc); err != nil { + t.Fatal(err) + } + c.WaitTimeout(500 * time.Millisecond) + c.Wait() + output, err := ioutil.ReadAll(stdout) + if err != nil { + t.Fatal(err) + } + + interfaces := regexp.MustCompile(`(?m)^[0-9]+: [a-zA-Z0-9]+`).FindAllString(string(output), -1) + if len(interfaces) != 1 { + t.Fatalf("Wrong interface count in test container: expected [1: lo], got [%s]", interfaces) + } + if interfaces[0] != "1: lo" { + t.Fatalf("Wrong interface in test container: expected [1: lo], got [%s]", interfaces) + } + +} diff --git a/docker/docker.go b/docker/docker.go index fb7c465369..2db50bf328 100644 --- a/docker/docker.go +++ b/docker/docker.go @@ -28,7 +28,7 @@ func main() { flDaemon := flag.Bool("d", false, "Daemon mode") flDebug := flag.Bool("D", false, "Debug mode") flAutoRestart := flag.Bool("r", false, "Restart previously running containers") - bridgeName := flag.String("b", "", "Attach containers to a pre-existing network bridge") + bridgeName := flag.String("b", "", "Attach containers to a pre-existing network bridge. Use 'none' to disable container networking") pidfile := flag.String("p", "/var/run/docker.pid", "File containing process PID") flGraphPath := flag.String("g", "/var/lib/docker", "Path to graph storage base dir.") flEnableCors := flag.Bool("api-enable-cors", false, "Enable CORS requests in the remote api.") diff --git a/docs/sources/commandline/command/run.rst b/docs/sources/commandline/command/run.rst index 19efd85821..db67ef0705 100644 --- a/docs/sources/commandline/command/run.rst +++ b/docs/sources/commandline/command/run.rst @@ -20,6 +20,7 @@ -h="": Container host name -i=false: Keep stdin open even if not attached -m=0: Memory limit (in bytes) + -n=true: Enable networking for this container -p=[]: Map a network port to the container -t=false: Allocate a pseudo-tty -u="": Username or UID diff --git a/lxc_template.go b/lxc_template.go index 93b795e901..d1f67e3376 100644 --- a/lxc_template.go +++ b/lxc_template.go @@ -13,6 +13,10 @@ lxc.utsname = {{.Id}} {{end}} #lxc.aa_profile = unconfined +{{if .Config.NetworkDisabled}} +# network is disabled (-n=false) +lxc.network.type = empty +{{else}} # network configuration lxc.network.type = veth lxc.network.flags = up @@ -20,6 +24,7 @@ lxc.network.link = {{.NetworkSettings.Bridge}} lxc.network.name = eth0 lxc.network.mtu = 1500 lxc.network.ipv4 = {{.NetworkSettings.IPAddress}}/{{.NetworkSettings.IPPrefixLen}} +{{end}} # root filesystem {{$ROOTFS := .RootfsPath}} diff --git a/network.go b/network.go index d2d6668b26..2e2dc7785c 100644 --- a/network.go +++ b/network.go @@ -17,6 +17,7 @@ var NetworkBridgeIface string const ( DefaultNetworkBridge = "docker0" + DisableNetworkBridge = "none" portRangeStart = 49153 portRangeEnd = 65535 ) @@ -472,10 +473,16 @@ type NetworkInterface struct { manager *NetworkManager extPorts []*Nat + disabled bool } // Allocate an external TCP port and map it to the interface func (iface *NetworkInterface) AllocatePort(spec string) (*Nat, error) { + + if iface.disabled { + return nil, fmt.Errorf("Trying to allocate port for interface %v, which is disabled", iface) // FIXME + } + nat, err := parseNat(spec) if err != nil { return nil, err @@ -571,6 +578,11 @@ func parseNat(spec string) (*Nat, error) { // Release: Network cleanup - release all resources func (iface *NetworkInterface) Release() { + + if iface.disabled { + return + } + for _, nat := range iface.extPorts { utils.Debugf("Unmaping %v/%v", nat.Proto, nat.Frontend) if err := iface.manager.portMapper.Unmap(nat.Frontend, nat.Proto); err != nil { @@ -598,10 +610,17 @@ type NetworkManager struct { tcpPortAllocator *PortAllocator udpPortAllocator *PortAllocator portMapper *PortMapper + + disabled bool } // Allocate a network interface func (manager *NetworkManager) Allocate() (*NetworkInterface, error) { + + if manager.disabled { + return &NetworkInterface{disabled: true}, nil + } + ip, err := manager.ipAllocator.Acquire() if err != nil { return nil, err @@ -615,6 +634,14 @@ func (manager *NetworkManager) Allocate() (*NetworkInterface, error) { } func newNetworkManager(bridgeIface string) (*NetworkManager, error) { + + if bridgeIface == DisableNetworkBridge { + manager := &NetworkManager{ + disabled: true, + } + return manager, nil + } + addr, err := getIfaceAddr(bridgeIface) if err != nil { // If the iface is not found, try to create it