1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Export all spec generation opts

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
Michael Crosby 2019-04-10 14:45:14 -04:00
parent cb902f4430
commit c478553640
6 changed files with 543 additions and 511 deletions

View file

@ -10,10 +10,7 @@ import (
"testing" "testing"
containertypes "github.com/docker/docker/api/types/container" containertypes "github.com/docker/docker/api/types/container"
"github.com/docker/docker/container"
"github.com/docker/docker/daemon/config" "github.com/docker/docker/daemon/config"
"github.com/docker/docker/oci"
"github.com/docker/docker/pkg/idtools"
"github.com/docker/docker/pkg/mount" "github.com/docker/docker/pkg/mount"
"gotest.tools/assert" "gotest.tools/assert"
is "gotest.tools/assert/cmp" is "gotest.tools/assert/cmp"
@ -115,54 +112,6 @@ func TestNotCleanupMounts(t *testing.T) {
} }
} }
// TestTmpfsDevShmSizeOverride checks that user-specified /dev/tmpfs mount
// size is not overridden by the default shmsize (that should only be used
// for default /dev/shm (as in "shareable" and "private" ipc modes).
// https://github.com/moby/moby/issues/35271
func TestTmpfsDevShmSizeOverride(t *testing.T) {
size := "777m"
mnt := "/dev/shm"
d := Daemon{
idMapping: &idtools.IdentityMapping{},
}
c := &container.Container{
HostConfig: &containertypes.HostConfig{
ShmSize: 48 * 1024, // size we should NOT end up with
},
}
ms := []container.Mount{
{
Source: "tmpfs",
Destination: mnt,
Data: "size=" + size,
},
}
// convert ms to spec
spec := oci.DefaultSpec()
err := setMounts(&d, &spec, c, ms)
assert.Check(t, err)
// Check the resulting spec for the correct size
found := false
for _, m := range spec.Mounts {
if m.Destination == mnt {
for _, o := range m.Options {
if !strings.HasPrefix(o, "size=") {
continue
}
t.Logf("%+v\n", m.Options)
assert.Check(t, is.Equal("size="+size, o))
found = true
}
}
}
if !found {
t.Fatal("/dev/shm not found in spec, or size option missing")
}
}
func TestValidateContainerIsolationLinux(t *testing.T) { func TestValidateContainerIsolationLinux(t *testing.T) {
d := Daemon{} d := Daemon{}

View file

@ -1,6 +1,8 @@
package daemon // import "github.com/docker/docker/daemon" package daemon // import "github.com/docker/docker/daemon"
import ( import (
"context"
"github.com/docker/docker/container" "github.com/docker/docker/container"
"github.com/docker/docker/daemon/exec" "github.com/docker/docker/daemon/exec"
"github.com/docker/docker/oci/caps" "github.com/docker/docker/oci/caps"
@ -54,6 +56,6 @@ func (daemon *Daemon) execSetPlatformOpt(c *container.Container, ec *exec.Config
} }
p.ApparmorProfile = appArmorProfile p.ApparmorProfile = appArmorProfile
} }
daemon.setRlimits(&specs.Spec{Process: p}, c) s := &specs.Spec{Process: p}
return nil return WithRlimits(daemon, c)(context.Background(), nil, nil, s)
} }

View file

@ -33,11 +33,11 @@ import (
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )
const ( const inContainerInitPath = "/sbin/" + daemonconfig.DefaultInitBinary
inContainerInitPath = "/sbin/" + daemonconfig.DefaultInitBinary
)
func (daemon *Daemon) setRlimits(s *specs.Spec, c *container.Container) error { // WithRlimits sets the container's rlimits along with merging the daemon's rlimits
func WithRlimits(daemon *Daemon, c *container.Container) coci.SpecOpts {
return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
var rlimits []specs.POSIXRlimit var rlimits []specs.POSIXRlimit
// We want to leave the original HostConfig alone so make a copy here // We want to leave the original HostConfig alone so make a copy here
@ -54,6 +54,100 @@ func (daemon *Daemon) setRlimits(s *specs.Spec, c *container.Container) error {
s.Process.Rlimits = rlimits s.Process.Rlimits = rlimits
return nil return nil
}
}
// WithLibnetwork sets the libnetwork hook
func WithLibnetwork(daemon *Daemon, c *container.Container) coci.SpecOpts {
return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
if s.Hooks == nil {
s.Hooks = &specs.Hooks{}
}
for _, ns := range s.Linux.Namespaces {
if ns.Type == "network" && ns.Path == "" && !c.Config.NetworkDisabled {
target := filepath.Join("/proc", strconv.Itoa(os.Getpid()), "exe")
s.Hooks.Prestart = append(s.Hooks.Prestart, specs.Hook{
Path: target,
Args: []string{
"libnetwork-setkey",
"-exec-root=" + daemon.configStore.GetExecRoot(),
c.ID,
daemon.netController.ID(),
},
})
}
}
return nil
}
}
// WithRootless sets the spec to the rootless configuration
func WithRootless(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
return specconv.ToRootless(s)
}
// WithOOMScore sets the oom score
func WithOOMScore(score *int) coci.SpecOpts {
return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
s.Process.OOMScoreAdj = score
return nil
}
}
// WithSelinux sets the selinux labels
func WithSelinux(c *container.Container) coci.SpecOpts {
return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
s.Process.SelinuxLabel = c.GetProcessLabel()
s.Linux.MountLabel = c.MountLabel
return nil
}
}
// WithApparmor sets the apparmor profile
func WithApparmor(c *container.Container) coci.SpecOpts {
return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
if apparmor.IsEnabled() {
var appArmorProfile string
if c.AppArmorProfile != "" {
appArmorProfile = c.AppArmorProfile
} else if c.HostConfig.Privileged {
appArmorProfile = "unconfined"
} else {
appArmorProfile = "docker-default"
}
if appArmorProfile == "docker-default" {
// 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 dynamically
// reload it if necessary.
if err := ensureDefaultAppArmorProfile(); err != nil {
return err
}
}
s.Process.ApparmorProfile = appArmorProfile
}
return nil
}
}
// WithCapabilities sets the container's capabilties
func WithCapabilities(c *container.Container) coci.SpecOpts {
return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
capabilities, err := caps.TweakCapabilities(
oci.DefaultCapabilities(),
c.HostConfig.CapAdd,
c.HostConfig.CapDrop,
c.HostConfig.Capabilities,
c.HostConfig.Privileged,
)
if err != nil {
return err
}
return oci.SetCapabilities(s, capabilities)
}
} }
func readUserFile(c *container.Container, p string) (io.ReadCloser, error) { func readUserFile(c *container.Container, p string) (io.ReadCloser, error) {
@ -119,7 +213,9 @@ func setNamespace(s *specs.Spec, ns specs.LinuxNamespace) {
s.Linux.Namespaces = append(s.Linux.Namespaces, ns) s.Linux.Namespaces = append(s.Linux.Namespaces, ns)
} }
func setNamespaces(daemon *Daemon, s *specs.Spec, c *container.Container) error { // WithNamespaces sets the container's namespaces
func WithNamespaces(daemon *Daemon, c *container.Container) coci.SpecOpts {
return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
userNS := false userNS := false
// user // user
if c.HostConfig.UsernsMode.IsPrivate() { if c.HostConfig.UsernsMode.IsPrivate() {
@ -212,6 +308,7 @@ func setNamespaces(daemon *Daemon, s *specs.Spec, c *container.Container) error
} }
return nil return nil
}
} }
func specMapping(s []idtools.IDMap) []specs.LinuxIDMapping { func specMapping(s []idtools.IDMap) []specs.LinuxIDMapping {
@ -361,7 +458,52 @@ func inSlice(slice []string, s string) bool {
return false return false
} }
func setMounts(daemon *Daemon, s *specs.Spec, c *container.Container, mounts []container.Mount) error { // WithMounts sets the container's mounts
func WithMounts(daemon *Daemon, c *container.Container) coci.SpecOpts {
return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) (err error) {
if err := daemon.setupContainerMountsRoot(c); err != nil {
return err
}
if err := daemon.setupIpcDirs(c); err != nil {
return err
}
defer func() {
if err != nil {
daemon.cleanupSecretDir(c)
}
}()
if err := daemon.setupSecretDir(c); err != nil {
return err
}
ms, err := daemon.setupMounts(c)
if err != nil {
return err
}
if !c.HostConfig.IpcMode.IsPrivate() && !c.HostConfig.IpcMode.IsEmpty() {
ms = append(ms, c.IpcMounts()...)
}
tmpfsMounts, err := c.TmpfsMounts()
if err != nil {
return err
}
ms = append(ms, tmpfsMounts...)
secretMounts, err := c.SecretMounts()
if err != nil {
return err
}
ms = append(ms, secretMounts...)
sort.Sort(mounts(ms))
mounts := ms
userMounts := make(map[string]struct{}) userMounts := make(map[string]struct{})
for _, m := range mounts { for _, m := range mounts {
userMounts[m.Destination] = struct{}{} userMounts[m.Destination] = struct{}{}
@ -532,9 +674,13 @@ func setMounts(daemon *Daemon, s *specs.Spec, c *container.Container, mounts []c
} }
return nil return nil
}
} }
func (daemon *Daemon) populateCommonSpec(s *specs.Spec, c *container.Container) error { // WithCommonOptions sets common docker options
func WithCommonOptions(daemon *Daemon, c *container.Container) coci.SpecOpts {
return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
if c.BaseFS == nil { if c.BaseFS == nil {
return errors.New("populateCommonSpec: BaseFS of container " + c.ID + " is unexpectedly nil") return errors.New("populateCommonSpec: BaseFS of container " + c.ID + " is unexpectedly nil")
} }
@ -585,9 +731,11 @@ func (daemon *Daemon) populateCommonSpec(s *specs.Spec, c *container.Container)
setLinuxDomainname(c, s) setLinuxDomainname(c, s)
return nil return nil
}
} }
func withCgroups(daemon *Daemon, c *container.Container) coci.SpecOpts { // WithCgroups sets the container's cgroups
func WithCgroups(daemon *Daemon, c *container.Container) coci.SpecOpts {
return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error { return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
var cgroupsPath string var cgroupsPath string
scopePrefix := "docker" scopePrefix := "docker"
@ -636,7 +784,8 @@ func withCgroups(daemon *Daemon, c *container.Container) coci.SpecOpts {
} }
} }
func withContainerDevices(daemon *Daemon, c *container.Container) coci.SpecOpts { // WithDevices sets the container's devices
func WithDevices(daemon *Daemon, c *container.Container) coci.SpecOpts {
return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error { return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
// Build lists of devices allowed and created within the container. // Build lists of devices allowed and created within the container.
var devs []specs.LinuxDevice var devs []specs.LinuxDevice
@ -684,7 +833,8 @@ func withContainerDevices(daemon *Daemon, c *container.Container) coci.SpecOpts
} }
} }
func withResources(c *container.Container) coci.SpecOpts { // WithResources applies the container resources
func WithResources(c *container.Container) coci.SpecOpts {
return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error { return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
r := c.HostConfig.Resources r := c.HostConfig.Resources
weightDevices, err := getBlkioWeightDevices(r) weightDevices, err := getBlkioWeightDevices(r)
@ -738,7 +888,8 @@ func withResources(c *container.Container) coci.SpecOpts {
} }
} }
func withSysctls(c *container.Container) coci.SpecOpts { // WithSysctls sets the container's sysctls
func WithSysctls(c *container.Container) coci.SpecOpts {
return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error { return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
// We merge the sysctls injected above with the HostConfig (latter takes // We merge the sysctls injected above with the HostConfig (latter takes
// precedence for backwards-compatibility reasons). // precedence for backwards-compatibility reasons).
@ -749,7 +900,8 @@ func withSysctls(c *container.Container) coci.SpecOpts {
} }
} }
func withUser(c *container.Container) coci.SpecOpts { // WithUser sets the container's user
func WithUser(c *container.Container) coci.SpecOpts {
return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error { return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
uid, gid, additionalGids, err := getUser(c, c.Config.User) uid, gid, additionalGids, err := getUser(c, c.Config.User)
if err != nil { if err != nil {
@ -767,133 +919,36 @@ func (daemon *Daemon) createSpec(c *container.Container) (retSpec *specs.Spec, e
opts []coci.SpecOpts opts []coci.SpecOpts
s = oci.DefaultSpec() s = oci.DefaultSpec()
) )
if err := daemon.populateCommonSpec(&s, c); err != nil {
return nil, err
}
opts = append(opts, opts = append(opts,
withCgroups(daemon, c), WithCommonOptions(daemon, c),
withResources(c), WithCgroups(daemon, c),
withSysctls(c), WithResources(c),
withContainerDevices(daemon, c), WithSysctls(c),
withUser(c), WithDevices(daemon, c),
WithUser(c),
WithRlimits(daemon, c),
WithNamespaces(daemon, c),
WithCapabilities(c),
WithSeccomp(daemon, c),
WithMounts(daemon, c),
WithLibnetwork(daemon, c),
WithApparmor(c),
WithSelinux(c),
WithOOMScore(&c.HostConfig.OomScoreAdj),
) )
if c.NoNewPrivileges {
if err := daemon.setRlimits(&s, c); err != nil { opts = append(opts, coci.WithNoNewPrivileges)
return nil, fmt.Errorf("linux runtime spec rlimits: %v", err)
} }
if err := setNamespaces(daemon, &s, c); err != nil {
return nil, fmt.Errorf("linux spec namespaces: %v", err)
}
capabilities, err := caps.TweakCapabilities(oci.DefaultCapabilities(), c.HostConfig.CapAdd, c.HostConfig.CapDrop, c.HostConfig.Capabilities, c.HostConfig.Privileged)
if err != nil {
return nil, fmt.Errorf("linux spec capabilities: %v", err)
}
if err := oci.SetCapabilities(&s, capabilities); err != nil {
return nil, fmt.Errorf("linux spec capabilities: %v", err)
}
if err := setSeccomp(daemon, &s, c); err != nil {
return nil, fmt.Errorf("linux seccomp: %v", err)
}
if err := daemon.setupContainerMountsRoot(c); err != nil {
return nil, err
}
if err := daemon.setupIpcDirs(c); err != nil {
return nil, err
}
defer func() {
if err != nil {
daemon.cleanupSecretDir(c)
}
}()
if err := daemon.setupSecretDir(c); err != nil {
return nil, err
}
ms, err := daemon.setupMounts(c)
if err != nil {
return nil, err
}
if !c.HostConfig.IpcMode.IsPrivate() && !c.HostConfig.IpcMode.IsEmpty() {
ms = append(ms, c.IpcMounts()...)
}
tmpfsMounts, err := c.TmpfsMounts()
if err != nil {
return nil, err
}
ms = append(ms, tmpfsMounts...)
secretMounts, err := c.SecretMounts()
if err != nil {
return nil, err
}
ms = append(ms, secretMounts...)
sort.Sort(mounts(ms))
if err := setMounts(daemon, &s, c, ms); err != nil {
return nil, fmt.Errorf("linux mounts: %v", err)
}
if s.Hooks == nil {
s.Hooks = &specs.Hooks{}
}
for _, ns := range s.Linux.Namespaces {
if ns.Type == "network" && ns.Path == "" && !c.Config.NetworkDisabled {
target := filepath.Join("/proc", strconv.Itoa(os.Getpid()), "exe")
s.Hooks.Prestart = append(s.Hooks.Prestart, specs.Hook{
Path: target,
Args: []string{"libnetwork-setkey", "-exec-root=" + daemon.configStore.GetExecRoot(), c.ID, daemon.netController.ID()},
})
}
}
if apparmor.IsEnabled() {
var appArmorProfile string
if c.AppArmorProfile != "" {
appArmorProfile = c.AppArmorProfile
} else if c.HostConfig.Privileged {
appArmorProfile = "unconfined"
} else {
appArmorProfile = "docker-default"
}
if appArmorProfile == "docker-default" {
// 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 dynamically
// reload it if necessary.
if err := ensureDefaultAppArmorProfile(); err != nil {
return nil, err
}
}
s.Process.ApparmorProfile = appArmorProfile
}
s.Process.SelinuxLabel = c.GetProcessLabel()
s.Process.NoNewPrivileges = c.NoNewPrivileges
s.Process.OOMScoreAdj = &c.HostConfig.OomScoreAdj
s.Linux.MountLabel = c.MountLabel
// Set the masked and readonly paths with regard to the host config options if they are set. // Set the masked and readonly paths with regard to the host config options if they are set.
if c.HostConfig.MaskedPaths != nil { if c.HostConfig.MaskedPaths != nil {
s.Linux.MaskedPaths = c.HostConfig.MaskedPaths opts = append(opts, coci.WithMaskedPaths(c.HostConfig.MaskedPaths))
} }
if c.HostConfig.ReadonlyPaths != nil { if c.HostConfig.ReadonlyPaths != nil {
s.Linux.ReadonlyPaths = c.HostConfig.ReadonlyPaths opts = append(opts, coci.WithReadonlyPaths(c.HostConfig.ReadonlyPaths))
} }
if daemon.configStore.Rootless { if daemon.configStore.Rootless {
if err := specconv.ToRootless(&s); err != nil { opts = append(opts, WithRootless)
return nil, err
}
} }
return &s, coci.ApplyOpts(context.Background(), nil, &containers.Container{ return &s, coci.ApplyOpts(context.Background(), nil, &containers.Container{
ID: c.ID, ID: c.ID,

View file

@ -3,17 +3,22 @@
package daemon // import "github.com/docker/docker/daemon" package daemon // import "github.com/docker/docker/daemon"
import ( import (
"context"
"fmt" "fmt"
"github.com/containerd/containerd/containers"
coci "github.com/containerd/containerd/oci"
"github.com/docker/docker/container" "github.com/docker/docker/container"
"github.com/opencontainers/runtime-spec/specs-go"
) )
var supportsSeccomp = false var supportsSeccomp = false
func setSeccomp(daemon *Daemon, rs *specs.Spec, c *container.Container) error { // WithSeccomp sets the seccomp profile
func WithSeccomp(daemon *Daemon, c *container.Container) coci.SpecOpts {
return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
if c.SeccompProfile != "" && c.SeccompProfile != "unconfined" { if c.SeccompProfile != "" && c.SeccompProfile != "unconfined" {
return fmt.Errorf("seccomp profiles are not supported on this daemon, you cannot specify a custom seccomp profile") return fmt.Errorf("seccomp profiles are not supported on this daemon, you cannot specify a custom seccomp profile")
} }
return nil return nil
}
} }

View file

@ -3,8 +3,11 @@
package daemon // import "github.com/docker/docker/daemon" package daemon // import "github.com/docker/docker/daemon"
import ( import (
"context"
"fmt" "fmt"
"github.com/containerd/containerd/containers"
coci "github.com/containerd/containerd/oci"
"github.com/docker/docker/container" "github.com/docker/docker/container"
"github.com/docker/docker/profiles/seccomp" "github.com/docker/docker/profiles/seccomp"
"github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-spec/specs-go"
@ -13,7 +16,9 @@ import (
var supportsSeccomp = true var supportsSeccomp = true
func setSeccomp(daemon *Daemon, rs *specs.Spec, c *container.Container) error { // WithSeccomp sets the seccomp profile
func WithSeccomp(daemon *Daemon, c *container.Container) coci.SpecOpts {
return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
var profile *specs.LinuxSeccomp var profile *specs.LinuxSeccomp
var err error var err error
@ -32,24 +37,25 @@ func setSeccomp(daemon *Daemon, rs *specs.Spec, c *container.Container) error {
return nil return nil
} }
if c.SeccompProfile != "" { if c.SeccompProfile != "" {
profile, err = seccomp.LoadProfile(c.SeccompProfile, rs) profile, err = seccomp.LoadProfile(c.SeccompProfile, s)
if err != nil { if err != nil {
return err return err
} }
} else { } else {
if daemon.seccompProfile != nil { if daemon.seccompProfile != nil {
profile, err = seccomp.LoadProfile(string(daemon.seccompProfile), rs) profile, err = seccomp.LoadProfile(string(daemon.seccompProfile), s)
if err != nil { if err != nil {
return err return err
} }
} else { } else {
profile, err = seccomp.GetDefaultProfile(rs) profile, err = seccomp.GetDefaultProfile(s)
if err != nil { if err != nil {
return err return err
} }
} }
} }
rs.Linux.Seccomp = profile s.Linux.Seccomp = profile
return nil return nil
}
} }

View file

@ -2,4 +2,19 @@
package daemon // import "github.com/docker/docker/daemon" package daemon // import "github.com/docker/docker/daemon"
import (
"context"
"github.com/containerd/containerd/containers"
coci "github.com/containerd/containerd/oci"
"github.com/docker/docker/container"
)
var supportsSeccomp = false var supportsSeccomp = false
// WithSeccomp sets the seccomp profile
func WithSeccomp(daemon *Daemon, c *container.Container) coci.SpecOpts {
return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
return nil
}
}