Add a WithPrivileged OCI constructor and the options needed to build it
Signed-off-by: Justin Cormack <justin.cormack@docker.com>
This commit is contained in:
parent
d1b3ea4061
commit
062c3a00ef
@ -27,6 +27,18 @@ import (
|
||||
// SpecOpts sets spec specific information to a newly generated OCI spec
|
||||
type SpecOpts func(context.Context, Client, *containers.Container, *specs.Spec) error
|
||||
|
||||
// Compose converts a sequence of spec operations into a single operation
|
||||
func Compose(opts ...SpecOpts) SpecOpts {
|
||||
return func(ctx context.Context, client Client, c *containers.Container, s *specs.Spec) error {
|
||||
for _, o := range opts {
|
||||
if err := o(ctx, client, c, s); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// setProcess sets Process to empty if unset
|
||||
func setProcess(s *specs.Spec) {
|
||||
if s.Process == nil {
|
||||
|
@ -443,20 +443,23 @@ func WithUsername(username string) SpecOpts {
|
||||
}
|
||||
}
|
||||
|
||||
// WithAllCapabilities set all linux capabilities for the process
|
||||
func WithAllCapabilities(_ context.Context, _ Client, _ *containers.Container, s *specs.Spec) error {
|
||||
// WithCapabilities sets Linux capabilities on the process
|
||||
func WithCapabilities(caps []string) SpecOpts {
|
||||
return func(_ context.Context, _ Client, _ *containers.Container, s *specs.Spec) error {
|
||||
setCapabilities(s)
|
||||
|
||||
caps := getAllCapabilities()
|
||||
|
||||
s.Process.Capabilities.Bounding = caps
|
||||
s.Process.Capabilities.Effective = caps
|
||||
s.Process.Capabilities.Permitted = caps
|
||||
s.Process.Capabilities.Inheritable = caps
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithAllCapabilities sets all linux capabilities for the process
|
||||
var WithAllCapabilities = WithCapabilities(getAllCapabilities())
|
||||
|
||||
func getAllCapabilities() []string {
|
||||
last := capability.CAP_LAST_CAP
|
||||
// hack for RHEL6 which has no /proc/sys/kernel/cap_last_cap
|
||||
@ -512,3 +515,93 @@ func getGIDFromPath(root string, filter func(user.Group) bool) (gid uint32, err
|
||||
func isRootfsAbs(root string) bool {
|
||||
return filepath.IsAbs(root)
|
||||
}
|
||||
|
||||
// WithMaskedPaths sets the masked paths option
|
||||
func WithMaskedPaths(paths []string) SpecOpts {
|
||||
return func(_ context.Context, _ Client, _ *containers.Container, s *specs.Spec) error {
|
||||
setLinux(s)
|
||||
s.Linux.MaskedPaths = paths
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithReadonlyPaths sets the read only paths option
|
||||
func WithReadonlyPaths(paths []string) SpecOpts {
|
||||
return func(_ context.Context, _ Client, _ *containers.Container, s *specs.Spec) error {
|
||||
setLinux(s)
|
||||
s.Linux.ReadonlyPaths = paths
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithWriteableSysfs makes any sysfs mounts writeable
|
||||
func WithWriteableSysfs(_ context.Context, _ Client, _ *containers.Container, s *specs.Spec) error {
|
||||
for i, m := range s.Mounts {
|
||||
if m.Type == "sysfs" {
|
||||
var options []string
|
||||
for _, o := range m.Options {
|
||||
if o == "ro" {
|
||||
o = "rw"
|
||||
}
|
||||
options = append(options, o)
|
||||
}
|
||||
s.Mounts[i].Options = options
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WithWriteableCgroupfs makes any cgroup mounts writeable
|
||||
func WithWriteableCgroupfs(_ context.Context, _ Client, _ *containers.Container, s *specs.Spec) error {
|
||||
for i, m := range s.Mounts {
|
||||
if m.Type == "cgroup" {
|
||||
var options []string
|
||||
for _, o := range m.Options {
|
||||
if o == "ro" {
|
||||
o = "rw"
|
||||
}
|
||||
options = append(options, o)
|
||||
}
|
||||
s.Mounts[i].Options = options
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WithSelinuxLabel sets the process SELinux label
|
||||
func WithSelinuxLabel(label string) SpecOpts {
|
||||
return func(_ context.Context, _ Client, _ *containers.Container, s *specs.Spec) error {
|
||||
setProcess(s)
|
||||
s.Process.SelinuxLabel = label
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithApparmorProfile sets the Apparmor profile for the process
|
||||
func WithApparmorProfile(profile string) SpecOpts {
|
||||
return func(_ context.Context, _ Client, _ *containers.Container, s *specs.Spec) error {
|
||||
setProcess(s)
|
||||
s.Process.ApparmorProfile = profile
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithSeccompUnconfined clears the seccomp profile
|
||||
func WithSeccompUnconfined(_ context.Context, _ Client, _ *containers.Container, s *specs.Spec) error {
|
||||
setLinux(s)
|
||||
s.Linux.Seccomp = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// WithPrivileged sets up options for a privileged container
|
||||
// TODO(justincormack) device handling
|
||||
var WithPrivileged = Compose(
|
||||
WithAllCapabilities,
|
||||
WithMaskedPaths(nil),
|
||||
WithReadonlyPaths(nil),
|
||||
WithWriteableSysfs,
|
||||
WithWriteableCgroupfs,
|
||||
WithSelinuxLabel(""),
|
||||
WithApparmorProfile(""),
|
||||
WithSeccompUnconfined,
|
||||
)
|
||||
|
@ -108,3 +108,116 @@ func TestWithLinuxNamespace(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithCapabilities(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := namespaces.WithNamespace(context.Background(), "testing")
|
||||
|
||||
s, err := GenerateSpec(ctx, nil, &containers.Container{ID: t.Name()},
|
||||
WithCapabilities([]string{"CAP_SYS_ADMIN"}),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(s.Process.Capabilities.Bounding) != 1 || s.Process.Capabilities.Bounding[0] != "CAP_SYS_ADMIN" {
|
||||
t.Error("Unexpected capabilities set")
|
||||
}
|
||||
if len(s.Process.Capabilities.Effective) != 1 || s.Process.Capabilities.Effective[0] != "CAP_SYS_ADMIN" {
|
||||
t.Error("Unexpected capabilities set")
|
||||
}
|
||||
if len(s.Process.Capabilities.Permitted) != 1 || s.Process.Capabilities.Permitted[0] != "CAP_SYS_ADMIN" {
|
||||
t.Error("Unexpected capabilities set")
|
||||
}
|
||||
if len(s.Process.Capabilities.Inheritable) != 1 || s.Process.Capabilities.Inheritable[0] != "CAP_SYS_ADMIN" {
|
||||
t.Error("Unexpected capabilities set")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithCapabilitiesNil(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := namespaces.WithNamespace(context.Background(), "testing")
|
||||
|
||||
s, err := GenerateSpec(ctx, nil, &containers.Container{ID: t.Name()},
|
||||
WithCapabilities(nil),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(s.Process.Capabilities.Bounding) != 0 {
|
||||
t.Errorf("Unexpected capabilities set: length is non zero (%d)", len(s.Process.Capabilities.Bounding))
|
||||
}
|
||||
if len(s.Process.Capabilities.Effective) != 0 {
|
||||
t.Errorf("Unexpected capabilities set: length is non zero (%d)", len(s.Process.Capabilities.Effective))
|
||||
}
|
||||
if len(s.Process.Capabilities.Permitted) != 0 {
|
||||
t.Errorf("Unexpected capabilities set: length is non zero (%d)", len(s.Process.Capabilities.Permitted))
|
||||
}
|
||||
if len(s.Process.Capabilities.Inheritable) != 0 {
|
||||
t.Errorf("Unexpected capabilities set: length is non zero (%d)", len(s.Process.Capabilities.Inheritable))
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithPrivileged(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := namespaces.WithNamespace(context.Background(), "testing")
|
||||
|
||||
s, err := GenerateSpec(ctx, nil, &containers.Container{ID: t.Name()},
|
||||
WithCapabilities(nil),
|
||||
WithMounts([]specs.Mount{
|
||||
{Type: "cgroup", Destination: "/sys/fs/cgroup", Options: []string{"ro"}},
|
||||
}),
|
||||
WithPrivileged,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(s.Process.Capabilities.Bounding) == 0 {
|
||||
t.Error("Expected capabilities to be set with privileged")
|
||||
}
|
||||
|
||||
var foundSys, foundCgroup bool
|
||||
for _, m := range s.Mounts {
|
||||
switch m.Type {
|
||||
case "sysfs":
|
||||
foundSys = true
|
||||
var found bool
|
||||
for _, o := range m.Options {
|
||||
switch o {
|
||||
case "ro":
|
||||
t.Errorf("Found unexpected read only %s mount", m.Type)
|
||||
case "rw":
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Did not find rw mount option for %s", m.Type)
|
||||
}
|
||||
case "cgroup":
|
||||
foundCgroup = true
|
||||
var found bool
|
||||
for _, o := range m.Options {
|
||||
switch o {
|
||||
case "ro":
|
||||
t.Errorf("Found unexpected read only %s mount", m.Type)
|
||||
case "rw":
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Did not find rw mount option for %s", m.Type)
|
||||
}
|
||||
}
|
||||
}
|
||||
if !foundSys {
|
||||
t.Error("Did not find mount for sysfs")
|
||||
}
|
||||
if !foundCgroup {
|
||||
t.Error("Did not find mount for cgroupfs")
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user