| ... | ... |
@@ -22,6 +22,7 @@ Upgrading from Docker 1.13.1 to 17.03.0 is expected to be simple and low-risk. |
| 22 | 22 |
### Contrib |
| 23 | 23 |
|
| 24 | 24 |
* Update various `bash` and `zsh` completion scripts [#30823](https://github.com/docker/docker/pull/30823), [#30945](https://github.com/docker/docker/pull/30945) and more... |
| 25 |
+* Block obsolete socket families in default seccomp profile - mitigates unpatched kernels' CVE-2017-6074 [#29076](https://github.com/docker/docker/pull/29076) |
|
| 25 | 26 |
|
| 26 | 27 |
### Networking |
| 27 | 28 |
|
| ... | ... |
@@ -36,6 +37,14 @@ Upgrading from Docker 1.13.1 to 17.03.0 is expected to be simple and low-risk. |
| 36 | 36 |
|
| 37 | 37 |
* Fix a deadlock in docker logs [#30223](https://github.com/docker/docker/pull/30223) |
| 38 | 38 |
* Fix cpu spin waiting for log write events [#31070](https://github.com/docker/docker/pull/31070) |
| 39 |
+* Fix a possible crash when using journald [#31231](https://github.com/docker/docker/pull/31231) [#31263](https://github.com/docker/docker/pull/31231) |
|
| 40 |
+* Fix a panic on close of nil channel [#31274](https://github.com/docker/docker/pull/31274) |
|
| 41 |
+* Fix duplicate mount point for `--volumes-from` in `docker run` [#29563](https://github.com/docker/docker/pull/29563) |
|
| 42 |
+* Fix `--cache-from` does not cache last step [#31189](https://github.com/docker/docker/pull/31189) |
|
| 43 |
+ |
|
| 44 |
+### Swarm Mode |
|
| 45 |
+ |
|
| 46 |
+* Shutdown leaks an error when the container was never started [#31279](https://github.com/docker/docker/pull/31279) |
|
| 39 | 47 |
|
| 40 | 48 |
### Swarm Mode |
| 41 | 49 |
|
| ... | ... |
@@ -215,7 +215,7 @@ func isValidParent(img, parent *image.Image) bool {
|
| 215 | 215 |
if len(parent.History) >= len(img.History) {
|
| 216 | 216 |
return false |
| 217 | 217 |
} |
| 218 |
- if len(parent.RootFS.DiffIDs) >= len(img.RootFS.DiffIDs) {
|
|
| 218 |
+ if len(parent.RootFS.DiffIDs) > len(img.RootFS.DiffIDs) {
|
|
| 219 | 219 |
return false |
| 220 | 220 |
} |
| 221 | 221 |
|
| ... | ... |
@@ -323,8 +323,10 @@ func (r *controller) Shutdown(ctx context.Context) error {
|
| 323 | 323 |
|
| 324 | 324 |
// remove container from service binding |
| 325 | 325 |
if err := r.adapter.deactivateServiceBinding(); err != nil {
|
| 326 |
- log.G(ctx).WithError(err).Errorf("failed to deactivate service binding for container %s", r.adapter.container.name())
|
|
| 327 |
- return err |
|
| 326 |
+ log.G(ctx).WithError(err).Warningf("failed to deactivate service binding for container %s", r.adapter.container.name())
|
|
| 327 |
+ // Don't return an error here, because failure to deactivate |
|
| 328 |
+ // the service binding is expected if the container was never |
|
| 329 |
+ // started. |
|
| 328 | 330 |
} |
| 329 | 331 |
|
| 330 | 332 |
if err := r.adapter.shutdown(ctx); err != nil {
|
| ... | ... |
@@ -237,7 +237,10 @@ drain: |
| 237 | 237 |
|
| 238 | 238 |
// free(NULL) is safe |
| 239 | 239 |
C.free(unsafe.Pointer(oldCursor)) |
| 240 |
- C.sd_journal_get_cursor(j, &cursor) |
|
| 240 |
+ if C.sd_journal_get_cursor(j, &cursor) != 0 {
|
|
| 241 |
+ // ensure that we won't be freeing an address that's invalid |
|
| 242 |
+ cursor = nil |
|
| 243 |
+ } |
|
| 241 | 244 |
return cursor |
| 242 | 245 |
} |
| 243 | 246 |
|
| ... | ... |
@@ -245,6 +248,9 @@ func (s *journald) followJournal(logWatcher *logger.LogWatcher, config logger.Re |
| 245 | 245 |
s.readers.mu.Lock() |
| 246 | 246 |
s.readers.readers[logWatcher] = logWatcher |
| 247 | 247 |
s.readers.mu.Unlock() |
| 248 |
+ |
|
| 249 |
+ newCursor := make(chan *C.char) |
|
| 250 |
+ |
|
| 248 | 251 |
go func() {
|
| 249 | 252 |
// Keep copying journal data out until we're notified to stop |
| 250 | 253 |
// or we hit an error. |
| ... | ... |
@@ -264,8 +270,8 @@ func (s *journald) followJournal(logWatcher *logger.LogWatcher, config logger.Re |
| 264 | 264 |
s.readers.mu.Lock() |
| 265 | 265 |
delete(s.readers.readers, logWatcher) |
| 266 | 266 |
s.readers.mu.Unlock() |
| 267 |
- C.sd_journal_close(j) |
|
| 268 | 267 |
close(logWatcher.Msg) |
| 268 |
+ newCursor <- cursor |
|
| 269 | 269 |
}() |
| 270 | 270 |
// Wait until we're told to stop. |
| 271 | 271 |
select {
|
| ... | ... |
@@ -274,6 +280,8 @@ func (s *journald) followJournal(logWatcher *logger.LogWatcher, config logger.Re |
| 274 | 274 |
C.close(pfd[1]) |
| 275 | 275 |
} |
| 276 | 276 |
|
| 277 |
+ cursor = <-newCursor |
|
| 278 |
+ |
|
| 277 | 279 |
return cursor |
| 278 | 280 |
} |
| 279 | 281 |
|
| ... | ... |
@@ -298,9 +306,9 @@ func (s *journald) readLogs(logWatcher *logger.LogWatcher, config logger.ReadCon |
| 298 | 298 |
following := false |
| 299 | 299 |
defer func(pfollowing *bool) {
|
| 300 | 300 |
if !*pfollowing {
|
| 301 |
- C.sd_journal_close(j) |
|
| 302 | 301 |
close(logWatcher.Msg) |
| 303 | 302 |
} |
| 303 |
+ C.sd_journal_close(j) |
|
| 304 | 304 |
}(&following) |
| 305 | 305 |
// Remove limits on the size of data items that we'll retrieve. |
| 306 | 306 |
rc = C.sd_journal_set_data_threshold(j, C.size_t(0)) |
| ... | ... |
@@ -85,6 +85,15 @@ func (daemon *Daemon) registerMountPoints(container *container.Container, hostCo |
| 85 | 85 |
} |
| 86 | 86 |
}() |
| 87 | 87 |
|
| 88 |
+ dereferenceIfExists := func(destination string) {
|
|
| 89 |
+ if v, ok := mountPoints[destination]; ok {
|
|
| 90 |
+ logrus.Debugf("Duplicate mount point '%s'", destination)
|
|
| 91 |
+ if v.Volume != nil {
|
|
| 92 |
+ daemon.volumes.Dereference(v.Volume, container.ID) |
|
| 93 |
+ } |
|
| 94 |
+ } |
|
| 95 |
+ } |
|
| 96 |
+ |
|
| 88 | 97 |
// 1. Read already configured mount points. |
| 89 | 98 |
for destination, point := range container.MountPoints {
|
| 90 | 99 |
mountPoints[destination] = point |
| ... | ... |
@@ -121,7 +130,7 @@ func (daemon *Daemon) registerMountPoints(container *container.Container, hostCo |
| 121 | 121 |
} |
| 122 | 122 |
cp.Volume = v |
| 123 | 123 |
} |
| 124 |
- |
|
| 124 |
+ dereferenceIfExists(cp.Destination) |
|
| 125 | 125 |
mountPoints[cp.Destination] = cp |
| 126 | 126 |
} |
| 127 | 127 |
} |
| ... | ... |
@@ -155,6 +164,7 @@ func (daemon *Daemon) registerMountPoints(container *container.Container, hostCo |
| 155 | 155 |
} |
| 156 | 156 |
|
| 157 | 157 |
binds[bind.Destination] = true |
| 158 |
+ dereferenceIfExists(bind.Destination) |
|
| 158 | 159 |
mountPoints[bind.Destination] = bind |
| 159 | 160 |
} |
| 160 | 161 |
|
| ... | ... |
@@ -199,6 +209,7 @@ func (daemon *Daemon) registerMountPoints(container *container.Container, hostCo |
| 199 | 199 |
} |
| 200 | 200 |
|
| 201 | 201 |
binds[mp.Destination] = true |
| 202 |
+ dereferenceIfExists(mp.Destination) |
|
| 202 | 203 |
mountPoints[mp.Destination] = mp |
| 203 | 204 |
} |
| 204 | 205 |
|
| ... | ... |
@@ -7081,6 +7081,27 @@ func (s *DockerSuite) TestBuildWithFailure(c *check.C) {
|
| 7081 | 7081 |
c.Assert(stdout, checker.Not(checker.Contains), "Step 2/2 : RUN nobody") |
| 7082 | 7082 |
} |
| 7083 | 7083 |
|
| 7084 |
+func (s *DockerSuite) TestBuildCacheFromEqualDiffIDsLength(c *check.C) {
|
|
| 7085 |
+ dockerfile := ` |
|
| 7086 |
+ FROM busybox |
|
| 7087 |
+ RUN echo "test" |
|
| 7088 |
+ ENTRYPOINT ["sh"]` |
|
| 7089 |
+ ctx, err := fakeContext(dockerfile, map[string]string{
|
|
| 7090 |
+ "Dockerfile": dockerfile, |
|
| 7091 |
+ }) |
|
| 7092 |
+ c.Assert(err, checker.IsNil) |
|
| 7093 |
+ defer ctx.Close() |
|
| 7094 |
+ |
|
| 7095 |
+ id1, err := buildImageFromContext("build1", ctx, true)
|
|
| 7096 |
+ c.Assert(err, checker.IsNil) |
|
| 7097 |
+ |
|
| 7098 |
+ // rebuild with cache-from |
|
| 7099 |
+ id2, out, err := buildImageFromContextWithOut("build2", ctx, true, "--cache-from=build1")
|
|
| 7100 |
+ c.Assert(err, checker.IsNil) |
|
| 7101 |
+ c.Assert(id1, checker.Equals, id2) |
|
| 7102 |
+ c.Assert(strings.Count(out, "Using cache"), checker.Equals, 2) |
|
| 7103 |
+} |
|
| 7104 |
+ |
|
| 7084 | 7105 |
func (s *DockerSuite) TestBuildCacheFrom(c *check.C) {
|
| 7085 | 7106 |
testRequires(c, DaemonIsLinux) // All tests that do save are skipped in windows |
| 7086 | 7107 |
dockerfile := ` |
| ... | ... |
@@ -3,6 +3,7 @@ package main |
| 3 | 3 |
import ( |
| 4 | 4 |
"fmt" |
| 5 | 5 |
"io/ioutil" |
| 6 |
+ "net/http" |
|
| 6 | 7 |
"os" |
| 7 | 8 |
"os/exec" |
| 8 | 9 |
"path/filepath" |
| ... | ... |
@@ -425,3 +426,162 @@ func (s *DockerSuite) TestVolumeCliInspectWithVolumeOpts(c *check.C) {
|
| 425 | 425 |
c.Assert(strings.TrimSpace(out), checker.Contains, fmt.Sprintf("%s:%s", k2, v2))
|
| 426 | 426 |
c.Assert(strings.TrimSpace(out), checker.Contains, fmt.Sprintf("%s:%s", k3, v3))
|
| 427 | 427 |
} |
| 428 |
+ |
|
| 429 |
+// Test case (1) for 21845: duplicate targets for --volumes-from |
|
| 430 |
+func (s *DockerSuite) TestDuplicateMountpointsForVolumesFrom(c *check.C) {
|
|
| 431 |
+ testRequires(c, DaemonIsLinux) |
|
| 432 |
+ |
|
| 433 |
+ image := "vimage" |
|
| 434 |
+ _, err := buildImage( |
|
| 435 |
+ image, |
|
| 436 |
+ ` |
|
| 437 |
+ FROM busybox |
|
| 438 |
+ VOLUME ["/tmp/data"] |
|
| 439 |
+ `, |
|
| 440 |
+ true) |
|
| 441 |
+ c.Assert(err, check.IsNil) |
|
| 442 |
+ |
|
| 443 |
+ dockerCmd(c, "run", "--name=data1", image, "true") |
|
| 444 |
+ dockerCmd(c, "run", "--name=data2", image, "true") |
|
| 445 |
+ |
|
| 446 |
+ out, _ := dockerCmd(c, "inspect", "--format", "{{(index .Mounts 0).Name}}", "data1")
|
|
| 447 |
+ data1 := strings.TrimSpace(out) |
|
| 448 |
+ c.Assert(data1, checker.Not(checker.Equals), "") |
|
| 449 |
+ |
|
| 450 |
+ out, _ = dockerCmd(c, "inspect", "--format", "{{(index .Mounts 0).Name}}", "data2")
|
|
| 451 |
+ data2 := strings.TrimSpace(out) |
|
| 452 |
+ c.Assert(data2, checker.Not(checker.Equals), "") |
|
| 453 |
+ |
|
| 454 |
+ // Both volume should exist |
|
| 455 |
+ out, _ = dockerCmd(c, "volume", "ls", "-q") |
|
| 456 |
+ c.Assert(strings.TrimSpace(out), checker.Contains, data1) |
|
| 457 |
+ c.Assert(strings.TrimSpace(out), checker.Contains, data2) |
|
| 458 |
+ |
|
| 459 |
+ out, _, err = dockerCmdWithError("run", "--name=app", "--volumes-from=data1", "--volumes-from=data2", "-d", "busybox", "top")
|
|
| 460 |
+ c.Assert(err, checker.IsNil, check.Commentf("Out: %s", out))
|
|
| 461 |
+ |
|
| 462 |
+ // Only the second volume will be referenced, this is backward compatible |
|
| 463 |
+ out, _ = dockerCmd(c, "inspect", "--format", "{{(index .Mounts 0).Name}}", "app")
|
|
| 464 |
+ c.Assert(strings.TrimSpace(out), checker.Equals, data2) |
|
| 465 |
+ |
|
| 466 |
+ dockerCmd(c, "rm", "-f", "-v", "app") |
|
| 467 |
+ dockerCmd(c, "rm", "-f", "-v", "data1") |
|
| 468 |
+ dockerCmd(c, "rm", "-f", "-v", "data2") |
|
| 469 |
+ |
|
| 470 |
+ // Both volume should not exist |
|
| 471 |
+ out, _ = dockerCmd(c, "volume", "ls", "-q") |
|
| 472 |
+ c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data1) |
|
| 473 |
+ c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data2) |
|
| 474 |
+} |
|
| 475 |
+ |
|
| 476 |
+// Test case (2) for 21845: duplicate targets for --volumes-from and -v (bind) |
|
| 477 |
+func (s *DockerSuite) TestDuplicateMountpointsForVolumesFromAndBind(c *check.C) {
|
|
| 478 |
+ testRequires(c, DaemonIsLinux) |
|
| 479 |
+ |
|
| 480 |
+ image := "vimage" |
|
| 481 |
+ _, err := buildImage(image, |
|
| 482 |
+ ` |
|
| 483 |
+ FROM busybox |
|
| 484 |
+ VOLUME ["/tmp/data"] |
|
| 485 |
+ `, |
|
| 486 |
+ true) |
|
| 487 |
+ c.Assert(err, check.IsNil) |
|
| 488 |
+ |
|
| 489 |
+ dockerCmd(c, "run", "--name=data1", image, "true") |
|
| 490 |
+ dockerCmd(c, "run", "--name=data2", image, "true") |
|
| 491 |
+ |
|
| 492 |
+ out, _ := dockerCmd(c, "inspect", "--format", "{{(index .Mounts 0).Name}}", "data1")
|
|
| 493 |
+ data1 := strings.TrimSpace(out) |
|
| 494 |
+ c.Assert(data1, checker.Not(checker.Equals), "") |
|
| 495 |
+ |
|
| 496 |
+ out, _ = dockerCmd(c, "inspect", "--format", "{{(index .Mounts 0).Name}}", "data2")
|
|
| 497 |
+ data2 := strings.TrimSpace(out) |
|
| 498 |
+ c.Assert(data2, checker.Not(checker.Equals), "") |
|
| 499 |
+ |
|
| 500 |
+ // Both volume should exist |
|
| 501 |
+ out, _ = dockerCmd(c, "volume", "ls", "-q") |
|
| 502 |
+ c.Assert(strings.TrimSpace(out), checker.Contains, data1) |
|
| 503 |
+ c.Assert(strings.TrimSpace(out), checker.Contains, data2) |
|
| 504 |
+ |
|
| 505 |
+ out, _, err = dockerCmdWithError("run", "--name=app", "--volumes-from=data1", "--volumes-from=data2", "-v", "/tmp/data:/tmp/data", "-d", "busybox", "top")
|
|
| 506 |
+ c.Assert(err, checker.IsNil, check.Commentf("Out: %s", out))
|
|
| 507 |
+ |
|
| 508 |
+ // No volume will be referenced (mount is /tmp/data), this is backward compatible |
|
| 509 |
+ out, _ = dockerCmd(c, "inspect", "--format", "{{(index .Mounts 0).Name}}", "app")
|
|
| 510 |
+ c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data1) |
|
| 511 |
+ c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data2) |
|
| 512 |
+ |
|
| 513 |
+ dockerCmd(c, "rm", "-f", "-v", "app") |
|
| 514 |
+ dockerCmd(c, "rm", "-f", "-v", "data1") |
|
| 515 |
+ dockerCmd(c, "rm", "-f", "-v", "data2") |
|
| 516 |
+ |
|
| 517 |
+ // Both volume should not exist |
|
| 518 |
+ out, _ = dockerCmd(c, "volume", "ls", "-q") |
|
| 519 |
+ c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data1) |
|
| 520 |
+ c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data2) |
|
| 521 |
+} |
|
| 522 |
+ |
|
| 523 |
+// Test case (3) for 21845: duplicate targets for --volumes-from and `Mounts` (API only) |
|
| 524 |
+func (s *DockerSuite) TestDuplicateMountpointsForVolumesFromAndMounts(c *check.C) {
|
|
| 525 |
+ testRequires(c, DaemonIsLinux) |
|
| 526 |
+ |
|
| 527 |
+ image := "vimage" |
|
| 528 |
+ _, err := buildImage(image, |
|
| 529 |
+ ` |
|
| 530 |
+ FROM busybox |
|
| 531 |
+ VOLUME ["/tmp/data"] |
|
| 532 |
+ `, |
|
| 533 |
+ true) |
|
| 534 |
+ c.Assert(err, check.IsNil) |
|
| 535 |
+ |
|
| 536 |
+ dockerCmd(c, "run", "--name=data1", image, "true") |
|
| 537 |
+ dockerCmd(c, "run", "--name=data2", image, "true") |
|
| 538 |
+ |
|
| 539 |
+ out, _ := dockerCmd(c, "inspect", "--format", "{{(index .Mounts 0).Name}}", "data1")
|
|
| 540 |
+ data1 := strings.TrimSpace(out) |
|
| 541 |
+ c.Assert(data1, checker.Not(checker.Equals), "") |
|
| 542 |
+ |
|
| 543 |
+ out, _ = dockerCmd(c, "inspect", "--format", "{{(index .Mounts 0).Name}}", "data2")
|
|
| 544 |
+ data2 := strings.TrimSpace(out) |
|
| 545 |
+ c.Assert(data2, checker.Not(checker.Equals), "") |
|
| 546 |
+ |
|
| 547 |
+ // Both volume should exist |
|
| 548 |
+ out, _ = dockerCmd(c, "volume", "ls", "-q") |
|
| 549 |
+ c.Assert(strings.TrimSpace(out), checker.Contains, data1) |
|
| 550 |
+ c.Assert(strings.TrimSpace(out), checker.Contains, data2) |
|
| 551 |
+ |
|
| 552 |
+ // Mounts is available in API |
|
| 553 |
+ status, body, err := sockRequest("POST", "/containers/create?name=app", map[string]interface{}{
|
|
| 554 |
+ "Image": "busybox", |
|
| 555 |
+ "Cmd": []string{"top"},
|
|
| 556 |
+ "HostConfig": map[string]interface{}{
|
|
| 557 |
+ "VolumesFrom": []string{
|
|
| 558 |
+ "data1", |
|
| 559 |
+ "data2", |
|
| 560 |
+ }, |
|
| 561 |
+ "Mounts": []map[string]interface{}{
|
|
| 562 |
+ {
|
|
| 563 |
+ "Type": "bind", |
|
| 564 |
+ "Source": "/tmp/data", |
|
| 565 |
+ "Target": "/tmp/data", |
|
| 566 |
+ }, |
|
| 567 |
+ }}, |
|
| 568 |
+ }) |
|
| 569 |
+ |
|
| 570 |
+ c.Assert(err, checker.IsNil, check.Commentf(string(body))) |
|
| 571 |
+ c.Assert(status, checker.Equals, http.StatusCreated, check.Commentf(string(body))) |
|
| 572 |
+ |
|
| 573 |
+ // No volume will be referenced (mount is /tmp/data), this is backward compatible |
|
| 574 |
+ out, _ = dockerCmd(c, "inspect", "--format", "{{(index .Mounts 0).Name}}", "app")
|
|
| 575 |
+ c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data1) |
|
| 576 |
+ c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data2) |
|
| 577 |
+ |
|
| 578 |
+ dockerCmd(c, "rm", "-f", "-v", "app") |
|
| 579 |
+ dockerCmd(c, "rm", "-f", "-v", "data1") |
|
| 580 |
+ dockerCmd(c, "rm", "-f", "-v", "data2") |
|
| 581 |
+ |
|
| 582 |
+ // Both volume should not exist |
|
| 583 |
+ out, _ = dockerCmd(c, "volume", "ls", "-q") |
|
| 584 |
+ c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data1) |
|
| 585 |
+ c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data2) |
|
| 586 |
+} |