Explorar o código

Validate hostname starting from 1.24 API.

In order to keep a little bit of "sanity" on the API side, validate
hostname only starting from v1.24 API version.

Signed-off-by: Vincent Demeester <vincent@sbr.pm>
Vincent Demeester %!s(int64=9) %!d(string=hai) anos
pai
achega
6daf3d2a78

+ 3 - 3
api/server/router/container/backend.go

@@ -32,17 +32,17 @@ type copyBackend interface {
 
 // stateBackend includes functions to implement to provide container state lifecycle functionality.
 type stateBackend interface {
-	ContainerCreate(types.ContainerCreateConfig) (types.ContainerCreateResponse, error)
+	ContainerCreate(config types.ContainerCreateConfig, validateHostname bool) (types.ContainerCreateResponse, error)
 	ContainerKill(name string, sig uint64) error
 	ContainerPause(name string) error
 	ContainerRename(oldName, newName string) error
 	ContainerResize(name string, height, width int) error
 	ContainerRestart(name string, seconds int) error
 	ContainerRm(name string, config *types.ContainerRmConfig) error
-	ContainerStart(name string, hostConfig *container.HostConfig) error
+	ContainerStart(name string, hostConfig *container.HostConfig, validateHostname bool) error
 	ContainerStop(name string, seconds int) error
 	ContainerUnpause(name string) error
-	ContainerUpdate(name string, hostConfig *container.HostConfig) ([]string, error)
+	ContainerUpdate(name string, hostConfig *container.HostConfig, validateHostname bool) ([]string, error)
 	ContainerWait(name string, timeout time.Duration) (int, error)
 }
 

+ 8 - 4
api/server/router/container/container_routes.go

@@ -132,10 +132,10 @@ func (s *containerRouter) postContainersStart(ctx context.Context, w http.Respon
 	// including r.TransferEncoding
 	// allow a nil body for backwards compatibility
 
+	version := httputils.VersionFromContext(ctx)
 	var hostConfig *container.HostConfig
 	// A non-nil json object is at least 7 characters.
 	if r.ContentLength > 7 || r.ContentLength == -1 {
-		version := httputils.VersionFromContext(ctx)
 		if versions.GreaterThanOrEqualTo(version, "1.24") {
 			return validationError{fmt.Errorf("starting container with HostConfig was deprecated since v1.10 and removed in v1.12")}
 		}
@@ -151,7 +151,8 @@ func (s *containerRouter) postContainersStart(ctx context.Context, w http.Respon
 		hostConfig = c
 	}
 
-	if err := s.backend.ContainerStart(vars["name"], hostConfig); err != nil {
+	validateHostname := versions.GreaterThanOrEqualTo(version, "1.24")
+	if err := s.backend.ContainerStart(vars["name"], hostConfig, validateHostname); err != nil {
 		return err
 	}
 	w.WriteHeader(http.StatusNoContent)
@@ -311,6 +312,7 @@ func (s *containerRouter) postContainerUpdate(ctx context.Context, w http.Respon
 		return err
 	}
 
+	version := httputils.VersionFromContext(ctx)
 	var updateConfig container.UpdateConfig
 
 	decoder := json.NewDecoder(r.Body)
@@ -324,7 +326,8 @@ func (s *containerRouter) postContainerUpdate(ctx context.Context, w http.Respon
 	}
 
 	name := vars["name"]
-	warnings, err := s.backend.ContainerUpdate(name, hostConfig)
+	validateHostname := versions.GreaterThanOrEqualTo(version, "1.24")
+	warnings, err := s.backend.ContainerUpdate(name, hostConfig, validateHostname)
 	if err != nil {
 		return err
 	}
@@ -351,13 +354,14 @@ func (s *containerRouter) postContainersCreate(ctx context.Context, w http.Respo
 	version := httputils.VersionFromContext(ctx)
 	adjustCPUShares := versions.LessThan(version, "1.19")
 
+	validateHostname := versions.GreaterThanOrEqualTo(version, "1.24")
 	ccr, err := s.backend.ContainerCreate(types.ContainerCreateConfig{
 		Name:             name,
 		Config:           config,
 		HostConfig:       hostConfig,
 		NetworkingConfig: networkingConfig,
 		AdjustCPUShares:  adjustCPUShares,
-	})
+	}, validateHostname)
 	if err != nil {
 		return err
 	}

+ 2 - 2
builder/builder.go

@@ -116,7 +116,7 @@ type Backend interface {
 	// ContainerAttachRaw attaches to container.
 	ContainerAttachRaw(cID string, stdin io.ReadCloser, stdout, stderr io.Writer, stream bool) error
 	// ContainerCreate creates a new Docker container and returns potential warnings
-	ContainerCreate(types.ContainerCreateConfig) (types.ContainerCreateResponse, error)
+	ContainerCreate(config types.ContainerCreateConfig, validateHostname bool) (types.ContainerCreateResponse, error)
 	// ContainerRm removes a container specified by `id`.
 	ContainerRm(name string, config *types.ContainerRmConfig) error
 	// Commit creates a new Docker image from an existing Docker container.
@@ -124,7 +124,7 @@ type Backend interface {
 	// ContainerKill stops the container execution abruptly.
 	ContainerKill(containerID string, sig uint64) error
 	// ContainerStart starts a new container
-	ContainerStart(containerID string, hostConfig *container.HostConfig) error
+	ContainerStart(containerID string, hostConfig *container.HostConfig, validateHostname bool) error
 	// ContainerWait stops processing until the given container is stopped.
 	ContainerWait(containerID string, timeout time.Duration) (int, error)
 	// ContainerUpdateCmdOnBuild updates container.Path and container.Args

+ 3 - 3
builder/dockerfile/internals.go

@@ -181,7 +181,7 @@ func (b *Builder) runContextCommand(args []string, allowRemote bool, allowLocalD
 		return nil
 	}
 
-	container, err := b.docker.ContainerCreate(types.ContainerCreateConfig{Config: b.runConfig})
+	container, err := b.docker.ContainerCreate(types.ContainerCreateConfig{Config: b.runConfig}, true)
 	if err != nil {
 		return err
 	}
@@ -508,7 +508,7 @@ func (b *Builder) create() (string, error) {
 	c, err := b.docker.ContainerCreate(types.ContainerCreateConfig{
 		Config:     b.runConfig,
 		HostConfig: hostConfig,
-	})
+	}, true)
 	if err != nil {
 		return "", err
 	}
@@ -552,7 +552,7 @@ func (b *Builder) run(cID string) (err error) {
 		}
 	}()
 
-	if err := b.docker.ContainerStart(cID, nil); err != nil {
+	if err := b.docker.ContainerStart(cID, nil, true); err != nil {
 		return err
 	}
 

+ 2 - 2
daemon/cluster/executor/backend.go

@@ -18,8 +18,8 @@ type Backend interface {
 	DeleteManagedNetwork(name string) error
 	SetupIngress(req clustertypes.NetworkCreateRequest, nodeIP string) error
 	PullImage(ctx context.Context, image, tag string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error
-	CreateManagedContainer(types.ContainerCreateConfig) (types.ContainerCreateResponse, error)
-	ContainerStart(name string, hostConfig *container.HostConfig) error
+	CreateManagedContainer(config types.ContainerCreateConfig, validateHostname bool) (types.ContainerCreateResponse, error)
+	ContainerStart(name string, hostConfig *container.HostConfig, validateHostname bool) error
 	ContainerStop(name string, seconds int) error
 	ConnectContainerToNetwork(containerName, networkName string, endpointConfig *network.EndpointSettings) error
 	UpdateContainerServiceConfig(containerName string, serviceConfig *clustertypes.ServiceConfig) error

+ 9 - 2
daemon/cluster/executor/container/adapter.go

@@ -9,8 +9,10 @@ import (
 	"syscall"
 
 	"github.com/Sirupsen/logrus"
+	"github.com/docker/docker/api/server/httputils"
 	executorpkg "github.com/docker/docker/daemon/cluster/executor"
 	"github.com/docker/engine-api/types"
+	"github.com/docker/engine-api/types/versions"
 	"github.com/docker/libnetwork"
 	"github.com/docker/swarmkit/api"
 	"github.com/docker/swarmkit/log"
@@ -115,13 +117,16 @@ func (c *containerAdapter) removeNetworks(ctx context.Context) error {
 func (c *containerAdapter) create(ctx context.Context, backend executorpkg.Backend) error {
 	var cr types.ContainerCreateResponse
 	var err error
+	version := httputils.VersionFromContext(ctx)
+	validateHostname := versions.GreaterThanOrEqualTo(version, "1.24")
+
 	if cr, err = backend.CreateManagedContainer(types.ContainerCreateConfig{
 		Name:       c.container.name(),
 		Config:     c.container.config(),
 		HostConfig: c.container.hostConfig(),
 		// Use the first network in container create
 		NetworkingConfig: c.container.createNetworkingConfig(),
-	}); err != nil {
+	}, validateHostname); err != nil {
 		return err
 	}
 
@@ -145,7 +150,9 @@ func (c *containerAdapter) create(ctx context.Context, backend executorpkg.Backe
 }
 
 func (c *containerAdapter) start(ctx context.Context) error {
-	return c.backend.ContainerStart(c.container.name(), nil)
+	version := httputils.VersionFromContext(ctx)
+	validateHostname := versions.GreaterThanOrEqualTo(version, "1.24")
+	return c.backend.ContainerStart(c.container.name(), nil, validateHostname)
 }
 
 func (c *containerAdapter) inspect(ctx context.Context) (types.ContainerJSON, error) {

+ 3 - 3
daemon/container.go

@@ -203,7 +203,7 @@ func (daemon *Daemon) setHostConfig(container *container.Container, hostConfig *
 
 // verifyContainerSettings performs validation of the hostconfig and config
 // structures.
-func (daemon *Daemon) verifyContainerSettings(hostConfig *containertypes.HostConfig, config *containertypes.Config, update bool) ([]string, error) {
+func (daemon *Daemon) verifyContainerSettings(hostConfig *containertypes.HostConfig, config *containertypes.Config, update bool, validateHostname bool) ([]string, error) {
 
 	// First perform verification of settings common across all platforms.
 	if config != nil {
@@ -222,10 +222,10 @@ func (daemon *Daemon) verifyContainerSettings(hostConfig *containertypes.HostCon
 		}
 
 		// Validate if the given hostname is RFC 1123 (https://tools.ietf.org/html/rfc1123) compliant.
-		if len(config.Hostname) > 0 {
+		if validateHostname && len(config.Hostname) > 0 {
 			// RFC1123 specifies that 63 bytes is the maximium length
 			// Windows has the limitation of 63 bytes in length
-			// Linux hostname is limited to HOST_NAME_MAX=64, not not including the terminating null byte.
+			// Linux hostname is limited to HOST_NAME_MAX=64, not including the terminating null byte.
 			// We limit the length to 63 bytes here to match RFC1035 and RFC1123.
 			matched, _ := regexp.MatchString("^(([[:alnum:]]|[[:alnum:]][[:alnum:]\\-]*[[:alnum:]])\\.)*([[:alnum:]]|[[:alnum:]][[:alnum:]\\-]*[[:alnum:]])$", config.Hostname)
 			if len(config.Hostname) > 63 || !matched {

+ 6 - 6
daemon/create.go

@@ -20,21 +20,21 @@ import (
 )
 
 // CreateManagedContainer creates a container that is managed by a Service
-func (daemon *Daemon) CreateManagedContainer(params types.ContainerCreateConfig) (types.ContainerCreateResponse, error) {
-	return daemon.containerCreate(params, true)
+func (daemon *Daemon) CreateManagedContainer(params types.ContainerCreateConfig, validateHostname bool) (types.ContainerCreateResponse, error) {
+	return daemon.containerCreate(params, true, validateHostname)
 }
 
 // ContainerCreate creates a regular container
-func (daemon *Daemon) ContainerCreate(params types.ContainerCreateConfig) (types.ContainerCreateResponse, error) {
-	return daemon.containerCreate(params, false)
+func (daemon *Daemon) ContainerCreate(params types.ContainerCreateConfig, validateHostname bool) (types.ContainerCreateResponse, error) {
+	return daemon.containerCreate(params, false, validateHostname)
 }
 
-func (daemon *Daemon) containerCreate(params types.ContainerCreateConfig, managed bool) (types.ContainerCreateResponse, error) {
+func (daemon *Daemon) containerCreate(params types.ContainerCreateConfig, managed bool, validateHostname bool) (types.ContainerCreateResponse, error) {
 	if params.Config == nil {
 		return types.ContainerCreateResponse{}, fmt.Errorf("Config cannot be empty in order to create a container")
 	}
 
-	warnings, err := daemon.verifyContainerSettings(params.HostConfig, params.Config, false)
+	warnings, err := daemon.verifyContainerSettings(params.HostConfig, params.Config, false, validateHostname)
 	if err != nil {
 		return types.ContainerCreateResponse{Warnings: warnings}, err
 	}

+ 2 - 2
daemon/start.go

@@ -18,7 +18,7 @@ import (
 )
 
 // ContainerStart starts a container.
-func (daemon *Daemon) ContainerStart(name string, hostConfig *containertypes.HostConfig) error {
+func (daemon *Daemon) ContainerStart(name string, hostConfig *containertypes.HostConfig, validateHostname bool) error {
 	container, err := daemon.GetContainer(name)
 	if err != nil {
 		return err
@@ -68,7 +68,7 @@ func (daemon *Daemon) ContainerStart(name string, hostConfig *containertypes.Hos
 
 	// check if hostConfig is in line with the current system settings.
 	// It may happen cgroups are umounted or the like.
-	if _, err = daemon.verifyContainerSettings(container.HostConfig, nil, false); err != nil {
+	if _, err = daemon.verifyContainerSettings(container.HostConfig, nil, false, validateHostname); err != nil {
 		return err
 	}
 	// Adapt for old containers in case we have updates in this function and

+ 2 - 2
daemon/update.go

@@ -7,10 +7,10 @@ import (
 )
 
 // ContainerUpdate updates configuration of the container
-func (daemon *Daemon) ContainerUpdate(name string, hostConfig *container.HostConfig) ([]string, error) {
+func (daemon *Daemon) ContainerUpdate(name string, hostConfig *container.HostConfig, validateHostname bool) ([]string, error) {
 	var warnings []string
 
-	warnings, err := daemon.verifyContainerSettings(hostConfig, nil, true)
+	warnings, err := daemon.verifyContainerSettings(hostConfig, nil, true, validateHostname)
 	if err != nil {
 		return warnings, err
 	}

+ 2 - 1
docs/reference/api/docker_remote_api.md

@@ -133,9 +133,10 @@ This section lists each version from latest to oldest.  Each listing includes a
 * `POST /containers/{name:.*}/copy` is now removed and errors out starting from this API version.
 * API errors are now returned as JSON instead of plain text.
 * `POST /containers/create` and `POST /containers/(id)/start` allow you to configure kernel parameters (sysctls) for use in the container.
-* `POST /v1.23/containers/<container ID>/exec` and `POST /v1.23/exec/<exec ID>/start`
+* `POST /containers/<container ID>/exec` and `POST /exec/<exec ID>/start`
   no longer expects a "Container" field to be present. This property was not used
   and is no longer sent by the docker client.
+* `POST /containers/create/` now validates the hostname (should be a valid RFC 1123 hostname).
 
 ### v1.23 API changes
 

+ 1 - 1
docs/reference/api/docker_remote_api_v1.24.md

@@ -361,7 +361,7 @@ Create a container
 **JSON parameters**:
 
 -   **Hostname** - A string value containing the hostname to use for the
-      container.
+      container. This must be a valid RFC 1123 hostname.
 -   **Domainname** - A string value containing the domain name to use
       for the container.
 -   **User** - A string value specifying the user inside the container.

+ 1 - 1
docs/reference/api/docker_remote_api_v1.25.md

@@ -362,7 +362,7 @@ Create a container
 **JSON parameters**:
 
 -   **Hostname** - A string value containing the hostname to use for the
-      container.
+      container. This must be a valid RFC 1123 hostname.
 -   **Domainname** - A string value containing the domain name to use
       for the container.
 -   **User** - A string value specifying the user inside the container.