diff --git a/components/ws-daemon/go.mod b/components/ws-daemon/go.mod index 05c758f237b46f..8f5b968ff79d71 100644 --- a/components/ws-daemon/go.mod +++ b/components/ws-daemon/go.mod @@ -153,7 +153,6 @@ require ( github.com/prometheus/common v0.48.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect github.com/rs/xid v1.5.0 // indirect - github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646 // indirect github.com/slok/go-http-metrics v0.10.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/testify v1.9.0 // indirect diff --git a/components/ws-daemon/pkg/cgroup/plugin_fuse_v2.go b/components/ws-daemon/pkg/cgroup/plugin_fuse_v2.go index 2eaabf76580cb6..246187a86fdc78 100644 --- a/components/ws-daemon/pkg/cgroup/plugin_fuse_v2.go +++ b/components/ws-daemon/pkg/cgroup/plugin_fuse_v2.go @@ -11,11 +11,11 @@ import ( "github.com/opencontainers/runc/libcontainer/cgroups/ebpf" "github.com/opencontainers/runc/libcontainer/cgroups/ebpf/devicefilter" "github.com/opencontainers/runc/libcontainer/devices" - "github.com/opencontainers/runc/libcontainer/specconv" "golang.org/x/sys/unix" "golang.org/x/xerrors" "github.com/gitpod-io/gitpod/common-go/log" + "github.com/gitpod-io/gitpod/ws-daemon/pkg/libcontainer/specconv" ) var ( diff --git a/components/ws-daemon/pkg/content/initializer.go b/components/ws-daemon/pkg/content/initializer.go index 5ff3a2a0496c4b..cee0ea8fc931ca 100644 --- a/components/ws-daemon/pkg/content/initializer.go +++ b/components/ws-daemon/pkg/content/initializer.go @@ -18,7 +18,6 @@ import ( "time" "github.com/google/uuid" - "github.com/opencontainers/runc/libcontainer/specconv" "github.com/opencontainers/runtime-spec/specs-go" "github.com/opentracing/opentracing-go" "github.com/sirupsen/logrus" @@ -31,6 +30,7 @@ import ( "github.com/gitpod-io/gitpod/content-service/pkg/archive" wsinit "github.com/gitpod-io/gitpod/content-service/pkg/initializer" "github.com/gitpod-io/gitpod/content-service/pkg/storage" + "github.com/gitpod-io/gitpod/ws-daemon/pkg/libcontainer/specconv" ) // RunInitializerOpts configure RunInitializer diff --git a/components/ws-daemon/pkg/libcontainer/README.md b/components/ws-daemon/pkg/libcontainer/README.md new file mode 100644 index 00000000000000..bb044a1b303ebb --- /dev/null +++ b/components/ws-daemon/pkg/libcontainer/README.md @@ -0,0 +1,4 @@ +# Why this package exists + +The only reason this package exists is because libcontainer decided to privatize some functions we depend on in the specconv package. +It was easier to copy the files over than re-thinking everything we're doing here. diff --git a/components/ws-daemon/pkg/libcontainer/specconv/example.go b/components/ws-daemon/pkg/libcontainer/specconv/example.go new file mode 100644 index 00000000000000..1a838e3059f179 --- /dev/null +++ b/components/ws-daemon/pkg/libcontainer/specconv/example.go @@ -0,0 +1,240 @@ +// Copyright The libcontainer authors + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Copied from: https://github.com/opencontainers/runc/blob/364ec0f1b4fa188ad96049c590ecb42fa70ea165/libcontainer/specconv/example.go#L1 +package specconv + +import ( + "os" + "path/filepath" + "strings" + + "github.com/opencontainers/runc/libcontainer/cgroups" + "github.com/opencontainers/runtime-spec/specs-go" +) + +// Example returns an example spec file, with many options set so a user can +// see what a standard spec file looks like. +func Example() *specs.Spec { + spec := &specs.Spec{ + Version: specs.Version, + Root: &specs.Root{ + Path: "rootfs", + Readonly: true, + }, + Process: &specs.Process{ + Terminal: true, + User: specs.User{}, + Args: []string{ + "sh", + }, + Env: []string{ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "TERM=xterm", + }, + Cwd: "/", + NoNewPrivileges: true, + Capabilities: &specs.LinuxCapabilities{ + Bounding: []string{ + "CAP_AUDIT_WRITE", + "CAP_KILL", + "CAP_NET_BIND_SERVICE", + }, + Permitted: []string{ + "CAP_AUDIT_WRITE", + "CAP_KILL", + "CAP_NET_BIND_SERVICE", + }, + Ambient: []string{ + "CAP_AUDIT_WRITE", + "CAP_KILL", + "CAP_NET_BIND_SERVICE", + }, + Effective: []string{ + "CAP_AUDIT_WRITE", + "CAP_KILL", + "CAP_NET_BIND_SERVICE", + }, + }, + Rlimits: []specs.POSIXRlimit{ + { + Type: "RLIMIT_NOFILE", + Hard: uint64(1024), + Soft: uint64(1024), + }, + }, + }, + Hostname: "runc", + Mounts: []specs.Mount{ + { + Destination: "/proc", + Type: "proc", + Source: "proc", + Options: nil, + }, + { + 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: "/sys/fs/cgroup", + Type: "cgroup", + Source: "cgroup", + Options: []string{"nosuid", "noexec", "nodev", "relatime", "ro"}, + }, + }, + Linux: &specs.Linux{ + MaskedPaths: []string{ + "/proc/acpi", + "/proc/asound", + "/proc/kcore", + "/proc/keys", + "/proc/latency_stats", + "/proc/timer_list", + "/proc/timer_stats", + "/proc/sched_debug", + "/sys/firmware", + "/proc/scsi", + }, + ReadonlyPaths: []string{ + "/proc/bus", + "/proc/fs", + "/proc/irq", + "/proc/sys", + "/proc/sysrq-trigger", + }, + Resources: &specs.LinuxResources{ + Devices: []specs.LinuxDeviceCgroup{ + { + Allow: false, + Access: "rwm", + }, + }, + }, + Namespaces: []specs.LinuxNamespace{ + { + Type: specs.PIDNamespace, + }, + { + Type: specs.NetworkNamespace, + }, + { + Type: specs.IPCNamespace, + }, + { + Type: specs.UTSNamespace, + }, + { + Type: specs.MountNamespace, + }, + }, + }, + } + if cgroups.IsCgroup2UnifiedMode() { + spec.Linux.Namespaces = append(spec.Linux.Namespaces, specs.LinuxNamespace{ + Type: specs.CgroupNamespace, + }) + } + return spec +} + +// ToRootless converts the given spec file into one that should work with +// rootless containers (euid != 0), by removing incompatible options and adding others that +// are needed. +func ToRootless(spec *specs.Spec) { + var namespaces []specs.LinuxNamespace + + // Remove networkns from the spec. + for _, ns := range spec.Linux.Namespaces { + switch ns.Type { + case specs.NetworkNamespace, specs.UserNamespace: + // Do nothing. + default: + namespaces = append(namespaces, ns) + } + } + // Add userns to the spec. + namespaces = append(namespaces, specs.LinuxNamespace{ + Type: specs.UserNamespace, + }) + spec.Linux.Namespaces = namespaces + + // Add mappings for the current user. + spec.Linux.UIDMappings = []specs.LinuxIDMapping{{ + HostID: uint32(os.Geteuid()), + ContainerID: 0, + Size: 1, + }} + spec.Linux.GIDMappings = []specs.LinuxIDMapping{{ + HostID: uint32(os.Getegid()), + ContainerID: 0, + Size: 1, + }} + + // Fix up mounts. + var mounts []specs.Mount + for _, mount := range spec.Mounts { + // Replace the /sys mount with an rbind. + if filepath.Clean(mount.Destination) == "/sys" { + mounts = append(mounts, specs.Mount{ + Source: "/sys", + Destination: "/sys", + Type: "none", + Options: []string{"rbind", "nosuid", "noexec", "nodev", "ro"}, + }) + continue + } + + // Remove all gid= and uid= mappings. + var options []string + for _, option := range mount.Options { + if !strings.HasPrefix(option, "gid=") && !strings.HasPrefix(option, "uid=") { + options = append(options, option) + } + } + + mount.Options = options + mounts = append(mounts, mount) + } + spec.Mounts = mounts + + // Remove cgroup settings. + spec.Linux.Resources = nil +} diff --git a/components/ws-daemon/pkg/libcontainer/specconv/spec_linux.go b/components/ws-daemon/pkg/libcontainer/specconv/spec_linux.go new file mode 100644 index 00000000000000..d44ea9e5719cca --- /dev/null +++ b/components/ws-daemon/pkg/libcontainer/specconv/spec_linux.go @@ -0,0 +1,163 @@ +// Copyright The libcontainer authors + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// gpl: Copied from: https://github.com/opencontainers/runc/blob/1f9e36c055b4eb97c38f8aae6ee50ca534962f77/libcontainer/specconv/spec_linux.go#L192 +package specconv + +import "github.com/opencontainers/runc/libcontainer/devices" + +// AllowedDevices is the set of devices which are automatically included for +// all containers. +// +// # XXX (cyphar) +// +// This behaviour is at the very least "questionable" (if not outright +// wrong) according to the runtime-spec. +// +// Yes, we have to include certain devices other than the ones the user +// specifies, but several devices listed here are not part of the spec +// (including "mknod for any device"?!). In addition, these rules are +// appended to the user-provided set which means that users *cannot disable +// this behaviour*. +// +// ... unfortunately I'm too scared to change this now because who knows how +// many people depend on this (incorrect and arguably insecure) behaviour. +var AllowedDevices = []*devices.Device{ + // allow mknod for any device + { + Rule: devices.Rule{ + Type: devices.CharDevice, + Major: devices.Wildcard, + Minor: devices.Wildcard, + Permissions: "m", + Allow: true, + }, + }, + { + Rule: devices.Rule{ + Type: devices.BlockDevice, + Major: devices.Wildcard, + Minor: devices.Wildcard, + Permissions: "m", + Allow: true, + }, + }, + { + Path: "/dev/null", + FileMode: 0o666, + Uid: 0, + Gid: 0, + Rule: devices.Rule{ + Type: devices.CharDevice, + Major: 1, + Minor: 3, + Permissions: "rwm", + Allow: true, + }, + }, + { + Path: "/dev/random", + FileMode: 0o666, + Uid: 0, + Gid: 0, + Rule: devices.Rule{ + Type: devices.CharDevice, + Major: 1, + Minor: 8, + Permissions: "rwm", + Allow: true, + }, + }, + { + Path: "/dev/full", + FileMode: 0o666, + Uid: 0, + Gid: 0, + Rule: devices.Rule{ + Type: devices.CharDevice, + Major: 1, + Minor: 7, + Permissions: "rwm", + Allow: true, + }, + }, + { + Path: "/dev/tty", + FileMode: 0o666, + Uid: 0, + Gid: 0, + Rule: devices.Rule{ + Type: devices.CharDevice, + Major: 5, + Minor: 0, + Permissions: "rwm", + Allow: true, + }, + }, + { + Path: "/dev/zero", + FileMode: 0o666, + Uid: 0, + Gid: 0, + Rule: devices.Rule{ + Type: devices.CharDevice, + Major: 1, + Minor: 5, + Permissions: "rwm", + Allow: true, + }, + }, + { + Path: "/dev/urandom", + FileMode: 0o666, + Uid: 0, + Gid: 0, + Rule: devices.Rule{ + Type: devices.CharDevice, + Major: 1, + Minor: 9, + Permissions: "rwm", + Allow: true, + }, + }, + // /dev/pts/ - pts namespaces are "coming soon" + { + Rule: devices.Rule{ + Type: devices.CharDevice, + Major: 136, + Minor: devices.Wildcard, + Permissions: "rwm", + Allow: true, + }, + }, + { + Rule: devices.Rule{ + Type: devices.CharDevice, + Major: 5, + Minor: 2, + Permissions: "rwm", + Allow: true, + }, + }, + // tuntap + { + Rule: devices.Rule{ + Type: devices.CharDevice, + Major: 10, + Minor: 200, + Permissions: "rwm", + Allow: true, + }, + }, +} diff --git a/install/installer/go.mod b/install/installer/go.mod index 31936828a5c319..d80bb8edb0f60f 100644 --- a/install/installer/go.mod +++ b/install/installer/go.mod @@ -242,7 +242,6 @@ require ( github.com/rs/xid v1.5.0 // indirect github.com/rubenv/sql-migrate v1.7.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646 // indirect github.com/segmentio/backo-go v0.0.0-20200129164019-23eae7c10bd3 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect github.com/shopspring/decimal v1.4.0 // indirect