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>
| ... | ... |
@@ -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. |
| ... | ... |
@@ -362,7 +362,7 @@ Create a container |
| 362 | 362 |
**JSON parameters**: |
| 363 | 363 |
|
| 364 | 364 |
- **Hostname** - A string value containing the hostname to use for the |
| 365 |
- container. |
|
| 365 |
+ container. This must be a valid RFC 1123 hostname. |
|
| 366 | 366 |
- **Domainname** - A string value containing the domain name to use |
| 367 | 367 |
for the container. |
| 368 | 368 |
- **User** - A string value specifying the user inside the container. |