diff -Nru containerd-1.2.6/debian/changelog containerd-1.2.6/debian/changelog --- containerd-1.2.6/debian/changelog 2019-05-16 03:43:29.000000000 +0000 +++ containerd-1.2.6/debian/changelog 2020-12-11 12:19:07.000000000 +0000 @@ -1,3 +1,42 @@ +containerd (1.2.6-0ubuntu1~16.04.6) xenial; urgency=medium + + * d/control: add a Breaks for docker.io lower than 18.09.7-0ubuntu1~16.04.7 + (LP: #1870514). The previous versions stop the docker daemon when a + containerd update is performed, this Breaks statement will make sure we + have a newer version which has the appropriate fix. + + -- Lucas Kanashiro Fri, 11 Dec 2020 09:19:07 -0300 + +containerd (1.2.6-0ubuntu1~16.04.5) xenial-security; urgency=medium + + * SECURITY UPDATE: Elevation of privilege vulnerability + - debian/patches/Add-runc.v2-multi-shim_partial2.patch: Add runc.v2 + multi-shim (partially). + - debian/patches/horten-the-unix-socket-path-for-shim.patch: horten the + unix socket path for shim. + - debian/patches/Include-extension-for-shim-binary-format-on-Windows.patch: + Include extension for shim binary format. + - debian/patches/CVE-2020-15257.patch: Use path based unix socket for shims + and use path-based unix socket for containerd-shim. + - CVE-2020-15257 + + -- Paulo Flabiano Smorigo Thu, 26 Nov 2020 18:31:47 +0000 + +containerd (1.2.6-0ubuntu1~16.04.4) xenial-security; urgency=medium + + * SECURITY UPDATE: Sensitive information disclosure + - debian/patches/CVE-2020-15157.patch: Improve fetch function and add + tests for it. + - CVE-2020-15157 + + -- Paulo Flabiano Smorigo Wed, 14 Oct 2020 13:21:22 +0000 + +containerd (1.2.6-0ubuntu1~16.04.3) xenial-security; urgency=medium + + * No change rebuild for the -security pocket + + -- Mike Salvatore Wed, 03 Jul 2019 08:06:38 -0400 + containerd (1.2.6-0ubuntu1~16.04.2) xenial; urgency=medium * Increase runc version requirement. diff -Nru containerd-1.2.6/debian/control containerd-1.2.6/debian/control --- containerd-1.2.6/debian/control 2019-05-16 03:42:14.000000000 +0000 +++ containerd-1.2.6/debian/control 2020-12-11 12:19:07.000000000 +0000 @@ -20,7 +20,7 @@ Package: containerd Architecture: linux-any Depends: runc (>= 1.0.0~rc7~), ${misc:Depends}, ${shlibs:Depends} -Breaks: docker.io (<= 18.09.3~) +Breaks: docker.io (<< 18.09.7-0ubuntu1~16.04.7) Built-Using: ${misc:Built-Using} Description: daemon to control runC Containerd is a daemon to control runC, built for performance and density. diff -Nru containerd-1.2.6/debian/patches/Add-runc.v2-multi-shim_partial2.patch containerd-1.2.6/debian/patches/Add-runc.v2-multi-shim_partial2.patch --- containerd-1.2.6/debian/patches/Add-runc.v2-multi-shim_partial2.patch 1970-01-01 00:00:00.000000000 +0000 +++ containerd-1.2.6/debian/patches/Add-runc.v2-multi-shim_partial2.patch 2020-12-11 12:19:07.000000000 +0000 @@ -0,0 +1,43 @@ +From 84a24711e885f10b50a2e84b756e25d126ac47af Mon Sep 17 00:00:00 2001 +From: Michael Crosby +Date: Fri, 8 Feb 2019 15:01:15 -0500 +Subject: [PATCH] Add runc.v2 multi-shim + +Signed-off-by: Michael Crosby +--- + runtime/v2/shim/util.go | 20 + + 22 files changed, 978 insertions(+), 92 deletions(-) + +--- containerd-1.2.6.orig/runtime/v2/shim/util.go ++++ containerd-1.2.6/runtime/v2/shim/util.go +@@ -19,6 +19,7 @@ package shim + import ( + "context" + "fmt" ++ "io/ioutil" + "net" + "os" + "os/exec" +@@ -126,3 +127,22 @@ func WriteAddress(path, address string) + } + return os.Rename(tempPath, path) + } ++ ++// ErrNoAddress is returned when the address file has no content ++var ErrNoAddress = errors.New("no shim address") ++ ++// ReadAddress returns the shim's abstract socket address from the path ++func ReadAddress(path string) (string, error) { ++ path, err := filepath.Abs(path) ++ if err != nil { ++ return "", err ++ } ++ data, err := ioutil.ReadFile(path) ++ if err != nil { ++ return "", err ++ } ++ if len(data) == 0 { ++ return "", ErrNoAddress ++ } ++ return string(data), nil ++} diff -Nru containerd-1.2.6/debian/patches/CVE-2020-15157.patch containerd-1.2.6/debian/patches/CVE-2020-15157.patch --- containerd-1.2.6/debian/patches/CVE-2020-15157.patch 1970-01-01 00:00:00.000000000 +0000 +++ containerd-1.2.6/debian/patches/CVE-2020-15157.patch 2020-12-11 12:19:07.000000000 +0000 @@ -0,0 +1,194 @@ +Description: Improve fetch function and add tests for it. +Origin: https://lists.cncf.io/g/containerd-security-announce/files/1.2-cve-2020-15157.patch +--- containerd-1.2.6.orig/remotes/docker/fetcher.go ++++ containerd-1.2.6/remotes/docker/fetcher.go +@@ -56,6 +56,23 @@ func (r dockerFetcher) Fetch(ctx context + } + + return newHTTPReadSeeker(desc.Size, func(offset int64) (io.ReadCloser, error) { ++ if len(desc.URLs) > 0 { ++ db := *r.dockerBase ++ db.auth = nil // do not authenticate ++ nr := dockerFetcher{ ++ dockerBase: &db, ++ } ++ for _, u := range desc.URLs { ++ log.G(ctx).WithField("url", u).Debug("trying alternative url") ++ rc, err := nr.open(ctx, u, desc.MediaType, offset) ++ if err != nil { ++ log.G(ctx).WithField("error", err).Debug("error trying url") ++ continue // try one of the other urls. ++ } ++ ++ return rc, nil ++ } ++ } + for _, u := range urls { + rc, err := r.open(ctx, u, desc.MediaType, offset) + if err != nil { +@@ -142,14 +159,6 @@ func (r dockerFetcher) open(ctx context. + func (r *dockerFetcher) getV2URLPaths(ctx context.Context, desc ocispec.Descriptor) ([]string, error) { + var urls []string + +- if len(desc.URLs) > 0 { +- // handle fetch via external urls. +- for _, u := range desc.URLs { +- log.G(ctx).WithField("url", u).Debug("adding alternative url") +- urls = append(urls, u) +- } +- } +- + switch desc.MediaType { + case images.MediaTypeDockerSchema2Manifest, images.MediaTypeDockerSchema2ManifestList, + images.MediaTypeDockerSchema1Manifest, +--- containerd-1.2.6.orig/remotes/docker/fetcher_test.go ++++ containerd-1.2.6/remotes/docker/fetcher_test.go +@@ -23,7 +23,12 @@ import ( + "math/rand" + "net/http" + "net/http/httptest" ++ "net/url" + "testing" ++ ++ "github.com/containerd/containerd/images" ++ digest "github.com/opencontainers/go-digest" ++ ocispec "github.com/opencontainers/image-spec/specs-go/v1" + ) + + func TestFetcherOpen(t *testing.T) { +@@ -92,3 +97,135 @@ func TestFetcherOpen(t *testing.T) { + t.Fatal("expected error opening with invalid server response") + } + } ++ ++func TestFetcherFetch(t *testing.T) { ++ content := make([]byte, 128) ++ rand.New(rand.NewSource(1)).Read(content) ++ start := 0 ++ ++ s := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { ++ t.Helper() ++ ++ if r.RequestURI == "/404" { ++ // no authorization must be provided with the initial GET ++ if r.Header["Authorization"] != nil { ++ t.Errorf("no authorization can be used with manifest-specified URLs") ++ return ++ } ++ ++ rw.WriteHeader(http.StatusNotFound) ++ return ++ } ++ ++ if r.RequestURI == "/401" { ++ if r.Header["Authorization"] == nil { ++ rw.Header().Set("Docker-Distribution-Api-Version", "registry/2.0") ++ rw.Header().Set("WWW-Authenticate", "Basic realm=\"https://url\"") ++ rw.WriteHeader(http.StatusUnauthorized) ++ return ++ } ++ ++ // no authorization must be provided for manifest-defined URLs ++ t.Errorf("no authorization can be used with manifest-specified URLs") ++ return ++ } ++ ++ if r.Header["Authorization"] == nil { ++ rw.Header().Set("Docker-Distribution-Api-Version", "registry/2.0") ++ rw.Header().Set("WWW-Authenticate", "Basic realm=\"https://url\"") ++ rw.WriteHeader(http.StatusUnauthorized) ++ return ++ } ++ ++ // authorizer must set Authorize header for the manifest URL ++ if start > 0 { ++ rw.Header().Set("content-range", fmt.Sprintf("bytes %d-127/128", start)) ++ } ++ rw.Header().Set("content-length", fmt.Sprintf("%d", len(content[start:]))) ++ rw.Write(content[start:]) ++ })) ++ defer s.Close() ++ ++ baseURL, _ := url.Parse(s.URL) ++ db := &dockerBase{ ++ client: s.Client(), ++ base: *baseURL, ++ } ++ db.auth = NewAuthorizer(db.client, func(a string) (string, string, error) { ++ return "Authorize", "Basic blah", nil ++ }) ++ ++ f := dockerFetcher{dockerBase: db} ++ ++ ctx := context.Background() ++ ++ desc := ocispec.Descriptor{ ++ MediaType: images.MediaTypeDockerSchema2Manifest, ++ Digest: digest.FromBytes([]byte("digest")), ++ Size: 10, ++ URLs: []string{fmt.Sprintf("%s/404", s.URL), fmt.Sprintf("%s/401", s.URL)}, ++ Annotations: map[string]string{}, ++ } ++ ++ rc, err := f.Fetch(ctx, desc) ++ if err != nil { ++ t.Fatalf("failed to open: %+v", err) ++ } ++ b, err := ioutil.ReadAll(rc) ++ if err != nil { ++ t.Fatal(err) ++ } ++ expected := content[0:] ++ if len(b) != len(expected) { ++ t.Errorf("unexpected length %d, expected %d", len(b), len(expected)) ++ return ++ } ++ for i, c := range expected { ++ if b[i] != c { ++ t.Errorf("unexpected byte %x at %d, expected %x", b[i], i, c) ++ return ++ } ++ } ++} ++ ++func TestFetcherGetV2URLPaths(t *testing.T) { ++ content := make([]byte, 128) ++ rand.New(rand.NewSource(1)).Read(content) ++ start := 0 ++ ++ s := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { ++ if start > 0 { ++ rw.Header().Set("content-range", fmt.Sprintf("bytes %d-127/128", start)) ++ } ++ rw.Header().Set("content-length", fmt.Sprintf("%d", len(content[start:]))) ++ rw.Write(content[start:]) ++ })) ++ defer s.Close() ++ ++ f := dockerFetcher{&dockerBase{ ++ client: s.Client(), ++ }} ++ ctx := context.Background() ++ ++ desc := ocispec.Descriptor{ ++ MediaType: images.MediaTypeDockerSchema2Manifest, ++ Digest: "digest", ++ Size: 10, ++ URLs: []string{"first", "second"}, ++ Annotations: map[string]string{}, ++ } ++ ++ urls, err := f.getV2URLPaths(ctx, desc) ++ ++ if err != nil { ++ t.Errorf("unexpected error %v", err) ++ return ++ } ++ ++ // blobs and manifest/digest ++ // URLs from the descriptor should not be added to the list of alternative sources ++ if len(urls) != 2 { ++ t.Errorf("unexpected number of urls: %d, expected %d", len(urls), 2) ++ return ++ } ++} diff -Nru containerd-1.2.6/debian/patches/CVE-2020-15257.patch containerd-1.2.6/debian/patches/CVE-2020-15257.patch --- containerd-1.2.6/debian/patches/CVE-2020-15257.patch 1970-01-01 00:00:00.000000000 +0000 +++ containerd-1.2.6/debian/patches/CVE-2020-15257.patch 2020-12-11 12:19:07.000000000 +0000 @@ -0,0 +1,511 @@ +From 88bf5aa76a1422859fc3c1d4547710d3d378c0eb Mon Sep 17 00:00:00 2001 +From: Samuel Karp +Date: Fri, 13 Nov 2020 13:42:09 -0800 +Subject: [PATCH] [containerd 1.3] Fix CVE-2020-15257 + +----------------------------------------------------------------------- + +Use path based unix socket for shims + +This allows filesystem based ACLs for configuring access to the socket of a +shim. + +Co-authored-by: Samuel Karp +Signed-off-by: Samuel Karp +Signed-off-by: Michael Crosby +Signed-off-by: Michael Crosby + +----------------------------------------------------------------------- + +containerd-shim: use path-based unix socket + +This allows filesystem-based ACLs for configuring access to the socket +of a shim. + +Ported from Michael Crosby's similar patch for v2 shims. + +Signed-off-by: Samuel Karp +--- + cmd/containerd-shim/main_unix.go | 16 ++++-- + cmd/ctr/commands/shim/shim.go | 8 ++- + runtime/v1/linux/bundle.go | 15 +++-- + runtime/v1/shim/client/client.go | 94 ++++++++++++++++++++++++++---- + runtime/v2/runc/v1/service.go | 18 ++++-- + runtime/v2/runc/v2/service.go | 47 +++++++++++---- + runtime/v2/shim/shim.go | 9 ++- + runtime/v2/shim/shim_unix.go | 8 +-- + runtime/v2/shim/util.go | 2 +- + runtime/v2/shim/util_unix.go | 98 ++++++++++++++++++++++++++++---- + runtime/v2/shim/util_windows.go | 6 ++ + 11 files changed, 263 insertions(+), 58 deletions(-) + +--- containerd-1.2.6.orig/cmd/containerd-shim/main_unix.go ++++ containerd-1.2.6/cmd/containerd-shim/main_unix.go +@@ -62,7 +62,7 @@ var ( + func init() { + flag.BoolVar(&debugFlag, "debug", false, "enable debug output in logs") + flag.StringVar(&namespaceFlag, "namespace", "", "namespace that owns the shim") +- flag.StringVar(&socketFlag, "socket", "", "abstract socket path to serve") ++ flag.StringVar(&socketFlag, "socket", "", "socket path to serve") + flag.StringVar(&addressFlag, "address", "", "grpc address back to main containerd") + flag.StringVar(&workdirFlag, "workdir", "", "path used to storge large temporary data") + flag.StringVar(&runtimeRootFlag, "runtime-root", proc.RuncRoot, "root directory for the runtime") +@@ -161,10 +161,18 @@ func serve(ctx context.Context, server * + l, err = net.FileListener(os.NewFile(3, "socket")) + path = "[inherited from parent]" + } else { +- if len(path) > 106 { +- return errors.Errorf("%q: unix socket path too long (> 106)", path) ++ const ( ++ abstractSocketPrefix = "\x00" ++ socketPathLimit = 106 ++ ) ++ p := strings.TrimPrefix(path, "unix://") ++ if len(p) == len(path) { ++ p = abstractSocketPrefix + p + } +- l, err = net.Listen("unix", "\x00"+path) ++ if len(p) > socketPathLimit { ++ return errors.Errorf("%q: unix socket path too long (> %d)", p, socketPathLimit) ++ } ++ l, err = net.Listen("unix", p) + } + if err != nil { + return err +--- containerd-1.2.6.orig/runtime/v1/linux/bundle.go ++++ containerd-1.2.6/runtime/v1/linux/bundle.go +@@ -90,7 +90,7 @@ func ShimRemote(c *Config, daemonAddress + return func(b *bundle, ns string, ropts *runctypes.RuncOptions) (shim.Config, client.Opt) { + config := b.shimConfig(ns, c, ropts) + return config, +- client.WithStart(c.Shim, b.shimAddress(ns), daemonAddress, cgroup, c.ShimDebug, exitHandler) ++ client.WithStart(c.Shim, b.shimAddress(ns, daemonAddress), daemonAddress, cgroup, c.ShimDebug, exitHandler) + } + } + +@@ -116,6 +116,11 @@ func (b *bundle) NewShimClient(ctx conte + + // Delete deletes the bundle from disk + func (b *bundle) Delete() error { ++ address, _ := b.loadAddress() ++ if address != "" { ++ // we don't care about errors here ++ client.RemoveSocket(address) ++ } + err := os.RemoveAll(b.path) + if err == nil { + return os.RemoveAll(b.workDir) +@@ -132,9 +137,11 @@ func (b *bundle) legacyShimAddress(names + return filepath.Join(string(filepath.Separator), "containerd-shim", namespace, b.id, "shim.sock") + } + +-func (b *bundle) shimAddress(namespace string) string { +- d := sha256.Sum256([]byte(filepath.Join(namespace, b.id))) +- return filepath.Join(string(filepath.Separator), "containerd-shim", fmt.Sprintf("%x.sock", d)) ++const socketRoot = "/run/containerd" ++ ++func (b *bundle) shimAddress(namespace, socketPath string) string { ++ d := sha256.Sum256([]byte(filepath.Join(socketPath, namespace, b.id))) ++ return fmt.Sprintf("unix://%s/%x", filepath.Join(socketRoot, "s"), d) + } + + func (b *bundle) loadAddress() (string, error) { +--- containerd-1.2.6.orig/runtime/v1/shim/client/client.go ++++ containerd-1.2.6/runtime/v1/shim/client/client.go +@@ -55,9 +55,17 @@ func WithStart(binary, address, daemonAd + return func(ctx context.Context, config shim.Config) (_ shimapi.ShimService, _ io.Closer, err error) { + socket, err := newSocket(address) + if err != nil { +- return nil, nil, err ++ if !eaddrinuse(err) { ++ return nil, nil, err ++ } ++ if err := RemoveSocket(address); err != nil { ++ return nil, nil, errors.Wrap(err, "remove already used socket") ++ } ++ if socket, err = newSocket(address); err != nil { ++ return nil, nil, err ++ } + } +- defer socket.Close() ++ + f, err := socket.File() + if err != nil { + return nil, nil, errors.Wrapf(err, "failed to get fd for socket %s", address) +@@ -75,6 +83,8 @@ func WithStart(binary, address, daemonAd + if err != nil { + cmd.Process.Kill() + } ++ socket.Close() ++ RemoveSocket(address) + }() + go func() { + cmd.Wait() +@@ -110,6 +120,26 @@ func WithStart(binary, address, daemonAd + } + } + ++func eaddrinuse(err error) bool { ++ cause := errors.Cause(err) ++ netErr, ok := cause.(*net.OpError) ++ if !ok { ++ return false ++ } ++ if netErr.Op != "listen" { ++ return false ++ } ++ syscallErr, ok := netErr.Err.(*os.SyscallError) ++ if !ok { ++ return false ++ } ++ errno, ok := syscallErr.Err.(syscall.Errno) ++ if !ok { ++ return false ++ } ++ return errno == syscall.EADDRINUSE ++} ++ + func newCommand(binary, daemonAddress string, debug bool, config shim.Config, socket *os.File) (*exec.Cmd, error) { + selfExe, err := os.Executable() + if err != nil { +@@ -169,31 +199,73 @@ func writeAddress(path, address string) + return os.Rename(tempPath, path) + } + ++const ( ++ abstractSocketPrefix = "\x00" ++ socketPathLimit = 106 ++) ++ ++type socket string ++ ++func (s socket) isAbstract() bool { ++ return !strings.HasPrefix(string(s), "unix://") ++} ++ ++func (s socket) path() string { ++ path := strings.TrimPrefix(string(s), "unix://") ++ // if there was no trim performed, we assume an abstract socket ++ if len(path) == len(s) { ++ path = abstractSocketPrefix + path ++ } ++ return path ++} ++ + func newSocket(address string) (*net.UnixListener, error) { +- if len(address) > 106 { +- return nil, errors.Errorf("%q: unix socket path too long (> 106)", address) ++ if len(address) > socketPathLimit { ++ return nil, errors.Errorf("%q: unix socket path too long (> %d)", address, socketPathLimit) ++ } ++ var ( ++ sock = socket(address) ++ path = sock.path() ++ ) ++ if !sock.isAbstract() { ++ if err := os.MkdirAll(filepath.Dir(path), 0600); err != nil { ++ return nil, errors.Wrapf(err, "%s", path) ++ } + } +- l, err := net.Listen("unix", "\x00"+address) ++ l, err := net.Listen("unix", path) + if err != nil { +- return nil, errors.Wrapf(err, "failed to listen to abstract unix socket %q", address) ++ return nil, errors.Wrapf(err, "failed to listen to unix socket %q (abstract: %t)", address, sock.isAbstract()) ++ } ++ if err := os.Chmod(path, 0600); err != nil { ++ l.Close() ++ return nil, err + } + + return l.(*net.UnixListener), nil + } + ++// RemoveSocket removes the socket at the specified address if ++// it exists on the filesystem ++func RemoveSocket(address string) error { ++ sock := socket(address) ++ if !sock.isAbstract() { ++ return os.Remove(sock.path()) ++ } ++ return nil ++} ++ + func connect(address string, d func(string, time.Duration) (net.Conn, error)) (net.Conn, error) { + return d(address, 100*time.Second) + } + +-func annonDialer(address string, timeout time.Duration) (net.Conn, error) { +- address = strings.TrimPrefix(address, "unix://") +- return net.DialTimeout("unix", "\x00"+address, timeout) ++func anonDialer(address string, timeout time.Duration) (net.Conn, error) { ++ return net.DialTimeout("unix", socket(address).path(), timeout) + } + + // WithConnect connects to an existing shim + func WithConnect(address string, onClose func()) Opt { + return func(ctx context.Context, config shim.Config) (shimapi.ShimService, io.Closer, error) { +- conn, err := connect(address, annonDialer) ++ conn, err := connect(address, anonDialer) + if err != nil { + return nil, nil, err + } +--- containerd-1.2.6.orig/runtime/v2/shim/shim.go ++++ containerd-1.2.6/runtime/v2/shim/shim.go +@@ -77,7 +77,7 @@ func parseFlags() { + flag.BoolVar(&debugFlag, "debug", false, "enable debug output in logs") + flag.StringVar(&namespaceFlag, "namespace", "", "namespace that owns the shim") + flag.StringVar(&idFlag, "id", "", "id of the task") +- flag.StringVar(&socketFlag, "socket", "", "abstract socket path to serve") ++ flag.StringVar(&socketFlag, "socket", "", "socket path to serve") + flag.StringVar(&bundlePath, "bundle", "", "path to the bundle if not workdir") + + flag.StringVar(&addressFlag, "address", "", "grpc address back to main containerd") +@@ -239,11 +239,15 @@ func serve(ctx context.Context, server * + return err + } + go func() { +- defer l.Close() + if err := server.Serve(ctx, l); err != nil && + !strings.Contains(err.Error(), "use of closed network connection") { + logrus.WithError(err).Fatal("containerd-shim: ttrpc server failure") + } ++ l.Close() ++ if address, err := ReadAddress("address"); err == nil { ++ _ = RemoveSocket(address) ++ } ++ + }() + return nil + } +--- containerd-1.2.6.orig/runtime/v2/shim/shim_unix.go ++++ containerd-1.2.6/runtime/v2/shim/shim_unix.go +@@ -58,15 +58,15 @@ func serveListener(path string) (net.Lis + l, err = net.FileListener(os.NewFile(3, "socket")) + path = "[inherited from parent]" + } else { +- if len(path) > 106 { +- return nil, errors.Errorf("%q: unix socket path too long (> 106)", path) ++ if len(path) > socketPathLimit { ++ return nil, errors.Errorf("%q: unix socket path too long (> %d)", path, socketPathLimit) + } +- l, err = net.Listen("unix", "\x00"+path) ++ l, err = net.Listen("unix", path) + } + if err != nil { + return nil, err + } +- logrus.WithField("socket", path).Debug("serving api on abstract socket") ++ logrus.WithField("socket", path).Debug("serving api on socket") + return l, nil + } + +--- containerd-1.2.6.orig/runtime/v2/shim/util.go ++++ containerd-1.2.6/runtime/v2/shim/util.go +@@ -129,7 +129,7 @@ func WriteAddress(path, address string) + // ErrNoAddress is returned when the address file has no content + var ErrNoAddress = errors.New("no shim address") + +-// ReadAddress returns the shim's abstract socket address from the path ++// ReadAddress returns the shim's socket address from the path + func ReadAddress(path string) (string, error) { + path, err := filepath.Abs(path) + if err != nil { +--- containerd-1.2.6.orig/runtime/v2/shim/util_unix.go ++++ containerd-1.2.6/runtime/v2/shim/util_unix.go +@@ -23,6 +23,7 @@ import ( + "crypto/sha256" + "fmt" + "net" ++ "os" + "path/filepath" + "strings" + "syscall" +@@ -33,7 +34,10 @@ import ( + "github.com/pkg/errors" + ) + +-const shimBinaryFormat = "containerd-shim-%s-%s" ++const ( ++ shimBinaryFormat = "containerd-shim-%s-%s" ++ socketPathLimit = 106 ++) + + func getSysProcAttr() *syscall.SysProcAttr { + return &syscall.SysProcAttr{ +@@ -46,30 +50,101 @@ func SetScore(pid int) error { + return sys.SetOOMScore(pid, sys.OOMScoreMaxKillable) + } + +-// SocketAddress returns an abstract socket address +-func SocketAddress(ctx context.Context, id string) (string, error) { ++const socketRoot = "/run/containerd" ++ ++// SocketAddress returns a socket address ++func SocketAddress(ctx context.Context, socketPath, id string) (string, error) { + ns, err := namespaces.NamespaceRequired(ctx) + if err != nil { + return "", err + } +- d := sha256.Sum256([]byte(filepath.Join(ns, id))) +- return filepath.Join(string(filepath.Separator), "containerd-shim", fmt.Sprintf("%x.sock", d)), nil ++ d := sha256.Sum256([]byte(filepath.Join(socketPath, ns, id))) ++ return fmt.Sprintf("unix://%s/%x", filepath.Join(socketRoot, "s"), d), nil + } + +-// AnonDialer returns a dialer for an abstract socket ++// AnonDialer returns a dialer for a socket + func AnonDialer(address string, timeout time.Duration) (net.Conn, error) { +- address = strings.TrimPrefix(address, "unix://") +- return net.DialTimeout("unix", "\x00"+address, timeout) ++ return net.DialTimeout("unix", socket(address).path(), timeout) + } + + // NewSocket returns a new socket + func NewSocket(address string) (*net.UnixListener, error) { +- if len(address) > 106 { +- return nil, errors.Errorf("%q: unix socket path too long (> 106)", address) ++ var ( ++ sock = socket(address) ++ path = sock.path() ++ ) ++ if !sock.isAbstract() { ++ if err := os.MkdirAll(filepath.Dir(path), 0600); err != nil { ++ return nil, errors.Wrapf(err, "%s", path) ++ } + } +- l, err := net.Listen("unix", "\x00"+address) ++ l, err := net.Listen("unix", path) + if err != nil { +- return nil, errors.Wrapf(err, "failed to listen to abstract unix socket %q", address) ++ return nil, err ++ } ++ if err := os.Chmod(path, 0600); err != nil { ++ os.Remove(sock.path()) ++ l.Close() ++ return nil, err + } + return l.(*net.UnixListener), nil + } ++ ++const abstractSocketPrefix = "\x00" ++ ++type socket string ++ ++func (s socket) isAbstract() bool { ++ return !strings.HasPrefix(string(s), "unix://") ++} ++ ++func (s socket) path() string { ++ path := strings.TrimPrefix(string(s), "unix://") ++ // if there was no trim performed, we assume an abstract socket ++ if len(path) == len(s) { ++ path = abstractSocketPrefix + path ++ } ++ return path ++} ++ ++// RemoveSocket removes the socket at the specified address if ++// it exists on the filesystem ++func RemoveSocket(address string) error { ++ sock := socket(address) ++ if !sock.isAbstract() { ++ return os.Remove(sock.path()) ++ } ++ return nil ++} ++ ++// SocketEaddrinuse returns true if the provided error is caused by the ++// EADDRINUSE error number ++func SocketEaddrinuse(err error) bool { ++ netErr, ok := err.(*net.OpError) ++ if !ok { ++ return false ++ } ++ if netErr.Op != "listen" { ++ return false ++ } ++ syscallErr, ok := netErr.Err.(*os.SyscallError) ++ if !ok { ++ return false ++ } ++ errno, ok := syscallErr.Err.(syscall.Errno) ++ if !ok { ++ return false ++ } ++ return errno == syscall.EADDRINUSE ++} ++ ++// CanConnect returns true if the socket provided at the address ++// is accepting new connections ++func CanConnect(address string) bool { ++ conn, err := AnonDialer(address, 100*time.Millisecond) ++ if err != nil { ++ return false ++ } ++ conn.Close() ++ return true ++} +--- containerd-1.2.6.orig/runtime/v2/shim/util_windows.go ++++ containerd-1.2.6/runtime/v2/shim/util_windows.go +@@ -90,3 +90,9 @@ func NewSocket(address string) (net.List + } + return l, nil + } ++ ++// RemoveSocket removes the socket at the specified address if ++// it exists on the filesystem ++func RemoveSocket(address string) error { ++ return nil ++} +--- containerd-1.2.6.orig/runtime/v2/runc/service.go ++++ containerd-1.2.6/runtime/v2/runc/service.go +@@ -142,20 +142,26 @@ func (s *service) StartShim(ctx context. + if err != nil { + return "", err + } +- address, err := shim.SocketAddress(ctx, id) ++ address, err := shim.SocketAddress(ctx, containerdAddress, id) + if err != nil { + return "", err + } + socket, err := shim.NewSocket(address) + if err != nil { +- return "", err ++ if !shim.SocketEaddrinuse(err) { ++ return "", err ++ } ++ if err := shim.RemoveSocket(address); err != nil { ++ return "", errors.Wrap(err, "remove already used socket") ++ } ++ if socket, err = shim.NewSocket(address); err != nil { ++ return "", err ++ } + } +- defer socket.Close() + f, err := socket.File() + if err != nil { + return "", err + } +- defer f.Close() + + cmd.ExtraFiles = append(cmd.ExtraFiles, f) + +@@ -164,6 +170,7 @@ func (s *service) StartShim(ctx context. + } + defer func() { + if err != nil { ++ _ = shim.RemoveSocket(address) + cmd.Process.Kill() + } + }() +@@ -581,6 +588,9 @@ func (s *service) Connect(ctx context.Co + + func (s *service) Shutdown(ctx context.Context, r *taskAPI.ShutdownRequest) (*ptypes.Empty, error) { + s.cancel() ++ if address, err := shim.ReadAddress("address"); err == nil { ++ _ = shim.RemoveSocket(address) ++ } + os.Exit(0) + return empty, nil + } diff -Nru containerd-1.2.6/debian/patches/horten-the-unix-socket-path-for-shim.patch containerd-1.2.6/debian/patches/horten-the-unix-socket-path-for-shim.patch --- containerd-1.2.6/debian/patches/horten-the-unix-socket-path-for-shim.patch 1970-01-01 00:00:00.000000000 +0000 +++ containerd-1.2.6/debian/patches/horten-the-unix-socket-path-for-shim.patch 2020-12-11 12:19:07.000000000 +0000 @@ -0,0 +1,220 @@ +From a631796fda6208cf46dea94da091bb550f8ea9f4 Mon Sep 17 00:00:00 2001 +From: Eric Lin +Date: Fri, 22 Feb 2019 15:56:08 +0800 +Subject: [PATCH] horten the unix socket path for shim + +Use sha256 hash to shorten the unix socket path to satisfy the +length limitation of abstract socket path + +This commit also backports the feature storing address path to +a file from v2 to keep compatibility + +Fixes #3032 + +Signed-off-by: Eric Lin +--- + container_test.go | 58 ++++++++++++++++++++++++++++++++ + runtime/v1/linux/bundle.go | 27 +++++++++++++-- + runtime/v1/shim/client/client.go | 25 ++++++++++++++ + runtime/v2/shim/util_unix.go | 5 ++- + 4 files changed, 112 insertions(+), 3 deletions(-) + +--- containerd-1.2.6.orig/container_test.go ++++ containerd-1.2.6/container_test.go +@@ -32,7 +32,9 @@ import ( + // Register the typeurl + "github.com/containerd/containerd/cio" + "github.com/containerd/containerd/containers" ++ "github.com/containerd/containerd/namespaces" + "github.com/containerd/containerd/oci" ++ "github.com/containerd/containerd/platforms" + _ "github.com/containerd/containerd/runtime" + "github.com/containerd/typeurl" + specs "github.com/opencontainers/runtime-spec/specs-go" +@@ -1528,3 +1530,59 @@ func TestContainerHook(t *testing.T) { + } + defer task.Delete(ctx, WithProcessKill) + } ++ ++func TestShimSockLength(t *testing.T) { ++ t.Parallel() ++ ++ // Max length of namespace should be 76 ++ namespace := strings.Repeat("n", 76) ++ ++ ctx, cancel := context.WithCancel(context.Background()) ++ defer cancel() ++ ++ ctx = namespaces.WithNamespace(ctx, namespace) ++ ++ client, err := newClient(t, address) ++ if err != nil { ++ t.Fatal(err) ++ } ++ defer client.Close() ++ ++ image, err := client.Pull(ctx, testImage, ++ WithPlatformMatcher(platforms.Default()), ++ WithPullUnpack, ++ ) ++ if err != nil { ++ t.Fatal(err) ++ } ++ ++ id := strings.Repeat("c", 64) ++ ++ // We don't have limitation with length of container name, ++ // but 64 bytes of sha256 is the common case ++ container, err := client.NewContainer(ctx, id, ++ WithNewSnapshot(id, image), ++ WithNewSpec(oci.WithImageConfig(image), withExitStatus(0)), ++ ) ++ if err != nil { ++ t.Fatal(err) ++ } ++ defer container.Delete(ctx, WithSnapshotCleanup) ++ ++ task, err := container.NewTask(ctx, empty()) ++ if err != nil { ++ t.Fatal(err) ++ } ++ defer task.Delete(ctx) ++ ++ statusC, err := task.Wait(ctx) ++ if err != nil { ++ t.Fatal(err) ++ } ++ ++ if err := task.Start(ctx); err != nil { ++ t.Fatal(err) ++ } ++ ++ <-statusC ++} +--- containerd-1.2.6.orig/runtime/v1/linux/bundle.go ++++ containerd-1.2.6/runtime/v1/linux/bundle.go +@@ -20,6 +20,8 @@ package linux + + import ( + "context" ++ "fmt" ++ "crypto/sha256" + "io/ioutil" + "os" + "path/filepath" +@@ -102,7 +104,7 @@ func ShimLocal(c *Config, exchange *exch + // ShimConnect is a ShimOpt for connecting to an existing remote shim + func ShimConnect(c *Config, onClose func()) ShimOpt { + return func(b *bundle, ns string, ropts *runctypes.RuncOptions) (shim.Config, client.Opt) { +- return b.shimConfig(ns, c, ropts), client.WithConnect(b.shimAddress(ns), onClose) ++ return b.shimConfig(ns, c, ropts), client.WithConnect(b.decideShimAddress(ns), onClose) + } + } + +@@ -126,10 +128,32 @@ func (b *bundle) Delete() error { + return errors.Wrapf(err, "Failed to remove both bundle and workdir locations: %v", err2) + } + +-func (b *bundle) shimAddress(namespace string) string { ++func (b *bundle) legacyShimAddress(namespace string) string { + return filepath.Join(string(filepath.Separator), "containerd-shim", namespace, b.id, "shim.sock") + } + ++func (b *bundle) shimAddress(namespace string) string { ++ d := sha256.Sum256([]byte(filepath.Join(namespace, b.id))) ++ return filepath.Join(string(filepath.Separator), "containerd-shim", fmt.Sprintf("%x.sock", d)) ++} ++ ++func (b *bundle) loadAddress() (string, error) { ++ addressPath := filepath.Join(b.path, "address") ++ data, err := ioutil.ReadFile(addressPath) ++ if err != nil { ++ return "", err ++ } ++ return string(data), nil ++} ++ ++func (b *bundle) decideShimAddress(namespace string) string { ++ address, err := b.loadAddress() ++ if err != nil { ++ return b.legacyShimAddress(namespace) ++ } ++ return address ++} ++ + func (b *bundle) shimConfig(namespace string, c *Config, runcOptions *runctypes.RuncOptions) shim.Config { + var ( + criuPath string +--- containerd-1.2.6.orig/runtime/v1/shim/client/client.go ++++ containerd-1.2.6/runtime/v1/shim/client/client.go +@@ -20,10 +20,12 @@ package client + + import ( + "context" ++ "fmt" + "io" + "net" + "os" + "os/exec" ++ "path/filepath" + "strings" + "sync" + "syscall" +@@ -83,6 +85,10 @@ func WithStart(binary, address, daemonAd + "address": address, + "debug": debug, + }).Infof("shim %s started", binary) ++ ++ if err := writeAddress(filepath.Join(config.Path, "address"), address); err != nil { ++ return nil, nil, err ++ } + // set shim in cgroup if it is provided + if cgroup != "" { + if err := setCgroup(cgroup, cmd); err != nil { +@@ -144,6 +150,25 @@ func newCommand(binary, daemonAddress st + return cmd, nil + } + ++// writeAddress writes a address file atomically ++func writeAddress(path, address string) error { ++ path, err := filepath.Abs(path) ++ if err != nil { ++ return err ++ } ++ tempPath := filepath.Join(filepath.Dir(path), fmt.Sprintf(".%s", filepath.Base(path))) ++ f, err := os.OpenFile(tempPath, os.O_RDWR|os.O_CREATE|os.O_EXCL|os.O_SYNC, 0666) ++ if err != nil { ++ return err ++ } ++ _, err = f.WriteString(address) ++ f.Close() ++ if err != nil { ++ return err ++ } ++ return os.Rename(tempPath, path) ++} ++ + func newSocket(address string) (*net.UnixListener, error) { + if len(address) > 106 { + return nil, errors.Errorf("%q: unix socket path too long (> 106)", address) +--- containerd-1.2.6.orig/runtime/v2/shim/util_unix.go ++++ containerd-1.2.6/runtime/v2/shim/util_unix.go +@@ -20,6 +20,8 @@ package shim + + import ( + "context" ++ "crypto/sha256" ++ "fmt" + "net" + "path/filepath" + "strings" +@@ -48,7 +50,8 @@ func SocketAddress(ctx context.Context, + if err != nil { + return "", err + } +- return filepath.Join(string(filepath.Separator), "containerd-shim", ns, id, "shim.sock"), nil ++ d := sha256.Sum256([]byte(filepath.Join(ns, id))) ++ return filepath.Join(string(filepath.Separator), "containerd-shim", fmt.Sprintf("%x.sock", d)), nil + } + + // AnonDialer returns a dialer for an abstract socket diff -Nru containerd-1.2.6/debian/patches/Include-extension-for-shim-binary-format-on-Windows.patch containerd-1.2.6/debian/patches/Include-extension-for-shim-binary-format-on-Windows.patch --- containerd-1.2.6/debian/patches/Include-extension-for-shim-binary-format-on-Windows.patch 1970-01-01 00:00:00.000000000 +0000 +++ containerd-1.2.6/debian/patches/Include-extension-for-shim-binary-format-on-Windows.patch 2020-12-11 12:19:07.000000000 +0000 @@ -0,0 +1,48 @@ +From 764afa0d180f5dbcf870350eb926745bde4a53dd Mon Sep 17 00:00:00 2001 +From: "Justin Terry (VM)" +Date: Mon, 28 Jan 2019 12:41:50 -0800 +Subject: [PATCH] Include extension for shim binary format on Windows + +Use full name including extension for shim binary format on Windows in order to +match any stat path faster without a fallback. + +Signed-off-by: Justin Terry (VM) +--- + runtime/v2/shim/util.go | 2 -- + runtime/v2/shim/util_unix.go | 2 ++ + runtime/v2/shim/util_windows.go | 2 ++ + 3 files changed, 4 insertions(+), 2 deletions(-) + +--- containerd-1.2.6.orig/runtime/v2/shim/util.go ++++ containerd-1.2.6/runtime/v2/shim/util.go +@@ -31,8 +31,6 @@ import ( + "github.com/pkg/errors" + ) + +-const shimBinaryFormat = "containerd-shim-%s-%s" +- + // Command returns the shim command with the provided args and configuration + func Command(ctx context.Context, runtime, containerdAddress, path string, cmdArgs ...string) (*exec.Cmd, error) { + ns, err := namespaces.NamespaceRequired(ctx) +--- containerd-1.2.6.orig/runtime/v2/shim/util_unix.go ++++ containerd-1.2.6/runtime/v2/shim/util_unix.go +@@ -33,6 +33,8 @@ import ( + "github.com/pkg/errors" + ) + ++const shimBinaryFormat = "containerd-shim-%s-%s" ++ + func getSysProcAttr() *syscall.SysProcAttr { + return &syscall.SysProcAttr{ + Setpgid: true, +--- containerd-1.2.6.orig/runtime/v2/shim/util_windows.go ++++ containerd-1.2.6/runtime/v2/shim/util_windows.go +@@ -29,6 +29,8 @@ import ( + "github.com/pkg/errors" + ) + ++const shimBinaryFormat = "containerd-shim-%s-%s.exe" ++ + func getSysProcAttr() *syscall.SysProcAttr { + return nil + } diff -Nru containerd-1.2.6/debian/patches/series containerd-1.2.6/debian/patches/series --- containerd-1.2.6/debian/patches/series 2019-04-16 01:50:07.000000000 +0000 +++ containerd-1.2.6/debian/patches/series 2020-12-11 12:19:07.000000000 +0000 @@ -1 +1,6 @@ disable-race-tests.patch +CVE-2020-15157.patch +Add-runc.v2-multi-shim_partial2.patch +horten-the-unix-socket-path-for-shim.patch +Include-extension-for-shim-binary-format-on-Windows.patch +CVE-2020-15257.patch