aboutsummaryrefslogtreecommitdiff
path: root/libpod
diff options
context:
space:
mode:
authorAshley Cui <acui@redhat.com>2021-01-15 01:27:23 -0500
committerAshley Cui <acui@redhat.com>2021-02-09 09:13:21 -0500
commit832a69b0bee6ec289521fbd59ddd480372493ee3 (patch)
tree4c8a14b7fad879dc454c37f8b59120cf74ceafd1 /libpod
parent2aaf631586e82192e6b7b992e6b5c8717eb792d7 (diff)
downloadpodman-832a69b0bee6ec289521fbd59ddd480372493ee3.tar.gz
podman-832a69b0bee6ec289521fbd59ddd480372493ee3.tar.bz2
podman-832a69b0bee6ec289521fbd59ddd480372493ee3.zip
Implement Secrets
Implement podman secret create, inspect, ls, rm Implement podman run/create --secret Secrets are blobs of data that are sensitive. Currently, the only secret driver supported is filedriver, which means creating a secret stores it in base64 unencrypted in a file. After creating a secret, a user can use the --secret flag to expose the secret inside the container at /run/secrets/[secretname] This secret will not be commited to an image on a podman commit Signed-off-by: Ashley Cui <acui@redhat.com>
Diffstat (limited to 'libpod')
-rw-r--r--libpod/container.go6
-rw-r--r--libpod/container_config.go5
-rw-r--r--libpod/container_inspect.go7
-rw-r--r--libpod/container_internal.go24
-rw-r--r--libpod/container_internal_linux.go47
-rw-r--r--libpod/define/container_inspect.go13
-rw-r--r--libpod/options.go23
-rw-r--r--libpod/runtime.go5
-rw-r--r--libpod/runtime_ctr.go13
9 files changed, 139 insertions, 4 deletions
diff --git a/libpod/container.go b/libpod/container.go
index ed7535bc8..e667cd991 100644
--- a/libpod/container.go
+++ b/libpod/container.go
@@ -10,6 +10,7 @@ import (
"github.com/containernetworking/cni/pkg/types"
cnitypes "github.com/containernetworking/cni/pkg/types/current"
+ "github.com/containers/common/pkg/secrets"
"github.com/containers/image/v5/manifest"
"github.com/containers/podman/v2/libpod/define"
"github.com/containers/podman/v2/libpod/lock"
@@ -1133,6 +1134,11 @@ func (c *Container) Umask() string {
return c.config.Umask
}
+//Secrets return the secrets in the container
+func (c *Container) Secrets() []*secrets.Secret {
+ return c.config.Secrets
+}
+
// Networks gets all the networks this container is connected to.
// Please do NOT use ctr.config.Networks, as this can be changed from those
// values at runtime via network connect and disconnect.
diff --git a/libpod/container_config.go b/libpod/container_config.go
index 93ac8807d..5d7e65f2b 100644
--- a/libpod/container_config.go
+++ b/libpod/container_config.go
@@ -4,6 +4,7 @@ import (
"net"
"time"
+ "github.com/containers/common/pkg/secrets"
"github.com/containers/image/v5/manifest"
"github.com/containers/podman/v2/pkg/namespaces"
"github.com/containers/storage"
@@ -146,6 +147,10 @@ type ContainerRootFSConfig struct {
// working directory if it does not exist. Some OCI runtimes do this by
// default, but others do not.
CreateWorkingDir bool `json:"createWorkingDir,omitempty"`
+ // Secrets lists secrets to mount into the container
+ Secrets []*secrets.Secret `json:"secrets,omitempty"`
+ // SecretPath is the secrets location in storage
+ SecretsPath string `json:"secretsPath"`
}
// ContainerSecurityConfig is an embedded sub-config providing security configuration
diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go
index ac7eae56b..f50c7dbfe 100644
--- a/libpod/container_inspect.go
+++ b/libpod/container_inspect.go
@@ -340,6 +340,13 @@ func (c *Container) generateInspectContainerConfig(spec *spec.Spec) *define.Insp
ctrConfig.Timezone = c.config.Timezone
+ for _, secret := range c.config.Secrets {
+ newSec := define.InspectSecret{}
+ newSec.Name = secret.Name
+ newSec.ID = secret.ID
+ ctrConfig.Secrets = append(ctrConfig.Secrets, &newSec)
+ }
+
// Pad Umask to 4 characters
if len(c.config.Umask) < 4 {
pad := strings.Repeat("0", 4-len(c.config.Umask))
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index 5a61f7fe6..b280e79d1 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -13,6 +13,7 @@ import (
"strings"
"time"
+ "github.com/containers/common/pkg/secrets"
"github.com/containers/podman/v2/libpod/define"
"github.com/containers/podman/v2/libpod/events"
"github.com/containers/podman/v2/pkg/cgroups"
@@ -29,6 +30,7 @@ import (
securejoin "github.com/cyphar/filepath-securejoin"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-tools/generate"
+ "github.com/opencontainers/selinux/go-selinux/label"
"github.com/opentracing/opentracing-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -2212,3 +2214,25 @@ func (c *Container) hasNamespace(namespace spec.LinuxNamespaceType) bool {
}
return false
}
+
+// extractSecretToStorage copies a secret's data from the secrets manager to the container's static dir
+func (c *Container) extractSecretToCtrStorage(name string) error {
+ manager, err := secrets.NewManager(c.runtime.GetSecretsStorageDir())
+ if err != nil {
+ return err
+ }
+ secr, data, err := manager.LookupSecretData(name)
+ if err != nil {
+ return err
+ }
+ secretFile := filepath.Join(c.config.SecretsPath, secr.Name)
+
+ err = ioutil.WriteFile(secretFile, data, 0644)
+ if err != nil {
+ return errors.Wrapf(err, "unable to create %s", secretFile)
+ }
+ if err := label.Relabel(secretFile, c.config.MountLabel, false); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index ba85a1f47..3583f8fdd 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -25,6 +25,7 @@ import (
"github.com/containers/common/pkg/apparmor"
"github.com/containers/common/pkg/config"
"github.com/containers/common/pkg/subscriptions"
+ "github.com/containers/common/pkg/umask"
"github.com/containers/podman/v2/libpod/define"
"github.com/containers/podman/v2/libpod/events"
"github.com/containers/podman/v2/pkg/annotations"
@@ -1643,14 +1644,30 @@ rootless=%d
c.state.BindMounts["/run/.containerenv"] = containerenvPath
}
- // Add Secret Mounts
- secretMounts := subscriptions.MountsWithUIDGID(c.config.MountLabel, c.state.RunDir, c.runtime.config.Containers.DefaultMountsFile, c.state.Mountpoint, c.RootUID(), c.RootGID(), rootless.IsRootless(), false)
- for _, mount := range secretMounts {
+ // Add Subscription Mounts
+ subscriptionMounts := subscriptions.MountsWithUIDGID(c.config.MountLabel, c.state.RunDir, c.runtime.config.Containers.DefaultMountsFile, c.state.Mountpoint, c.RootUID(), c.RootGID(), rootless.IsRootless(), false)
+ for _, mount := range subscriptionMounts {
if _, ok := c.state.BindMounts[mount.Destination]; !ok {
c.state.BindMounts[mount.Destination] = mount.Source
}
}
+ // Secrets are mounted by getting the secret data from the secrets manager,
+ // copying the data into the container's static dir,
+ // then mounting the copied dir into /run/secrets.
+ // The secrets mounting must come after subscription mounts, since subscription mounts
+ // creates the /run/secrets dir in the container where we mount as well.
+ if len(c.Secrets()) > 0 {
+ // create /run/secrets if subscriptions did not create
+ if err := c.createSecretMountDir(); err != nil {
+ return errors.Wrapf(err, "error creating secrets mount")
+ }
+ for _, secret := range c.Secrets() {
+ src := filepath.Join(c.config.SecretsPath, secret.Name)
+ dest := filepath.Join("/run/secrets", secret.Name)
+ c.state.BindMounts[dest] = src
+ }
+ }
return nil
}
@@ -2368,3 +2385,27 @@ func (c *Container) checkFileExistsInRootfs(file string) (bool, error) {
}
return true, nil
}
+
+// Creates and mounts an empty dir to mount secrets into, if it does not already exist
+func (c *Container) createSecretMountDir() error {
+ src := filepath.Join(c.state.RunDir, "/run/secrets")
+ _, err := os.Stat(src)
+ if os.IsNotExist(err) {
+ oldUmask := umask.Set(0)
+ defer umask.Set(oldUmask)
+
+ if err := os.MkdirAll(src, 0644); err != nil {
+ return err
+ }
+ if err := label.Relabel(src, c.config.MountLabel, false); err != nil {
+ return err
+ }
+ if err := os.Chown(src, c.RootUID(), c.RootGID()); err != nil {
+ return err
+ }
+ c.state.BindMounts["/run/secrets"] = src
+ return nil
+ }
+
+ return err
+}
diff --git a/libpod/define/container_inspect.go b/libpod/define/container_inspect.go
index 9a93e2ffd..2cdd53cbc 100644
--- a/libpod/define/container_inspect.go
+++ b/libpod/define/container_inspect.go
@@ -62,6 +62,8 @@ type InspectContainerConfig struct {
SystemdMode bool `json:"SystemdMode,omitempty"`
// Umask is the umask inside the container.
Umask string `json:"Umask,omitempty"`
+ // Secrets are the secrets mounted in the container
+ Secrets []*InspectSecret `json:"Secrets,omitempty"`
}
// InspectRestartPolicy holds information about the container's restart policy.
@@ -705,3 +707,14 @@ type DriverData struct {
Name string `json:"Name"`
Data map[string]string `json:"Data"`
}
+
+// InspectHostPort provides information on a port on the host that a container's
+// port is bound to.
+type InspectSecret struct {
+ // IP on the host we are bound to. "" if not specified (binding to all
+ // IPs).
+ Name string `json:"Name"`
+ // Port on the host we are bound to. No special formatting - just an
+ // integer stuffed into a string.
+ ID string `json:"ID"`
+}
diff --git a/libpod/options.go b/libpod/options.go
index 20f62ee37..74ee60fef 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -8,6 +8,7 @@ import (
"syscall"
"github.com/containers/common/pkg/config"
+ "github.com/containers/common/pkg/secrets"
"github.com/containers/image/v5/manifest"
"github.com/containers/image/v5/types"
"github.com/containers/podman/v2/libpod/define"
@@ -1687,6 +1688,28 @@ func WithUmask(umask string) CtrCreateOption {
}
}
+// WithSecrets adds secrets to the container
+func WithSecrets(secretNames []string) CtrCreateOption {
+ return func(ctr *Container) error {
+ if ctr.valid {
+ return define.ErrCtrFinalized
+ }
+ manager, err := secrets.NewManager(ctr.runtime.GetSecretsStorageDir())
+ if err != nil {
+ return err
+ }
+ for _, name := range secretNames {
+ secr, err := manager.Lookup(name)
+ if err != nil {
+ return err
+ }
+ ctr.config.Secrets = append(ctr.config.Secrets, secr)
+ }
+
+ return nil
+ }
+}
+
// Pod Creation Options
// WithInfraImage sets the infra image for libpod.
diff --git a/libpod/runtime.go b/libpod/runtime.go
index 0dc220b52..1ad39fe2f 100644
--- a/libpod/runtime.go
+++ b/libpod/runtime.go
@@ -904,3 +904,8 @@ func (r *Runtime) getVolumePlugin(name string) (*plugin.VolumePlugin, error) {
return plugin.GetVolumePlugin(name, pluginPath)
}
+
+// GetSecretsStoreageDir returns the directory that the secrets manager should take
+func (r *Runtime) GetSecretsStorageDir() string {
+ return filepath.Join(r.store.GraphRoot(), "secrets")
+}
diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go
index d2bcd8db3..49cf42626 100644
--- a/libpod/runtime_ctr.go
+++ b/libpod/runtime_ctr.go
@@ -422,6 +422,18 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai
}
}()
+ ctr.config.SecretsPath = filepath.Join(ctr.config.StaticDir, "secrets")
+ err = os.MkdirAll(ctr.config.SecretsPath, 0644)
+ if err != nil {
+ return nil, err
+ }
+ for _, secr := range ctr.config.Secrets {
+ err = ctr.extractSecretToCtrStorage(secr.Name)
+ if err != nil {
+ return nil, err
+ }
+ }
+
if ctr.config.ConmonPidFile == "" {
ctr.config.ConmonPidFile = filepath.Join(ctr.state.RunDir, "conmon.pid")
}
@@ -492,7 +504,6 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai
toLock.lock.Lock()
defer toLock.lock.Unlock()
}
-
// Add the container to the state
// TODO: May be worth looking into recovering from name/ID collisions here
if ctr.config.Pod != "" {