[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#768071: marked as done (unblock: docker.io/1.3.1~dfsg1-2)



Your message dated Tue, 11 Nov 2014 08:07:13 +0100
with message-id <5461B5A1.4040701@thykier.net>
and subject line Re: Bug#768071: unblock: docker.io/1.3.1~dfsg1-1
has caused the Debian Bug report #768071,
regarding unblock: docker.io/1.3.1~dfsg1-2
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact owner@bugs.debian.org
immediately.)


-- 
768071: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=768071
Debian Bug Tracking System
Contact owner@bugs.debian.org with problems
--- Begin Message ---
Package: release.debian.org
Severity: normal
User: release.debian.org@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 <admwiggin@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)
 	}

--- End Message ---
--- Begin Message ---
On 2014-11-07 22:22, Tianon Gravi wrote:
> retitle 768071 unblock: docker.io/1.3.1~dfsg1-2
> thanks
> 
> per paultag's instructions (updated debdiff is attached)
> 
> ♥,
> - Tianon
>   4096R / B42F 6819 007F 00F8 8E36  4FD4 036A 9C25 BF35 7DD4
> 

Unblocked, thanks.

I hope we will not need a .2 any time soon... :)

~Niels

--- End Message ---

Reply to: