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
|
// SpecOpts sets spec specific information to a newly generated OCI spec
|
||||||
type SpecOpts func(context.Context, Client, *containers.Container, *specs.Spec) error
|
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
|
// setProcess sets Process to empty if unset
|
||||||
func setProcess(s *specs.Spec) {
|
func setProcess(s *specs.Spec) {
|
||||||
if s.Process == nil {
|
if s.Process == nil {
|
||||||
|
@ -443,20 +443,23 @@ func WithUsername(username string) SpecOpts {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithAllCapabilities set all linux capabilities for the process
|
// WithCapabilities sets Linux capabilities on the process
|
||||||
func WithAllCapabilities(_ context.Context, _ Client, _ *containers.Container, s *specs.Spec) error {
|
func WithCapabilities(caps []string) SpecOpts {
|
||||||
setCapabilities(s)
|
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
|
||||||
|
|
||||||
s.Process.Capabilities.Bounding = caps
|
return nil
|
||||||
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 {
|
func getAllCapabilities() []string {
|
||||||
last := capability.CAP_LAST_CAP
|
last := capability.CAP_LAST_CAP
|
||||||
// hack for RHEL6 which has no /proc/sys/kernel/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 {
|
func isRootfsAbs(root string) bool {
|
||||||
return filepath.IsAbs(root)
|
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