diff --git a/docker-mount-secrets.patch b/docker-mount-secrets.patch deleted file mode 100644 index 4cc67d2..0000000 --- a/docker-mount-secrets.patch +++ /dev/null @@ -1,391 +0,0 @@ -diff --git a/container/container_unix.go b/container/container_unix.go -index f92d586..70cc74f 100644 ---- a/container/container_unix.go -+++ b/container/container_unix.go -@@ -39,6 +39,8 @@ type Container struct { - HostsPath string - ShmPath string - ResolvConfPath string -+ // SUSE:secrets :: We need to add the container-specific secrets path here. -+ SuseSecretsPath string - SeccompProfile string - NoNewPrivileges bool - } -@@ -281,6 +283,67 @@ func (container *Container) UnmountSecrets() error { - return detachMounted(container.SecretMountPath()) - } - -+// SUSE:secrets :: SuseSecretsResourcePath returns the path to the container's -+// personal /run/secrets tmpfs. -+func (container *Container) SuseSecretsResourcePath() (string, error) { -+ return container.GetRootResourcePath("suse.secrets") -+} -+ -+// SUSE:secrets :: SuseSecretMounts returns the list of mounts required for the -+// SUSE-specific /run/secrets patch. The container's personal /run/secrets tmpfs -+// has already been set up at this point. -+func (container *Container) SuseSecretMounts() []Mount { -+ var mounts []Mount -+ -+ logrus.WithFields(logrus.Fields{ -+ "container": container.ID, -+ "path": container.SuseSecretsPath, -+ "hasmount": container.HasMountFor("/run/secrets"), -+ }).Debug("SUSE:secrets :: adding container secrets to mountpoint") -+ -+ // TODO(SUSE): How do we register for HasMountFor(). -+ if !container.HasMountFor("/run/secrets") { -+ label.SetFileLabel(container.SuseSecretsPath, container.MountLabel) -+ mounts = append(mounts, Mount{ -+ Source: container.SuseSecretsPath, -+ Destination: "/run/secrets", -+ Writable: true, -+ Propagation: string(volume.DefaultPropagationMode), -+ }) -+ } -+ -+ return mounts -+} -+ -+// SUSE:secrets :: Unmounts the container's personal /run/secrets tmpfs using the -+// provided function. This is done to clean up the mountpoints properly. -+func (container *Container) UnmountSuseSecretMounts(unmount func(string) error) { -+ logrus.WithFields(logrus.Fields{ -+ "container": container.ID, -+ "hasmount": container.HasMountFor("/run/secrets"), -+ }).Debug("SUSE:secrets :: requested to clean up container secrets") -+ -+ if !container.HasMountFor("/run/secrets") { -+ logrus.Debugf("SUSE:secrets :: cleaning up secrets mount for container") -+ -+ suseSecretsPath, err := container.SuseSecretsResourcePath() -+ if err != nil { -+ logrus.Error("SUSE:secrets :: failed to clean up secrets mounts: no secrets resource path found for container %v: %v", container.ID, err) -+ } -+ -+ if suseSecretsPath != "" { -+ logrus.WithFields(logrus.Fields{ -+ "path": suseSecretsPath, -+ }).Debugf("SUSE:secrets :: actually unmounting conatiner secrets") -+ -+ if err := unmount(suseSecretsPath); err != nil && !os.IsNotExist(err) { -+ // We can't error out here. -+ logrus.Warnf("SUSE:secrets :: failed to clean up secrets mounts: failed to umount %s: %v", suseSecretsPath, err) -+ } -+ } -+ } -+} -+ - // UpdateContainer updates configuration of a container. - func (container *Container) UpdateContainer(hostConfig *containertypes.HostConfig) error { - container.Lock() -diff --git a/daemon/container_operations_unix.go b/daemon/container_operations_unix.go -index 2296045..d6f7889 100644 ---- a/daemon/container_operations_unix.go -+++ b/daemon/container_operations_unix.go -@@ -87,6 +87,56 @@ func (daemon *Daemon) getPidContainer(container *container.Container) (*containe - return c, nil - } - -+// SUSE:secrets :: Create a container's personal /run/secrets tmpfs and fill it -+// with the host's credentials. -+func (daemon *Daemon) setupSuseSecrets(c *container.Container) (err error) { -+ c.SuseSecretsPath, err = c.SuseSecretsResourcePath() -+ if err != nil { -+ return err -+ } -+ -+ if !c.HasMountFor("/run/secrets") { -+ rootUID, rootGID := daemon.GetRemappedUIDGID() -+ if err = idtools.MkdirAllAs(c.SuseSecretsPath, 0700, rootUID, rootGID); err != nil { -+ return fmt.Errorf("SUSE:secrets :: failed to create container secret: %v", err) -+ } -+ if err = syscall.Mount("tmpfs", c.SuseSecretsPath, "tmpfs", uintptr(syscall.MS_NOEXEC|syscall.MS_NOSUID|syscall.MS_NODEV), label.FormatMountLabel("", c.GetMountLabel())); err != nil { -+ return fmt.Errorf("SUSE:secrets :: mounting secrets tmpfs: %v", err) -+ } -+ // We need to defer a cleanup, to make sure errors that occur before the container -+ // starts don't cause wasted memory due to tmpfs-es that aren't being used. -+ defer func() { -+ if err != nil { -+ logrus.Infof("SUSE::secrets :: cleaning up secrets mount due to failed setup") -+ c.UnmountSuseSecretMounts(detachMounted) -+ } -+ }() -+ if err = os.Chown(c.SuseSecretsPath, rootUID, rootGID); err != nil { -+ return fmt.Errorf("SUSE:secrets :: failed to chown container secret to (uid=%d,gid=%d): %v", rootUID, rootGID, err) -+ } -+ -+ // Now we need to inject the credentials. But in order to play properly with -+ // user namespaces, they must be owned by rootUID:rootGID. -+ -+ data, err := getHostSuseSecretData() -+ if err != nil { -+ return fmt.Errorf("SUSE:secrets :: failed to get host secret data: %v", err) -+ } -+ -+ uidMap, gidMap := daemon.GetUIDGIDMaps() -+ for _, s := range data { -+ if err := s.SaveTo(c.SuseSecretsPath, uidMap, gidMap); err != nil { -+ logrus.WithFields(logrus.Fields{ -+ "s.path": s.Path, -+ "path": c.SuseSecretsPath, -+ }).Errorf("SUSE:secrets :: failed to save secret data: %v", err) -+ } -+ } -+ } -+ -+ return -+} -+ - func (daemon *Daemon) setupIpcDirs(c *container.Container) error { - var err error - -diff --git a/daemon/initlayer/setup_unix.go b/daemon/initlayer/setup_unix.go -index e83c275..8c56310 100644 ---- a/daemon/initlayer/setup_unix.go -+++ b/daemon/initlayer/setup_unix.go -@@ -18,8 +18,10 @@ import ( - // the container from unwanted side-effects on the rw layer. - func Setup(initLayer string, rootUID, rootGID int) error { - for pth, typ := range map[string]string{ -- "/dev/pts": "dir", -- "/dev/shm": "dir", -+ "/dev/pts": "dir", -+ "/dev/shm": "dir", -+ // SUSE:secrets :: We need to add the mountpoint in the init layer. -+ "/run/secrets": "dir", - "/proc": "dir", - "/sys": "dir", - "/.dockerenv": "file", -diff --git a/daemon/oci_linux.go b/daemon/oci_linux.go -index 1daefc5..8e27f1d 100644 ---- a/daemon/oci_linux.go -+++ b/daemon/oci_linux.go -@@ -693,6 +693,10 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) { - if err := daemon.setupSecretDir(c); err != nil { - return nil, err - } -+ // SUSE:secrets :: We need to set up the container-specific secrets tmpfs here. -+ if err := daemon.setupSuseSecrets(c); err != nil { -+ return nil, err -+ } - - ms, err := daemon.setupMounts(c) - if err != nil { -@@ -711,6 +715,9 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) { - ms = append(ms, *m) - } - -+ // SUSE:secrets :: We add the mounts to the OCI config which containerd then uses. -+ ms = append(ms, c.SuseSecretMounts()...) -+ - sort.Sort(mounts(ms)) - if err := setMounts(daemon, &s, c, ms); err != nil { - return nil, fmt.Errorf("linux mounts: %v", err) -diff --git a/daemon/start.go b/daemon/start.go -index 6c94fd5..9bbdc21 100644 ---- a/daemon/start.go -+++ b/daemon/start.go -@@ -205,6 +205,12 @@ func (daemon *Daemon) Cleanup(container *container.Container) { - - container.UnmountIpcMounts(detachMounted) - -+ // TODO(SUSE): Make sure this gets called by containerCleanup. Do we need to -+ // port this part of the patch there as well? -+ -+ // SUSE:secrets :: We need to unmount stuff here so that we clean up properly. -+ container.UnmountSuseSecretMounts(detachMounted) -+ - if err := daemon.conditionalUnmountOnCleanup(container); err != nil { - // FIXME: remove once reference counting for graphdrivers has been refactored - // Ensure that all the mounts are gone -diff --git a/daemon/suse_secrets.go b/daemon/suse_secrets.go -new file mode 100644 -index 0000000..417a1a9 ---- /dev/null -+++ b/b/daemon/suse_secrets.go -@@ -0,0 +1,184 @@ -+package daemon -+ -+// SUSE:secrets :: This is a set of functions to copy host credentials into a -+// container's /run/secrets. -+ -+import ( -+ "io/ioutil" -+ "os" -+ "path/filepath" -+ "syscall" -+ -+ "github.com/Sirupsen/logrus" -+ "github.com/docker/docker/pkg/idtools" -+) -+ -+// TODO(SUSE): We need to reimplement this to use tar. Immediately. -+ -+// Creating a fake file. -+type SuseFakeFile struct { -+ Path string -+ Uid int -+ Gid int -+ Mode os.FileMode -+ Data []byte -+} -+ -+func (s *SuseFakeFile) SaveTo(dir string, uidMap, gidMap []idtools.IDMap) error { -+ // Create non-existant path components with an owner of root (other FakeFiles -+ // will clean this up if the owner is critical). -+ rootUid, rootGid, err := idtools.GetRootUIDGID(uidMap, gidMap) -+ -+ path := filepath.Join(dir, s.Path) -+ if err := idtools.MkdirAllNewAs(filepath.Dir(path), 0755, rootUid, rootGid); err != nil && !os.IsExist(err) { -+ return err -+ } -+ -+ uid, err := idtools.ToHost(s.Uid, uidMap) -+ if err != nil { -+ return err -+ } -+ -+ gid, err := idtools.ToHost(s.Gid, gidMap) -+ if err != nil { -+ return err -+ } -+ -+ if s.Mode.IsDir() { -+ if err := idtools.MkdirAs(path, s.Mode, uid, gid); err != nil { -+ return err -+ } -+ } else { -+ if err := ioutil.WriteFile(path, s.Data, s.Mode); err != nil { -+ return err -+ } -+ } -+ -+ return os.Chown(path, uid, gid) -+} -+ -+// readDir will recurse into a directory prefix/dir, and return the set of secrets -+// in that directory. The Path attribute of each has the prefix stripped. Symlinks -+// are evaluated. -+func readDir(prefix, dir string) ([]*SuseFakeFile, error) { -+ var suseFiles []*SuseFakeFile -+ -+ path := filepath.Join(prefix, dir) -+ -+ fi, err := os.Stat(path) -+ if err != nil { -+ // Ignore dangling symlinks. -+ if os.IsNotExist(err) { -+ logrus.Warnf("SUSE:secrets :: dangling symlink: %s", path) -+ return suseFiles, nil -+ } -+ return nil, err -+ } -+ -+ stat, ok := fi.Sys().(*syscall.Stat_t) -+ if !ok { -+ logrus.Warnf("SUSE:secrets :: failed to cast directory stat_t: defaulting to owned by root:root: %s", path) -+ } -+ -+ suseFiles = append(suseFiles, &SuseFakeFile{ -+ Path: dir, -+ Uid: int(stat.Uid), -+ Gid: int(stat.Gid), -+ Mode: fi.Mode(), -+ }) -+ -+ files, err := ioutil.ReadDir(path) -+ if err != nil { -+ return nil, err -+ } -+ -+ for _, f := range files { -+ subpath := filepath.Join(dir, f.Name()) -+ -+ if f.IsDir() { -+ secrets, err := readDir(prefix, subpath) -+ if err != nil { -+ return nil, err -+ } -+ suseFiles = append(suseFiles, secrets...) -+ } else { -+ secrets, err := readFile(prefix, subpath) -+ if err != nil { -+ return nil, err -+ } -+ suseFiles = append(suseFiles, secrets...) -+ } -+ } -+ -+ return suseFiles, nil -+} -+ -+func readFile(prefix, file string) ([]*SuseFakeFile, error) { -+ var suseFiles []*SuseFakeFile -+ -+ path := filepath.Join(prefix, file) -+ fi, err := os.Stat(path) -+ if err != nil { -+ // Ignore dangling symlinks. -+ if os.IsNotExist(err) { -+ logrus.Warnf("SUSE:secrets :: dangling symlink: %s", path) -+ return suseFiles, nil -+ } -+ return nil, err -+ } -+ -+ stat, ok := fi.Sys().(*syscall.Stat_t) -+ if !ok { -+ logrus.Warnf("SUSE:secrets :: failed to cast file stat_t: defaulting to owned by root:root: %s", path) -+ } -+ -+ if fi.IsDir() { -+ secrets, err := readDir(prefix, file) -+ if err != nil { -+ return nil, err -+ } -+ suseFiles = append(suseFiles, secrets...) -+ } else { -+ bytes, err := ioutil.ReadFile(path) -+ if err != nil { -+ return nil, err -+ } -+ suseFiles = append(suseFiles, &SuseFakeFile{ -+ Path: file, -+ Uid: int(stat.Uid), -+ Gid: int(stat.Gid), -+ Mode: fi.Mode(), -+ Data: bytes, -+ }) -+ } -+ -+ return suseFiles, nil -+} -+ -+func getHostSuseSecretData() ([]*SuseFakeFile, error) { -+ secrets := []*SuseFakeFile{} -+ -+ credentials, err := readDir("/etc/zypp", "credentials.d") -+ if err != nil { -+ if os.IsNotExist(err) { -+ credentials = []*SuseFakeFile{} -+ } else { -+ logrus.Errorf("SUSE:secrets :: error while reading zypp credentials: %s", err) -+ return nil, err -+ } -+ } -+ secrets = append(secrets, credentials...) -+ -+ suseConnect, err := readFile("/etc", "SUSEConnect") -+ if err != nil { -+ if os.IsNotExist(err) { -+ suseConnect = []*SuseFakeFile{} -+ } else { -+ logrus.Errorf("SUSE:secrets :: error while reading /etc/SUSEConnect: %s", err) -+ return nil, err -+ } -+ } -+ secrets = append(secrets, suseConnect...) -+ -+ return secrets, nil -+} diff --git a/docker.changes b/docker.changes index ab7640a..c0cc976 100644 --- a/docker.changes +++ b/docker.changes @@ -1,23 +1,28 @@ ------------------------------------------------------------------- -Mon Mar 6 18:11:47 UTC 2017 - jmassaguerpla@suse.com +Wed Mar 8 00:48:46 UTC 2017 - asarai@suse.com -- fix docker-mount-secrets.patch to apply to docker-1.13.0 +- Clean up docker-mount-secrets.patch to use the new swarm secrets internals of + Docker 1.13.0, which removes the need to implement any secret handling + ourselves. This resulted in a split up of the patch. + - docker-mount-secrets.patch + + secrets-0001-daemon-allow-directory-creation-in-run-secrets.patch + + secrets-0002-SUSE-implement-SUSE-container-secrets.patch ------------------------------------------------------------------- Mon Mar 6 15:31:02 UTC 2017 - jmassaguerpla@suse.com -- Remove old plugins.json to prevent docker-1.13 to fail to start +- Remove old plugins.json to prevent docker-1.13 to fail to start ------------------------------------------------------------------- Mon Mar 6 12:52:14 UTC 2017 - jmassaguerpla@suse.com -- Fix bsc#1026827: systemd TasksMax default throttles docker +- Fix bsc#1026827: systemd TasksMax default throttles docker ------------------------------------------------------------------- Mon Mar 6 10:09:14 UTC 2017 - jmassaguerpla@suse.com - Fix post section by adding shadow as a package requirement - Otherwise the groupadd instruction fails + Otherwise the groupadd instruction fails ------------------------------------------------------------------- Sun Mar 5 04:54:52 UTC 2017 - asarai@suse.com diff --git a/docker.spec b/docker.spec index 55f305f..683d10c 100644 --- a/docker.spec +++ b/docker.spec @@ -68,7 +68,8 @@ Patch103: boltdb_bolt_add_brokenUnaligned.patch # which is not snapshotted when images are committed. Note that if you modify # this patch, please also modify the patch in the suse-secrets-v # branch in http://github.com/suse/docker.mirror. -Patch200: docker-mount-secrets.patch +Patch200: secrets-0001-daemon-allow-directory-creation-in-run-secrets.patch +Patch201: secrets-0002-SUSE-implement-SUSE-container-secrets.patch Patch300: integration-cli-fix-TestInfoEnsureSucceeds.patch Patch301: pr31549-cmd-docker-fix-TestDaemonCommand.patch BuildRequires: audit @@ -175,6 +176,7 @@ Test package for docker. It contains the source code and the tests. # nothing %else %patch200 -p1 +%patch201 -p1 %endif %ifnarch %{go_arches} %patch100 -p1 diff --git a/secrets-0001-daemon-allow-directory-creation-in-run-secrets.patch b/secrets-0001-daemon-allow-directory-creation-in-run-secrets.patch new file mode 100644 index 0000000..c1665a2 --- /dev/null +++ b/secrets-0001-daemon-allow-directory-creation-in-run-secrets.patch @@ -0,0 +1,39 @@ +From 0c4cf4fac76f2a5272a808665985a2e0df6af0db Mon Sep 17 00:00:00 2001 +From: Aleksa Sarai +Date: Wed, 8 Mar 2017 12:41:54 +1100 +Subject: [PATCH 1/2] daemon: allow directory creation in /run/secrets + +Since FileMode can have the directory bit set, allow a SecretStore +implementation to return secrets that are actually directories. This is +useful for creating directories and subdirectories of secrets. + +Backport: https://github.com/docker/docker/pull/31632 +Signed-off-by: Aleksa Sarai +--- + daemon/container_operations_unix.go | 10 ++++++++-- + 1 file changed, 8 insertions(+), 2 deletions(-) + +diff --git a/daemon/container_operations_unix.go b/daemon/container_operations_unix.go +index 2296045765d4..8527a7907373 100644 +--- a/daemon/container_operations_unix.go ++++ b/daemon/container_operations_unix.go +@@ -195,8 +195,14 @@ func (daemon *Daemon) setupSecretDir(c *container.Container) (setupErr error) { + if secret == nil { + return fmt.Errorf("unable to get secret from secret store") + } +- if err := ioutil.WriteFile(fPath, secret.Spec.Data, s.File.Mode); err != nil { +- return errors.Wrap(err, "error injecting secret") ++ if s.File.Mode.IsDir() { ++ if err := os.Mkdir(fPath, s.File.Mode); err != nil { ++ return errors.Wrap(err, "error injecting secret dir") ++ } ++ } else { ++ if err := ioutil.WriteFile(fPath, secret.Spec.Data, s.File.Mode); err != nil { ++ return errors.Wrap(err, "error injecting secret") ++ } + } + + uid, err := strconv.Atoi(s.File.UID) +-- +2.12.0 + diff --git a/secrets-0002-SUSE-implement-SUSE-container-secrets.patch b/secrets-0002-SUSE-implement-SUSE-container-secrets.patch new file mode 100644 index 0000000..a785381 --- /dev/null +++ b/secrets-0002-SUSE-implement-SUSE-container-secrets.patch @@ -0,0 +1,290 @@ +From a3106907d4786ed7d624201debdd43dc41fb4b97 Mon Sep 17 00:00:00 2001 +From: Aleksa Sarai +Date: Wed, 8 Mar 2017 11:43:29 +1100 +Subject: [PATCH 2/2] SUSE: implement SUSE container secrets + +This allows for us to pass in host credentials to a container, allowing +for SUSEConnect to work with containers. + +THIS PATCH IS NOT TO BE UPSTREAMED, DUE TO THE FACT THAT IT IS +SUSE-SPECIFIC, AND UPSTREAM DOES NOT APPROVE OF THIS CONCEPT BECAUSE IT +MAKES BUILDS NOT ENTIRELY REPRODUCIBLE. + +Signed-off-by: Aleksa Sarai +--- + daemon/start.go | 5 + + daemon/suse_secrets.go | 246 +++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 251 insertions(+) + create mode 100644 daemon/suse_secrets.go + +diff --git a/daemon/start.go b/daemon/start.go +index 6c94fd5482d0..3c06eed778d7 100644 +--- a/daemon/start.go ++++ b/daemon/start.go +@@ -146,6 +146,11 @@ func (daemon *Daemon) containerStart(container *container.Container, checkpoint + return err + } + ++ // SUSE:secrets -- inject the SUSE secret store ++ if err := daemon.injectSuseSecretStore(container); err != nil { ++ return err ++ } ++ + spec, err := daemon.createSpec(container) + if err != nil { + return err +diff --git a/daemon/suse_secrets.go b/daemon/suse_secrets.go +new file mode 100644 +index 000000000000..591abc998e67 +--- /dev/null ++++ b/daemon/suse_secrets.go +@@ -0,0 +1,246 @@ ++/* ++ * suse-secrets: patch for Docker to implement SUSE secrets ++ * Copyright (C) 2017 SUSE LLC. ++ * ++ * Licensed under the Apache License, Version 2.0 (the "License"); ++ * you may not use this file except in compliance with the License. ++ * You may obtain a copy of the License at ++ * ++ * http://www.apache.org/licenses/LICENSE-2.0 ++ * ++ * Unless required by applicable law or agreed to in writing, software ++ * distributed under the License is distributed on an "AS IS" BASIS, ++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++ * See the License for the specific language governing permissions and ++ * limitations under the License. ++ */ ++ ++package daemon ++ ++import ( ++ "fmt" ++ "io/ioutil" ++ "os" ++ "path/filepath" ++ "syscall" ++ ++ "github.com/Sirupsen/logrus" ++ "github.com/docker/distribution/digest" ++ "github.com/docker/docker/container" ++ ++ swarmtypes "github.com/docker/docker/api/types/swarm" ++ swarmexec "github.com/docker/swarmkit/agent/exec" ++ swarmapi "github.com/docker/swarmkit/api" ++) ++ ++func init() { ++ // Output to tell us in logs that SUSE:secrets is enabled. ++ logrus.Infof("SUSE:secrets :: enabled") ++} ++ ++// Creating a fake file. ++type SuseFakeFile struct { ++ Path string ++ Uid int ++ Gid int ++ Mode os.FileMode ++ Data []byte ++} ++ ++func (s SuseFakeFile) id() string { ++ return fmt.Sprintf("suse::%s:%s", digest.FromBytes(s.Data), s.Path) ++} ++ ++func (s SuseFakeFile) toSecret() *swarmapi.Secret { ++ return &swarmapi.Secret{ ++ ID: s.id(), ++ Internal: true, ++ Spec: swarmapi.SecretSpec{ ++ Data: s.Data, ++ }, ++ } ++} ++ ++func (s SuseFakeFile) toSecretReference() *swarmtypes.SecretReference { ++ return &swarmtypes.SecretReference{ ++ SecretID: s.id(), ++ SecretName: s.id(), ++ File: &swarmtypes.SecretReferenceFileTarget{ ++ Name: s.Path, ++ UID: fmt.Sprintf("%d", s.Uid), ++ GID: fmt.Sprintf("%d", s.Gid), ++ Mode: s.Mode, ++ }, ++ } ++} ++ ++// readDir will recurse into a directory prefix/dir, and return the set of secrets ++// in that directory. The Path attribute of each has the prefix stripped. Symlinks ++// are evaluated. ++func readDir(prefix, dir string) ([]*SuseFakeFile, error) { ++ var suseFiles []*SuseFakeFile ++ ++ path := filepath.Join(prefix, dir) ++ ++ fi, err := os.Stat(path) ++ if err != nil { ++ // Ignore dangling symlinks. ++ if os.IsNotExist(err) { ++ logrus.Warnf("SUSE:secrets :: dangling symlink: %s", path) ++ return suseFiles, nil ++ } ++ return nil, err ++ } ++ ++ stat, ok := fi.Sys().(*syscall.Stat_t) ++ if !ok { ++ logrus.Warnf("SUSE:secrets :: failed to cast directory stat_t: defaulting to owned by root:root: %s", path) ++ } ++ ++ suseFiles = append(suseFiles, &SuseFakeFile{ ++ Path: dir, ++ Uid: int(stat.Uid), ++ Gid: int(stat.Gid), ++ Mode: fi.Mode(), ++ }) ++ ++ files, err := ioutil.ReadDir(path) ++ if err != nil { ++ return nil, err ++ } ++ ++ for _, f := range files { ++ subpath := filepath.Join(dir, f.Name()) ++ ++ if f.IsDir() { ++ secrets, err := readDir(prefix, subpath) ++ if err != nil { ++ return nil, err ++ } ++ suseFiles = append(suseFiles, secrets...) ++ } else { ++ secrets, err := readFile(prefix, subpath) ++ if err != nil { ++ return nil, err ++ } ++ suseFiles = append(suseFiles, secrets...) ++ } ++ } ++ ++ return suseFiles, nil ++} ++ ++// readFile returns a secret given a file under a given prefix. ++func readFile(prefix, file string) ([]*SuseFakeFile, error) { ++ var suseFiles []*SuseFakeFile ++ ++ path := filepath.Join(prefix, file) ++ fi, err := os.Stat(path) ++ if err != nil { ++ // Ignore dangling symlinks. ++ if os.IsNotExist(err) { ++ logrus.Warnf("SUSE:secrets :: dangling symlink: %s", path) ++ return suseFiles, nil ++ } ++ return nil, err ++ } ++ ++ stat, ok := fi.Sys().(*syscall.Stat_t) ++ if !ok { ++ logrus.Warnf("SUSE:secrets :: failed to cast file stat_t: defaulting to owned by root:root: %s", path) ++ } ++ ++ if fi.IsDir() { ++ secrets, err := readDir(prefix, file) ++ if err != nil { ++ return nil, err ++ } ++ suseFiles = append(suseFiles, secrets...) ++ } else { ++ bytes, err := ioutil.ReadFile(path) ++ if err != nil { ++ return nil, err ++ } ++ suseFiles = append(suseFiles, &SuseFakeFile{ ++ Path: file, ++ Uid: int(stat.Uid), ++ Gid: int(stat.Gid), ++ Mode: fi.Mode(), ++ Data: bytes, ++ }) ++ } ++ ++ return suseFiles, nil ++} ++ ++// getHostSuseSecretData returns the list of SuseFakeFiles the need to be added ++// as SUSE secrets. ++func getHostSuseSecretData() ([]*SuseFakeFile, error) { ++ secrets := []*SuseFakeFile{} ++ ++ credentials, err := readDir("/etc/zypp", "credentials.d") ++ if err != nil { ++ if os.IsNotExist(err) { ++ credentials = []*SuseFakeFile{} ++ } else { ++ logrus.Errorf("SUSE:secrets :: error while reading zypp credentials: %s", err) ++ return nil, err ++ } ++ } ++ secrets = append(secrets, credentials...) ++ ++ suseConnect, err := readFile("/etc", "SUSEConnect") ++ if err != nil { ++ if os.IsNotExist(err) { ++ suseConnect = []*SuseFakeFile{} ++ } else { ++ logrus.Errorf("SUSE:secrets :: error while reading /etc/SUSEConnect: %s", err) ++ return nil, err ++ } ++ } ++ secrets = append(secrets, suseConnect...) ++ ++ return secrets, nil ++} ++ ++// In order to reduce the amount of code touched outside of this file, we ++// implement the swarm API for SecretGetter. This asserts that this requirement ++// will always be matched. ++var _ swarmexec.SecretGetter = &suseSecretGetter{} ++ ++type suseSecretGetter struct { ++ dfl swarmexec.SecretGetter ++ secrets map[string]*swarmapi.Secret ++} ++ ++func (s *suseSecretGetter) Get(id string) *swarmapi.Secret { ++ logrus.Debugf("SUSE:secrets :: id=%s requested from suseSecretGetter", id) ++ ++ secret, ok := s.secrets[id] ++ if !ok { ++ // fallthrough ++ return s.dfl.Get(id) ++ } ++ ++ return secret ++} ++ ++func (daemon *Daemon) injectSuseSecretStore(c *container.Container) error { ++ newSecretStore := &suseSecretGetter{ ++ dfl: c.SecretStore, ++ secrets: make(map[string]*swarmapi.Secret), ++ } ++ ++ secrets, err := getHostSuseSecretData() ++ if err != nil { ++ return err ++ } ++ ++ for _, secret := range secrets { ++ newSecretStore.secrets[secret.id()] = secret.toSecret() ++ c.SecretReferences = append(c.SecretReferences, secret.toSecretReference()) ++ } ++ ++ c.SecretStore = newSecretStore ++ return nil ++} +-- +2.12.0 +