Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for mount policy enforcement. #1311

Merged
merged 2 commits into from
Apr 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions internal/guest/policy/default.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
//go:build linux
// +build linux

package policy

import (
oci "github.com/opencontainers/runtime-spec/specs-go"

internalSpec "github.com/Microsoft/hcsshim/internal/guest/spec"
"github.com/Microsoft/hcsshim/pkg/securitypolicy"
)

func ExtendPolicyWithNetworkingMounts(sandboxID string, enforcer securitypolicy.SecurityPolicyEnforcer, spec *oci.Spec) error {
roSpec := &oci.Spec{
Root: spec.Root,
}
networkingMounts := internalSpec.GenerateWorkloadContainerNetworkMounts(sandboxID, roSpec)
if err := enforcer.ExtendDefaultMounts(networkingMounts); err != nil {
return err
}
return nil
}

// DefaultCRIMounts returns default mounts added to linux spec by containerD.
func DefaultCRIMounts() []oci.Mount {
return []oci.Mount{
{
Destination: "/proc",
Type: "proc",
Source: "proc",
Options: []string{"nosuid", "noexec", "nodev"},
},
{
Destination: "/dev",
Type: "tmpfs",
Source: "tmpfs",
Options: []string{"nosuid", "strictatime", "mode=755", "size=65536k"},
},
{
Destination: "/dev/pts",
Type: "devpts",
Source: "devpts",
Options: []string{"nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620", "gid=5"},
},
{
Destination: "/dev/shm",
Type: "tmpfs",
Source: "shm",
Options: []string{"nosuid", "noexec", "nodev", "mode=1777", "size=65536k"},
},
{
Destination: "/dev/mqueue",
Type: "mqueue",
Source: "mqueue",
Options: []string{"nosuid", "noexec", "nodev"},
},
{
Destination: "/sys",
Type: "sysfs",
Source: "sysfs",
Options: []string{"nosuid", "noexec", "nodev", "ro"},
},
{
Destination: "/run",
Type: "tmpfs",
Source: "tmpfs",
Options: []string{"nosuid", "strictatime", "mode=755", "size=65536k"},
},
// cgroup mount is always added by default, regardless if it is present
// in the mount constraints or not. If the user chooses to override it,
// then a corresponding mount constraint should be present.
{
Source: "cgroup",
Destination: "/sys/fs/cgroup",
Type: "cgroup",
Options: []string{"nosuid", "noexec", "nodev", "relatime", "ro"},
},
}
}
5 changes: 3 additions & 2 deletions internal/guest/runtime/hcsv2/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/Microsoft/hcsshim/internal/guest/gcserr"
"github.com/Microsoft/hcsshim/internal/guest/prot"
"github.com/Microsoft/hcsshim/internal/guest/runtime"
specInternal "github.com/Microsoft/hcsshim/internal/guest/spec"
"github.com/Microsoft/hcsshim/internal/guest/stdio"
"github.com/Microsoft/hcsshim/internal/guest/storage"
"github.com/Microsoft/hcsshim/internal/guest/transport"
Expand Down Expand Up @@ -158,12 +159,12 @@ func (c *Container) Delete(ctx context.Context) error {
entity.Info("opengcs::Container::Delete")
if c.isSandbox {
// remove user mounts in sandbox container
if err := storage.UnmountAllInPath(ctx, getSandboxMountsDir(c.id), true); err != nil {
if err := storage.UnmountAllInPath(ctx, specInternal.SandboxMountsDir(c.id), true); err != nil {
entity.WithError(err).Error("failed to unmount sandbox mounts")
}

// remove hugepages mounts in sandbox container
if err := storage.UnmountAllInPath(ctx, getSandboxHugePageMountsDir(c.id), true); err != nil {
if err := storage.UnmountAllInPath(ctx, specInternal.HugePagesMountsDir(c.id), true); err != nil {
entity.WithError(err).Error("failed to unmount hugepages mounts")
}
}
Expand Down
22 changes: 5 additions & 17 deletions internal/guest/runtime/hcsv2/sandbox_container.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,33 +15,21 @@ import (
"go.opencensus.io/trace"

"github.com/Microsoft/hcsshim/internal/guest/network"
"github.com/Microsoft/hcsshim/internal/guestpath"
specInternal "github.com/Microsoft/hcsshim/internal/guest/spec"
"github.com/Microsoft/hcsshim/internal/oc"
"github.com/Microsoft/hcsshim/pkg/annotations"
)

func getSandboxRootDir(id string) string {
return filepath.Join(guestpath.LCOWRootPrefixInUVM, id)
}

func getSandboxHugePageMountsDir(id string) string {
return filepath.Join(getSandboxRootDir(id), "hugepages")
}

func getSandboxMountsDir(id string) string {
return filepath.Join(getSandboxRootDir(id), "sandboxMounts")
}

func getSandboxHostnamePath(id string) string {
return filepath.Join(getSandboxRootDir(id), "hostname")
return filepath.Join(specInternal.SandboxRootDir(id), "hostname")
}

func getSandboxHostsPath(id string) string {
return filepath.Join(getSandboxRootDir(id), "hosts")
return filepath.Join(specInternal.SandboxRootDir(id), "hosts")
}

func getSandboxResolvPath(id string) string {
return filepath.Join(getSandboxRootDir(id), "resolv.conf")
return filepath.Join(specInternal.SandboxRootDir(id), "resolv.conf")
}

func setupSandboxContainerSpec(ctx context.Context, id string, spec *oci.Spec) (err error) {
Expand All @@ -51,7 +39,7 @@ func setupSandboxContainerSpec(ctx context.Context, id string, spec *oci.Spec) (
span.AddAttributes(trace.StringAttribute("cid", id))

// Generate the sandbox root dir
rootDir := getSandboxRootDir(id)
rootDir := specInternal.SandboxRootDir(id)
if err := os.MkdirAll(rootDir, 0755); err != nil {
return errors.Wrapf(err, "failed to create sandbox root directory %q", rootDir)
}
Expand Down
58 changes: 30 additions & 28 deletions internal/guest/runtime/hcsv2/spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ import (
"github.com/Microsoft/hcsshim/pkg/annotations"
)

const (
devShmPath = "/dev/shm"
)

// getNetworkNamespaceID returns the `ToLower` of
// `spec.Windows.Network.NetworkNamespace` or `""`.
func getNetworkNamespaceID(spec *oci.Spec) string {
Expand All @@ -38,17 +42,6 @@ func isRootReadonly(spec *oci.Spec) bool {
return false
}

// isInMounts returns `true` if `target` matches a `Destination` in any of
// `mounts`.
func isInMounts(target string, mounts []oci.Mount) bool {
for _, m := range mounts {
if m.Destination == target {
return true
}
}
return false
}

// removeMount removes mount from the array if `target` matches `Destination`
func removeMount(target string, mounts []oci.Mount) []oci.Mount {
var result []oci.Mount
Expand Down Expand Up @@ -202,25 +195,13 @@ func getGroup(spec *oci.Spec, filter func(user.Group) bool) (user.Group, error)
func applyAnnotationsToSpec(ctx context.Context, spec *oci.Spec) error {
// Check if we need to override container's /dev/shm
if val, ok := spec.Annotations[annotations.LCOWDevShmSizeInKb]; ok {
sz, err := strconv.ParseInt(val, 10, 64)
mt, err := devShmMountWithSize(val)
anmaxvl marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return errors.Wrap(err, "/dev/shm size must be a valid integer")
}
if sz <= 0 {
return errors.Errorf("/dev/shm size must be a positive integer, got: %d", sz)
}

// Use the same options as in upstream https://github.com/containerd/containerd/blob/0def98e462706286e6eaeff4a90be22fda75e761/oci/mounts.go#L49
size := fmt.Sprintf("size=%dk", sz)
mt := oci.Mount{
Destination: "/dev/shm",
Type: "tmpfs",
Source: "shm",
Options: []string{"nosuid", "noexec", "nodev", "mode=1777", size},
return err
}
spec.Mounts = removeMount("/dev/shm", spec.Mounts)
spec.Mounts = append(spec.Mounts, mt)
log.G(ctx).WithField("size", size).Debug("set custom /dev/shm size")
spec.Mounts = removeMount(devShmPath, spec.Mounts)
spec.Mounts = append(spec.Mounts, *mt)
log.G(ctx).WithField("sizeKB", val).Debug("set custom /dev/shm size")
}

// Check if we need to do any capability/device mappings
Expand Down Expand Up @@ -263,3 +244,24 @@ func addLDConfigHook(_ context.Context, spec *oci.Spec, args, env []string) erro
ldConfigHook := hooks.NewOCIHook("/sbin/ldconfig", args, env)
return hooks.AddOCIHook(spec, hooks.Prestart, ldConfigHook)
}

// devShmMountWithSize returns a /dev/shm device mount with size set to
// `sizeString` if it represents a valid size in KB, returns error otherwise.
func devShmMountWithSize(sizeString string) (*oci.Mount, error) {
size, err := strconv.ParseUint(sizeString, 10, 64)
if err != nil {
return nil, fmt.Errorf("/dev/shm size must be a valid integer: %w", err)
}
if size == 0 {
return nil, errors.New("/dev/shm size must be non-zero")
}

// Use the same options as in upstream https://github.com/containerd/containerd/blob/0def98e462706286e6eaeff4a90be22fda75e761/oci/mounts.go#L49
sizeKB := fmt.Sprintf("size=%sk", sizeString)
return &oci.Mount{
Source: "shm",
Destination: devShmPath,
Type: "tmpfs",
Options: []string{"nosuid", "noexec", "nodev", "mode=1777", sizeKB},
}, nil
}
7 changes: 4 additions & 3 deletions internal/guest/runtime/hcsv2/standalone_container.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"go.opencensus.io/trace"

"github.com/Microsoft/hcsshim/internal/guest/network"
specInternal "github.com/Microsoft/hcsshim/internal/guest/spec"
"github.com/Microsoft/hcsshim/internal/guestpath"
"github.com/Microsoft/hcsshim/internal/oc"
)
Expand Down Expand Up @@ -62,7 +63,7 @@ func setupStandaloneContainerSpec(ctx context.Context, id string, spec *oci.Spec
}

// Write the hostname
if !isInMounts("/etc/hostname", spec.Mounts) {
if !specInternal.MountPresent("/etc/hostname", spec.Mounts) {
standaloneHostnamePath := getStandaloneHostnamePath(id)
if err := ioutil.WriteFile(standaloneHostnamePath, []byte(hostname+"\n"), 0644); err != nil {
return errors.Wrapf(err, "failed to write hostname to %q", standaloneHostnamePath)
Expand All @@ -81,7 +82,7 @@ func setupStandaloneContainerSpec(ctx context.Context, id string, spec *oci.Spec
}

// Write the hosts
if !isInMounts("/etc/hosts", spec.Mounts) {
if !specInternal.MountPresent("/etc/hosts", spec.Mounts) {
standaloneHostsContent := network.GenerateEtcHostsContent(ctx, hostname)
standaloneHostsPath := getStandaloneHostsPath(id)
if err := ioutil.WriteFile(standaloneHostsPath, []byte(standaloneHostsContent), 0644); err != nil {
Expand All @@ -101,7 +102,7 @@ func setupStandaloneContainerSpec(ctx context.Context, id string, spec *oci.Spec
}

// Write resolv.conf
if !isInMounts("/etc/resolv.conf", spec.Mounts) {
if !specInternal.MountPresent("/etc/resolv.conf", spec.Mounts) {
ns := getOrAddNetworkNamespace(getNetworkNamespaceID(spec))
var searches, servers []string
for _, n := range ns.Adapters() {
Expand Down
25 changes: 22 additions & 3 deletions internal/guest/runtime/hcsv2/uvm.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@ import (
"syscall"
"time"

"github.com/Microsoft/hcsshim/internal/guest/policy"
"github.com/mattn/go-shellwords"
"github.com/pkg/errors"

"github.com/Microsoft/hcsshim/internal/guest/gcserr"
"github.com/Microsoft/hcsshim/internal/guest/prot"
"github.com/Microsoft/hcsshim/internal/guest/runtime"
"github.com/Microsoft/hcsshim/internal/guest/spec"
"github.com/Microsoft/hcsshim/internal/guest/stdio"
"github.com/Microsoft/hcsshim/internal/guest/storage"
"github.com/Microsoft/hcsshim/internal/guest/storage/overlay"
Expand Down Expand Up @@ -104,6 +106,10 @@ func (h *Host) SetSecurityPolicy(base64Policy string) error {
return err
}

if err := p.ExtendDefaultMounts(policy.DefaultCRIMounts()); err != nil {
return err
}

h.securityPolicyEnforcer = p
h.securityPolicyEnforcerSet = true

Expand Down Expand Up @@ -133,7 +139,7 @@ func (h *Host) GetContainer(id string) (*Container, error) {
}

func setupSandboxMountsPath(id string) (err error) {
mountPath := getSandboxMountsDir(id)
mountPath := spec.SandboxMountsDir(id)
if err := os.MkdirAll(mountPath, 0755); err != nil {
return errors.Wrapf(err, "failed to create sandboxMounts dir in sandbox %v", id)
}
Expand All @@ -147,7 +153,7 @@ func setupSandboxMountsPath(id string) (err error) {
}

func setupSandboxHugePageMountsPath(id string) error {
mountPath := getSandboxHugePageMountsDir(id)
mountPath := spec.HugePagesMountsDir(id)
if err := os.MkdirAll(mountPath, 0755); err != nil {
return errors.Wrapf(err, "failed to create hugepage Mounts dir in sandbox %v", id)
}
Expand Down Expand Up @@ -176,6 +182,8 @@ func (h *Host) CreateContainer(ctx context.Context, id string, settings *prot.VM

var namespaceID string
criType, isCRI := settings.OCISpecification.Annotations[annotations.KubernetesContainerType]
// for sandbox container sandboxID is same as container id
sandboxID := id
if isCRI {
switch criType {
case "sandbox":
Expand All @@ -187,7 +195,7 @@ func (h *Host) CreateContainer(ctx context.Context, id string, settings *prot.VM
}
defer func() {
if err != nil {
_ = os.RemoveAll(getSandboxRootDir(id))
_ = os.RemoveAll(spec.SandboxRootDir(id))
}
}()

Expand All @@ -198,8 +206,13 @@ func (h *Host) CreateContainer(ctx context.Context, id string, settings *prot.VM
if err = setupSandboxHugePageMountsPath(id); err != nil {
return nil, err
}

if err := policy.ExtendPolicyWithNetworkingMounts(id, h.securityPolicyEnforcer, settings.OCISpecification); err != nil {
return nil, err
}
case "container":
sid, ok := settings.OCISpecification.Annotations[annotations.KubernetesSandboxID]
sandboxID = sid
if !ok || sid == "" {
return nil, errors.Errorf("unsupported 'io.kubernetes.cri.sandbox-id': '%s'", sid)
}
Expand All @@ -211,6 +224,9 @@ func (h *Host) CreateContainer(ctx context.Context, id string, settings *prot.VM
_ = os.RemoveAll(getWorkloadRootDir(id))
}
}()
if err := policy.ExtendPolicyWithNetworkingMounts(sandboxID, h.securityPolicyEnforcer, settings.OCISpecification); err != nil {
return nil, err
}
default:
return nil, errors.Errorf("unsupported 'io.kubernetes.cri.container-type': '%s'", criType)
}
Expand All @@ -227,6 +243,9 @@ func (h *Host) CreateContainer(ctx context.Context, id string, settings *prot.VM
}()
}

if err := h.securityPolicyEnforcer.EnforceMountPolicy(sandboxID, id, settings.OCISpecification); err != nil {
return nil, err
}
// Export security policy as one of the process's environment variables so that application and sidecar
// containers can have access to it. The security policy is required by containers which need to extract
// init-time claims found in the security policy.
Expand Down
Loading