diff -up docker-d84a070e476ce923dd03e28232564a87704613ab/daemon/container.go.secrets docker-d84a070e476ce923dd03e28232564a87704613ab/daemon/container.go --- docker-d84a070e476ce923dd03e28232564a87704613ab/daemon/container.go.secrets 2014-07-22 20:29:56.000000000 -0400 +++ docker-d84a070e476ce923dd03e28232564a87704613ab/daemon/container.go 2014-09-10 16:34:28.489624522 -0400 @@ -277,6 +277,9 @@ func (container *Container) Start() (err if err := populateCommand(container, env); err != nil { return err } + if err := container.setupSecretFiles(); err != nil { + return err + } if err := setupMountsForContainer(container); err != nil { return err } @@ -284,7 +287,21 @@ func (container *Container) Start() (err return err } - return container.waitForStart() + if err := container.waitForStart(); err != nil { + return err + } + + // Now the container is running, unmount the secrets on the host + secretsPath, err := container.secretsPath() + if err != nil { + return err + } + + if err := syscall.Unmount(secretsPath, syscall.MNT_DETACH); err != nil { + return err + } + + return nil } func (container *Container) Run() error { @@ -711,6 +728,10 @@ func (container *Container) jsonPath() ( return container.getRootResourcePath("config.json") } +func (container *Container) secretsPath() (string, error) { + return container.getRootResourcePath("secrets") +} + // This method must be exported to be used from the lxc template // This directory is only usable when the container is running func (container *Container) RootfsPath() string { @@ -962,6 +983,31 @@ func (container *Container) verifyDaemon } } +func (container *Container) setupSecretFiles() error { + secretsPath, err := container.secretsPath() + if err != nil { + return err + } + + if err := os.MkdirAll(secretsPath, 0700); err != nil { + return err + } + + if err := syscall.Mount("tmpfs", secretsPath, "tmpfs", uintptr(syscall.MS_NOEXEC|syscall.MS_NOSUID|syscall.MS_NODEV), label.FormatMountLabel("", container.GetMountLabel())); err != nil { + return fmt.Errorf("mounting secret tmpfs: %s", err) + } + + data, err := getHostSecretData() + if err != nil { + return err + } + for _, s := range data { + s.SaveTo(secretsPath) + } + + return nil +} + func (container *Container) setupLinkedContainers() ([]string, error) { var ( env []string diff -up docker-d84a070e476ce923dd03e28232564a87704613ab/daemon/secrets.go.secrets docker-d84a070e476ce923dd03e28232564a87704613ab/daemon/secrets.go --- docker-d84a070e476ce923dd03e28232564a87704613ab/daemon/secrets.go.secrets 2014-09-10 16:28:26.922130421 -0400 +++ docker-d84a070e476ce923dd03e28232564a87704613ab/daemon/secrets.go 2014-09-10 16:28:26.922130421 -0400 @@ -0,0 +1,86 @@ +package daemon + +import ( + "io/ioutil" + "os" + "path/filepath" +) + +type Secret struct { + Name string + IsDir bool + HostBased bool +} + +type SecretData struct { + Name string + Data []byte +} + +func (s SecretData) SaveTo(dir string) error { + path := filepath.Join(dir, s.Name) + if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil && !os.IsExist(err) { + return err + } + if err := ioutil.WriteFile(path, s.Data, 0755); err != nil { + return err + } + return nil +} + +func readAll(root, prefix string) ([]SecretData, error) { + path := filepath.Join(root, prefix) + + data := []SecretData{} + + files, err := ioutil.ReadDir(path) + if err != nil { + if os.IsNotExist(err) { + return data, nil + } + + return nil, err + } + + for _, f := range files { + fileData, err := readFile(root, filepath.Join(prefix, f.Name())) + if err != nil { + // If the file did not exist, might be a dangling symlink + // Ignore the error + if os.IsNotExist(err) { + continue + } + return nil, err + } + data = append(data, fileData...) + } + + return data, nil +} + +func readFile(root, name string) ([]SecretData, error) { + path := filepath.Join(root, name) + + s, err := os.Stat(path) + if err != nil { + return nil, err + } + + if s.IsDir() { + dirData, err := readAll(root, name) + if err != nil { + return nil, err + } + return dirData, nil + } else { + bytes, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + return []SecretData{{Name: name, Data: bytes}}, nil + } +} + +func getHostSecretData() ([]SecretData, error) { + return readAll("/usr/share/rhel/secrets", "") +} diff -up docker-d84a070e476ce923dd03e28232564a87704613ab/daemon/volumes.go.secrets docker-d84a070e476ce923dd03e28232564a87704613ab/daemon/volumes.go --- docker-d84a070e476ce923dd03e28232564a87704613ab/daemon/volumes.go.secrets 2014-09-10 16:28:26.922130421 -0400 +++ docker-d84a070e476ce923dd03e28232564a87704613ab/daemon/volumes.go 2014-09-10 16:40:34.683228046 -0400 @@ -48,6 +48,13 @@ func setupMountsForContainer(container * mounts = append(mounts, execdriver.Mount{container.HostsPath, "/etc/hosts", false, true}) } + secretsPath, err := container.secretsPath() + if err != nil { + return err + } + + mounts = append(mounts, execdriver.Mount{secretsPath, "/run/secrets", true, true}) + // Mount user specified volumes // Note, these are not private because you may want propagation of (un)mounts from host // volumes. For instance if you use -v /usr:/usr and the host later mounts /usr/share you diff -up docker-d84a070e476ce923dd03e28232564a87704613ab/graph/graph.go.secrets docker-d84a070e476ce923dd03e28232564a87704613ab/graph/graph.go --- docker-d84a070e476ce923dd03e28232564a87704613ab/graph/graph.go.secrets 2014-07-22 20:29:56.000000000 -0400 +++ docker-d84a070e476ce923dd03e28232564a87704613ab/graph/graph.go 2014-09-10 16:28:26.923130420 -0400 @@ -266,6 +266,7 @@ func SetupInitLayer(initLayer string) er "/etc/hostname": "file", "/dev/console": "file", "/etc/mtab": "/proc/mounts", + "/run/secrets": "dir", } { parts := strings.Split(pth, "/") prev := "/"