303 lines
8.3 KiB
Diff
303 lines
8.3 KiB
Diff
|
Index: docker-1.10.0/daemon/start.go
|
||
|
===================================================================
|
||
|
--- docker-1.10.0.orig/daemon/start.go
|
||
|
+++ docker-1.10.0/daemon/start.go
|
||
|
@@ -1,12 +1,17 @@
|
||
|
package daemon
|
||
|
|
||
|
import (
|
||
|
+ "fmt"
|
||
|
+ "os"
|
||
|
+ "path/filepath"
|
||
|
"runtime"
|
||
|
+ "syscall"
|
||
|
|
||
|
"github.com/Sirupsen/logrus"
|
||
|
"github.com/docker/docker/container"
|
||
|
derr "github.com/docker/docker/errors"
|
||
|
"github.com/docker/docker/runconfig"
|
||
|
+ "github.com/docker/docker/vendor/src/github.com/opencontainers/runc/libcontainer/label"
|
||
|
containertypes "github.com/docker/engine-api/types/container"
|
||
|
)
|
||
|
|
||
|
@@ -134,6 +139,10 @@ func (daemon *Daemon) containerStart(con
|
||
|
}
|
||
|
}
|
||
|
|
||
|
+ if err := daemon.setupSecretFiles(container); err != nil {
|
||
|
+ return err
|
||
|
+ }
|
||
|
+
|
||
|
mounts, err := daemon.setupMounts(container)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
@@ -142,13 +151,96 @@ func (daemon *Daemon) containerStart(con
|
||
|
mounts = append(mounts, container.TmpfsMounts()...)
|
||
|
|
||
|
container.Command.Mounts = mounts
|
||
|
+
|
||
|
if err := daemon.waitForStart(container); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
+
|
||
|
+ // Now the container is running, unmount the secrets on the host
|
||
|
+ if err := daemon.UnmountSecrets(container, false); err != nil {
|
||
|
+ return err
|
||
|
+ }
|
||
|
+
|
||
|
container.HasBeenStartedBefore = true
|
||
|
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 (daemon *Daemon) UnmountSecrets(container *container.Container, force bool) error {
|
||
|
+ secretsPath, err := daemon.secretsPath(container)
|
||
|
+ if err != nil {
|
||
|
+ return err
|
||
|
+ }
|
||
|
+
|
||
|
+ logrus.WithFields(logrus.Fields{
|
||
|
+ "container": container.ID,
|
||
|
+ "path": secretsPath,
|
||
|
+ "force": force,
|
||
|
+ }).Debug("SUSE:secrets -> unmounting container secrets")
|
||
|
+
|
||
|
+ 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 (daemon *Daemon) secretsPath(container *container.Container) (string, error) {
|
||
|
+ return container.GetRootResourcePath("secrets")
|
||
|
+}
|
||
|
+
|
||
|
+func (daemon *Daemon) setupSecretFiles(container *container.Container) error {
|
||
|
+ secretsPath, err := daemon.secretsPath(container)
|
||
|
+ if err != nil {
|
||
|
+ return err
|
||
|
+ }
|
||
|
+
|
||
|
+ logrus.WithFields(logrus.Fields{
|
||
|
+ "container": container.ID,
|
||
|
+ "path": secretsPath,
|
||
|
+ }).Debug("SUSE:secrets -> setting up container secrets")
|
||
|
+
|
||
|
+ 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
|
||
|
+}
|
||
|
+
|
||
|
func (daemon *Daemon) waitForStart(container *container.Container) error {
|
||
|
return container.StartMonitor(daemon, container.HostConfig.RestartPolicy)
|
||
|
}
|
||
|
Index: docker-1.10.0/daemon/delete.go
|
||
|
===================================================================
|
||
|
--- docker-1.10.0.orig/daemon/delete.go
|
||
|
+++ docker-1.10.0/daemon/delete.go
|
||
|
@@ -122,6 +122,17 @@ func (daemon *Daemon) cleanupContainer(c
|
||
|
}
|
||
|
}()
|
||
|
|
||
|
+ // 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
|
||
|
+ if err = daemon.UnmountSecrets(container, true); err != nil {
|
||
|
+ logrus.Errorf("SUSE:secrets -> Error unmounting secrets in cleanup: %v", err)
|
||
|
+ }
|
||
|
+
|
||
|
if err = os.RemoveAll(container.Root); err != nil {
|
||
|
return derr.ErrorCodeRmFS.WithArgs(container.ID, err)
|
||
|
}
|
||
|
Index: docker-1.10.0/daemon/volumes_unix.go
|
||
|
===================================================================
|
||
|
--- docker-1.10.0.orig/daemon/volumes_unix.go
|
||
|
+++ docker-1.10.0/daemon/volumes_unix.go
|
||
|
@@ -7,6 +7,7 @@ import (
|
||
|
"sort"
|
||
|
"strconv"
|
||
|
|
||
|
+ "github.com/Sirupsen/logrus"
|
||
|
"github.com/docker/docker/container"
|
||
|
"github.com/docker/docker/daemon/execdriver"
|
||
|
"github.com/docker/docker/volume"
|
||
|
@@ -18,6 +19,29 @@ import (
|
||
|
// calls Setup() on each. It also looks to see if is a network mount such as
|
||
|
// /etc/resolv.conf, and if it is not, appends it to the array of mounts.
|
||
|
func (daemon *Daemon) setupMounts(container *container.Container) ([]execdriver.Mount, error) {
|
||
|
+ if _, exists := container.MountPoints["/run/secrets"]; !exists {
|
||
|
+ const (
|
||
|
+ name = "suse:secrets"
|
||
|
+ dest = "/run/secrets"
|
||
|
+ rw = true
|
||
|
+ )
|
||
|
+
|
||
|
+ secretsPath, err := daemon.secretsPath(container)
|
||
|
+ if err != nil {
|
||
|
+ return nil, err
|
||
|
+ }
|
||
|
+
|
||
|
+ logrus.WithFields(logrus.Fields{
|
||
|
+ "name": name,
|
||
|
+ "rw": rw,
|
||
|
+ "path": secretsPath,
|
||
|
+ "dest": dest,
|
||
|
+ "container": container.ID,
|
||
|
+ }).Debug("SUSE:secrets -> adding /run/secrets to bind-mount points")
|
||
|
+
|
||
|
+ container.AddBindMountPoint(name, secretsPath, dest, rw)
|
||
|
+ }
|
||
|
+
|
||
|
var mounts []execdriver.Mount
|
||
|
for _, m := range container.MountPoints {
|
||
|
if err := daemon.lazyInitializeVolume(container.ID, m); err != nil {
|
||
|
Index: docker-1.10.0/daemon/secrets.go
|
||
|
===================================================================
|
||
|
--- /dev/null
|
||
|
+++ docker-1.10.0/daemon/secrets.go
|
||
|
@@ -0,0 +1,103 @@
|
||
|
+package daemon
|
||
|
+
|
||
|
+import (
|
||
|
+ "io/ioutil"
|
||
|
+ "os"
|
||
|
+ "path/filepath"
|
||
|
+
|
||
|
+ log "github.com/Sirupsen/logrus"
|
||
|
+)
|
||
|
+
|
||
|
+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
|
||
|
+}
|