Skip to content

Commit

Permalink
Support uid,gid,mode options for secrets
Browse files Browse the repository at this point in the history
Support UID, GID, Mode options for mount type secrets. Also, change
default secret permissions to 444 so all users can read secret.

Signed-off-by: Ashley Cui <[email protected]>
  • Loading branch information
ashley-cui committed Jul 12, 2021
1 parent 60d12f7 commit 6f9d963
Show file tree
Hide file tree
Showing 17 changed files with 197 additions and 49 deletions.
53 changes: 47 additions & 6 deletions cmd/podman/common/specgen.go
Original file line number Diff line number Diff line change
Expand Up @@ -778,28 +778,38 @@ func parseThrottleIOPsDevices(iopsDevices []string) (map[string]specs.LinuxThrot
return td, nil
}

func parseSecrets(secrets []string) ([]string, map[string]string, error) {
func parseSecrets(secrets []string) ([]specgen.Secret, map[string]string, error) {
secretParseError := errors.New("error parsing secret")
var mount []string
var mount []specgen.Secret
envs := make(map[string]string)
for _, val := range secrets {
// mount only tells if user has set an option that can only be used with mount secret type
mountOnly := false
source := ""
secretType := ""
target := ""
var uid, gid uint32
// default mode 444 octal = 292 decimal
var mode uint32 = 292
split := strings.Split(val, ",")

// --secret mysecret
if len(split) == 1 {
source = val
mount = append(mount, source)
mountSecret := specgen.Secret{
Source: val,
UID: uid,
GID: gid,
Mode: mode,
}
mount = append(mount, mountSecret)
continue
}
// --secret mysecret,opt=opt
if !strings.Contains(split[0], "=") {
source = split[0]
split = split[1:]
}
// TODO: implement other secret options

for _, val := range split {
kv := strings.SplitN(val, "=", 2)
if len(kv) < 2 {
Expand All @@ -818,6 +828,28 @@ func parseSecrets(secrets []string) ([]string, map[string]string, error) {
secretType = kv[1]
case "target":
target = kv[1]
case "mode":
mountOnly = true
mode64, err := strconv.ParseUint(kv[1], 8, 32)
if err != nil {
return nil, nil, errors.Wrapf(secretParseError, "mode %s invalid", kv[1])
}
mode = uint32(mode64)
case "uid", "UID":
mountOnly = true
uid64, err := strconv.ParseUint(kv[1], 10, 32)
if err != nil {
return nil, nil, errors.Wrapf(secretParseError, "UID %s invalid", kv[1])
}
uid = uint32(uid64)
case "gid", "GID":
mountOnly = true
gid64, err := strconv.ParseUint(kv[1], 10, 32)
if err != nil {
return nil, nil, errors.Wrapf(secretParseError, "GID %s invalid", kv[1])
}
gid = uint32(gid64)

default:
return nil, nil, errors.Wrapf(secretParseError, "option %s invalid", val)
}
Expand All @@ -833,9 +865,18 @@ func parseSecrets(secrets []string) ([]string, map[string]string, error) {
if target != "" {
return nil, nil, errors.Wrapf(secretParseError, "target option is invalid for mounted secrets")
}
mount = append(mount, source)
mountSecret := specgen.Secret{
Source: source,
UID: uid,
GID: gid,
Mode: mode,
}
mount = append(mount, mountSecret)
}
if secretType == "env" {
if mountOnly {
return nil, nil, errors.Wrap(secretParseError, "UID, GID, Mode options cannot be set with secret type env")
}
if target == "" {
target = source
}
Expand Down
3 changes: 3 additions & 0 deletions docs/source/markdown/podman-create.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -859,6 +859,9 @@ Secret Options

- `type=mount|env` : How the secret will be exposed to the container. Default mount.
- `target=target` : Target of secret. Defauts to secret name.
- `uid=0` : UID of secret. Defaults to 0. Mount secret type only.
- `gid=0` : GID of secret. Defaults to 0. Mount secret type only.
- `mode=0` : Mode of secret. Defaults to 0444. Mount secret type only.

#### **--security-opt**=*option*

Expand Down
3 changes: 3 additions & 0 deletions docs/source/markdown/podman-run.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -911,6 +911,9 @@ Secret Options

- `type=mount|env` : How the secret will be exposed to the container. Default mount.
- `target=target` : Target of secret. Defauts to secret name.
- `uid=0` : UID of secret. Defaults to 0. Mount secret type only.
- `gid=0` : GID of secret. Defaults to 0. Mount secret type only.
- `mode=0` : Mode of secret. Defaults to 0444. Mount secret type only.

#### **--security-opt**=*option*

Expand Down
14 changes: 13 additions & 1 deletion libpod/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,18 @@ type ContainerImageVolume struct {
ReadWrite bool `json:"rw"`
}

// ContainerSecret is a secret that is mounted in a container
type ContainerSecret struct {
// Secret is the secret
*secrets.Secret
// UID is tbe UID of the secret file
UID uint32
// GID is the GID of the secret file
GID uint32
// Mode is the mode of the secret file
Mode uint32
}

// ContainerNetworkDescriptions describes the relationship between the CNI
// network and the ethN where N is an integer
type ContainerNetworkDescriptions map[string]int
Expand Down Expand Up @@ -1136,7 +1148,7 @@ func (c *Container) Umask() string {
}

//Secrets return the secrets in the container
func (c *Container) Secrets() []*secrets.Secret {
func (c *Container) Secrets() []*ContainerSecret {
return c.config.Secrets
}

Expand Down
2 changes: 1 addition & 1 deletion libpod/container_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ type ContainerRootFSConfig struct {
// default, but others do not.
CreateWorkingDir bool `json:"createWorkingDir,omitempty"`
// Secrets lists secrets to mount into the container
Secrets []*secrets.Secret `json:"secrets,omitempty"`
Secrets []*ContainerSecret `json:"secrets,omitempty"`
// SecretPath is the secrets location in storage
SecretsPath string `json:"secretsPath"`
// Volatile specifies whether the container storage can be optimized
Expand Down
4 changes: 3 additions & 1 deletion libpod/container_inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -343,11 +343,13 @@ func (c *Container) generateInspectContainerConfig(spec *spec.Spec) *define.Insp
ctrConfig.CreateCommand = c.config.CreateCommand

ctrConfig.Timezone = c.config.Timezone

for _, secret := range c.config.Secrets {
newSec := define.InspectSecret{}
newSec.Name = secret.Name
newSec.ID = secret.ID
newSec.UID = secret.UID
newSec.GID = secret.GID
newSec.Mode = secret.Mode
ctrConfig.Secrets = append(ctrConfig.Secrets, &newSec)
}

Expand Down
19 changes: 15 additions & 4 deletions libpod/container_internal.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (

metadata "github.com/checkpoint-restore/checkpointctl/lib"
"github.com/containers/buildah/copier"
"github.com/containers/common/pkg/secrets"
butil "github.com/containers/buildah/util"
"github.com/containers/podman/v3/libpod/define"
"github.com/containers/podman/v3/libpod/events"
"github.com/containers/podman/v3/pkg/cgroups"
Expand All @@ -24,6 +24,7 @@ import (
"github.com/containers/podman/v3/pkg/hooks/exec"
"github.com/containers/podman/v3/pkg/rootless"
"github.com/containers/podman/v3/pkg/selinux"
"github.com/containers/podman/v3/pkg/util"
"github.com/containers/storage"
"github.com/containers/storage/pkg/archive"
"github.com/containers/storage/pkg/idtools"
Expand Down Expand Up @@ -2188,21 +2189,31 @@ func (c *Container) hasNamespace(namespace spec.LinuxNamespaceType) bool {
}

// 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())
func (c *Container) extractSecretToCtrStorage(secr *ContainerSecret) error {
manager, err := c.runtime.SecretsManager()
if err != nil {
return err
}
secr, data, err := manager.LookupSecretData(name)
_, data, err := manager.LookupSecretData(secr.Name)
if err != nil {
return err
}
secretFile := filepath.Join(c.config.SecretsPath, secr.Name)

hostUID, hostGID, err := butil.GetHostIDs(util.IDtoolsToRuntimeSpec(c.config.IDMappings.UIDMap), util.IDtoolsToRuntimeSpec(c.config.IDMappings.GIDMap), secr.UID, secr.GID)
if err != nil {
return errors.Wrap(err, "unable to extract secret")
}
err = ioutil.WriteFile(secretFile, data, 0644)
if err != nil {
return errors.Wrapf(err, "unable to create %s", secretFile)
}
if err := os.Lchown(secretFile, int(hostUID), int(hostGID)); err != nil {
return err
}
if err := os.Chmod(secretFile, os.FileMode(secr.Mode)); err != nil {
return err
}
if err := label.Relabel(secretFile, c.config.MountLabel, false); err != nil {
return err
}
Expand Down
8 changes: 5 additions & 3 deletions libpod/container_internal_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import (
"github.com/containers/common/pkg/apparmor"
"github.com/containers/common/pkg/chown"
"github.com/containers/common/pkg/config"
"github.com/containers/common/pkg/secrets"
"github.com/containers/common/pkg/subscriptions"
"github.com/containers/common/pkg/umask"
"github.com/containers/podman/v3/libpod/define"
Expand Down Expand Up @@ -759,7 +758,10 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
return nil, errors.Wrapf(err, "error setting up OCI Hooks")
}
if len(c.config.EnvSecrets) > 0 {
manager, err := secrets.NewManager(c.runtime.GetSecretsStorageDir())
manager, err := c.runtime.SecretsManager()
if err != nil {
return nil, err
}
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -2413,7 +2415,7 @@ func (c *Container) createSecretMountDir() error {
oldUmask := umask.Set(0)
defer umask.Set(oldUmask)

if err := os.MkdirAll(src, 0644); err != nil {
if err := os.MkdirAll(src, 0755); err != nil {
return err
}
if err := label.Relabel(src, c.config.MountLabel, false); err != nil {
Expand Down
15 changes: 9 additions & 6 deletions libpod/define/container_inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -713,13 +713,16 @@ type DriverData struct {
Data map[string]string `json:"Data"`
}

// InspectHostPort provides information on a port on the host that a container's
// port is bound to.
// InspectSecret contains information on secrets mounted inside the container
type InspectSecret struct {
// IP on the host we are bound to. "" if not specified (binding to all
// IPs).
// Name is the name of the secret
Name string `json:"Name"`
// Port on the host we are bound to. No special formatting - just an
// integer stuffed into a string.
// ID is the ID of the secret
ID string `json:"ID"`
// ID is the UID of the mounted secret file
UID uint32 `json:"UID"`
// ID is the GID of the mounted secret file
GID uint32 `json:"GID"`
// ID is the ID of the mode of the mounted secret file
Mode uint32 `json:"Mode"`
}
17 changes: 3 additions & 14 deletions libpod/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -1712,23 +1712,12 @@ func WithUmask(umask string) CtrCreateOption {
}

// WithSecrets adds secrets to the container
func WithSecrets(secretNames []string) CtrCreateOption {
func WithSecrets(containerSecrets []*ContainerSecret) 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)
}

ctr.config.Secrets = containerSecrets
return nil
}
}
Expand All @@ -1740,7 +1729,7 @@ func WithEnvSecrets(envSecrets map[string]string) CtrCreateOption {
if ctr.valid {
return define.ErrCtrFinalized
}
manager, err := secrets.NewManager(ctr.runtime.GetSecretsStorageDir())
manager, err := ctr.runtime.SecretsManager()
if err != nil {
return err
}
Expand Down
15 changes: 15 additions & 0 deletions libpod/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/containers/buildah/pkg/parse"
"github.com/containers/common/libimage"
"github.com/containers/common/pkg/config"
"github.com/containers/common/pkg/secrets"
"github.com/containers/image/v5/pkg/sysregistriesv2"
is "github.com/containers/image/v5/storage"
"github.com/containers/image/v5/types"
Expand Down Expand Up @@ -106,6 +107,8 @@ type Runtime struct {

// noStore indicates whether we need to interact with a store or not
noStore bool
// secretsManager manages secrets
secretsManager *secrets.SecretsManager
}

// SetXdgDirs ensures the XDG_RUNTIME_DIR env and XDG_CONFIG_HOME variables are set.
Expand Down Expand Up @@ -1091,6 +1094,18 @@ func (r *Runtime) GetSecretsStorageDir() string {
return filepath.Join(r.store.GraphRoot(), "secrets")
}

// SecretsManager returns the directory that the secrets manager should take
func (r *Runtime) SecretsManager() (*secrets.SecretsManager, error) {
if r.secretsManager == nil {
manager, err := secrets.NewManager(r.GetSecretsStorageDir())
if err != nil {
return nil, err
}
r.secretsManager = manager
}
return r.secretsManager, nil
}

func graphRootMounted() bool {
f, err := os.OpenFile("/run/.containerenv", os.O_RDONLY, os.ModePerm)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion libpod/runtime_ctr.go
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai
return nil, err
}
for _, secr := range ctr.config.Secrets {
err = ctr.extractSecretToCtrStorage(secr.Name)
err = ctr.extractSecretToCtrStorage(secr)
if err != nil {
return nil, err
}
Expand Down
3 changes: 1 addition & 2 deletions pkg/domain/infra/abi/play.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (

"github.com/containers/common/libimage"
"github.com/containers/common/pkg/config"
"github.com/containers/common/pkg/secrets"
"github.com/containers/image/v5/types"
"github.com/containers/podman/v3/libpod"
"github.com/containers/podman/v3/libpod/define"
Expand Down Expand Up @@ -161,7 +160,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
)

// Create the secret manager before hand
secretsManager, err := secrets.NewManager(ic.Libpod.GetSecretsStorageDir())
secretsManager, err := ic.Libpod.SecretsManager()
if err != nil {
return nil, err
}
Expand Down
Loading

0 comments on commit 6f9d963

Please sign in to comment.