Accepting request 477670 from home:cyphar:containers

This massively cleans up the docker mount secrets patch we have to use the new
Docker Swarm framework for /run/secrets (which doesn't require swarm mode with
these patches).

OBS-URL: https://build.opensuse.org/request/show/477670
OBS-URL: https://build.opensuse.org/package/show/Virtualization:containers/docker?expand=0&rev=172
This commit is contained in:
Jordi Massaguer 2017-03-08 09:03:15 +00:00 committed by Git OBS Bridge
parent f0d9c665f7
commit 9c1f006520
5 changed files with 342 additions and 397 deletions

View File

@ -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
+}

View File

@ -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

View File

@ -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<version>
# 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

View File

@ -0,0 +1,39 @@
From 0c4cf4fac76f2a5272a808665985a2e0df6af0db Mon Sep 17 00:00:00 2001
From: Aleksa Sarai <asarai@suse.de>
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 <asarai@suse.de>
---
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

View File

@ -0,0 +1,290 @@
From a3106907d4786ed7d624201debdd43dc41fb4b97 Mon Sep 17 00:00:00 2001
From: Aleksa Sarai <asarai@suse.de>
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 <asarai@suse.de>
---
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