Package: release.debian.org Severity: normal User: release.debian....@packages.debian.org Usertags: unblock
Please unblock package docker.io. This is a new upstream backport release with security (including CVE-2014-5277[1]) and minor functionality fixes on top of 1.3.0, especially in the Dockerfile parser relating to environment variable substitution[2]. Attached is the output of: | debdiff docker.io_1.3.0~dfsg1-1.dsc docker.io_1.3.1~dfsg1-1.dsc > docker.io_1.3.1.debdiff (from the version in Jessie to the version now in Sid) unblock docker.io/1.3.1~dfsg1-1 ♥, - Tianon 4096R / B42F 6819 007F 00F8 8E36 4FD4 036A 9C25 BF35 7DD4 [1]: https://groups.google.com/d/topic/docker-user/oYm0i3xShJU/discussion [2]: https://github.com/docker/docker/blob/v1.3.1/CHANGELOG.md#131-2014-10-28
diff -Nru docker.io-1.3.0~dfsg1/CHANGELOG.md docker.io-1.3.1~dfsg1/CHANGELOG.md --- docker.io-1.3.0~dfsg1/CHANGELOG.md 2014-10-15 19:15:24.000000000 +0000 +++ docker.io-1.3.1~dfsg1/CHANGELOG.md 2014-10-30 13:44:46.000000000 +0000 @@ -1,5 +1,23 @@ # Changelog +## 1.3.1 (2014-10-28) + +#### Security +* Prevent fallback to SSL protocols < TLS 1.0 for client, daemon and registry ++ Secure HTTPS connection to registries with certificate verification and without HTTP fallback unless `--insecure-registry` is specified + +#### Runtime +- Fix issue where volumes would not be shared + +#### Client +- Fix issue with `--iptables=false` not automatically setting `--ip-masq=false` +- Fix docker run output to non-TTY stdout + +#### Builder +- Fix escaping `$` for environment variables +- Fix issue with lowercase `onbuild` Dockerfile instruction +- Restrict envrionment variable expansion to `ENV`, `ADD`, `COPY`, `WORKDIR`, `EXPOSE`, `VOLUME` and `USER` + ## 1.3.0 (2014-10-14) #### Notable features since 1.2.0 diff -Nru docker.io-1.3.0~dfsg1/VERSION docker.io-1.3.1~dfsg1/VERSION --- docker.io-1.3.0~dfsg1/VERSION 2014-10-15 19:15:24.000000000 +0000 +++ docker.io-1.3.1~dfsg1/VERSION 2014-10-30 13:44:46.000000000 +0000 @@ -1 +1 @@ -1.3.0 +1.3.1 diff -Nru docker.io-1.3.0~dfsg1/api/client/commands.go docker.io-1.3.1~dfsg1/api/client/commands.go --- docker.io-1.3.0~dfsg1/api/client/commands.go 2014-10-15 19:15:24.000000000 +0000 +++ docker.io-1.3.1~dfsg1/api/client/commands.go 2014-10-30 13:44:46.000000000 +0000 @@ -1986,6 +1986,10 @@ } func (cli *DockerCli) pullImage(image string) error { + return cli.pullImageCustomOut(image, cli.out) +} + +func (cli *DockerCli) pullImageCustomOut(image string, out io.Writer) error { v := url.Values{} repos, tag := parsers.ParseRepositoryTag(image) // pull only the image tagged 'latest' if no tag was specified @@ -2014,7 +2018,7 @@ registryAuthHeader := []string{ base64.URLEncoding.EncodeToString(buf), } - if err = cli.stream("POST", "/images/create?"+v.Encode(), nil, cli.out, map[string][]string{"X-Registry-Auth": registryAuthHeader}); err != nil { + if err = cli.stream("POST", "/images/create?"+v.Encode(), nil, out, map[string][]string{"X-Registry-Auth": registryAuthHeader}); err != nil { return err } return nil @@ -2081,7 +2085,8 @@ if statusCode == 404 { fmt.Fprintf(cli.err, "Unable to find image '%s' locally\n", config.Image) - if err = cli.pullImage(config.Image); err != nil { + // we don't want to write to stdout anything apart from container.ID + if err = cli.pullImageCustomOut(config.Image, cli.err); err != nil { return nil, err } // Retry diff -Nru docker.io-1.3.0~dfsg1/api/server/server.go docker.io-1.3.1~dfsg1/api/server/server.go --- docker.io-1.3.0~dfsg1/api/server/server.go 2014-10-15 19:15:24.000000000 +0000 +++ docker.io-1.3.1~dfsg1/api/server/server.go 2014-10-30 13:44:46.000000000 +0000 @@ -1439,6 +1439,8 @@ tlsConfig := &tls.Config{ NextProtos: []string{"http/1.1"}, Certificates: []tls.Certificate{cert}, + // Avoid fallback on insecure SSL protocols + MinVersion: tls.VersionTLS10, } if job.GetenvBool("TlsVerify") { certPool := x509.NewCertPool() diff -Nru docker.io-1.3.0~dfsg1/builder/dispatchers.go docker.io-1.3.1~dfsg1/builder/dispatchers.go --- docker.io-1.3.0~dfsg1/builder/dispatchers.go 2014-10-15 19:15:24.000000000 +0000 +++ docker.io-1.3.1~dfsg1/builder/dispatchers.go 2014-10-30 13:44:46.000000000 +0000 @@ -11,6 +11,7 @@ "fmt" "io/ioutil" "path/filepath" + "regexp" "strings" "github.com/docker/docker/nat" @@ -129,7 +130,7 @@ return fmt.Errorf("%s isn't allowed as an ONBUILD trigger", triggerInstruction) } - original = strings.TrimSpace(strings.TrimLeft(original, "ONBUILD")) + original = regexp.MustCompile(`(?i)^\s*ONBUILD\s*`).ReplaceAllString(original, "") b.Config.OnBuild = append(b.Config.OnBuild, original) return b.commit("", b.Config.Cmd, fmt.Sprintf("ONBUILD %s", original)) @@ -194,7 +195,7 @@ defer func(cmd []string) { b.Config.Cmd = cmd }(cmd) - log.Debugf("Command to be executed: %v", b.Config.Cmd) + log.Debugf("[BUILDER] Command to be executed: %v", b.Config.Cmd) hit, err := b.probeCache() if err != nil { @@ -233,7 +234,7 @@ func cmd(b *Builder, args []string, attributes map[string]bool, original string) error { b.Config.Cmd = handleJsonArgs(args, attributes) - if !attributes["json"] && len(b.Config.Entrypoint) == 0 { + if !attributes["json"] { b.Config.Cmd = append([]string{"/bin/sh", "-c"}, b.Config.Cmd...) } @@ -260,14 +261,14 @@ parsed := handleJsonArgs(args, attributes) switch { - case len(parsed) == 0: - // ENTYRPOINT [] - b.Config.Entrypoint = nil case attributes["json"]: // ENTRYPOINT ["echo", "hi"] b.Config.Entrypoint = parsed + case len(parsed) == 0: + // ENTRYPOINT [] + b.Config.Entrypoint = nil default: - // ENTYRPOINT echo hi + // ENTRYPOINT echo hi b.Config.Entrypoint = []string{"/bin/sh", "-c", parsed[0]} } diff -Nru docker.io-1.3.0~dfsg1/builder/evaluator.go docker.io-1.3.1~dfsg1/builder/evaluator.go --- docker.io-1.3.0~dfsg1/builder/evaluator.go 2014-10-15 19:15:24.000000000 +0000 +++ docker.io-1.3.1~dfsg1/builder/evaluator.go 2014-10-30 13:44:46.000000000 +0000 @@ -41,6 +41,17 @@ ErrDockerfileEmpty = errors.New("Dockerfile cannot be empty") ) +// Environment variable interpolation will happen on these statements only. +var replaceEnvAllowed = map[string]struct{}{ + "env": {}, + "add": {}, + "copy": {}, + "workdir": {}, + "expose": {}, + "volume": {}, + "user": {}, +} + var evaluateTable map[string]func(*Builder, []string, map[string]bool, string) error func init() { @@ -149,7 +160,7 @@ b.dockerfile = ast // some initializations that would not have been supplied by the caller. - b.Config = &runconfig.Config{Entrypoint: []string{}, Cmd: nil} + b.Config = &runconfig.Config{} b.TmpContainers = map[string]struct{}{} for i, n := range b.dockerfile.Children { @@ -196,13 +207,18 @@ if cmd == "onbuild" { ast = ast.Next.Children[0] - strs = append(strs, b.replaceEnv(ast.Value)) + strs = append(strs, ast.Value) msg += " " + ast.Value } for ast.Next != nil { ast = ast.Next - strs = append(strs, b.replaceEnv(ast.Value)) + var str string + str = ast.Value + if _, ok := replaceEnvAllowed[cmd]; ok { + str = b.replaceEnv(ast.Value) + } + strs = append(strs, str) msg += " " + ast.Value } diff -Nru docker.io-1.3.0~dfsg1/builder/parser/parser.go docker.io-1.3.1~dfsg1/builder/parser/parser.go --- docker.io-1.3.0~dfsg1/builder/parser/parser.go 2014-10-15 19:15:24.000000000 +0000 +++ docker.io-1.3.1~dfsg1/builder/parser/parser.go 2014-10-30 13:44:46.000000000 +0000 @@ -87,10 +87,11 @@ if sexp.Value != "" || sexp.Next != nil || sexp.Children != nil { node.Next = sexp - node.Attributes = attrs - node.Original = line } + node.Attributes = attrs + node.Original = line + return "", node, nil } diff -Nru docker.io-1.3.0~dfsg1/builder/support.go docker.io-1.3.1~dfsg1/builder/support.go --- docker.io-1.3.0~dfsg1/builder/support.go 2014-10-15 19:15:24.000000000 +0000 +++ docker.io-1.3.1~dfsg1/builder/support.go 2014-10-30 13:44:46.000000000 +0000 @@ -10,13 +10,26 @@ // `\$` - match literal $ // `[[:alnum:]_]+` - match things like `$SOME_VAR` // `{[[:alnum:]_]+}` - match things like `${SOME_VAR}` - tokenEnvInterpolation = regexp.MustCompile(`(\\\\+|[^\\]|\b|\A)\$([[:alnum:]_]+|{[[:alnum:]_]+})`) + tokenEnvInterpolation = regexp.MustCompile(`(\\|\\\\+|[^\\]|\b|\A)\$([[:alnum:]_]+|{[[:alnum:]_]+})`) // this intentionally punts on more exotic interpolations like ${SOME_VAR%suffix} and lets the shell handle those directly ) // handle environment replacement. Used in dispatcher. func (b *Builder) replaceEnv(str string) string { for _, match := range tokenEnvInterpolation.FindAllString(str, -1) { + idx := strings.Index(match, "\\$") + if idx != -1 { + if idx+2 >= len(match) { + str = strings.Replace(str, match, "\\$", -1) + continue + } + + prefix := match[:idx] + stripped := match[idx+2:] + str = strings.Replace(str, match, prefix+"$"+stripped, -1) + continue + } + match = match[strings.Index(match, "$"):] matchKey := strings.Trim(match, "${}") diff -Nru docker.io-1.3.0~dfsg1/builtins/builtins.go docker.io-1.3.1~dfsg1/builtins/builtins.go --- docker.io-1.3.0~dfsg1/builtins/builtins.go 2014-10-15 19:15:24.000000000 +0000 +++ docker.io-1.3.1~dfsg1/builtins/builtins.go 2014-10-30 13:44:46.000000000 +0000 @@ -10,7 +10,6 @@ "github.com/docker/docker/engine" "github.com/docker/docker/events" "github.com/docker/docker/pkg/parsers/kernel" - "github.com/docker/docker/registry" ) func Register(eng *engine.Engine) error { @@ -26,7 +25,8 @@ if err := eng.Register("version", dockerVersion); err != nil { return err } - return registry.NewService().Install(eng) + + return nil } // remote: a RESTful api for cross-docker communication diff -Nru docker.io-1.3.0~dfsg1/daemon/config.go docker.io-1.3.1~dfsg1/daemon/config.go --- docker.io-1.3.0~dfsg1/daemon/config.go 2014-10-15 19:15:24.000000000 +0000 +++ docker.io-1.3.1~dfsg1/daemon/config.go 2014-10-30 13:44:46.000000000 +0000 @@ -31,6 +31,7 @@ BridgeIface string BridgeIP string FixedCIDR string + InsecureRegistries []string InterContainerCommunication bool GraphDriver string GraphOptions []string @@ -55,6 +56,7 @@ flag.StringVar(&config.BridgeIP, []string{"#bip", "-bip"}, "", "Use this CIDR notation address for the network bridge's IP, not compatible with -b") flag.StringVar(&config.BridgeIface, []string{"b", "-bridge"}, "", "Attach containers to a pre-existing network bridge\nuse 'none' to disable container networking") flag.StringVar(&config.FixedCIDR, []string{"-fixed-cidr"}, "", "IPv4 subnet for fixed IPs (ex: 10.20.0.0/16)\nthis subnet must be nested in the bridge subnet (which is defined by -b or --bip)") + opts.ListVar(&config.InsecureRegistries, []string{"-insecure-registry"}, "Enable insecure communication with specified registries (no certificate verification for HTTPS and enable HTTP fallback)") flag.BoolVar(&config.InterContainerCommunication, []string{"#icc", "-icc"}, true, "Enable inter-container communication") flag.StringVar(&config.GraphDriver, []string{"s", "-storage-driver"}, "", "Force the Docker runtime to use a specific storage driver") flag.StringVar(&config.ExecDriver, []string{"e", "-exec-driver"}, "native", "Force the Docker runtime to use a specific exec driver") diff -Nru docker.io-1.3.0~dfsg1/daemon/daemon.go docker.io-1.3.1~dfsg1/daemon/daemon.go --- docker.io-1.3.0~dfsg1/daemon/daemon.go 2014-10-15 19:15:24.000000000 +0000 +++ docker.io-1.3.1~dfsg1/daemon/daemon.go 2014-10-30 13:44:46.000000000 +0000 @@ -731,7 +731,7 @@ return nil, fmt.Errorf("You specified --iptables=false with --icc=false. ICC uses iptables to function. Please set --icc or --iptables to true.") } if !config.EnableIptables && config.EnableIpMasq { - return nil, fmt.Errorf("You specified --iptables=false with --ipmasq=true. IP masquerading uses iptables to function. Please set --ipmasq to false or --iptables to true.") + config.EnableIpMasq = false } config.DisableNetwork = config.BridgeIface == disableNetworkBridge @@ -831,7 +831,7 @@ } log.Debugf("Creating repository list") - repositories, err := graph.NewTagStore(path.Join(config.Root, "repositories-"+driver.String()), g, config.Mirrors) + repositories, err := graph.NewTagStore(path.Join(config.Root, "repositories-"+driver.String()), g, config.Mirrors, config.InsecureRegistries) if err != nil { return nil, fmt.Errorf("Couldn't create Tag store: %s", err) } diff -Nru docker.io-1.3.0~dfsg1/daemon/volumes.go docker.io-1.3.1~dfsg1/daemon/volumes.go --- docker.io-1.3.0~dfsg1/daemon/volumes.go 2014-10-15 19:15:24.000000000 +0000 +++ docker.io-1.3.1~dfsg1/daemon/volumes.go 2014-10-30 13:44:46.000000000 +0000 @@ -133,6 +133,7 @@ // Get the rest of the volumes for path := range container.Config.Volumes { // Check if this is already added as a bind-mount + path = filepath.Clean(path) if _, exists := mounts[path]; exists { continue } @@ -182,6 +183,8 @@ return "", "", false, fmt.Errorf("cannot bind mount volume: %s volume paths must be absolute.", path) } + path = filepath.Clean(path) + mountToPath = filepath.Clean(mountToPath) return path, mountToPath, writable, nil } diff -Nru docker.io-1.3.0~dfsg1/debian/changelog docker.io-1.3.1~dfsg1/debian/changelog --- docker.io-1.3.0~dfsg1/debian/changelog 2014-10-17 06:56:10.000000000 +0000 +++ docker.io-1.3.1~dfsg1/debian/changelog 2014-11-03 15:26:38.000000000 +0000 @@ -1,3 +1,11 @@ +docker.io (1.3.1~dfsg1-1) unstable; urgency=high + + * Update to 1.3.1 upstream release + - fix for CVE-2014-5277 + - https://groups.google.com/d/topic/docker-user/oYm0i3xShJU/discussion + + -- Tianon Gravi <admwig...@gmail.com> Mon, 03 Nov 2014 08:26:29 -0700 + docker.io (1.3.0~dfsg1-1) unstable; urgency=medium * Updated to 1.3.0 upstream release. diff -Nru docker.io-1.3.0~dfsg1/debian/upstream-version-gitcommits docker.io-1.3.1~dfsg1/debian/upstream-version-gitcommits --- docker.io-1.3.0~dfsg1/debian/upstream-version-gitcommits 2014-10-17 06:34:38.000000000 +0000 +++ docker.io-1.3.1~dfsg1/debian/upstream-version-gitcommits 2014-10-31 21:26:53.000000000 +0000 @@ -33,3 +33,4 @@ 1.1.2: d84a070 1.2.0: fa7b24f 1.3.0: c78088f +1.3.1: 4e9bbfa diff -Nru docker.io-1.3.0~dfsg1/docker/daemon.go docker.io-1.3.1~dfsg1/docker/daemon.go --- docker.io-1.3.0~dfsg1/docker/daemon.go 2014-10-15 19:15:24.000000000 +0000 +++ docker.io-1.3.1~dfsg1/docker/daemon.go 2014-10-30 13:44:46.000000000 +0000 @@ -14,6 +14,7 @@ "github.com/docker/docker/engine" flag "github.com/docker/docker/pkg/mflag" "github.com/docker/docker/pkg/signal" + "github.com/docker/docker/registry" ) const CanDaemon = true @@ -33,11 +34,17 @@ } eng := engine.New() signal.Trap(eng.Shutdown) + // Load builtins if err := builtins.Register(eng); err != nil { log.Fatal(err) } + // load registry service + if err := registry.NewService(daemonCfg.InsecureRegistries).Install(eng); err != nil { + log.Fatal(err) + } + // load the daemon in the background so we can immediately start // the http api so that connections don't fail while the daemon // is booting diff -Nru docker.io-1.3.0~dfsg1/docker/docker.go docker.io-1.3.1~dfsg1/docker/docker.go --- docker.io-1.3.0~dfsg1/docker/docker.go 2014-10-15 19:15:24.000000000 +0000 +++ docker.io-1.3.1~dfsg1/docker/docker.go 2014-10-30 13:44:46.000000000 +0000 @@ -93,6 +93,8 @@ } tlsConfig.Certificates = []tls.Certificate{cert} } + // Avoid fallback to SSL protocols < TLS1.0 + tlsConfig.MinVersion = tls.VersionTLS10 } if *flTls || *flTlsVerify { diff -Nru docker.io-1.3.0~dfsg1/docs/mkdocs.yml docker.io-1.3.1~dfsg1/docs/mkdocs.yml --- docker.io-1.3.0~dfsg1/docs/mkdocs.yml 2014-10-15 19:15:24.000000000 +0000 +++ docker.io-1.3.1~dfsg1/docs/mkdocs.yml 2014-10-30 13:44:46.000000000 +0000 @@ -26,6 +26,7 @@ # Introduction: - ['index.md', 'About', 'Docker'] +- ['release-notes.md', 'About', 'Release Notes'] - ['introduction/index.md', '**HIDDEN**'] - ['introduction/understanding-docker.md', 'About', 'Understanding Docker'] diff -Nru docker.io-1.3.0~dfsg1/graph/pull.go docker.io-1.3.1~dfsg1/graph/pull.go --- docker.io-1.3.0~dfsg1/graph/pull.go 2014-10-15 19:15:24.000000000 +0000 +++ docker.io-1.3.1~dfsg1/graph/pull.go 2014-10-30 13:44:46.000000000 +0000 @@ -113,7 +113,9 @@ return job.Error(err) } - endpoint, err := registry.NewEndpoint(hostname) + secure := registry.IsSecure(hostname, s.insecureRegistries) + + endpoint, err := registry.NewEndpoint(hostname, secure) if err != nil { return job.Error(err) } diff -Nru docker.io-1.3.0~dfsg1/graph/push.go docker.io-1.3.1~dfsg1/graph/push.go --- docker.io-1.3.0~dfsg1/graph/push.go 2014-10-15 19:15:24.000000000 +0000 +++ docker.io-1.3.1~dfsg1/graph/push.go 2014-10-30 13:44:46.000000000 +0000 @@ -214,7 +214,9 @@ return job.Error(err) } - endpoint, err := registry.NewEndpoint(hostname) + secure := registry.IsSecure(hostname, s.insecureRegistries) + + endpoint, err := registry.NewEndpoint(hostname, secure) if err != nil { return job.Error(err) } diff -Nru docker.io-1.3.0~dfsg1/graph/tags.go docker.io-1.3.1~dfsg1/graph/tags.go --- docker.io-1.3.0~dfsg1/graph/tags.go 2014-10-15 19:15:24.000000000 +0000 +++ docker.io-1.3.1~dfsg1/graph/tags.go 2014-10-30 13:44:46.000000000 +0000 @@ -23,10 +23,11 @@ ) type TagStore struct { - path string - graph *Graph - mirrors []string - Repositories map[string]Repository + path string + graph *Graph + mirrors []string + insecureRegistries []string + Repositories map[string]Repository sync.Mutex // FIXME: move push/pull-related fields // to a helper type @@ -54,18 +55,20 @@ return true } -func NewTagStore(path string, graph *Graph, mirrors []string) (*TagStore, error) { +func NewTagStore(path string, graph *Graph, mirrors []string, insecureRegistries []string) (*TagStore, error) { abspath, err := filepath.Abs(path) if err != nil { return nil, err } + store := &TagStore{ - path: abspath, - graph: graph, - mirrors: mirrors, - Repositories: make(map[string]Repository), - pullingPool: make(map[string]chan struct{}), - pushingPool: make(map[string]chan struct{}), + path: abspath, + graph: graph, + mirrors: mirrors, + insecureRegistries: insecureRegistries, + Repositories: make(map[string]Repository), + pullingPool: make(map[string]chan struct{}), + pushingPool: make(map[string]chan struct{}), } // Load the json file if it exists, otherwise create it. if err := store.reload(); os.IsNotExist(err) { diff -Nru docker.io-1.3.0~dfsg1/graph/tags_unit_test.go docker.io-1.3.1~dfsg1/graph/tags_unit_test.go --- docker.io-1.3.0~dfsg1/graph/tags_unit_test.go 2014-10-15 19:15:24.000000000 +0000 +++ docker.io-1.3.1~dfsg1/graph/tags_unit_test.go 2014-10-30 13:44:46.000000000 +0000 @@ -53,7 +53,7 @@ if err != nil { t.Fatal(err) } - store, err := NewTagStore(path.Join(root, "tags"), graph, nil) + store, err := NewTagStore(path.Join(root, "tags"), graph, nil, nil) if err != nil { t.Fatal(err) } diff -Nru docker.io-1.3.0~dfsg1/hack/release.sh docker.io-1.3.1~dfsg1/hack/release.sh --- docker.io-1.3.0~dfsg1/hack/release.sh 2014-10-15 19:15:24.000000000 +0000 +++ docker.io-1.3.1~dfsg1/hack/release.sh 2014-10-30 13:44:46.000000000 +0000 @@ -270,7 +270,7 @@ done # Upload keys - s3cmd sync /.gnupg/ s3://$BUCKET/ubuntu/.gnupg/ + s3cmd sync $HOME/.gnupg/ s3://$BUCKET/ubuntu/.gnupg/ gpg --armor --export releasedocker > bundles/$VERSION/ubuntu/gpg s3cmd --acl-public put bundles/$VERSION/ubuntu/gpg s3://$BUCKET/gpg @@ -355,8 +355,8 @@ setup_gpg() { # Make sure that we have our keys - mkdir -p /.gnupg/ - s3cmd sync s3://$BUCKET/ubuntu/.gnupg/ /.gnupg/ || true + mkdir -p $HOME/.gnupg/ + s3cmd sync s3://$BUCKET/ubuntu/.gnupg/ $HOME/.gnupg/ || true gpg --list-keys releasedocker >/dev/null || { gpg --gen-key --batch <<EOF Key-Type: RSA diff -Nru docker.io-1.3.0~dfsg1/integration-cli/docker_cli_build_test.go docker.io-1.3.1~dfsg1/integration-cli/docker_cli_build_test.go --- docker.io-1.3.0~dfsg1/integration-cli/docker_cli_build_test.go 2014-10-15 19:15:24.000000000 +0000 +++ docker.io-1.3.1~dfsg1/integration-cli/docker_cli_build_test.go 2014-10-30 13:44:46.000000000 +0000 @@ -2,6 +2,7 @@ import ( "archive/tar" + "encoding/json" "fmt" "io/ioutil" "os" @@ -15,6 +16,396 @@ "github.com/docker/docker/pkg/archive" ) +func TestBuildShCmdJSONEntrypoint(t *testing.T) { + name := "testbuildshcmdjsonentrypoint" + defer deleteImages(name) + + _, err := buildImage( + name, + ` + FROM busybox + ENTRYPOINT ["/bin/echo"] + CMD echo test + `, + true) + + if err != nil { + t.Fatal(err) + } + + out, _, err := runCommandWithOutput( + exec.Command( + dockerBinary, + "run", + name)) + + if err != nil { + t.Fatal(err) + } + + if strings.TrimSpace(out) != "/bin/sh -c echo test" { + t.Fatal("CMD did not contain /bin/sh -c") + } + + logDone("build - CMD should always contain /bin/sh -c when specified without JSON") +} + +func TestBuildEnvironmentReplacementUser(t *testing.T) { + name := "testbuildenvironmentreplacement" + defer deleteImages(name) + + _, err := buildImage(name, ` + FROM scratch + ENV user foo + USER ${user} + `, true) + if err != nil { + t.Fatal(err) + } + + res, err := inspectFieldJSON(name, "Config.User") + if err != nil { + t.Fatal(err) + } + + if res != `"foo"` { + t.Fatal("User foo from environment not in Config.User on image") + } + + logDone("build - user environment replacement") +} + +func TestBuildEnvironmentReplacementVolume(t *testing.T) { + name := "testbuildenvironmentreplacement" + defer deleteImages(name) + + _, err := buildImage(name, ` + FROM scratch + ENV volume /quux + VOLUME ${volume} + `, true) + if err != nil { + t.Fatal(err) + } + + res, err := inspectFieldJSON(name, "Config.Volumes") + if err != nil { + t.Fatal(err) + } + + var volumes map[string]interface{} + + if err := json.Unmarshal([]byte(res), &volumes); err != nil { + t.Fatal(err) + } + + if _, ok := volumes["/quux"]; !ok { + t.Fatal("Volume /quux from environment not in Config.Volumes on image") + } + + logDone("build - volume environment replacement") +} + +func TestBuildEnvironmentReplacementExpose(t *testing.T) { + name := "testbuildenvironmentreplacement" + defer deleteImages(name) + + _, err := buildImage(name, ` + FROM scratch + ENV port 80 + EXPOSE ${port} + `, true) + if err != nil { + t.Fatal(err) + } + + res, err := inspectFieldJSON(name, "Config.ExposedPorts") + if err != nil { + t.Fatal(err) + } + + var exposedPorts map[string]interface{} + + if err := json.Unmarshal([]byte(res), &exposedPorts); err != nil { + t.Fatal(err) + } + + if _, ok := exposedPorts["80/tcp"]; !ok { + t.Fatal("Exposed port 80 from environment not in Config.ExposedPorts on image") + } + + logDone("build - expose environment replacement") +} + +func TestBuildEnvironmentReplacementWorkdir(t *testing.T) { + name := "testbuildenvironmentreplacement" + defer deleteImages(name) + + _, err := buildImage(name, ` + FROM busybox + ENV MYWORKDIR /work + RUN mkdir ${MYWORKDIR} + WORKDIR ${MYWORKDIR} + `, true) + + if err != nil { + t.Fatal(err) + } + + logDone("build - workdir environment replacement") +} + +func TestBuildEnvironmentReplacementAddCopy(t *testing.T) { + name := "testbuildenvironmentreplacement" + defer deleteImages(name) + + ctx, err := fakeContext(` + FROM scratch + ENV baz foo + ENV quux bar + ENV dot . + + ADD ${baz} ${dot} + COPY ${quux} ${dot} + `, + map[string]string{ + "foo": "test1", + "bar": "test2", + }) + + if err != nil { + t.Fatal(err) + } + + if _, err := buildImageFromContext(name, ctx, true); err != nil { + t.Fatal(err) + } + + logDone("build - add/copy environment replacement") +} + +func TestBuildEnvironmentReplacementEnv(t *testing.T) { + name := "testbuildenvironmentreplacement" + + defer deleteImages(name) + + _, err := buildImage(name, + ` + FROM scratch + ENV foo foo + ENV bar ${foo} + `, true) + + if err != nil { + t.Fatal(err) + } + + res, err := inspectFieldJSON(name, "Config.Env") + if err != nil { + t.Fatal(err) + } + + envResult := []string{} + + if err = unmarshalJSON([]byte(res), &envResult); err != nil { + t.Fatal(err) + } + + found := false + + for _, env := range envResult { + parts := strings.SplitN(env, "=", 2) + if parts[0] == "bar" { + found = true + if parts[1] != "foo" { + t.Fatal("Could not find replaced var for env `bar`: got %q instead of `foo`", parts[1]) + } + } + } + + if !found { + t.Fatal("Never found the `bar` env variable") + } + + logDone("build - env environment replacement") +} + +func TestBuildHandleEscapes(t *testing.T) { + name := "testbuildhandleescapes" + + defer deleteImages(name) + + _, err := buildImage(name, + ` + FROM scratch + ENV FOO bar + VOLUME ${FOO} + `, true) + + if err != nil { + t.Fatal(err) + } + + var result map[string]map[string]struct{} + + res, err := inspectFieldJSON(name, "Config.Volumes") + if err != nil { + t.Fatal(err) + } + + if err = unmarshalJSON([]byte(res), &result); err != nil { + t.Fatal(err) + } + + if _, ok := result["bar"]; !ok { + t.Fatal("Could not find volume bar set from env foo in volumes table") + } + + _, err = buildImage(name, + ` + FROM scratch + ENV FOO bar + VOLUME \${FOO} + `, true) + + if err != nil { + t.Fatal(err) + } + + res, err = inspectFieldJSON(name, "Config.Volumes") + if err != nil { + t.Fatal(err) + } + + if err = unmarshalJSON([]byte(res), &result); err != nil { + t.Fatal(err) + } + + if _, ok := result["${FOO}"]; !ok { + t.Fatal("Could not find volume ${FOO} set from env foo in volumes table") + } + + // this test in particular provides *7* backslashes and expects 6 to come back. + // Like above, the first escape is swallowed and the rest are treated as + // literals, this one is just less obvious because of all the character noise. + + _, err = buildImage(name, + ` + FROM scratch + ENV FOO bar + VOLUME \\\\\\\${FOO} + `, true) + + if err != nil { + t.Fatal(err) + } + + res, err = inspectFieldJSON(name, "Config.Volumes") + if err != nil { + t.Fatal(err) + } + + if err = unmarshalJSON([]byte(res), &result); err != nil { + t.Fatal(err) + } + + if _, ok := result[`\\\\\\${FOO}`]; !ok { + t.Fatal(`Could not find volume \\\\\\${FOO} set from env foo in volumes table`) + } + + logDone("build - handle escapes") +} + +func TestBuildOnBuildLowercase(t *testing.T) { + name := "testbuildonbuildlowercase" + name2 := "testbuildonbuildlowercase2" + + defer deleteImages(name, name2) + + _, err := buildImage(name, + ` + FROM busybox + onbuild run echo quux + `, true) + + if err != nil { + t.Fatal(err) + } + + _, out, err := buildImageWithOut(name2, fmt.Sprintf(` + FROM %s + `, name), true) + + if err != nil { + t.Fatal(err) + } + + if !strings.Contains(out, "quux") { + t.Fatalf("Did not receive the expected echo text, got %s", out) + } + + if strings.Contains(out, "ONBUILD ONBUILD") { + t.Fatalf("Got an ONBUILD ONBUILD error with no error: got %s", out) + } + + logDone("build - handle case-insensitive onbuild statement") +} + +func TestBuildEnvEscapes(t *testing.T) { + name := "testbuildenvescapes" + defer deleteAllContainers() + defer deleteImages(name) + _, err := buildImage(name, + ` + FROM busybox + ENV TEST foo + CMD echo \$ + `, + true) + + out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "-t", name)) + + if err != nil { + t.Fatal(err) + } + + if strings.TrimSpace(out) != "$" { + t.Fatalf("Env TEST was not overwritten with bar when foo was supplied to dockerfile: was %q", strings.TrimSpace(out)) + } + + logDone("build - env should handle \\$ properly") +} + +func TestBuildEnvOverwrite(t *testing.T) { + name := "testbuildenvoverwrite" + defer deleteAllContainers() + defer deleteImages(name) + + _, err := buildImage(name, + ` + FROM busybox + ENV TEST foo + CMD echo ${TEST} + `, + true) + + if err != nil { + t.Fatal(err) + } + + out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "-e", "TEST=bar", "-t", name)) + + if err != nil { + t.Fatal(err) + } + + if strings.TrimSpace(out) != "bar" { + t.Fatalf("Env TEST was not overwritten with bar when foo was supplied to dockerfile: was %q", strings.TrimSpace(out)) + } + + logDone("build - env should overwrite builder ENV during run") +} + func TestBuildOnBuildForbiddenMaintainerInSourceImage(t *testing.T) { name := "testbuildonbuildforbiddenmaintainerinsourceimage" defer deleteImages(name) @@ -1272,6 +1663,49 @@ logDone("build - expose") } +func TestBuildEmptyEntrypointInheritance(t *testing.T) { + name := "testbuildentrypointinheritance" + name2 := "testbuildentrypointinheritance2" + defer deleteImages(name, name2) + + _, err := buildImage(name, + `FROM busybox + ENTRYPOINT ["/bin/echo"]`, + true) + if err != nil { + t.Fatal(err) + } + res, err := inspectField(name, "Config.Entrypoint") + if err != nil { + t.Fatal(err) + } + + expected := "[/bin/echo]" + if res != expected { + t.Fatalf("Entrypoint %s, expected %s", res, expected) + } + + _, err = buildImage(name2, + fmt.Sprintf(`FROM %s + ENTRYPOINT []`, name), + true) + if err != nil { + t.Fatal(err) + } + res, err = inspectField(name2, "Config.Entrypoint") + if err != nil { + t.Fatal(err) + } + + expected = "[]" + + if res != expected { + t.Fatalf("Entrypoint %s, expected %s", res, expected) + } + + logDone("build - empty entrypoint inheritance") +} + func TestBuildEmptyEntrypoint(t *testing.T) { name := "testbuildentrypoint" defer deleteImages(name) @@ -2328,6 +2762,7 @@ name := "testbuildenvusage" defer deleteImages(name) dockerfile := `FROM busybox +ENV HOME /root ENV PATH $HOME/bin:$PATH ENV PATH /tmp:$PATH RUN [ "$PATH" = "/tmp:$HOME/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" ] diff -Nru docker.io-1.3.0~dfsg1/integration-cli/docker_cli_daemon_test.go docker.io-1.3.1~dfsg1/integration-cli/docker_cli_daemon_test.go --- docker.io-1.3.0~dfsg1/integration-cli/docker_cli_daemon_test.go 2014-10-15 19:15:24.000000000 +0000 +++ docker.io-1.3.1~dfsg1/integration-cli/docker_cli_daemon_test.go 2014-10-30 13:44:46.000000000 +0000 @@ -82,3 +82,13 @@ logDone("daemon - volume refs are restored") } + +func TestDaemonStartIptablesFalse(t *testing.T) { + d := NewDaemon(t) + if err := d.Start("--iptables=false"); err != nil { + t.Fatalf("we should have been able to start the daemon with passing iptables=false: %v", err) + } + d.Stop() + + logDone("daemon - started daemon with iptables=false") +} diff -Nru docker.io-1.3.0~dfsg1/integration-cli/docker_cli_run_test.go docker.io-1.3.1~dfsg1/integration-cli/docker_cli_run_test.go --- docker.io-1.3.0~dfsg1/integration-cli/docker_cli_run_test.go 2014-10-15 19:15:24.000000000 +0000 +++ docker.io-1.3.1~dfsg1/integration-cli/docker_cli_run_test.go 2014-10-30 13:44:46.000000000 +0000 @@ -2,6 +2,7 @@ import ( "bufio" + "bytes" "fmt" "io/ioutil" "net" @@ -2374,3 +2375,68 @@ logDone("run - volumes not recreated on start") } + +func TestRunNoOutputFromPullInStdout(t *testing.T) { + defer deleteAllContainers() + // just run with unknown image + cmd := exec.Command(dockerBinary, "run", "asdfsg") + stdout := bytes.NewBuffer(nil) + cmd.Stdout = stdout + if err := cmd.Run(); err == nil { + t.Fatal("Run with unknown image should fail") + } + if stdout.Len() != 0 { + t.Fatalf("Stdout contains output from pull: %s", stdout) + } + logDone("run - no output from pull in stdout") +} + +func TestRunVolumesCleanPaths(t *testing.T) { + defer deleteAllContainers() + + if _, err := buildImage("run_volumes_clean_paths", + `FROM busybox + VOLUME /foo/`, + true); err != nil { + t.Fatal(err) + } + defer deleteImages("run_volumes_clean_paths") + + cmd := exec.Command(dockerBinary, "run", "-v", "/foo", "-v", "/bar/", "--name", "dark_helmet", "run_volumes_clean_paths") + if out, _, err := runCommandWithOutput(cmd); err != nil { + t.Fatal(err, out) + } + + out, err := inspectFieldMap("dark_helmet", "Volumes", "/foo/") + if err != nil { + t.Fatal(err) + } + if out != "<no value>" { + t.Fatalf("Found unexpected volume entry for '/foo/' in volumes\n%q", out) + } + + out, err = inspectFieldMap("dark_helmet", "Volumes", "/foo") + if err != nil { + t.Fatal(err) + } + if !strings.Contains(out, volumesStoragePath) { + t.Fatalf("Volume was not defined for /foo\n%q", out) + } + + out, err = inspectFieldMap("dark_helmet", "Volumes", "/bar/") + if err != nil { + t.Fatal(err) + } + if out != "<no value>" { + t.Fatalf("Found unexpected volume entry for '/bar/' in volumes\n%q", out) + } + out, err = inspectFieldMap("dark_helmet", "Volumes", "/bar") + if err != nil { + t.Fatal(err) + } + if !strings.Contains(out, volumesStoragePath) { + t.Fatalf("Volume was not defined for /bar\n%q", out) + } + + logDone("run - volume paths are cleaned") +} diff -Nru docker.io-1.3.0~dfsg1/integration-cli/docker_test_vars.go docker.io-1.3.1~dfsg1/integration-cli/docker_test_vars.go --- docker.io-1.3.0~dfsg1/integration-cli/docker_test_vars.go 2014-10-15 19:15:24.000000000 +0000 +++ docker.io-1.3.1~dfsg1/integration-cli/docker_test_vars.go 2014-10-30 13:44:46.000000000 +0000 @@ -16,8 +16,10 @@ // the private registry to use for tests privateRegistryURL = "127.0.0.1:5000" - execDriverPath = "/var/lib/docker/execdriver/native" - volumesConfigPath = "/var/lib/docker/volumes" + dockerBasePath = "/var/lib/docker" + execDriverPath = dockerBasePath + "/execdriver/native" + volumesConfigPath = dockerBasePath + "/volumes" + volumesStoragePath = dockerBasePath + "/vfs/dir" workingDirectory string ) diff -Nru docker.io-1.3.0~dfsg1/integration-cli/docker_utils.go docker.io-1.3.1~dfsg1/integration-cli/docker_utils.go --- docker.io-1.3.0~dfsg1/integration-cli/docker_utils.go 2014-10-15 19:15:24.000000000 +0000 +++ docker.io-1.3.1~dfsg1/integration-cli/docker_utils.go 2014-10-30 13:44:46.000000000 +0000 @@ -507,6 +507,16 @@ return strings.TrimSpace(out), nil } +func inspectFieldMap(name, path, field string) (string, error) { + format := fmt.Sprintf("{{index .%s %q}}", path, field) + inspectCmd := exec.Command(dockerBinary, "inspect", "-f", format, name) + out, exitCode, err := runCommandWithOutput(inspectCmd) + if err != nil || exitCode != 0 { + return "", fmt.Errorf("failed to inspect %s: %s", name, out) + } + return strings.TrimSpace(out), nil +} + func getIDByName(name string) (string, error) { return inspectField(name, "Id") } diff -Nru docker.io-1.3.0~dfsg1/registry/endpoint.go docker.io-1.3.1~dfsg1/registry/endpoint.go --- docker.io-1.3.0~dfsg1/registry/endpoint.go 2014-10-15 19:15:24.000000000 +0000 +++ docker.io-1.3.1~dfsg1/registry/endpoint.go 2014-10-30 13:44:46.000000000 +0000 @@ -2,7 +2,6 @@ import ( "encoding/json" - "errors" "fmt" "io/ioutil" "net/http" @@ -34,9 +33,9 @@ return hostname, DefaultAPIVersion } -func NewEndpoint(hostname string) (*Endpoint, error) { +func NewEndpoint(hostname string, secure bool) (*Endpoint, error) { var ( - endpoint Endpoint + endpoint = Endpoint{secure: secure} trimmedHostname string err error ) @@ -49,14 +48,27 @@ return nil, err } + // Try HTTPS ping to registry endpoint.URL.Scheme = "https" if _, err := endpoint.Ping(); err != nil { - log.Debugf("Registry %s does not work (%s), falling back to http", endpoint, err) - // TODO: Check if http fallback is enabled + + //TODO: triggering highland build can be done there without "failing" + + if secure { + // If registry is secure and HTTPS failed, show user the error and tell them about `--insecure-registry` + // in case that's what they need. DO NOT accept unknown CA certificates, and DO NOT fallback to HTTP. + return nil, fmt.Errorf("Invalid registry endpoint %s: %v. If this private registry supports only HTTP or HTTPS with an unknown CA certificate, please add `--insecure-registry %s` to the daemon's arguments. In the case of HTTPS, if you have access to the registry's CA certificate, no need for the flag; simply place the CA certificate at /etc/docker/certs.d/%s/ca.crt", endpoint, err, endpoint.URL.Host, endpoint.URL.Host) + } + + // If registry is insecure and HTTPS failed, fallback to HTTP. + log.Debugf("Error from registry %q marked as insecure: %v. Insecurely falling back to HTTP", endpoint, err) endpoint.URL.Scheme = "http" - if _, err = endpoint.Ping(); err != nil { - return nil, errors.New("Invalid Registry endpoint: " + err.Error()) + _, err2 := endpoint.Ping() + if err2 == nil { + return &endpoint, nil } + + return nil, fmt.Errorf("Invalid registry endpoint %q. HTTPS attempt: %v. HTTP attempt: %v", endpoint, err, err2) } return &endpoint, nil @@ -65,6 +77,7 @@ type Endpoint struct { URL *url.URL Version APIVersion + secure bool } // Get the formated URL for the root of this registry Endpoint @@ -88,7 +101,7 @@ return RegistryInfo{Standalone: false}, err } - resp, _, err := doRequest(req, nil, ConnectTimeout) + resp, _, err := doRequest(req, nil, ConnectTimeout, e.secure) if err != nil { return RegistryInfo{Standalone: false}, err } @@ -127,3 +140,19 @@ log.Debugf("RegistryInfo.Standalone: %t", info.Standalone) return info, nil } + +// IsSecure returns false if the provided hostname is part of the list of insecure registries. +// Insecure registries accept HTTP and/or accept HTTPS with certificates from unknown CAs. +func IsSecure(hostname string, insecureRegistries []string) bool { + if hostname == IndexServerAddress() { + return true + } + + for _, h := range insecureRegistries { + if hostname == h { + return false + } + } + + return true +} diff -Nru docker.io-1.3.0~dfsg1/registry/registry.go docker.io-1.3.1~dfsg1/registry/registry.go --- docker.io-1.3.0~dfsg1/registry/registry.go 2014-10-15 19:15:24.000000000 +0000 +++ docker.io-1.3.1~dfsg1/registry/registry.go 2014-10-30 13:44:46.000000000 +0000 @@ -14,6 +14,7 @@ "strings" "time" + "github.com/docker/docker/pkg/log" "github.com/docker/docker/utils" ) @@ -35,13 +36,21 @@ ConnectTimeout ) -func newClient(jar http.CookieJar, roots *x509.CertPool, cert *tls.Certificate, timeout TimeoutType) *http.Client { - tlsConfig := tls.Config{RootCAs: roots} +func newClient(jar http.CookieJar, roots *x509.CertPool, cert *tls.Certificate, timeout TimeoutType, secure bool) *http.Client { + tlsConfig := tls.Config{ + RootCAs: roots, + // Avoid fallback to SSL protocols < TLS1.0 + MinVersion: tls.VersionTLS10, + } if cert != nil { tlsConfig.Certificates = append(tlsConfig.Certificates, *cert) } + if !secure { + tlsConfig.InsecureSkipVerify = true + } + httpTransport := &http.Transport{ DisableKeepAlives: true, Proxy: http.ProxyFromEnvironment, @@ -78,69 +87,76 @@ } } -func doRequest(req *http.Request, jar http.CookieJar, timeout TimeoutType) (*http.Response, *http.Client, error) { - hasFile := func(files []os.FileInfo, name string) bool { - for _, f := range files { - if f.Name() == name { - return true - } - } - return false - } - - hostDir := path.Join("/etc/docker/certs.d", req.URL.Host) - fs, err := ioutil.ReadDir(hostDir) - if err != nil && !os.IsNotExist(err) { - return nil, nil, err - } - +func doRequest(req *http.Request, jar http.CookieJar, timeout TimeoutType, secure bool) (*http.Response, *http.Client, error) { var ( pool *x509.CertPool certs []*tls.Certificate ) - for _, f := range fs { - if strings.HasSuffix(f.Name(), ".crt") { - if pool == nil { - pool = x509.NewCertPool() - } - data, err := ioutil.ReadFile(path.Join(hostDir, f.Name())) - if err != nil { - return nil, nil, err - } - pool.AppendCertsFromPEM(data) + if secure && req.URL.Scheme == "https" { + hasFile := func(files []os.FileInfo, name string) bool { + for _, f := range files { + if f.Name() == name { + return true + } + } + return false } - if strings.HasSuffix(f.Name(), ".cert") { - certName := f.Name() - keyName := certName[:len(certName)-5] + ".key" - if !hasFile(fs, keyName) { - return nil, nil, fmt.Errorf("Missing key %s for certificate %s", keyName, certName) - } - cert, err := tls.LoadX509KeyPair(path.Join(hostDir, certName), path.Join(hostDir, keyName)) - if err != nil { - return nil, nil, err - } - certs = append(certs, &cert) + + hostDir := path.Join("/etc/docker/certs.d", req.URL.Host) + log.Debugf("hostDir: %s", hostDir) + fs, err := ioutil.ReadDir(hostDir) + if err != nil && !os.IsNotExist(err) { + return nil, nil, err } - if strings.HasSuffix(f.Name(), ".key") { - keyName := f.Name() - certName := keyName[:len(keyName)-4] + ".cert" - if !hasFile(fs, certName) { - return nil, nil, fmt.Errorf("Missing certificate %s for key %s", certName, keyName) + + for _, f := range fs { + if strings.HasSuffix(f.Name(), ".crt") { + if pool == nil { + pool = x509.NewCertPool() + } + log.Debugf("crt: %s", hostDir+"/"+f.Name()) + data, err := ioutil.ReadFile(path.Join(hostDir, f.Name())) + if err != nil { + return nil, nil, err + } + pool.AppendCertsFromPEM(data) + } + if strings.HasSuffix(f.Name(), ".cert") { + certName := f.Name() + keyName := certName[:len(certName)-5] + ".key" + log.Debugf("cert: %s", hostDir+"/"+f.Name()) + if !hasFile(fs, keyName) { + return nil, nil, fmt.Errorf("Missing key %s for certificate %s", keyName, certName) + } + cert, err := tls.LoadX509KeyPair(path.Join(hostDir, certName), path.Join(hostDir, keyName)) + if err != nil { + return nil, nil, err + } + certs = append(certs, &cert) + } + if strings.HasSuffix(f.Name(), ".key") { + keyName := f.Name() + certName := keyName[:len(keyName)-4] + ".cert" + log.Debugf("key: %s", hostDir+"/"+f.Name()) + if !hasFile(fs, certName) { + return nil, nil, fmt.Errorf("Missing certificate %s for key %s", certName, keyName) + } } } } if len(certs) == 0 { - client := newClient(jar, pool, nil, timeout) + client := newClient(jar, pool, nil, timeout, secure) res, err := client.Do(req) if err != nil { return nil, nil, err } return res, client, nil } + for i, cert := range certs { - client := newClient(jar, pool, cert, timeout) + client := newClient(jar, pool, cert, timeout, secure) res, err := client.Do(req) // If this is the last cert, otherwise, continue to next cert if 403 or 5xx if i == len(certs)-1 || err == nil && res.StatusCode != 403 && res.StatusCode < 500 { diff -Nru docker.io-1.3.0~dfsg1/registry/registry_test.go docker.io-1.3.1~dfsg1/registry/registry_test.go --- docker.io-1.3.0~dfsg1/registry/registry_test.go 2014-10-15 19:15:24.000000000 +0000 +++ docker.io-1.3.1~dfsg1/registry/registry_test.go 2014-10-30 13:44:46.000000000 +0000 @@ -18,7 +18,7 @@ func spawnTestRegistrySession(t *testing.T) *Session { authConfig := &AuthConfig{} - endpoint, err := NewEndpoint(makeURL("/v1/")) + endpoint, err := NewEndpoint(makeURL("/v1/"), false) if err != nil { t.Fatal(err) } @@ -30,7 +30,7 @@ } func TestPingRegistryEndpoint(t *testing.T) { - ep, err := NewEndpoint(makeURL("/v1/")) + ep, err := NewEndpoint(makeURL("/v1/"), false) if err != nil { t.Fatal(err) } diff -Nru docker.io-1.3.0~dfsg1/registry/service.go docker.io-1.3.1~dfsg1/registry/service.go --- docker.io-1.3.0~dfsg1/registry/service.go 2014-10-15 19:15:24.000000000 +0000 +++ docker.io-1.3.1~dfsg1/registry/service.go 2014-10-30 13:44:46.000000000 +0000 @@ -13,12 +13,15 @@ // 'pull': Download images from any registry (TODO) // 'push': Upload images to any registry (TODO) type Service struct { + insecureRegistries []string } // NewService returns a new instance of Service ready to be // installed no an engine. -func NewService() *Service { - return &Service{} +func NewService(insecureRegistries []string) *Service { + return &Service{ + insecureRegistries: insecureRegistries, + } } // Install installs registry capabilities to eng. @@ -32,15 +35,12 @@ // and returns OK if authentication was sucessful. // It can be used to verify the validity of a client's credentials. func (s *Service) Auth(job *engine.Job) engine.Status { - var ( - err error - authConfig = &AuthConfig{} - ) + var authConfig = new(AuthConfig) job.GetenvJson("authConfig", authConfig) - // TODO: this is only done here because auth and registry need to be merged into one pkg + if addr := authConfig.ServerAddress; addr != "" && addr != IndexServerAddress() { - endpoint, err := NewEndpoint(addr) + endpoint, err := NewEndpoint(addr, IsSecure(addr, s.insecureRegistries)) if err != nil { return job.Error(err) } @@ -49,11 +49,13 @@ } authConfig.ServerAddress = endpoint.String() } + status, err := Login(authConfig, HTTPRequestFactory(nil)) if err != nil { return job.Error(err) } job.Printf("%s\n", status) + return engine.StatusOK } @@ -89,7 +91,10 @@ if err != nil { return job.Error(err) } - endpoint, err := NewEndpoint(hostname) + + secure := IsSecure(hostname, s.insecureRegistries) + + endpoint, err := NewEndpoint(hostname, secure) if err != nil { return job.Error(err) } diff -Nru docker.io-1.3.0~dfsg1/registry/session.go docker.io-1.3.1~dfsg1/registry/session.go --- docker.io-1.3.0~dfsg1/registry/session.go 2014-10-15 19:15:24.000000000 +0000 +++ docker.io-1.3.1~dfsg1/registry/session.go 2014-10-30 13:44:46.000000000 +0000 @@ -64,7 +64,7 @@ } func (r *Session) doRequest(req *http.Request) (*http.Response, *http.Client, error) { - return doRequest(req, r.jar, r.timeout) + return doRequest(req, r.jar, r.timeout, r.indexEndpoint.secure) } // Retrieve the history of a given image from the Registry. diff -Nru docker.io-1.3.0~dfsg1/runconfig/merge.go docker.io-1.3.1~dfsg1/runconfig/merge.go --- docker.io-1.3.0~dfsg1/runconfig/merge.go 2014-10-15 19:15:24.000000000 +0000 +++ docker.io-1.3.1~dfsg1/runconfig/merge.go 2014-10-30 13:44:46.000000000 +0000 @@ -88,7 +88,10 @@ if len(userConf.Cmd) == 0 { userConf.Cmd = imageConf.Cmd } - userConf.Entrypoint = imageConf.Entrypoint + + if userConf.Entrypoint == nil { + userConf.Entrypoint = imageConf.Entrypoint + } } if userConf.WorkingDir == "" { userConf.WorkingDir = imageConf.WorkingDir diff -Nru docker.io-1.3.0~dfsg1/volumes/repository.go docker.io-1.3.1~dfsg1/volumes/repository.go --- docker.io-1.3.0~dfsg1/volumes/repository.go 2014-10-15 19:15:24.000000000 +0000 +++ docker.io-1.3.1~dfsg1/volumes/repository.go 2014-10-30 13:44:46.000000000 +0000 @@ -55,6 +55,7 @@ return nil, err } } + path = filepath.Clean(path) path, err = filepath.EvalSymlinks(path) if err != nil { @@ -126,7 +127,7 @@ if err != nil { return nil } - return r.volumes[path] + return r.volumes[filepath.Clean(path)] } func (r *Repository) Add(volume *Volume) error { @@ -160,7 +161,7 @@ if err != nil { return err } - volume := r.get(path) + volume := r.get(filepath.Clean(path)) if volume == nil { return fmt.Errorf("Volume %s does not exist", path) }