1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Merge pull request #32571 from ehazlett/secret-targets

Support Custom Secret Targets
This commit is contained in:
Brian Goff 2017-05-10 16:26:35 -04:00 committed by GitHub
commit 08fdce7738
10 changed files with 167 additions and 49 deletions

View file

@ -948,3 +948,21 @@ func (container *Container) InitializeStdio(iop libcontainerd.IOPipe) error {
return nil return nil
} }
// SecretMountPath returns the path of the secret mount for the container
func (container *Container) SecretMountPath() string {
return filepath.Join(container.Root, "secrets")
}
// SecretFilePath returns the path to the location of a secret on the host.
func (container *Container) SecretFilePath(secretRef swarmtypes.SecretReference) string {
return filepath.Join(container.SecretMountPath(), secretRef.SecretID)
}
func getSecretTargetPath(r *swarmtypes.SecretReference) string {
if filepath.IsAbs(r.File.Name) {
return r.File.Name
}
return filepath.Join(containerSecretMountPath, r.File.Name)
}

View file

@ -12,8 +12,8 @@ func detachMounted(path string) error {
return unix.Unmount(path, 0) return unix.Unmount(path, 0)
} }
// SecretMount returns the mount for the secret path // SecretMounts returns the mounts for the secret path
func (container *Container) SecretMount() *Mount { func (container *Container) SecretMounts() []Mount {
return nil return nil
} }

View file

@ -1,9 +1,11 @@
package container package container
import ( import (
"path/filepath"
"testing" "testing"
"github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/container"
swarmtypes "github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/pkg/signal" "github.com/docker/docker/pkg/signal"
) )
@ -58,3 +60,17 @@ func TestContainerStopTimeout(t *testing.T) {
t.Fatalf("Expected 15, got %v", s) t.Fatalf("Expected 15, got %v", s)
} }
} }
func TestContainerSecretReferenceDestTarget(t *testing.T) {
ref := &swarmtypes.SecretReference{
File: &swarmtypes.SecretReferenceFileTarget{
Name: "app",
},
}
d := getSecretTargetPath(ref)
expected := filepath.Join(containerSecretMountPath, "app")
if d != expected {
t.Fatalf("expected secret dest %q; received %q", expected, d)
}
}

View file

@ -163,11 +163,6 @@ func (container *Container) NetworkMounts() []Mount {
return mounts return mounts
} }
// SecretMountPath returns the path of the secret mount for the container
func (container *Container) SecretMountPath() string {
return filepath.Join(container.Root, "secrets")
}
// CopyImagePathContent copies files in destination to the volume. // CopyImagePathContent copies files in destination to the volume.
func (container *Container) CopyImagePathContent(v volume.Volume, destination string) error { func (container *Container) CopyImagePathContent(v volume.Volume, destination string) error {
rootfs, err := symlink.FollowSymlinkInScope(filepath.Join(container.BaseFS, destination), container.BaseFS) rootfs, err := symlink.FollowSymlinkInScope(filepath.Join(container.BaseFS, destination), container.BaseFS)
@ -253,17 +248,21 @@ func (container *Container) IpcMounts() []Mount {
return mounts return mounts
} }
// SecretMount returns the mount for the secret path // SecretMounts returns the mounts for the secret path.
func (container *Container) SecretMount() *Mount { func (container *Container) SecretMounts() []Mount {
if len(container.SecretReferences) > 0 { var mounts []Mount
return &Mount{ for _, r := range container.SecretReferences {
Source: container.SecretMountPath(), if r.File == nil {
Destination: containerSecretMountPath, continue
Writable: false,
} }
mounts = append(mounts, Mount{
Source: container.SecretFilePath(*r),
Destination: getSecretTargetPath(r),
Writable: false,
})
} }
return nil return mounts
} }
// UnmountSecrets unmounts the local tmpfs for secrets // UnmountSecrets unmounts the local tmpfs for secrets

View file

@ -10,6 +10,10 @@ import (
containertypes "github.com/docker/docker/api/types/container" containertypes "github.com/docker/docker/api/types/container"
) )
const (
containerSecretMountPath = `C:\ProgramData\Docker\secrets`
)
// Container holds fields specific to the Windows implementation. See // Container holds fields specific to the Windows implementation. See
// CommonContainer for standard fields common to all containers. // CommonContainer for standard fields common to all containers.
type Container struct { type Container struct {
@ -43,8 +47,8 @@ func (container *Container) IpcMounts() []Mount {
return nil return nil
} }
// SecretMount returns the mount for the secret path // SecretMounts returns the mounts for the secret path
func (container *Container) SecretMount() *Mount { func (container *Container) SecretMounts() []Mount {
return nil return nil
} }

View file

@ -177,13 +177,9 @@ func (daemon *Daemon) setupSecretDir(c *container.Container) (setupErr error) {
return fmt.Errorf("secret target type is not a file target") return fmt.Errorf("secret target type is not a file target")
} }
targetPath := filepath.Clean(s.File.Name) // secrets are created in the SecretMountPath on the host, at a
// ensure that the target is a filename only; no paths allowed // single level
if targetPath != filepath.Base(targetPath) { fPath := c.SecretFilePath(*s)
return fmt.Errorf("error creating secret: secret must not be a path")
}
fPath := filepath.Join(localMountPath, targetPath)
if err := idtools.MkdirAllAs(filepath.Dir(fPath), 0700, rootUID, rootGID); err != nil { if err := idtools.MkdirAllAs(filepath.Dir(fPath), 0700, rootUID, rootGID); err != nil {
return errors.Wrap(err, "error creating secret mount path") return errors.Wrap(err, "error creating secret mount path")
} }

View file

@ -750,8 +750,8 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) {
} }
ms = append(ms, tmpfsMounts...) ms = append(ms, tmpfsMounts...)
if m := c.SecretMount(); m != nil { if m := c.SecretMounts(); m != nil {
ms = append(ms, *m) ms = append(ms, m...)
} }
sort.Sort(mounts(ms)) sort.Sort(mounts(ms))

View file

@ -11,5 +11,5 @@ VNDR_COMMIT=c56e082291115e369f77601f9c071dd0b87c7120
BINDATA_COMMIT=a0ff2567cfb70903282db057e799fd826784d41d BINDATA_COMMIT=a0ff2567cfb70903282db057e799fd826784d41d
# CLI # CLI
DOCKERCLI_REPO=https://github.com/docker/cli DOCKERCLI_REPO=https://github.com/aaronlehmann/cli
DOCKERCLI_COMMIT=c3648a9c9400d45524cc71b8fca4085b192c626f DOCKERCLI_COMMIT=fd5a5910409fe5ed862b29de45a66f2bf8056894

View file

@ -5,6 +5,7 @@ package main
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"path/filepath"
"strings" "strings"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
@ -90,23 +91,41 @@ func (s *DockerSwarmSuite) TestServiceCreateWithSecretSimple(c *check.C) {
c.Assert(refs[0].File.Name, checker.Equals, testName) c.Assert(refs[0].File.Name, checker.Equals, testName)
c.Assert(refs[0].File.UID, checker.Equals, "0") c.Assert(refs[0].File.UID, checker.Equals, "0")
c.Assert(refs[0].File.GID, checker.Equals, "0") c.Assert(refs[0].File.GID, checker.Equals, "0")
out, err = d.Cmd("service", "rm", serviceName)
c.Assert(err, checker.IsNil, check.Commentf(out))
d.DeleteSecret(c, testName)
} }
func (s *DockerSwarmSuite) TestServiceCreateWithSecretSourceTarget(c *check.C) { func (s *DockerSwarmSuite) TestServiceCreateWithSecretSourceTargetPaths(c *check.C) {
d := s.AddDaemon(c, true, true) d := s.AddDaemon(c, true, true)
serviceName := "test-service-secret" testPaths := map[string]string{
testName := "test_secret" "app": "/etc/secret",
id := d.CreateSecret(c, swarm.SecretSpec{ "test_secret": "test_secret",
Annotations: swarm.Annotations{ "relative_secret": "relative/secret",
Name: testName, "escapes_in_container": "../secret",
}, }
Data: []byte("TESTINGDATA"),
})
c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("secrets: %s", id))
testTarget := "testing"
out, err := d.Cmd("service", "create", "--name", serviceName, "--secret", fmt.Sprintf("source=%s,target=%s", testName, testTarget), "busybox", "top") var secretFlags []string
for testName, testTarget := range testPaths {
id := d.CreateSecret(c, swarm.SecretSpec{
Annotations: swarm.Annotations{
Name: testName,
},
Data: []byte("TESTINGDATA " + testName + " " + testTarget),
})
c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("secrets: %s", id))
secretFlags = append(secretFlags, "--secret", fmt.Sprintf("source=%s,target=%s", testName, testTarget))
}
serviceName := "svc"
serviceCmd := []string{"service", "create", "--name", serviceName}
serviceCmd = append(serviceCmd, secretFlags...)
serviceCmd = append(serviceCmd, "busybox", "top")
out, err := d.Cmd(serviceCmd...)
c.Assert(err, checker.IsNil, check.Commentf(out)) c.Assert(err, checker.IsNil, check.Commentf(out))
out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Secrets }}", serviceName) out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Secrets }}", serviceName)
@ -114,11 +133,82 @@ func (s *DockerSwarmSuite) TestServiceCreateWithSecretSourceTarget(c *check.C) {
var refs []swarm.SecretReference var refs []swarm.SecretReference
c.Assert(json.Unmarshal([]byte(out), &refs), checker.IsNil) c.Assert(json.Unmarshal([]byte(out), &refs), checker.IsNil)
c.Assert(refs, checker.HasLen, 1) c.Assert(refs, checker.HasLen, len(testPaths))
c.Assert(refs[0].SecretName, checker.Equals, testName) var tasks []swarm.Task
c.Assert(refs[0].File, checker.Not(checker.IsNil)) waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
c.Assert(refs[0].File.Name, checker.Equals, testTarget) tasks = d.GetServiceTasks(c, serviceName)
return len(tasks) > 0, nil
}, checker.Equals, true)
task := tasks[0]
waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
if task.NodeID == "" || task.Status.ContainerStatus.ContainerID == "" {
task = d.GetTask(c, task.ID)
}
return task.NodeID != "" && task.Status.ContainerStatus.ContainerID != "", nil
}, checker.Equals, true)
for testName, testTarget := range testPaths {
path := testTarget
if !filepath.IsAbs(path) {
path = filepath.Join("/run/secrets", path)
}
out, err := d.Cmd("exec", task.Status.ContainerStatus.ContainerID, "cat", path)
c.Assert(err, checker.IsNil)
c.Assert(out, checker.Equals, "TESTINGDATA "+testName+" "+testTarget)
}
out, err = d.Cmd("service", "rm", serviceName)
c.Assert(err, checker.IsNil, check.Commentf(out))
}
func (s *DockerSwarmSuite) TestServiceCreateWithSecretReferencedTwice(c *check.C) {
d := s.AddDaemon(c, true, true)
id := d.CreateSecret(c, swarm.SecretSpec{
Annotations: swarm.Annotations{
Name: "mysecret",
},
Data: []byte("TESTINGDATA"),
})
c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("secrets: %s", id))
serviceName := "svc"
out, err := d.Cmd("service", "create", "--name", serviceName, "--secret", "source=mysecret,target=target1", "--secret", "source=mysecret,target=target2", "busybox", "top")
c.Assert(err, checker.IsNil, check.Commentf(out))
out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Secrets }}", serviceName)
c.Assert(err, checker.IsNil)
var refs []swarm.SecretReference
c.Assert(json.Unmarshal([]byte(out), &refs), checker.IsNil)
c.Assert(refs, checker.HasLen, 2)
var tasks []swarm.Task
waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
tasks = d.GetServiceTasks(c, serviceName)
return len(tasks) > 0, nil
}, checker.Equals, true)
task := tasks[0]
waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
if task.NodeID == "" || task.Status.ContainerStatus.ContainerID == "" {
task = d.GetTask(c, task.ID)
}
return task.NodeID != "" && task.Status.ContainerStatus.ContainerID != "", nil
}, checker.Equals, true)
for _, target := range []string{"target1", "target2"} {
c.Assert(err, checker.IsNil, check.Commentf(out))
path := filepath.Join("/run/secrets", target)
out, err := d.Cmd("exec", task.Status.ContainerStatus.ContainerID, "cat", path)
c.Assert(err, checker.IsNil)
c.Assert(out, checker.Equals, "TESTINGDATA")
}
out, err = d.Cmd("service", "rm", serviceName)
c.Assert(err, checker.IsNil, check.Commentf(out))
} }
func (s *DockerSwarmSuite) TestServiceCreateMountTmpfs(c *check.C) { func (s *DockerSwarmSuite) TestServiceCreateMountTmpfs(c *check.C) {

View file

@ -4,7 +4,6 @@ import (
"encoding/csv" "encoding/csv"
"fmt" "fmt"
"os" "os"
"path/filepath"
"strconv" "strconv"
"strings" "strings"
@ -53,10 +52,6 @@ func (o *SecretOpt) Set(value string) error {
case "source", "src": case "source", "src":
options.SecretName = value options.SecretName = value
case "target": case "target":
tDir, _ := filepath.Split(value)
if tDir != "" {
return fmt.Errorf("target must not be a path")
}
options.File.Name = value options.File.Name = value
case "uid": case "uid":
options.File.UID = value options.File.UID = value