File docker-mount-secrets.patch of Package docker.1559
Index: docker-1.8.3/daemon/container.go
===================================================================
--- docker-1.8.3.orig/daemon/container.go
+++ docker-1.8.3/daemon/container.go
@@ -289,13 +289,63 @@ func (container *Container) Start() (err
return err
}
+ if err := container.setupSecretFiles(); err != nil {
+ return err
+ }
+
mounts, err := container.setupMounts()
if err != nil {
return err
}
container.command.Mounts = mounts
- return container.waitForStart()
+
+ if err := container.waitForStart(); err != nil {
+ return err
+ }
+
+ // Now the container is running, unmount the secrets on the host
+ if err := container.UnmountSecrets(false); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// unmount secrets on the host. Performs a lazy unmount by default unless
+// `force` is set to true.
+// No unmount operation is invoked if the secrets mount point has already been
+// unmounted.
+func (container *Container) UnmountSecrets(force bool) error {
+ secretsPath, err := container.secretsPath()
+ if err != nil {
+ return err
+ }
+
+ var stat_dot, stat_dot_dot syscall.Stat_t
+ if err := syscall.Stat(secretsPath, &stat_dot); err != nil {
+ return fmt.Errorf("Something went wrong while getting stats for dot: %v", err)
+ }
+ if err := syscall.Stat(filepath.Join(secretsPath, ".."), &stat_dot_dot); err != nil {
+ return fmt.Errorf("Something went wrong while getting stats for dot dot: %v", err)
+ }
+
+ // Compare device IDs for /<secretsPath>/. and /<secretsPath>/..
+ // If the device IDs are different then the secrets directory is actually
+ // mounted. Otherwise it has already been unmounted, hence there's nothing
+ // to do (calling unmount would return an error)
+ if stat_dot.Dev != stat_dot_dot.Dev {
+ // By default perform lazy unmount
+ flag := syscall.MNT_DETACH
+ if force {
+ flag = syscall.MNT_FORCE
+ }
+ if err := syscall.Unmount(secretsPath, flag); err != nil {
+ return err
+ }
+ }
+
+ return nil
}
func (container *Container) Run() error {
@@ -599,6 +649,10 @@ func (container *Container) jsonPath() (
return container.GetRootResourcePath("config.json")
}
+func (container *Container) secretsPath() (string, error) {
+ return container.GetRootResourcePath("secrets")
+}
+
// This method must be exported to be used from the lxc template
// This directory is only usable when the container is running
func (container *Container) RootfsPath() string {
@@ -1241,3 +1295,28 @@ func (container *Container) copyImagePat
return v.Unmount()
}
+
+func (container *Container) setupSecretFiles() error {
+ secretsPath, err := container.secretsPath()
+ if err != nil {
+ return err
+ }
+
+ if err := os.MkdirAll(secretsPath, 0700); err != nil {
+ return err
+ }
+
+ if err := syscall.Mount("tmpfs", secretsPath, "tmpfs", uintptr(syscall.MS_NOEXEC|syscall.MS_NOSUID|syscall.MS_NODEV), label.FormatMountLabel("", container.GetMountLabel())); err != nil {
+ return fmt.Errorf("mounting secret tmpfs: %s", err)
+ }
+
+ data, err := getHostSecretData()
+ if err != nil {
+ return err
+ }
+ for _, s := range data {
+ s.SaveTo(secretsPath)
+ }
+
+ return nil
+}
Index: docker-1.8.3/daemon/secrets.go
===================================================================
--- /dev/null
+++ docker-1.8.3/daemon/secrets.go
@@ -0,0 +1,102 @@
+package daemon
+
+import (
+ log "github.com/Sirupsen/logrus"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+)
+
+type Secret struct {
+ Name string
+ IsDir bool
+ HostBased bool
+}
+
+type SecretData struct {
+ Name string
+ Data []byte
+}
+
+func (s SecretData) SaveTo(dir string) error {
+ path := filepath.Join(dir, s.Name)
+ if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil && !os.IsExist(err) {
+ return err
+ }
+ if err := ioutil.WriteFile(path, s.Data, 0755); err != nil {
+ return err
+ }
+ return nil
+}
+
+func readAll(root, prefix string) ([]SecretData, error) {
+ path := filepath.Join(root, prefix)
+
+ data := []SecretData{}
+
+ files, err := ioutil.ReadDir(path)
+ if err != nil {
+ if os.IsNotExist(err) {
+ return data, nil
+ }
+
+ return nil, err
+ }
+
+ for _, f := range files {
+ fileData, err := readFile(root, filepath.Join(prefix, f.Name()))
+ if err != nil {
+ // If the file did not exist, might be a dangling symlink
+ // Ignore the error
+ if os.IsNotExist(err) {
+ continue
+ }
+ return nil, err
+ }
+ data = append(data, fileData...)
+ }
+
+ return data, nil
+}
+
+func readFile(root, name string) ([]SecretData, error) {
+ path := filepath.Join(root, name)
+
+ s, err := os.Stat(path)
+ if err != nil {
+ return nil, err
+ }
+
+ if s.IsDir() {
+ dirData, err := readAll(root, name)
+ if err != nil {
+ return nil, err
+ }
+ return dirData, nil
+ } else {
+ bytes, err := ioutil.ReadFile(path)
+ if err != nil {
+ return nil, err
+ }
+ return []SecretData{{Name: name, Data: bytes}}, nil
+ }
+}
+
+func getHostSecretData() ([]SecretData, error) {
+ credentials, err := readAll("/etc/zypp/", "credentials.d")
+ if err != nil {
+ log.Errorf("Error while reading zypp credentials: %s", err)
+ return credentials, err
+ }
+
+ suseConnect, err := readFile("/etc", "SUSEConnect")
+ if err != nil {
+ if os.IsNotExist(err) {
+ suseConnect = []SecretData{}
+ } else {
+ log.Errorf("Error while reading /etc/SUSEConnect: %s", err)
+ return nil, err
+ }
+ }
+ return append(credentials, suseConnect...), nil
+}
Index: docker-1.8.3/daemon/volumes_linux.go
===================================================================
--- docker-1.8.3.orig/daemon/volumes_linux.go
+++ docker-1.8.3/daemon/volumes_linux.go
@@ -45,7 +45,19 @@ func (container *Container) setupMounts(
}
}
+ secretsPath, err := container.secretsPath()
+ if err != nil {
+ return nil, err
+ }
+
+ mounts = append(mounts, execdriver.Mount{
+ Source: secretsPath,
+ Destination: "/run/secrets",
+ Writable: true,
+ })
+
mounts = sortMounts(mounts)
+
return append(mounts, container.networkMounts()...), nil
}
Index: docker-1.8.3/graph/graph_unix.go
===================================================================
--- docker-1.8.3.orig/graph/graph_unix.go
+++ docker-1.8.3/graph/graph_unix.go
@@ -34,6 +34,7 @@ func SetupInitLayer(initLayer string) er
"/etc/hostname": "file",
"/dev/console": "file",
"/etc/mtab": "/proc/mounts",
+ "/run/secrets": "dir",
} {
parts := strings.Split(pth, "/")
prev := "/"
Index: docker-1.8.3/daemon/delete.go
===================================================================
--- docker-1.8.3.orig/daemon/delete.go
+++ docker-1.8.3/daemon/delete.go
@@ -123,6 +123,15 @@ func (daemon *Daemon) rm(container *Cont
}
}
+ // Force unmount of the secrets tmpfs storage added by SUSE's Docker daemon.
+ // This is unmounted automatically at container start time, however the unmount
+ // is done with the 'lazy' flag. This can introduce some race conditions, for
+ // example when the container dies immediately (e.g. wrong entry point). In
+ // that case the secrets directory has not been unmounted yet, causing the
+ // removal of the container to fail because the file system is still reported
+ // as in use. See bnc#954797
+ container.UnmountSecrets(true)
+
if err = os.RemoveAll(container.root); err != nil {
return fmt.Errorf("Unable to remove filesystem for %v: %v", container.ID, err)
}