Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
SUSE:SLE-12-SP5:Update
docker.1867
docker-mount-secrets.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File docker-mount-secrets.patch of Package docker.1867
Index: docker-1.9.1/daemon/container.go =================================================================== --- docker-1.9.1.orig/daemon/container.go +++ docker-1.9.1/daemon/container.go @@ -315,6 +315,10 @@ func (container *Container) Start() (err } } + if err := container.setupSecretFiles(); err != nil { + return err + } + mounts, err := container.setupMounts() if err != nil { return err @@ -322,7 +326,53 @@ func (container *Container) Start() (err mounts = append(mounts, container.ipcMounts()...) 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 } // streamConfig.StdinPipe returns a WriteCloser which can be used to feed data @@ -619,6 +669,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 { @@ -1159,3 +1213,28 @@ func (container *Container) stopSignal() } return int(stopSignal) } + +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.9.1/daemon/secrets.go =================================================================== --- /dev/null +++ docker-1.9.1/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.9.1/daemon/delete.go =================================================================== --- docker-1.9.1.orig/daemon/delete.go +++ docker-1.9.1/daemon/delete.go @@ -128,6 +128,15 @@ func (daemon *Daemon) rm(container *Cont return derr.ErrorCodeRmInit.WithArgs(daemon.driver, initID, err) } + // 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 derr.ErrorCodeRmFS.WithArgs(container.ID, err) } Index: docker-1.9.1/daemon/daemon_unix.go =================================================================== --- docker-1.9.1.orig/daemon/daemon_unix.go +++ docker-1.9.1/daemon/daemon_unix.go @@ -528,6 +528,7 @@ func setupInitLayer(initLayer string, ro "/etc/hostname": "file", "/dev/console": "file", "/etc/mtab": "/proc/mounts", + "/run/secrets": "dir", } { parts := strings.Split(pth, "/") prev := "/" Index: docker-1.9.1/daemon/volumes_unix.go =================================================================== --- docker-1.9.1.orig/daemon/volumes_unix.go +++ docker-1.9.1/daemon/volumes_unix.go @@ -54,6 +54,17 @@ 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) netMounts := container.networkMounts() // if we are going to mount any of the network files from container
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor