Merge pull request #32208 from johnstep/windows-secrets

Add Windows secrets support
This commit is contained in:
John Howard 2017-05-16 13:41:53 -07:00 committed by GitHub
commit 55fd0d0115
5 changed files with 144 additions and 15 deletions

View file

@ -8,10 +8,12 @@ import (
"path/filepath"
containertypes "github.com/docker/docker/api/types/container"
"github.com/docker/docker/pkg/system"
)
const (
containerSecretMountPath = `C:\ProgramData\Docker\secrets`
containerSecretMountPath = `C:\ProgramData\Docker\secrets`
containerInternalSecretMountPath = `C:\ProgramData\Docker\internal\secrets`
)
// Container holds fields specific to the Windows implementation. See
@ -47,14 +49,46 @@ func (container *Container) IpcMounts() []Mount {
return nil
}
// SecretMounts returns the mounts for the secret path
func (container *Container) SecretMounts() []Mount {
// CreateSecretSymlinks creates symlinks to files in the secret mount.
func (container *Container) CreateSecretSymlinks() error {
for _, r := range container.SecretReferences {
if r.File == nil {
continue
}
resolvedPath, _, err := container.ResolvePath(getSecretTargetPath(r))
if err != nil {
return err
}
if err := system.MkdirAll(filepath.Dir(resolvedPath), 0); err != nil {
return err
}
if err := os.Symlink(filepath.Join(containerInternalSecretMountPath, r.SecretID), resolvedPath); err != nil {
return err
}
}
return nil
}
// SecretMounts returns the mount for the secret path.
// All secrets are stored in a single mount on Windows. Target symlinks are
// created for each secret, pointing to the files in this mount.
func (container *Container) SecretMounts() []Mount {
var mounts []Mount
if len(container.SecretReferences) > 0 {
mounts = append(mounts, Mount{
Source: container.SecretMountPath(),
Destination: containerInternalSecretMountPath,
Writable: false,
})
}
return mounts
}
// UnmountSecrets unmounts the fs for secrets
func (container *Container) UnmountSecrets() error {
return nil
return os.RemoveAll(container.SecretMountPath())
}
// DetachAndUnmount unmounts all volumes.

View file

@ -1,10 +1,15 @@
// +build windows
package daemon
import (
"fmt"
"io/ioutil"
"os"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/container"
"github.com/docker/docker/pkg/system"
"github.com/docker/libnetwork"
"github.com/pkg/errors"
)
func (daemon *Daemon) setupLinkedContainers(container *container.Container) ([]string, error) {
@ -35,6 +40,57 @@ func detachMounted(path string) error {
return nil
}
func (daemon *Daemon) setupSecretDir(c *container.Container) (setupErr error) {
if len(c.SecretReferences) == 0 {
return nil
}
localMountPath := c.SecretMountPath()
logrus.Debugf("secrets: setting up secret dir: %s", localMountPath)
// create local secret root
if err := system.MkdirAllWithACL(localMountPath, 0); err != nil {
return errors.Wrap(err, "error creating secret local directory")
}
defer func() {
if setupErr != nil {
if err := os.RemoveAll(localMountPath); err != nil {
logrus.Errorf("error cleaning up secret mount: %s", err)
}
}
}()
if c.DependencyStore == nil {
return fmt.Errorf("secret store is not initialized")
}
for _, s := range c.SecretReferences {
// TODO (ehazlett): use type switch when more are supported
if s.File == nil {
logrus.Error("secret target type is not a file target")
continue
}
// secrets are created in the SecretMountPath on the host, at a
// single level
fPath := c.SecretFilePath(*s)
logrus.WithFields(logrus.Fields{
"name": s.File.Name,
"path": fPath,
}).Debug("injecting secret")
secret := c.DependencyStore.Secrets().Get(s.SecretID)
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")
}
}
return nil
}
func killProcessDirectly(container *container.Container) error {
return nil
}

View file

@ -25,11 +25,51 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) {
// In base spec
s.Hostname = c.FullHostname()
if err := daemon.setupSecretDir(c); err != nil {
return nil, err
}
// In s.Mounts
mounts, err := daemon.setupMounts(c)
if err != nil {
return nil, err
}
var isHyperV bool
if c.HostConfig.Isolation.IsDefault() {
// Container using default isolation, so take the default from the daemon configuration
isHyperV = daemon.defaultIsolation.IsHyperV()
} else {
// Container may be requesting an explicit isolation mode.
isHyperV = c.HostConfig.Isolation.IsHyperV()
}
// If the container has not been started, and has secrets, create symlinks
// to each secret. If it has been started before, the symlinks should have
// already been created. Also, it is important to not mount a Hyper-V
// container that has been started before, to protect the host from the
// container; for example, from malicious mutation of NTFS data structures.
if !c.HasBeenStartedBefore && len(c.SecretReferences) > 0 {
// The container file system is mounted before this function is called,
// except for Hyper-V containers, so mount it here in that case.
if isHyperV {
if err := daemon.Mount(c); err != nil {
return nil, err
}
}
err := c.CreateSecretSymlinks()
if isHyperV {
daemon.Unmount(c)
}
if err != nil {
return nil, err
}
}
if m := c.SecretMounts(); m != nil {
mounts = append(mounts, m...)
}
for _, mount := range mounts {
m := specs.Mount{
Source: mount.Source,
@ -64,14 +104,6 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) {
s.Process.User.Username = c.Config.User
// In spec.Root. This is not set for Hyper-V containers
var isHyperV bool
if c.HostConfig.Isolation.IsDefault() {
// Container using default isolation, so take the default from the daemon configuration
isHyperV = daemon.defaultIsolation.IsHyperV()
} else {
// Container may be requesting an explicit isolation mode.
isHyperV = c.HostConfig.Isolation.IsHyperV()
}
if !isHyperV {
s.Root.Path = c.BaseFS
}

View file

@ -1,4 +1,4 @@
// +build !linux
// +build !linux,!windows
package daemon

View file

@ -0,0 +1,7 @@
// +build windows
package daemon
func secretsSupported() bool {
return true
}