Files
moby/daemon/exec_linux.go
T
Paweł GronowskiandYoufu Zhang 235d2876f8 api: Add container umask configuration
Add HostConfig.Umask and pass it to the OCI process spec for container
entrypoints, exec processes, and healthchecks.

Preserve the configured umask when exec user resolution reconstructs the
OCI User value, so runtimes receive it in the per-exec process spec.

Keep the field aligned with OCI's uint32 representation - raw JSON
clients need to use 18 for octal 0022 for example.

Co-authored-by: Youfu Zhang <zhangyoufu@gmail.com>
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-09-03 10:41:23 +02:00

97 lines
2.8 KiB
Go

package daemon
import (
"context"
containerd "github.com/containerd/containerd/v2/client"
coci "github.com/containerd/containerd/v2/pkg/oci"
"github.com/moby/moby/v2/daemon/config"
"github.com/moby/moby/v2/daemon/container"
"github.com/moby/moby/v2/daemon/pkg/oci/caps"
"github.com/opencontainers/runtime-spec/specs-go"
)
func getUserFromContainerd(ctx context.Context, containerdCli *containerd.Client, ec *container.ExecConfig) (specs.User, error) {
ctr, err := containerdCli.LoadContainer(ctx, ec.Container.ID)
if err != nil {
return specs.User{}, err
}
cinfo, err := ctr.Info(ctx)
if err != nil {
return specs.User{}, err
}
spec, err := ctr.Spec(ctx)
if err != nil {
return specs.User{}, err
}
opts := []coci.SpecOpts{
coci.WithUser(ec.User),
coci.WithAdditionalGIDs(ec.User),
coci.WithAppendAdditionalGroups(ec.Container.HostConfig.GroupAdd...),
}
for _, opt := range opts {
if err := opt(ctx, containerdCli, &cinfo, spec); err != nil {
return specs.User{}, err
}
}
return spec.Process.User, nil
}
func (daemon *Daemon) execSetPlatformOpt(ctx context.Context, daemonCfg *config.Config, ec *container.ExecConfig, p *specs.Process) error {
if ec.User != "" {
var user specs.User
var err error
if daemon.UsesSnapshotter() {
user, err = getUserFromContainerd(ctx, daemon.containerdClient, ec)
} else {
user, err = getUser(ec.Container, ec.User)
}
if err != nil {
return err
}
// Preserve the umask inherited from the container's process spec.
user.Umask = p.User.Umask
p.User = user
}
if ec.Privileged {
p.Capabilities = &specs.LinuxCapabilities{
Bounding: caps.GetAllCapabilities(),
Permitted: caps.GetAllCapabilities(),
Effective: caps.GetAllCapabilities(),
}
}
if appArmorSupported() {
var appArmorProfile string
if ec.Container.AppArmorProfile != "" {
appArmorProfile = ec.Container.AppArmorProfile
} else if ec.Container.HostConfig.Privileged {
// `docker exec --privileged` does not currently disable AppArmor
// profiles. Privileged configuration of the container is inherited
appArmorProfile = unconfinedAppArmorProfile
} else {
appArmorProfile = defaultAppArmorProfile
}
if appArmorProfile == defaultAppArmorProfile {
// Unattended upgrades and other fun services can unload AppArmor
// profiles inadvertently. Since we cannot store our profile in
// /etc/apparmor.d, nor can we practically add other ways of
// telling the system to keep our profile loaded, in order to make
// sure that we keep the default profile enabled we load it again
// if it is missing.
if err := daemon.loadDefaultAppArmorProfileIfMissing(); err != nil {
return err
}
}
p.ApparmorProfile = appArmorProfile
}
s := &specs.Spec{Process: p}
return withRlimits(daemon, daemonCfg, ec.Container)(ctx, nil, nil, s)
}