Merge pull request #32208 from johnstep/windows-secrets
Add Windows secrets support
This commit is contained in:
commit
55fd0d0115
5 changed files with 144 additions and 15 deletions
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// +build !linux
|
||||
// +build !linux,!windows
|
||||
|
||||
package daemon
|
||||
|
||||
|
|
7
daemon/secrets_windows.go
Normal file
7
daemon/secrets_windows.go
Normal file
|
@ -0,0 +1,7 @@
|
|||
// +build windows
|
||||
|
||||
package daemon
|
||||
|
||||
func secretsSupported() bool {
|
||||
return true
|
||||
}
|
Loading…
Reference in a new issue