From 6daf3d2a783fd042e870c8af8bbd19fc28989505 Mon Sep 17 00:00:00 2001 From: Vincent Demeester Date: Wed, 6 Jul 2016 09:13:59 +0200 Subject: [PATCH] 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 --- api/server/router/container/backend.go | 6 +++--- api/server/router/container/container_routes.go | 12 ++++++++---- builder/builder.go | 4 ++-- builder/dockerfile/internals.go | 6 +++--- daemon/cluster/executor/backend.go | 4 ++-- daemon/cluster/executor/container/adapter.go | 11 +++++++++-- daemon/container.go | 6 +++--- daemon/create.go | 12 ++++++------ daemon/start.go | 4 ++-- daemon/update.go | 4 ++-- docs/reference/api/docker_remote_api.md | 3 ++- docs/reference/api/docker_remote_api_v1.24.md | 2 +- docs/reference/api/docker_remote_api_v1.25.md | 2 +- 13 files changed, 44 insertions(+), 32 deletions(-) diff --git a/api/server/router/container/backend.go b/api/server/router/container/backend.go index b3cc625ff2..444260af9f 100644 --- a/api/server/router/container/backend.go +++ b/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) } diff --git a/api/server/router/container/container_routes.go b/api/server/router/container/container_routes.go index cff1046baf..977ce2522d 100644 --- a/api/server/router/container/container_routes.go +++ b/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 } diff --git a/builder/builder.go b/builder/builder.go index 65e7f02346..125e56ab22 100644 --- a/builder/builder.go +++ b/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 diff --git a/builder/dockerfile/internals.go b/builder/dockerfile/internals.go index 0bf9a15fdd..50755f2642 100644 --- a/builder/dockerfile/internals.go +++ b/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 } diff --git a/daemon/cluster/executor/backend.go b/daemon/cluster/executor/backend.go index e23ebee343..7a168850d3 100644 --- a/daemon/cluster/executor/backend.go +++ b/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 diff --git a/daemon/cluster/executor/container/adapter.go b/daemon/cluster/executor/container/adapter.go index 9a959ae4cc..6261079a71 100644 --- a/daemon/cluster/executor/container/adapter.go +++ b/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) { diff --git a/daemon/container.go b/daemon/container.go index a2d1f47cda..b9f63dedde 100644 --- a/daemon/container.go +++ b/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 { diff --git a/daemon/create.go b/daemon/create.go index 48e7245916..13424f4755 100644 --- a/daemon/create.go +++ b/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 } diff --git a/daemon/start.go b/daemon/start.go index 8def3ccd1b..7a0bc2121c 100644 --- a/daemon/start.go +++ b/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 diff --git a/daemon/update.go b/daemon/update.go index 05a41b2e95..0a5e76d1cd 100644 --- a/daemon/update.go +++ b/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 } diff --git a/docs/reference/api/docker_remote_api.md b/docs/reference/api/docker_remote_api.md index 109568c723..180ea94fd5 100644 --- a/docs/reference/api/docker_remote_api.md +++ b/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//exec` and `POST /v1.23/exec//start` +* `POST /containers//exec` and `POST /exec//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 diff --git a/docs/reference/api/docker_remote_api_v1.24.md b/docs/reference/api/docker_remote_api_v1.24.md index 313b868879..a9861e7107 100644 --- a/docs/reference/api/docker_remote_api_v1.24.md +++ b/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. diff --git a/docs/reference/api/docker_remote_api_v1.25.md b/docs/reference/api/docker_remote_api_v1.25.md index 0921ca3aaf..a1ae73b19f 100644 --- a/docs/reference/api/docker_remote_api_v1.25.md +++ b/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.