Browse code

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>
(cherry picked from commit 6daf3d2a783fd042e870c8af8bbd19fc28989505)
Signed-off-by: Tibor Vass <tibor@docker.com>

Vincent Demeester authored on 2016/07/06 16:13:59
Showing 12 changed files
... ...
@@ -32,17 +32,17 @@ type copyBackend interface {
32 32
 
33 33
 // stateBackend includes functions to implement to provide container state lifecycle functionality.
34 34
 type stateBackend interface {
35
-	ContainerCreate(types.ContainerCreateConfig) (types.ContainerCreateResponse, error)
35
+	ContainerCreate(config types.ContainerCreateConfig, validateHostname bool) (types.ContainerCreateResponse, error)
36 36
 	ContainerKill(name string, sig uint64) error
37 37
 	ContainerPause(name string) error
38 38
 	ContainerRename(oldName, newName string) error
39 39
 	ContainerResize(name string, height, width int) error
40 40
 	ContainerRestart(name string, seconds int) error
41 41
 	ContainerRm(name string, config *types.ContainerRmConfig) error
42
-	ContainerStart(name string, hostConfig *container.HostConfig) error
42
+	ContainerStart(name string, hostConfig *container.HostConfig, validateHostname bool) error
43 43
 	ContainerStop(name string, seconds int) error
44 44
 	ContainerUnpause(name string) error
45
-	ContainerUpdate(name string, hostConfig *container.HostConfig) ([]string, error)
45
+	ContainerUpdate(name string, hostConfig *container.HostConfig, validateHostname bool) ([]string, error)
46 46
 	ContainerWait(name string, timeout time.Duration) (int, error)
47 47
 }
48 48
 
... ...
@@ -132,10 +132,10 @@ func (s *containerRouter) postContainersStart(ctx context.Context, w http.Respon
132 132
 	// including r.TransferEncoding
133 133
 	// allow a nil body for backwards compatibility
134 134
 
135
+	version := httputils.VersionFromContext(ctx)
135 136
 	var hostConfig *container.HostConfig
136 137
 	// A non-nil json object is at least 7 characters.
137 138
 	if r.ContentLength > 7 || r.ContentLength == -1 {
138
-		version := httputils.VersionFromContext(ctx)
139 139
 		if versions.GreaterThanOrEqualTo(version, "1.24") {
140 140
 			return validationError{fmt.Errorf("starting container with HostConfig was deprecated since v1.10 and removed in v1.12")}
141 141
 		}
... ...
@@ -151,7 +151,8 @@ func (s *containerRouter) postContainersStart(ctx context.Context, w http.Respon
151 151
 		hostConfig = c
152 152
 	}
153 153
 
154
-	if err := s.backend.ContainerStart(vars["name"], hostConfig); err != nil {
154
+	validateHostname := versions.GreaterThanOrEqualTo(version, "1.24")
155
+	if err := s.backend.ContainerStart(vars["name"], hostConfig, validateHostname); err != nil {
155 156
 		return err
156 157
 	}
157 158
 	w.WriteHeader(http.StatusNoContent)
... ...
@@ -311,6 +312,7 @@ func (s *containerRouter) postContainerUpdate(ctx context.Context, w http.Respon
311 311
 		return err
312 312
 	}
313 313
 
314
+	version := httputils.VersionFromContext(ctx)
314 315
 	var updateConfig container.UpdateConfig
315 316
 
316 317
 	decoder := json.NewDecoder(r.Body)
... ...
@@ -324,7 +326,8 @@ func (s *containerRouter) postContainerUpdate(ctx context.Context, w http.Respon
324 324
 	}
325 325
 
326 326
 	name := vars["name"]
327
-	warnings, err := s.backend.ContainerUpdate(name, hostConfig)
327
+	validateHostname := versions.GreaterThanOrEqualTo(version, "1.24")
328
+	warnings, err := s.backend.ContainerUpdate(name, hostConfig, validateHostname)
328 329
 	if err != nil {
329 330
 		return err
330 331
 	}
... ...
@@ -351,13 +354,14 @@ func (s *containerRouter) postContainersCreate(ctx context.Context, w http.Respo
351 351
 	version := httputils.VersionFromContext(ctx)
352 352
 	adjustCPUShares := versions.LessThan(version, "1.19")
353 353
 
354
+	validateHostname := versions.GreaterThanOrEqualTo(version, "1.24")
354 355
 	ccr, err := s.backend.ContainerCreate(types.ContainerCreateConfig{
355 356
 		Name:             name,
356 357
 		Config:           config,
357 358
 		HostConfig:       hostConfig,
358 359
 		NetworkingConfig: networkingConfig,
359 360
 		AdjustCPUShares:  adjustCPUShares,
360
-	})
361
+	}, validateHostname)
361 362
 	if err != nil {
362 363
 		return err
363 364
 	}
... ...
@@ -116,7 +116,7 @@ type Backend interface {
116 116
 	// ContainerAttachRaw attaches to container.
117 117
 	ContainerAttachRaw(cID string, stdin io.ReadCloser, stdout, stderr io.Writer, stream bool) error
118 118
 	// ContainerCreate creates a new Docker container and returns potential warnings
119
-	ContainerCreate(types.ContainerCreateConfig) (types.ContainerCreateResponse, error)
119
+	ContainerCreate(config types.ContainerCreateConfig, validateHostname bool) (types.ContainerCreateResponse, error)
120 120
 	// ContainerRm removes a container specified by `id`.
121 121
 	ContainerRm(name string, config *types.ContainerRmConfig) error
122 122
 	// Commit creates a new Docker image from an existing Docker container.
... ...
@@ -124,7 +124,7 @@ type Backend interface {
124 124
 	// ContainerKill stops the container execution abruptly.
125 125
 	ContainerKill(containerID string, sig uint64) error
126 126
 	// ContainerStart starts a new container
127
-	ContainerStart(containerID string, hostConfig *container.HostConfig) error
127
+	ContainerStart(containerID string, hostConfig *container.HostConfig, validateHostname bool) error
128 128
 	// ContainerWait stops processing until the given container is stopped.
129 129
 	ContainerWait(containerID string, timeout time.Duration) (int, error)
130 130
 	// ContainerUpdateCmdOnBuild updates container.Path and container.Args
... ...
@@ -181,7 +181,7 @@ func (b *Builder) runContextCommand(args []string, allowRemote bool, allowLocalD
181 181
 		return nil
182 182
 	}
183 183
 
184
-	container, err := b.docker.ContainerCreate(types.ContainerCreateConfig{Config: b.runConfig})
184
+	container, err := b.docker.ContainerCreate(types.ContainerCreateConfig{Config: b.runConfig}, true)
185 185
 	if err != nil {
186 186
 		return err
187 187
 	}
... ...
@@ -508,7 +508,7 @@ func (b *Builder) create() (string, error) {
508 508
 	c, err := b.docker.ContainerCreate(types.ContainerCreateConfig{
509 509
 		Config:     b.runConfig,
510 510
 		HostConfig: hostConfig,
511
-	})
511
+	}, true)
512 512
 	if err != nil {
513 513
 		return "", err
514 514
 	}
... ...
@@ -552,7 +552,7 @@ func (b *Builder) run(cID string) (err error) {
552 552
 		}
553 553
 	}()
554 554
 
555
-	if err := b.docker.ContainerStart(cID, nil); err != nil {
555
+	if err := b.docker.ContainerStart(cID, nil, true); err != nil {
556 556
 		return err
557 557
 	}
558 558
 
... ...
@@ -18,8 +18,8 @@ type Backend interface {
18 18
 	DeleteManagedNetwork(name string) error
19 19
 	SetupIngress(req clustertypes.NetworkCreateRequest, nodeIP string) error
20 20
 	PullImage(ctx context.Context, image, tag string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error
21
-	CreateManagedContainer(types.ContainerCreateConfig) (types.ContainerCreateResponse, error)
22
-	ContainerStart(name string, hostConfig *container.HostConfig) error
21
+	CreateManagedContainer(config types.ContainerCreateConfig, validateHostname bool) (types.ContainerCreateResponse, error)
22
+	ContainerStart(name string, hostConfig *container.HostConfig, validateHostname bool) error
23 23
 	ContainerStop(name string, seconds int) error
24 24
 	ConnectContainerToNetwork(containerName, networkName string, endpointConfig *network.EndpointSettings) error
25 25
 	UpdateContainerServiceConfig(containerName string, serviceConfig *clustertypes.ServiceConfig) error
... ...
@@ -9,8 +9,10 @@ import (
9 9
 	"syscall"
10 10
 
11 11
 	"github.com/Sirupsen/logrus"
12
+	"github.com/docker/docker/api/server/httputils"
12 13
 	executorpkg "github.com/docker/docker/daemon/cluster/executor"
13 14
 	"github.com/docker/engine-api/types"
15
+	"github.com/docker/engine-api/types/versions"
14 16
 	"github.com/docker/libnetwork"
15 17
 	"github.com/docker/swarmkit/api"
16 18
 	"github.com/docker/swarmkit/log"
... ...
@@ -115,13 +117,16 @@ func (c *containerAdapter) removeNetworks(ctx context.Context) error {
115 115
 func (c *containerAdapter) create(ctx context.Context, backend executorpkg.Backend) error {
116 116
 	var cr types.ContainerCreateResponse
117 117
 	var err error
118
+	version := httputils.VersionFromContext(ctx)
119
+	validateHostname := versions.GreaterThanOrEqualTo(version, "1.24")
120
+
118 121
 	if cr, err = backend.CreateManagedContainer(types.ContainerCreateConfig{
119 122
 		Name:       c.container.name(),
120 123
 		Config:     c.container.config(),
121 124
 		HostConfig: c.container.hostConfig(),
122 125
 		// Use the first network in container create
123 126
 		NetworkingConfig: c.container.createNetworkingConfig(),
124
-	}); err != nil {
127
+	}, validateHostname); err != nil {
125 128
 		return err
126 129
 	}
127 130
 
... ...
@@ -145,7 +150,9 @@ func (c *containerAdapter) create(ctx context.Context, backend executorpkg.Backe
145 145
 }
146 146
 
147 147
 func (c *containerAdapter) start(ctx context.Context) error {
148
-	return c.backend.ContainerStart(c.container.name(), nil)
148
+	version := httputils.VersionFromContext(ctx)
149
+	validateHostname := versions.GreaterThanOrEqualTo(version, "1.24")
150
+	return c.backend.ContainerStart(c.container.name(), nil, validateHostname)
149 151
 }
150 152
 
151 153
 func (c *containerAdapter) inspect(ctx context.Context) (types.ContainerJSON, error) {
... ...
@@ -203,7 +203,7 @@ func (daemon *Daemon) setHostConfig(container *container.Container, hostConfig *
203 203
 
204 204
 // verifyContainerSettings performs validation of the hostconfig and config
205 205
 // structures.
206
-func (daemon *Daemon) verifyContainerSettings(hostConfig *containertypes.HostConfig, config *containertypes.Config, update bool) ([]string, error) {
206
+func (daemon *Daemon) verifyContainerSettings(hostConfig *containertypes.HostConfig, config *containertypes.Config, update bool, validateHostname bool) ([]string, error) {
207 207
 
208 208
 	// First perform verification of settings common across all platforms.
209 209
 	if config != nil {
... ...
@@ -222,10 +222,10 @@ func (daemon *Daemon) verifyContainerSettings(hostConfig *containertypes.HostCon
222 222
 		}
223 223
 
224 224
 		// Validate if the given hostname is RFC 1123 (https://tools.ietf.org/html/rfc1123) compliant.
225
-		if len(config.Hostname) > 0 {
225
+		if validateHostname && len(config.Hostname) > 0 {
226 226
 			// RFC1123 specifies that 63 bytes is the maximium length
227 227
 			// Windows has the limitation of 63 bytes in length
228
-			// Linux hostname is limited to HOST_NAME_MAX=64, not not including the terminating null byte.
228
+			// Linux hostname is limited to HOST_NAME_MAX=64, not including the terminating null byte.
229 229
 			// We limit the length to 63 bytes here to match RFC1035 and RFC1123.
230 230
 			matched, _ := regexp.MatchString("^(([[:alnum:]]|[[:alnum:]][[:alnum:]\\-]*[[:alnum:]])\\.)*([[:alnum:]]|[[:alnum:]][[:alnum:]\\-]*[[:alnum:]])$", config.Hostname)
231 231
 			if len(config.Hostname) > 63 || !matched {
... ...
@@ -20,21 +20,21 @@ import (
20 20
 )
21 21
 
22 22
 // CreateManagedContainer creates a container that is managed by a Service
23
-func (daemon *Daemon) CreateManagedContainer(params types.ContainerCreateConfig) (types.ContainerCreateResponse, error) {
24
-	return daemon.containerCreate(params, true)
23
+func (daemon *Daemon) CreateManagedContainer(params types.ContainerCreateConfig, validateHostname bool) (types.ContainerCreateResponse, error) {
24
+	return daemon.containerCreate(params, true, validateHostname)
25 25
 }
26 26
 
27 27
 // ContainerCreate creates a regular container
28
-func (daemon *Daemon) ContainerCreate(params types.ContainerCreateConfig) (types.ContainerCreateResponse, error) {
29
-	return daemon.containerCreate(params, false)
28
+func (daemon *Daemon) ContainerCreate(params types.ContainerCreateConfig, validateHostname bool) (types.ContainerCreateResponse, error) {
29
+	return daemon.containerCreate(params, false, validateHostname)
30 30
 }
31 31
 
32
-func (daemon *Daemon) containerCreate(params types.ContainerCreateConfig, managed bool) (types.ContainerCreateResponse, error) {
32
+func (daemon *Daemon) containerCreate(params types.ContainerCreateConfig, managed bool, validateHostname bool) (types.ContainerCreateResponse, error) {
33 33
 	if params.Config == nil {
34 34
 		return types.ContainerCreateResponse{}, fmt.Errorf("Config cannot be empty in order to create a container")
35 35
 	}
36 36
 
37
-	warnings, err := daemon.verifyContainerSettings(params.HostConfig, params.Config, false)
37
+	warnings, err := daemon.verifyContainerSettings(params.HostConfig, params.Config, false, validateHostname)
38 38
 	if err != nil {
39 39
 		return types.ContainerCreateResponse{Warnings: warnings}, err
40 40
 	}
... ...
@@ -18,7 +18,7 @@ import (
18 18
 )
19 19
 
20 20
 // ContainerStart starts a container.
21
-func (daemon *Daemon) ContainerStart(name string, hostConfig *containertypes.HostConfig) error {
21
+func (daemon *Daemon) ContainerStart(name string, hostConfig *containertypes.HostConfig, validateHostname bool) error {
22 22
 	container, err := daemon.GetContainer(name)
23 23
 	if err != nil {
24 24
 		return err
... ...
@@ -68,7 +68,7 @@ func (daemon *Daemon) ContainerStart(name string, hostConfig *containertypes.Hos
68 68
 
69 69
 	// check if hostConfig is in line with the current system settings.
70 70
 	// It may happen cgroups are umounted or the like.
71
-	if _, err = daemon.verifyContainerSettings(container.HostConfig, nil, false); err != nil {
71
+	if _, err = daemon.verifyContainerSettings(container.HostConfig, nil, false, validateHostname); err != nil {
72 72
 		return err
73 73
 	}
74 74
 	// Adapt for old containers in case we have updates in this function and
... ...
@@ -7,10 +7,10 @@ import (
7 7
 )
8 8
 
9 9
 // ContainerUpdate updates configuration of the container
10
-func (daemon *Daemon) ContainerUpdate(name string, hostConfig *container.HostConfig) ([]string, error) {
10
+func (daemon *Daemon) ContainerUpdate(name string, hostConfig *container.HostConfig, validateHostname bool) ([]string, error) {
11 11
 	var warnings []string
12 12
 
13
-	warnings, err := daemon.verifyContainerSettings(hostConfig, nil, true)
13
+	warnings, err := daemon.verifyContainerSettings(hostConfig, nil, true, validateHostname)
14 14
 	if err != nil {
15 15
 		return warnings, err
16 16
 	}
... ...
@@ -133,9 +133,10 @@ This section lists each version from latest to oldest.  Each listing includes a
133 133
 * `POST /containers/{name:.*}/copy` is now removed and errors out starting from this API version.
134 134
 * API errors are now returned as JSON instead of plain text.
135 135
 * `POST /containers/create` and `POST /containers/(id)/start` allow you to configure kernel parameters (sysctls) for use in the container.
136
-* `POST /v1.23/containers/<container ID>/exec` and `POST /v1.23/exec/<exec ID>/start`
136
+* `POST /containers/<container ID>/exec` and `POST /exec/<exec ID>/start`
137 137
   no longer expects a "Container" field to be present. This property was not used
138 138
   and is no longer sent by the docker client.
139
+* `POST /containers/create/` now validates the hostname (should be a valid RFC 1123 hostname).
139 140
 
140 141
 ### v1.23 API changes
141 142
 
... ...
@@ -361,7 +361,7 @@ Create a container
361 361
 **JSON parameters**:
362 362
 
363 363
 -   **Hostname** - A string value containing the hostname to use for the
364
-      container.
364
+      container. This must be a valid RFC 1123 hostname.
365 365
 -   **Domainname** - A string value containing the domain name to use
366 366
       for the container.
367 367
 -   **User** - A string value specifying the user inside the container.