mirror of
https://github.com/hanzoai/visor.git
synced 2026-09-24 17:04:14 +00:00
Merge pull request #13929 from EtiennePerot:sentry-binary
PiperOrigin-RevId: 959992675
This commit is contained in:
@@ -6,7 +6,7 @@ _templates:
|
||||
- exit_status: "*"
|
||||
limit: 2
|
||||
common: &common
|
||||
timeout_in_minutes: 30
|
||||
timeout_in_minutes: 55
|
||||
retry:
|
||||
<<: *retry_settings
|
||||
source_test: &source_test
|
||||
|
||||
@@ -29,6 +29,7 @@ release_files(
|
||||
"//shim:containerd-shim-runsc-v1",
|
||||
],
|
||||
sidecars = SIDECARS,
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
nogo_config(
|
||||
|
||||
@@ -115,12 +115,13 @@ RUNTIME ?= runsc
|
||||
else
|
||||
RUNTIME ?= $(BRANCH_NAME)
|
||||
endif
|
||||
RUNTIME_DIR ?= $(shell dirname $(shell mktemp -u))/$(RUNTIME)
|
||||
RUNSC_TARGET ?= //runsc
|
||||
RUNTIME_BIN ?= $(RUNTIME_DIR)/runsc
|
||||
RUNTIME_LOG_DIR ?= $(RUNTIME_DIR)/logs
|
||||
RUNTIME_LOGS ?= $(RUNTIME_LOG_DIR)/runsc.log.%TEST%.%TIMESTAMP%.%COMMAND%
|
||||
RUNTIME_ARGS ?=
|
||||
RUNTIME_DIR ?= $(shell dirname $(shell mktemp -u))/$(RUNTIME)
|
||||
RUNSC_TARGET ?= //runsc
|
||||
RUNTIME_BIN ?= $(RUNTIME_DIR)/runsc
|
||||
EXTRA_SIDECAR_TARGETS ?= # Extra binaries to install under gvisor-bin/.
|
||||
RUNTIME_LOG_DIR ?= $(RUNTIME_DIR)/logs
|
||||
RUNTIME_LOGS ?= $(RUNTIME_LOG_DIR)/runsc.log.%TEST%.%TIMESTAMP%.%COMMAND%
|
||||
RUNTIME_ARGS ?=
|
||||
DOCKER_DAEMON_CONFIG_PATH ?= /etc/docker/daemon.json
|
||||
DOCKER_RELOAD_COMMAND ?= sudo systemctl reload docker
|
||||
|
||||
@@ -139,8 +140,9 @@ ifeq (,$(STAGED_BINARIES))
|
||||
@$(call copy,//debian:gvisor-bin-tar,$(RUNTIME_DIR))
|
||||
@tar -C "$(RUNTIME_DIR)" -xf "$(RUNTIME_DIR)/gvisor-bin-tar.tar"
|
||||
@rm -f "$(RUNTIME_DIR)/gvisor-bin-tar.tar"
|
||||
@$(if $(EXTRA_SIDECAR_TARGETS),$(call copy,$(EXTRA_SIDECAR_TARGETS),$(RUNTIME_DIR)/gvisor-bin))
|
||||
else
|
||||
gcloud storage cat "${STAGED_BINARIES}" | \
|
||||
@gcloud storage cat "${STAGED_BINARIES}" | \
|
||||
tar -C "$(RUNTIME_DIR)" -zxvf - ./runsc ./gvisor-bin && \
|
||||
chmod -R a+rx "$(RUNTIME_BIN)" "$(RUNTIME_DIR)/gvisor-bin"
|
||||
endif
|
||||
@@ -274,7 +276,7 @@ network-tests: iptables-tests packetdrill-tests packetimpact-tests
|
||||
# To run multiple specific syscall tests:
|
||||
# make syscall-tests TARGETS="//test/syscalls:signalfd_test_runsc_systrap_shared //test/syscalls:link_test_runsc_systrap_shared"
|
||||
syscall-tests: $(RUNTIME_BIN)
|
||||
@$(call test,$(OPTIONS) --test_env=RUNTIME=$(RUNTIME_BIN) --cxxopt=-Werror $(PARTITIONS) $(if $(TARGETS),-- $(TARGETS),test/syscalls/... test/rtnetlink/...))
|
||||
@$(call test,$(OPTIONS) --test_env=RUNTIME=$(RUNTIME_BIN) --test_env=GVISOR_SIDECAR_BINARIES_DIR=$(RUNTIME_DIR)/gvisor-bin --cxxopt=-Werror $(PARTITIONS) $(if $(TARGETS),-- $(TARGETS),test/syscalls/... test/rtnetlink/...))
|
||||
.PHONY: syscall-tests
|
||||
|
||||
packetimpact-tests:
|
||||
@@ -430,6 +432,7 @@ plugin-network-tests: integration-test-images $(RUNTIME_BIN)
|
||||
@$(call test_runtime_cached,$(RUNTIME)-dpdk, --test_arg=-test.run=ConnectToSelf $(INTEGRATION_TARGETS))
|
||||
|
||||
plugin-network-tests: RUNSC_TARGET=--config plugin-tldk //runsc:runsc-plugin-stack
|
||||
plugin-network-tests: EXTRA_SIDECAR_TARGETS=--config plugin-tldk //runsc/cmd/sentry:gvisor_sentry_plugin_stack
|
||||
|
||||
overlay-tests: integration-test-images $(RUNTIME_BIN)
|
||||
@$(call install_runtime_noreload,$(RUNTIME)-overlay,--overlay2=all:dir=/tmp)
|
||||
|
||||
@@ -177,7 +177,7 @@ type ExpectedOpts struct {
|
||||
// Sandbox config options follow.
|
||||
|
||||
DirectFS bool
|
||||
HostNetwork bool
|
||||
NetworkMode string
|
||||
EnableRaw bool
|
||||
RequiresCapSysPtrace bool
|
||||
SpecHasUserNamespace bool
|
||||
@@ -204,11 +204,16 @@ type ExpectedOpts struct {
|
||||
ExtraMounts []string
|
||||
}
|
||||
|
||||
// HostNetwork returns true if the sandbox is using hostinet.
|
||||
func (e *ExpectedOpts) HostNetwork() bool {
|
||||
return e.NetworkMode == "host"
|
||||
}
|
||||
|
||||
// Expected returns the posture a sandbox started with the given configuration
|
||||
// must have.
|
||||
func Expected(opts ExpectedOpts) *Posture {
|
||||
applyCaps := opts.DirectFS || opts.HostNetwork
|
||||
newUserNS := !applyCaps || opts.SpecHasUserNamespace || (opts.DirectFS && !opts.HostNetwork)
|
||||
applyCaps := opts.DirectFS || opts.HostNetwork()
|
||||
newUserNS := !applyCaps || opts.SpecHasUserNamespace || (opts.DirectFS && !opts.HostNetwork())
|
||||
return &Posture{
|
||||
Creds: expectedCreds(applyCaps),
|
||||
Caps: expectedCaps(opts, applyCaps),
|
||||
@@ -242,7 +247,7 @@ func expectedCaps(opts ExpectedOpts, applyCaps bool) Capabilities {
|
||||
if opts.DirectFS {
|
||||
caps = append(caps, directfsCaps...)
|
||||
}
|
||||
if opts.HostNetwork {
|
||||
if opts.HostNetwork() {
|
||||
held := make(map[string]bool, len(opts.Ref.PermittedCaps))
|
||||
for _, c := range opts.Ref.PermittedCaps {
|
||||
held[c] = true
|
||||
@@ -281,7 +286,7 @@ func expectedNamespaces(opts ExpectedOpts, newUserNS bool) Namespaces {
|
||||
switch {
|
||||
case opts.NetNamespaceTarget != "":
|
||||
ns.Net = NSJoined(opts.NetNamespaceTarget)
|
||||
case opts.HostNetwork:
|
||||
case opts.HostNetwork():
|
||||
ns.Net = NSInherited
|
||||
default:
|
||||
ns.Net = NSNew
|
||||
@@ -343,7 +348,7 @@ func expectedAttrs(opts ExpectedOpts, applyCaps, newUserNS bool) Attrs {
|
||||
}
|
||||
|
||||
var env []string
|
||||
if opts.CgoEnabled {
|
||||
if opts.NetworkMode == "plugin" {
|
||||
env = append(env, "GLIBC_TUNABLES=glibc.pthread.rseq=0")
|
||||
}
|
||||
if opts.GoDebug != "" {
|
||||
|
||||
@@ -311,8 +311,8 @@ type Attrs struct {
|
||||
// process. False under shim.
|
||||
ChildOfRef bool
|
||||
|
||||
// Environ is the environment, sorted, with `RUNSC_START_TIME_NANOS`
|
||||
// removed.
|
||||
// Environ is the environment, sorted, with variables like
|
||||
// `RUNSC_START_TIME_NANOS` and `GVISOR_ENFORCE_RELEASE` removed.
|
||||
Environ []string
|
||||
|
||||
// Argv0 is argv[0], which `runsc` should set to "runsc-sandbox".
|
||||
@@ -1105,8 +1105,8 @@ func parseLimits(s string) (map[string]string, error) {
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// startTimeEnvRE matches the start-time env variable `runsc` uses.
|
||||
var startTimeEnvRE = regexp.MustCompile(`^RUNSC_START_TIME_NANOS=\d+$`)
|
||||
// ignoredEnvRegExp matches the start-time env variable `runsc` uses.
|
||||
var ignoredEnvRegExp = regexp.MustCompile(`^(RUNSC_START_TIME_NANOS=\d+|GVISOR_ENFORCE_RELEASE=.*)$`)
|
||||
|
||||
func collectAttrs(p, ref *proc, status, refStatus map[string]string, userNS string) (Attrs, error) {
|
||||
var a Attrs
|
||||
@@ -1134,8 +1134,8 @@ func collectAttrs(p, ref *proc, status, refStatus map[string]string, userNS stri
|
||||
return a, fmt.Errorf("reading environ: %w", err)
|
||||
}
|
||||
for _, e := range env {
|
||||
if startTimeEnvRE.MatchString(e) {
|
||||
continue // Skip start time env var
|
||||
if ignoredEnvRegExp.MatchString(e) {
|
||||
continue
|
||||
}
|
||||
a.Environ = append(a.Environ, e)
|
||||
}
|
||||
|
||||
@@ -146,13 +146,21 @@ func ImageByName(name string) string {
|
||||
// ConfigureExePath configures the executable for runsc in the test environment.
|
||||
func ConfigureExePath() error {
|
||||
if *runscPath == "" {
|
||||
path, err := FindFile("runsc/runsc")
|
||||
path, err := FindFile("release/runsc")
|
||||
if err != nil {
|
||||
return err
|
||||
path, err = FindFile("runsc/runsc")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
*runscPath = path
|
||||
}
|
||||
specutils.ExePath = *runscPath
|
||||
if os.Getenv("GVISOR_SIDECAR_BINARIES_DIR") == "" {
|
||||
if dir, err := FindFile("release/gvisor-bin"); err == nil {
|
||||
os.Setenv("GVISOR_SIDECAR_BINARIES_DIR", dir)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ go_library(
|
||||
"//runsc/cmd",
|
||||
"//runsc/cmd/alias",
|
||||
"//runsc/cmd/nvproxy",
|
||||
"//runsc/cmd/sentry/sentrycmd",
|
||||
"//runsc/cmd/trace",
|
||||
"//runsc/cmd/util",
|
||||
"//runsc/gvisorbinaries/embed",
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"gvisor.dev/gvisor/runsc/cmd"
|
||||
"gvisor.dev/gvisor/runsc/cmd/alias"
|
||||
"gvisor.dev/gvisor/runsc/cmd/nvproxy"
|
||||
"gvisor.dev/gvisor/runsc/cmd/sentry/sentrycmd"
|
||||
"gvisor.dev/gvisor/runsc/cmd/trace"
|
||||
"gvisor.dev/gvisor/runsc/cmd/util"
|
||||
|
||||
@@ -94,9 +95,9 @@ func commands() (map[util.SubCommand]string, []subcommands.Command) {
|
||||
new(cmd.MetricServer): metricGroup,
|
||||
|
||||
// Internal commands.
|
||||
new(cmd.Boot): internalGroup,
|
||||
new(cmd.Gofer): internalGroup,
|
||||
new(cmd.Umount): internalGroup,
|
||||
new(sentrycmd.Boot): internalGroup,
|
||||
new(cmd.Gofer): internalGroup,
|
||||
new(sentrycmd.Umount): internalGroup,
|
||||
}
|
||||
|
||||
// Merge alias commands.
|
||||
|
||||
+3
-18
@@ -8,9 +8,7 @@ package(
|
||||
go_library(
|
||||
name = "cmd",
|
||||
srcs = [
|
||||
"boot.go",
|
||||
"checkpoint.go",
|
||||
"chroot.go",
|
||||
"cmd.go",
|
||||
"cpu_features.go",
|
||||
"create.go",
|
||||
@@ -19,7 +17,6 @@ go_library(
|
||||
"do.go",
|
||||
"events.go",
|
||||
"exec.go",
|
||||
"fd_mapping.go",
|
||||
"features.go",
|
||||
"fscheckpoint.go",
|
||||
"gofer.go",
|
||||
@@ -51,7 +48,6 @@ go_library(
|
||||
"symbolize.go",
|
||||
"syscalls.go",
|
||||
"tar.go",
|
||||
"umount_unsafe.go",
|
||||
"update.go",
|
||||
"usage.go",
|
||||
"wait.go",
|
||||
@@ -60,31 +56,20 @@ go_library(
|
||||
force_add_state_pkg = True,
|
||||
visibility = ["//runsc:__subpackages__"],
|
||||
deps = [
|
||||
"//pkg/abi/linux",
|
||||
"//pkg/abi/tpu",
|
||||
"//pkg/cleanup",
|
||||
"//pkg/coretag",
|
||||
"//pkg/coverage",
|
||||
"//pkg/cpuid",
|
||||
"//pkg/fd",
|
||||
"//pkg/hostarch",
|
||||
"//pkg/lisafs",
|
||||
"//pkg/log",
|
||||
"//pkg/metric",
|
||||
"//pkg/prometheus",
|
||||
"//pkg/rdma",
|
||||
"//pkg/ring0",
|
||||
"//pkg/sentry/checkpoint",
|
||||
"//pkg/sentry/control",
|
||||
"//pkg/sentry/devices/nvproxy/nvconf",
|
||||
"//pkg/sentry/hostmm",
|
||||
"//pkg/sentry/kernel",
|
||||
"//pkg/sentry/kernel/auth",
|
||||
"//pkg/sentry/platform",
|
||||
"//pkg/sentry/syscalls/linux",
|
||||
"//pkg/state/pretty",
|
||||
"//pkg/state/statefile",
|
||||
"//pkg/tcpip/nftables",
|
||||
"//pkg/unet",
|
||||
"//pkg/urpc",
|
||||
"//runsc/boot",
|
||||
@@ -107,7 +92,6 @@ go_library(
|
||||
"//runsc/version",
|
||||
"//sandboxexec/proto:sandbox_options_go_proto",
|
||||
"@com_github_google_subcommands//:go_default_library",
|
||||
"@com_github_moby_sys_capability//:go_default_library",
|
||||
"@com_github_opencontainers_runtime_spec//specs-go:go_default_library",
|
||||
"@org_golang_google_protobuf//encoding/prototext:go_default_library",
|
||||
"@org_golang_google_protobuf//proto:go_default_library",
|
||||
@@ -121,7 +105,6 @@ go_test(
|
||||
srcs = [
|
||||
"bootposture_test.go",
|
||||
"capability_test.go",
|
||||
"chroot_test.go",
|
||||
"delete_test.go",
|
||||
"exec_test.go",
|
||||
"features_test.go",
|
||||
@@ -135,7 +118,8 @@ go_test(
|
||||
"spec_test.go",
|
||||
],
|
||||
data = [
|
||||
"//runsc",
|
||||
"//:release", # keep
|
||||
"//runsc", # keep
|
||||
],
|
||||
library = ":cmd",
|
||||
deps = [
|
||||
@@ -148,6 +132,7 @@ go_test(
|
||||
"//pkg/test/testutil",
|
||||
"//runsc/boot",
|
||||
"//runsc/cmd/sandboxsetup",
|
||||
"//runsc/cmd/sentry/sentrycmd",
|
||||
"//runsc/cmd/util",
|
||||
"//runsc/config",
|
||||
"//runsc/container",
|
||||
|
||||
@@ -153,7 +153,7 @@ func testBootPosture(t *testing.T, tc postureCase) {
|
||||
}
|
||||
opts := sandboxposture.ExpectedOpts{
|
||||
DirectFS: tc.directfs,
|
||||
HostNetwork: isHostNetwork,
|
||||
NetworkMode: tc.network.String(),
|
||||
EnableRaw: conf.EnableRaw,
|
||||
RequiresCapSysPtrace: sandboxposture.PlatformCapSysPtrace[conf.Platform],
|
||||
GoDebug: sandboxposture.PlatformGoDebug[conf.Platform],
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/test/testutil"
|
||||
"gvisor.dev/gvisor/runsc/cmd/sandboxsetup"
|
||||
"gvisor.dev/gvisor/runsc/cmd/sentry/sentrycmd"
|
||||
"gvisor.dev/gvisor/runsc/config"
|
||||
"gvisor.dev/gvisor/runsc/container"
|
||||
"gvisor.dev/gvisor/runsc/specutils"
|
||||
@@ -131,7 +132,7 @@ func testCapabilities(t *testing.T, directfs bool) {
|
||||
}
|
||||
if directfs {
|
||||
// With directfs, the sandbox has additional capabilities.
|
||||
wantSandboxCaps = specutils.MergeCapabilities(wantSandboxCaps, directfsSandboxLinuxCaps)
|
||||
wantSandboxCaps = specutils.MergeCapabilities(wantSandboxCaps, sentrycmd.DirectfsSandboxLinuxCaps)
|
||||
}
|
||||
// Check that sandbox and gofer have the proper capabilities.
|
||||
if err := checkProcessCaps(c.Sandbox.Getpid(), wantSandboxCaps); err != nil {
|
||||
|
||||
+2
-1
@@ -31,6 +31,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/sentry/control"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
"gvisor.dev/gvisor/runsc/cmd/sandboxsetup"
|
||||
"gvisor.dev/gvisor/runsc/cmd/util"
|
||||
"gvisor.dev/gvisor/runsc/config"
|
||||
"gvisor.dev/gvisor/runsc/console"
|
||||
@@ -60,7 +61,7 @@ type Exec struct {
|
||||
|
||||
// passFDs are user-supplied FDs from the host to be exposed to the
|
||||
// sandboxed app.
|
||||
passFDs fdMappings
|
||||
passFDs sandboxsetup.FDMappings
|
||||
|
||||
// execFD is the host file descriptor used for program execution.
|
||||
execFD int
|
||||
|
||||
+1
-22
@@ -19,7 +19,6 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
|
||||
"github.com/google/subcommands"
|
||||
@@ -553,30 +552,10 @@ func (g *goferSyncFDs) syncUsernsForRootless(uid, gid uint32) {
|
||||
if g.usernsFD < 0 {
|
||||
return
|
||||
}
|
||||
syncUsernsForRootless(g.usernsFD, uid, gid)
|
||||
sandboxsetup.SyncUsernsForRootless(g.usernsFD, uid, gid)
|
||||
g.usernsFD = -1
|
||||
}
|
||||
|
||||
// syncUsernsForRootless waits on usernsFD to be closed and then sets
|
||||
// UID/GID to uid/gid. Note that this function calls runtime.LockOSThread().
|
||||
//
|
||||
// Postcondition: All callers must re-exec themselves after this returns.
|
||||
func syncUsernsForRootless(fd int, uid uint32, gid uint32) {
|
||||
if err := sandboxsetup.WaitForFD(fd, "userns sync FD"); err != nil {
|
||||
util.Fatalf("failed to sync on userns FD: %v", err)
|
||||
}
|
||||
|
||||
// SETUID changes UID on the current system thread, so we have
|
||||
// to re-execute current binary.
|
||||
runtime.LockOSThread()
|
||||
if _, _, errno := unix.RawSyscall(unix.SYS_SETUID, uintptr(uid), 0, 0); errno != 0 {
|
||||
util.Fatalf("failed to set UID: %v", errno)
|
||||
}
|
||||
if _, _, errno := unix.RawSyscall(unix.SYS_SETGID, uintptr(gid), 0, 0); errno != 0 {
|
||||
util.Fatalf("failed to set GID: %v", errno)
|
||||
}
|
||||
}
|
||||
|
||||
// syncChroot waits on chrootFD to be closed.
|
||||
// Used for synchronization during container filesystem setup which is done
|
||||
// from the non-gofer process.
|
||||
|
||||
+2
-1
@@ -23,6 +23,7 @@ import (
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/runsc/cmd/sandboxsetup"
|
||||
"gvisor.dev/gvisor/runsc/cmd/util"
|
||||
"gvisor.dev/gvisor/runsc/config"
|
||||
"gvisor.dev/gvisor/runsc/container"
|
||||
@@ -40,7 +41,7 @@ type Run struct {
|
||||
|
||||
// passFDs are user-supplied FDs from the host to be exposed to the
|
||||
// sandboxed app.
|
||||
passFDs fdMappings
|
||||
passFDs sandboxsetup.FDMappings
|
||||
|
||||
// execFD is the host file descriptor used for program execution.
|
||||
execFD int
|
||||
|
||||
@@ -9,6 +9,7 @@ go_library(
|
||||
name = "sandboxsetup",
|
||||
srcs = [
|
||||
"caps.go",
|
||||
"fdmappings.go",
|
||||
"flags.go",
|
||||
"fs.go",
|
||||
"gofer_mount.go",
|
||||
@@ -19,6 +20,7 @@ go_library(
|
||||
"//pkg/fd",
|
||||
"//pkg/log",
|
||||
"//pkg/unet",
|
||||
"//runsc/boot",
|
||||
"//runsc/cmd/util",
|
||||
"//runsc/config",
|
||||
"//runsc/flag",
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cmd
|
||||
package sandboxsetup
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -22,11 +22,11 @@ import (
|
||||
"gvisor.dev/gvisor/runsc/boot"
|
||||
)
|
||||
|
||||
// fdMappings can be used with flags that appear multiple times.
|
||||
type fdMappings []boot.FDMapping
|
||||
// FDMappings can be used with flags that appear multiple times.
|
||||
type FDMappings []boot.FDMapping
|
||||
|
||||
// String implements flag.Value.
|
||||
func (i *fdMappings) String() string {
|
||||
func (i *FDMappings) String() string {
|
||||
var mappings []string
|
||||
for _, m := range *i {
|
||||
mappings = append(mappings, fmt.Sprintf("%v:%v", m.Host, m.Guest))
|
||||
@@ -35,18 +35,18 @@ func (i *fdMappings) String() string {
|
||||
}
|
||||
|
||||
// Get implements flag.Value.
|
||||
func (i *fdMappings) Get() any {
|
||||
func (i *FDMappings) Get() any {
|
||||
return i
|
||||
}
|
||||
|
||||
// GetArray returns an array of mappings.
|
||||
func (i *fdMappings) GetArray() []boot.FDMapping {
|
||||
func (i *FDMappings) GetArray() []boot.FDMapping {
|
||||
return *i
|
||||
}
|
||||
|
||||
// Set implements flag.Value and appends a mapping from the command line to the
|
||||
// mappings array. Set(String()) should be idempotent.
|
||||
func (i *fdMappings) Set(s string) error {
|
||||
func (i *FDMappings) Set(s string) error {
|
||||
for _, m := range strings.Split(s, ",") {
|
||||
split := strings.Split(m, ":")
|
||||
if len(split) != 2 {
|
||||
@@ -112,6 +112,26 @@ func PrepareArgs(subCmdName string, fSet *flag.FlagSet, override map[string]stri
|
||||
return args
|
||||
}
|
||||
|
||||
// SyncUsernsForRootless waits on usernsFD to be closed and then sets
|
||||
// UID/GID to uid/gid. Note that this function calls runtime.LockOSThread().
|
||||
//
|
||||
// Postcondition: All callers must re-exec themselves after this returns.
|
||||
func SyncUsernsForRootless(fd int, uid uint32, gid uint32) {
|
||||
if err := WaitForFD(fd, "userns sync FD"); err != nil {
|
||||
util.Fatalf("failed to sync on userns FD: %v", err)
|
||||
}
|
||||
|
||||
// SETUID changes UID on the current system thread, so we have
|
||||
// to re-execute current binary.
|
||||
runtime.LockOSThread()
|
||||
if _, _, errno := unix.RawSyscall(unix.SYS_SETUID, uintptr(uid), 0, 0); errno != 0 {
|
||||
util.Fatalf("failed to set UID: %v", errno)
|
||||
}
|
||||
if _, _, errno := unix.RawSyscall(unix.SYS_SETGID, uintptr(gid), 0, 0); errno != 0 {
|
||||
util.Fatalf("failed to set GID: %v", errno)
|
||||
}
|
||||
}
|
||||
|
||||
// ExecProcUmounter executes a child process that umounts /proc when the
|
||||
// returned pipe is closed.
|
||||
func ExecProcUmounter() (*exec.Cmd, *os.File) {
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
load("//tools:defs.bzl", "go_binary")
|
||||
load("//tools:deps.bzl", "deps_test")
|
||||
|
||||
package(
|
||||
default_applicable_licenses = ["//:license"],
|
||||
licenses = ["notice"],
|
||||
)
|
||||
|
||||
go_binary(
|
||||
name = "gvisor_sentry",
|
||||
srcs = ["sentry_main.go"],
|
||||
noasan = True,
|
||||
pure = True,
|
||||
visibility = ["//visibility:public"],
|
||||
x_defs = {"gvisor.dev/gvisor/runsc/version.version": "{STABLE_VERSION}"},
|
||||
deps = [
|
||||
"//runsc/cli",
|
||||
"//runsc/cmd/sentry/sentrycmd",
|
||||
"//runsc/cmd/util",
|
||||
"//runsc/gvisorbinaries",
|
||||
],
|
||||
)
|
||||
|
||||
go_binary(
|
||||
name = "gvisor_sentry_plugin_stack",
|
||||
srcs = ["sentry_main_plugin_stack.go"],
|
||||
gotags = ["network_plugins"],
|
||||
nogo = False,
|
||||
pure = False,
|
||||
static = True,
|
||||
tags = ["network_plugins"],
|
||||
visibility = ["//visibility:public"],
|
||||
x_defs = {"gvisor.dev/gvisor/runsc/version.version": "{STABLE_VERSION}"},
|
||||
deps = [
|
||||
"//pkg/sentry/socket/plugin/stack",
|
||||
"//runsc/cli",
|
||||
"//runsc/cmd/sentry/sentrycmd",
|
||||
"//runsc/cmd/util",
|
||||
"//runsc/gvisorbinaries",
|
||||
],
|
||||
)
|
||||
|
||||
deps_test(
|
||||
name = "gvisor_sentry_deps_test",
|
||||
# The sentry binaries contain the sentry, and nothing else.
|
||||
# Keep this tight.
|
||||
allowed = [
|
||||
"//runsc/cli",
|
||||
"//runsc/cmd/sandboxsetup",
|
||||
"//runsc/cmd/sentry/sentrycmd",
|
||||
"//runsc/cmd/util",
|
||||
"//runsc/config",
|
||||
"//runsc/donation",
|
||||
"//runsc/flag",
|
||||
"//runsc/gvisorbinaries",
|
||||
"//runsc/profile",
|
||||
"//runsc/starttime",
|
||||
"//runsc/version",
|
||||
|
||||
# External deps.
|
||||
"@com_github_cenkalti_backoff//:go_default_library",
|
||||
"@com_github_google_btree//:go_default_library",
|
||||
"@com_github_google_subcommands//:go_default_library",
|
||||
"@com_github_moby_sys_capability//:go_default_library",
|
||||
"@com_github_mohae_deepcopy//:go_default_library",
|
||||
"@com_github_opencontainers_runtime_spec//specs-go:go_default_library",
|
||||
"@com_github_opencontainers_runtime_spec//specs-go/features:go_default_library",
|
||||
"@io_bazel_rules_go//go/tools/coverdata",
|
||||
"@org_golang_x_exp//constraints:go_default_library",
|
||||
"@org_golang_x_mod//semver:go_default_library",
|
||||
"@org_golang_x_sync//errgroup:go_default_library",
|
||||
"@org_golang_x_sys//cpu:go_default_library",
|
||||
"@org_golang_x_sys//unix:go_default_library",
|
||||
"@org_golang_x_time//rate:go_default_library",
|
||||
],
|
||||
allowed_prefixes = [
|
||||
"@org_golang_google_protobuf//",
|
||||
"//pkg",
|
||||
"//runsc/boot",
|
||||
"//runsc/specutils",
|
||||
],
|
||||
definitely_not_allowed_prefixes = [
|
||||
# Container-management side of runsc:
|
||||
"//runsc/container",
|
||||
"//runsc/sandbox",
|
||||
|
||||
# Gofers:
|
||||
"//runsc/fsgofer",
|
||||
|
||||
# Other sidecar binaries:
|
||||
"//runsc/checkpointgofer",
|
||||
"//runsc/cmd/metricserver",
|
||||
"//runsc/metricserver",
|
||||
],
|
||||
targets = [
|
||||
":gvisor_sentry",
|
||||
":gvisor_sentry_plugin_stack",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright 2026 The gVisor 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.
|
||||
|
||||
// The `gvisor_sentry` binary runs the gVisor sentry, aka the gVisor kernel.
|
||||
package main
|
||||
|
||||
import (
|
||||
"gvisor.dev/gvisor/runsc/cli"
|
||||
"gvisor.dev/gvisor/runsc/cmd/sentry/sentrycmd"
|
||||
"gvisor.dev/gvisor/runsc/cmd/util"
|
||||
"gvisor.dev/gvisor/runsc/gvisorbinaries"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cli.Run(&gvisorbinaries.GvisorSentry, map[util.SubCommand]string{
|
||||
new(sentrycmd.Boot): "internal use only",
|
||||
new(sentrycmd.Umount): "internal use only",
|
||||
}, nil)
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright 2026 The gVisor 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.
|
||||
|
||||
//go:build !false && network_plugins
|
||||
// +build !false,network_plugins
|
||||
|
||||
// The `gvisor_sentry_plugin_stack` binary runs the gVisor sentry with a
|
||||
// third-party plugin network stack linked in.
|
||||
package main
|
||||
|
||||
import (
|
||||
_ "gvisor.dev/gvisor/pkg/sentry/socket/plugin/stack"
|
||||
"gvisor.dev/gvisor/runsc/cli"
|
||||
"gvisor.dev/gvisor/runsc/cmd/sentry/sentrycmd"
|
||||
"gvisor.dev/gvisor/runsc/cmd/util"
|
||||
"gvisor.dev/gvisor/runsc/gvisorbinaries"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cli.Run(&gvisorbinaries.GvisorSentryPluginStack, map[util.SubCommand]string{
|
||||
new(sentrycmd.Boot): "internal use only",
|
||||
new(sentrycmd.Umount): "internal use only",
|
||||
}, nil)
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
load("//tools:defs.bzl", "go_library", "go_test")
|
||||
|
||||
package(
|
||||
default_applicable_licenses = ["//:license"],
|
||||
licenses = ["notice"],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "sentrycmd",
|
||||
srcs = [
|
||||
"boot.go",
|
||||
"chroot.go",
|
||||
"umount_unsafe.go",
|
||||
],
|
||||
visibility = ["//runsc:__subpackages__"],
|
||||
deps = [
|
||||
"//pkg/abi/linux",
|
||||
"//pkg/abi/tpu",
|
||||
"//pkg/coretag",
|
||||
"//pkg/cpuid",
|
||||
"//pkg/fd",
|
||||
"//pkg/hostarch",
|
||||
"//pkg/log",
|
||||
"//pkg/metric",
|
||||
"//pkg/prometheus",
|
||||
"//pkg/rdma",
|
||||
"//pkg/ring0",
|
||||
"//pkg/sentry/devices/nvproxy/nvconf",
|
||||
"//pkg/sentry/hostmm",
|
||||
"//pkg/sentry/platform",
|
||||
"//pkg/sentry/syscalls/linux",
|
||||
"//pkg/tcpip/nftables",
|
||||
"//runsc/boot",
|
||||
"//runsc/cmd/sandboxsetup",
|
||||
"//runsc/cmd/util",
|
||||
"//runsc/config",
|
||||
"//runsc/flag",
|
||||
"//runsc/profile",
|
||||
"//runsc/specutils",
|
||||
"@com_github_google_subcommands//:go_default_library",
|
||||
"@com_github_moby_sys_capability//:go_default_library",
|
||||
"@com_github_opencontainers_runtime_spec//specs-go:go_default_library",
|
||||
"@org_golang_x_sys//unix:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "sentrycmd_test",
|
||||
size = "small",
|
||||
srcs = ["chroot_test.go"],
|
||||
library = ":sentrycmd",
|
||||
deps = [
|
||||
"//runsc/config",
|
||||
"//runsc/specutils",
|
||||
"@com_github_opencontainers_runtime_spec//specs-go:go_default_library",
|
||||
"@org_golang_x_sys//unix:go_default_library",
|
||||
],
|
||||
)
|
||||
@@ -12,7 +12,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cmd
|
||||
// Package sentrycmd holds the subcommands run by the sentry process.
|
||||
package sentrycmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -29,6 +30,7 @@ import (
|
||||
"github.com/moby/sys/capability"
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/coretag"
|
||||
"gvisor.dev/gvisor/pkg/cpuid"
|
||||
"gvisor.dev/gvisor/pkg/fd"
|
||||
@@ -63,9 +65,9 @@ var (
|
||||
"CAP_FSETID",
|
||||
}
|
||||
|
||||
// directfsSandboxLinuxCaps is the minimal set of capabilities needed by the
|
||||
// DirectfsSandboxLinuxCaps is the minimal set of capabilities needed by the
|
||||
// sandbox to operate on files in directfs mode.
|
||||
directfsSandboxLinuxCaps = &specs.LinuxCapabilities{
|
||||
DirectfsSandboxLinuxCaps = &specs.LinuxCapabilities{
|
||||
Bounding: directfsSandboxCaps,
|
||||
Effective: directfsSandboxCaps,
|
||||
Permitted: directfsSandboxCaps,
|
||||
@@ -116,7 +118,7 @@ type Boot struct {
|
||||
stdioFDs sandboxsetup.IntFlags
|
||||
|
||||
// passFDs are mappings of user-supplied host to guest file descriptors.
|
||||
passFDs fdMappings
|
||||
passFDs sandboxsetup.FDMappings
|
||||
|
||||
// execFD is the host file descriptor used for program execution.
|
||||
execFD int
|
||||
@@ -365,7 +367,7 @@ func (b *Boot) Execute(_ context.Context, f *flag.FlagSet, args ...any) subcomma
|
||||
}
|
||||
|
||||
if b.syncUsernsFD >= 0 {
|
||||
syncUsernsForRootless(b.syncUsernsFD, uint32(b.uid), uint32(b.gid))
|
||||
sandboxsetup.SyncUsernsForRootless(b.syncUsernsFD, uint32(b.uid), uint32(b.gid))
|
||||
argOverride["sync-userns-fd"] = "-1"
|
||||
}
|
||||
|
||||
@@ -457,7 +459,7 @@ func (b *Boot) Execute(_ context.Context, f *flag.FlagSet, args ...any) subcomma
|
||||
}
|
||||
|
||||
if conf.DirectFS {
|
||||
caps = specutils.MergeCapabilities(caps, directfsSandboxLinuxCaps)
|
||||
caps = specutils.MergeCapabilities(caps, DirectfsSandboxLinuxCaps)
|
||||
}
|
||||
if conf.Network == config.NetworkHost {
|
||||
curCaps, err := capability.NewPid2(0)
|
||||
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cmd
|
||||
package sentrycmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -12,9 +12,10 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cmd
|
||||
package sentrycmd
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
@@ -23,8 +24,18 @@ import (
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/runsc/config"
|
||||
"gvisor.dev/gvisor/runsc/specutils"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
flag.Parse()
|
||||
if err := specutils.MaybeRunAsRoot(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error running as root: %v", err)
|
||||
os.Exit(123)
|
||||
}
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
func setup(t *testing.T) (string, string) {
|
||||
t.Helper()
|
||||
testDir := t.TempDir()
|
||||
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cmd
|
||||
package sentrycmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -67,7 +67,7 @@ go_test(
|
||||
],
|
||||
}),
|
||||
data = [
|
||||
"//runsc",
|
||||
"//:release",
|
||||
"//test/cmd/test_app",
|
||||
],
|
||||
library = ":container",
|
||||
|
||||
@@ -59,6 +59,7 @@ import (
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
"gvisor.dev/gvisor/runsc/config"
|
||||
@@ -68,8 +69,10 @@ import (
|
||||
|
||||
// Sidecar binary filenames under the "gvisor-bin/" directory.
|
||||
const (
|
||||
metricServerName = "runsc-metric-server"
|
||||
checkpointGoferName = "checkpointgofer"
|
||||
metricServerName = "runsc-metric-server"
|
||||
checkpointGoferName = "checkpointgofer"
|
||||
gvisorSentryName = "gvisor_sentry"
|
||||
gvisorSentryPluginStackName = "gvisor_sentry_plugin_stack"
|
||||
)
|
||||
|
||||
// binDirName is the name of the directory holding sidecar binaries.
|
||||
@@ -93,6 +96,9 @@ const (
|
||||
enforceReleaseTestonlySkip = "TESTONLY_SKIP"
|
||||
)
|
||||
|
||||
// Special release name used in CI that is also ignored.
|
||||
const buildkiteTestVersion = "buildkite-test-branch-dirty"
|
||||
|
||||
// cutSkip returns the expected release embedded in `val` (env var).
|
||||
func cutSkip(val, prefix string) (string, bool) {
|
||||
if val == prefix {
|
||||
@@ -106,9 +112,11 @@ func cutSkip(val, prefix string) (string, bool) {
|
||||
// Set from config flag.
|
||||
var ReleaseEnforcementPolicy = config.SidecarNever
|
||||
|
||||
// withEnforceRelease returns envv with `GVISOR_ENFORCE_RELEASE` set for
|
||||
// sidecar processes.
|
||||
func withEnforceRelease(envv []string) []string {
|
||||
// WithEnforceRelease returns envv with `GVISOR_ENFORCE_RELEASE` set for
|
||||
// sidecar processes. Exec and ForkExec apply it automatically
|
||||
// Callers that spawn a sidecar through other means (manual `exec.Cmd`)
|
||||
// must apply it to the sidecar process's env themselves.
|
||||
func WithEnforceRelease(envv []string) []string {
|
||||
val := os.Getenv(enforceReleaseEnv)
|
||||
prefix, want := "", ""
|
||||
if w, ok := cutSkip(val, enforceReleaseTestonlySkip); ok {
|
||||
@@ -171,6 +179,10 @@ func VerifyMatchingRelease(b *Binary) {
|
||||
if b == nil {
|
||||
return
|
||||
}
|
||||
if got == buildkiteTestVersion {
|
||||
log.Infof("Sidecar release match check not enforced%s; this build is %q.", who, got)
|
||||
return
|
||||
}
|
||||
var msg string
|
||||
info := testOnly
|
||||
switch want {
|
||||
@@ -199,10 +211,16 @@ var (
|
||||
MetricServer = Binary{Name: metricServerName}
|
||||
// CheckpointGofer is the checkpoint gofer sidecar binary.
|
||||
CheckpointGofer = Binary{Name: checkpointGoferName}
|
||||
// GvisorSentry is the sentry (kernel) sidecar binary.
|
||||
GvisorSentry = Binary{Name: gvisorSentryName}
|
||||
// GvisorSentryPluginStack is the sentry sidecar binary with a plugin
|
||||
// network stack linked in.
|
||||
// Only present in plugin-enabled installations, so not part of `All`.
|
||||
GvisorSentryPluginStack = Binary{Name: gvisorSentryPluginStackName}
|
||||
)
|
||||
|
||||
// All lists every known sidecar.
|
||||
var All = []*Binary{&MetricServer, &CheckpointGofer}
|
||||
// All lists every sidecar present in a standard installation.
|
||||
var All = []*Binary{&MetricServer, &CheckpointGofer, &GvisorSentry}
|
||||
|
||||
// Options is the set of options used to execute a sidecar binary.
|
||||
type Options struct {
|
||||
@@ -262,6 +280,10 @@ func resolveDir() (string, error) {
|
||||
if dir := os.Getenv(sidecarBinariesDirEnv); dir != "" {
|
||||
return dir, nil
|
||||
}
|
||||
dir := filepath.Join(filepath.Dir(specutils.ExePath), binDirName)
|
||||
if fi, err := os.Stat(dir); err == nil && fi.IsDir() {
|
||||
return dir, nil
|
||||
}
|
||||
exe, err := filepath.EvalSymlinks(specutils.ExePath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("cannot resolve path to runsc binary %q: %w", specutils.ExePath, err)
|
||||
@@ -331,7 +353,7 @@ func (b *Binary) warnEmbeddedDeprecated(opts *Options) {
|
||||
// Exec resolves the binary and replaces the current process with it. It only
|
||||
// returns if execution could not be started.
|
||||
func (b *Binary) Exec(opts Options) error {
|
||||
opts.Envv = withEnforceRelease(opts.Envv)
|
||||
opts.Envv = WithEnforceRelease(opts.Envv)
|
||||
if p, err := b.Path(); err == nil {
|
||||
log.Infof("sidecar %q found: executing %s (%v)", b.Name, p, &opts)
|
||||
return execDisk(p, opts)
|
||||
@@ -346,7 +368,7 @@ func (b *Binary) Exec(opts Options) error {
|
||||
// ForkExec resolves the binary and runs it in a new process, returning the
|
||||
// child's PID.
|
||||
func (b *Binary) ForkExec(opts Options) (int, error) {
|
||||
opts.Envv = withEnforceRelease(opts.Envv)
|
||||
opts.Envv = WithEnforceRelease(opts.Envv)
|
||||
if p, err := b.Path(); err == nil {
|
||||
log.Infof("sidecar %q: executing %s (%v)", b.Name, p, &opts)
|
||||
return forkExecDisk(p, opts)
|
||||
|
||||
@@ -942,8 +942,22 @@ func (s *Sandbox) createSandboxProcess(conf *config.Config, args *Args, startSyn
|
||||
}
|
||||
lfOpts.Command = "boot" // Revert command to "boot".
|
||||
|
||||
sentryBin := &gvisorbinaries.GvisorSentry
|
||||
sentryUsesCgo := false
|
||||
if conf.Network == config.NetworkPlugin {
|
||||
sentryBin = &gvisorbinaries.GvisorSentryPluginStack
|
||||
sentryUsesCgo = true
|
||||
}
|
||||
bootBinPath := specutils.ExePath
|
||||
if p, err := sentryBin.Path(); err == nil {
|
||||
log.Infof("Sidecar %q found: booting sandbox with %s", sentryBin.Name, p)
|
||||
bootBinPath = p
|
||||
} else {
|
||||
log.Warningf("Sidecar %q not usable (%v): booting sandbox with runsc itself", sentryBin.Name, err)
|
||||
}
|
||||
|
||||
// Relay all the config flags to the sandbox process.
|
||||
cmd := exec.Command(specutils.ExePath, conf.ToFlags()...)
|
||||
cmd := exec.Command(bootBinPath, conf.ToFlags()...)
|
||||
cmd.SysProcAttr = &unix.SysProcAttr{
|
||||
// Detach from this session, otherwise cmd will get SIGHUP and SIGCONT
|
||||
// when re-parented.
|
||||
@@ -963,12 +977,17 @@ func (s *Sandbox) createSandboxProcess(conf *config.Config, args *Args, startSyn
|
||||
// All flags after this must be for the boot command
|
||||
cmd.Args = append(cmd.Args, "boot", "--bundle="+args.BundleDir)
|
||||
|
||||
// Clear environment variables, unless --TESTONLY-unsafe-nonroot is set.
|
||||
if !conf.TestOnlyAllowRunAsCurrentUserWithoutChroot {
|
||||
// Setting cmd.Env = nil causes cmd to inherit the current process's env.
|
||||
if conf.TestOnlyAllowRunAsCurrentUserWithoutChroot {
|
||||
// --TESTONLY-unsafe-nonroot is set, so keep env.
|
||||
cmd.Env = os.Environ()
|
||||
} else {
|
||||
// Clear environment variables, unless --TESTONLY-unsafe-nonroot is set.
|
||||
cmd.Env = []string{}
|
||||
}
|
||||
if config.CgoEnabled {
|
||||
if bootBinPath != specutils.ExePath {
|
||||
cmd.Env = gvisorbinaries.WithEnforceRelease(cmd.Env)
|
||||
}
|
||||
if sentryUsesCgo {
|
||||
// Platforms that use stub processes are not compatible with
|
||||
// the glibc rseq, because they unmap everything from a process
|
||||
// address space.
|
||||
|
||||
+6
-6
@@ -13,8 +13,8 @@ go_test(
|
||||
"integration_test.go",
|
||||
],
|
||||
|
||||
# runsc is needed to invalidate the bazel cache in case of any code changes.
|
||||
data = ["//runsc"],
|
||||
# Needed to invalidate the bazel cache in case of any code changes.
|
||||
data = ["//:release"],
|
||||
library = ":integration",
|
||||
tags = [
|
||||
# Requires docker and runsc to be configured before the test runs.
|
||||
@@ -40,8 +40,8 @@ go_test(
|
||||
"integration_runtime_test.go",
|
||||
"systemd_test.go",
|
||||
],
|
||||
# runsc is needed to invalidate the bazel cache in case of any code changes.
|
||||
data = ["//runsc"],
|
||||
# Needed to invalidate the bazel cache in case of any code changes.
|
||||
data = ["//:release"],
|
||||
library = ":integration",
|
||||
tags = [
|
||||
# Requires docker and runsc to be configured before the test runs.
|
||||
@@ -65,8 +65,8 @@ go_test(
|
||||
srcs = [
|
||||
"runtime_in_docker_test.go",
|
||||
],
|
||||
# runsc is needed to invalidate the bazel cache in case of any code changes.
|
||||
data = ["//runsc"],
|
||||
# Needed to invalidate the bazel cache in case of any code changes.
|
||||
data = ["//:release"],
|
||||
library = ":integration",
|
||||
tags = [
|
||||
# Requires docker and runsc to be configured before the test runs.
|
||||
|
||||
@@ -132,9 +132,12 @@ func (test testVariant) failureCases() []testVariant {
|
||||
// https://gvisor.dev/blog/2024/09/23/safe-ride-into-the-dangerzone/
|
||||
func TestGVisorInDocker(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
runscPath, err := testutil.FindFile("runsc/runsc")
|
||||
runscPath, err := testutil.FindFile("release/runsc")
|
||||
if err != nil {
|
||||
t.Fatalf("Cannot locate runsc binary: %v", err)
|
||||
runscPath, err = testutil.FindFile("runsc/runsc")
|
||||
if err != nil {
|
||||
t.Fatalf("Cannot locate runsc binary: %v", err)
|
||||
}
|
||||
}
|
||||
for _, test := range []testVariant{
|
||||
{
|
||||
|
||||
+12
-12
@@ -8,7 +8,7 @@ package(
|
||||
go_test(
|
||||
name = "smoke_test",
|
||||
srcs = ["smoke_test.go"],
|
||||
# runsc is needed to invalidate the bazel cache in case of any code changes.
|
||||
# Needed to invalidate the bazel cache in case of any code changes.
|
||||
data = ["//runsc"],
|
||||
tags = [
|
||||
"manual",
|
||||
@@ -22,8 +22,8 @@ go_test(
|
||||
go_test(
|
||||
name = "pytorch_test",
|
||||
srcs = ["pytorch_test.go"],
|
||||
# runsc is needed to invalidate the bazel cache in case of any code changes.
|
||||
data = ["//runsc"],
|
||||
# Needed to invalidate the bazel cache in case of any code changes.
|
||||
data = ["//:release"],
|
||||
tags = [
|
||||
"manual",
|
||||
"noguitar",
|
||||
@@ -36,8 +36,8 @@ go_test(
|
||||
go_test(
|
||||
name = "textgen_test",
|
||||
srcs = ["textgen_test.go"],
|
||||
# runsc is needed to invalidate the bazel cache in case of any code changes.
|
||||
data = ["//runsc"],
|
||||
# Needed to invalidate the bazel cache in case of any code changes.
|
||||
data = ["//:release"],
|
||||
embedsrcs = ["gvisor.png"],
|
||||
tags = [
|
||||
"manual",
|
||||
@@ -56,7 +56,7 @@ go_test(
|
||||
name = "sglang_test",
|
||||
srcs = ["sglang_test.go"],
|
||||
# runsc is needed to invalidate the bazel cache in case of any code changes.
|
||||
data = ["//runsc"],
|
||||
data = ["//:release"],
|
||||
tags = [
|
||||
"manual",
|
||||
"noguitar",
|
||||
@@ -73,7 +73,7 @@ go_test(
|
||||
name = "sr_test",
|
||||
srcs = ["sr_test.go"],
|
||||
# runsc is needed to invalidate the bazel cache in case of any code changes.
|
||||
data = ["//runsc"],
|
||||
data = ["//:release"],
|
||||
tags = [
|
||||
"manual",
|
||||
"noguitar",
|
||||
@@ -92,7 +92,7 @@ go_test(
|
||||
timeout = "eternal", # YES_I_REALLY_NEED_AN_ETERNAL_TEST
|
||||
srcs = ["cuda_test.go"],
|
||||
# runsc is needed to invalidate the bazel cache in case of any code changes.
|
||||
data = ["//runsc"],
|
||||
data = ["//:release"],
|
||||
tags = [
|
||||
"manual",
|
||||
"noguitar",
|
||||
@@ -110,7 +110,7 @@ go_test(
|
||||
timeout = "eternal", # YES_I_REALLY_NEED_AN_ETERNAL_TEST
|
||||
srcs = ["cuda_12_8_test.go"],
|
||||
# runsc is needed to invalidate the bazel cache in case of any code changes.
|
||||
data = ["//runsc"],
|
||||
data = ["//:release"],
|
||||
tags = [
|
||||
"manual",
|
||||
"noguitar",
|
||||
@@ -127,7 +127,7 @@ go_test(
|
||||
name = "ffmpeg_test",
|
||||
srcs = ["ffmpeg_test.go"],
|
||||
# runsc is needed to invalidate the bazel cache in case of any code changes.
|
||||
data = ["//runsc"],
|
||||
data = ["//:release"],
|
||||
tags = [
|
||||
"manual",
|
||||
"noguitar",
|
||||
@@ -141,7 +141,7 @@ go_test(
|
||||
name = "imagegen_test",
|
||||
srcs = ["imagegen_test.go"],
|
||||
# runsc is needed to invalidate the bazel cache in case of any code changes.
|
||||
data = ["//runsc"],
|
||||
data = ["//:release"],
|
||||
tags = [
|
||||
"manual",
|
||||
"noguitar",
|
||||
@@ -179,7 +179,7 @@ go_test(
|
||||
name = "triton_test",
|
||||
srcs = ["triton_test.go"],
|
||||
# runsc is needed to invalidate the bazel cache in case of any code changes.
|
||||
data = ["//runsc"],
|
||||
data = ["//:release"],
|
||||
tags = [
|
||||
"manual",
|
||||
"noguitar",
|
||||
|
||||
+2
-2
@@ -14,8 +14,8 @@ go_test(
|
||||
data = [
|
||||
"latin10k.txt",
|
||||
"mysql.sql",
|
||||
# runsc is needed to invalidate the bazel cache in case of any code changes.
|
||||
"//runsc",
|
||||
# Needed to invalidate the bazel cache in case of any code changes.
|
||||
"//:release",
|
||||
],
|
||||
library = ":image",
|
||||
tags = [
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ go_test(
|
||||
"sandboxposture_test.go",
|
||||
],
|
||||
data = [
|
||||
"//runsc",
|
||||
"//:release",
|
||||
],
|
||||
library = ":root",
|
||||
pure = True,
|
||||
|
||||
@@ -173,6 +173,7 @@ func TestSandboxProcessEnv(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got = regexp.MustCompile("(^|\x00)RUNSC_START_TIME_NANOS=\\d+\x00").ReplaceAll(got, []byte("$1"))
|
||||
got = regexp.MustCompile("(^|\x00)GVISOR_ENFORCE_RELEASE=[^\x00]*\x00").ReplaceAll(got, []byte("$1"))
|
||||
if len(got) != 0 && string(got) != "GLIBC_TUNABLES=glibc.pthread.rseq=0\x00" {
|
||||
t.Errorf("sandbox process's environment is not empty: got %s (%v)", string(got), got)
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ func TestSandboxPostureDocker(t *testing.T) {
|
||||
|
||||
want := sandboxposture.Expected(sandboxposture.ExpectedOpts{
|
||||
DirectFS: conf.DirectFS,
|
||||
HostNetwork: conf.Network == config.NetworkHost,
|
||||
NetworkMode: conf.Network.String(),
|
||||
EnableRaw: conf.EnableRaw,
|
||||
RequiresCapSysPtrace: sandboxposture.PlatformCapSysPtrace[conf.Platform],
|
||||
GoDebug: sandboxposture.PlatformGoDebug[conf.Platform],
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ go_binary(
|
||||
testonly = 1,
|
||||
srcs = ["main.go"],
|
||||
data = [
|
||||
"//runsc",
|
||||
"//:release",
|
||||
"//test/runner/fuse",
|
||||
"//test/runner/setup_container",
|
||||
],
|
||||
|
||||
@@ -63,10 +63,9 @@ _runtime_test = rule(
|
||||
executable = True,
|
||||
cfg = "target",
|
||||
),
|
||||
# runsc is needed to invalidate the bazel cache in case of any code changes.
|
||||
# Needed to invalidate the bazel cache in case of any code changes.
|
||||
"_runsc": attr.label(
|
||||
default = "//runsc:runsc",
|
||||
executable = True,
|
||||
default = "//:release",
|
||||
cfg = "target",
|
||||
),
|
||||
},
|
||||
|
||||
+58
-9
@@ -11,30 +11,56 @@ DepsInfo = provider(
|
||||
def _deps_check_impl(target, ctx):
|
||||
# Check the target's dependencies and add any of our own deps.
|
||||
deps = []
|
||||
for dep in ctx.rule.attr.deps:
|
||||
deps.append(dep)
|
||||
if hasattr(ctx.rule.attr, "deps"):
|
||||
for dep in ctx.rule.attr.deps:
|
||||
deps.append(dep)
|
||||
|
||||
nodes = {}
|
||||
if len(deps) != 0:
|
||||
nodes[target] = deps
|
||||
|
||||
# Keep and propagate each dep's providers.
|
||||
for dep in ctx.rule.attr.deps:
|
||||
nodes.update(dep[DepsInfo].nodes)
|
||||
if hasattr(ctx.rule.attr, "deps"):
|
||||
for dep in ctx.rule.attr.deps:
|
||||
if DepsInfo in dep:
|
||||
nodes.update(dep[DepsInfo].nodes)
|
||||
|
||||
if hasattr(ctx.rule.attr, "actual_binary"):
|
||||
dep = ctx.rule.attr.actual_binary
|
||||
if dep and DepsInfo in dep:
|
||||
nodes.update(dep[DepsInfo].nodes)
|
||||
|
||||
# Map target (the wrapper target) as depending on the wrapped target's deps.
|
||||
if dep in dep[DepsInfo].nodes:
|
||||
nodes[target] = dep[DepsInfo].nodes[dep]
|
||||
|
||||
return [DepsInfo(nodes = nodes)]
|
||||
|
||||
_deps_check = aspect(
|
||||
implementation = _deps_check_impl,
|
||||
attr_aspects = ["deps"],
|
||||
attr_aspects = ["deps", "actual_binary"],
|
||||
)
|
||||
|
||||
def _workspace_of(label):
|
||||
# Under bzlmod, external labels have canonical workspace names like
|
||||
# "gazelle++go_deps+org_golang_x_sys".
|
||||
# Strip prefixes so that "@org_golang_x_sys//..." prefixes match.
|
||||
return label.workspace_name.rsplit("+", 1)[-1]
|
||||
|
||||
def _parse_prefix(prefix):
|
||||
workspace, pkg = prefix.split("//", 1)
|
||||
if len(workspace) > 0 and workspace[0] == "@":
|
||||
workspace = workspace[1:]
|
||||
return workspace, pkg
|
||||
|
||||
def _matches_prefix(label, prefix):
|
||||
workspace, pkg = _parse_prefix(prefix)
|
||||
return _workspace_of(label) == workspace and label.package.startswith(pkg)
|
||||
|
||||
def _is_allowed(target, allowlist, prefixes):
|
||||
# Check for allowed prefixes.
|
||||
for prefix in prefixes:
|
||||
workspace, pfx = prefix.split("//", 1)
|
||||
if len(workspace) > 0 and workspace[0] == "@":
|
||||
workspace = workspace[1:]
|
||||
if target.workspace_name == workspace and target.package.startswith(pfx):
|
||||
if _matches_prefix(target, prefix):
|
||||
return True
|
||||
|
||||
# Check the allowlist.
|
||||
@@ -44,7 +70,24 @@ def _is_allowed(target, allowlist, prefixes):
|
||||
|
||||
return False
|
||||
|
||||
def _check_definitely_not_allowed(ctx):
|
||||
# `deps_test` already rejects any dependency that is not explicitly
|
||||
# allowed, but this rejects additions to the allowlist itself, to make
|
||||
# it obvious at review time that something that should definitely not
|
||||
# be added is still being added. Defense in depth?
|
||||
for denied in ctx.attr.definitely_not_allowed_prefixes:
|
||||
denied_workspace, denied_pkg = _parse_prefix(denied)
|
||||
for allowed in ctx.attr.allowed:
|
||||
if _matches_prefix(allowed.label, denied):
|
||||
fail("allowed entry %s matches definitely_not_allowed_prefixes entry %s" % (allowed.label, denied))
|
||||
for prefix in ctx.attr.allowed_prefixes:
|
||||
workspace, pkg = _parse_prefix(prefix)
|
||||
if workspace == denied_workspace and (pkg.startswith(denied_pkg) or denied_pkg.startswith(pkg)):
|
||||
fail("allowed_prefixes entry %s matches definitely_not_allowed_prefixes entry %s" % (prefix, denied))
|
||||
|
||||
def _deps_test_impl(ctx):
|
||||
_check_definitely_not_allowed(ctx)
|
||||
|
||||
nodes = {}
|
||||
for target in ctx.attr.targets:
|
||||
for (node_target, node_deps) in target[DepsInfo].nodes.items():
|
||||
@@ -114,6 +157,12 @@ deps_test = rule(
|
||||
"allowed_prefixes": attr.string_list(
|
||||
doc = "Any packages beginning with these prefixes are allowed.",
|
||||
),
|
||||
"definitely_not_allowed_prefixes": attr.string_list(
|
||||
doc = "Packages beginning with these prefixes must not be " +
|
||||
"covered by allowed or allowed_prefixes. Makes " +
|
||||
"'never add these' possible to be defined explicitly in " +
|
||||
"the BUILD file.",
|
||||
),
|
||||
},
|
||||
test = True,
|
||||
)
|
||||
|
||||
+22
-20
@@ -5,6 +5,7 @@
|
||||
SIDECARS = {
|
||||
"//runsc/checkpointgofer:checkpointgofer_binary": "checkpointgofer",
|
||||
"//runsc/cmd/metricserver:runsc-metric-server": "runsc-metric-server",
|
||||
"//runsc/cmd/sentry:gvisor_sentry": "gvisor_sentry",
|
||||
}
|
||||
|
||||
def _single_file(target):
|
||||
@@ -15,37 +16,38 @@ def _single_file(target):
|
||||
|
||||
def _release_files_impl(ctx):
|
||||
outputs = []
|
||||
inputs = []
|
||||
commands = []
|
||||
|
||||
# Top-level binaries
|
||||
for target in ctx.attr.bins:
|
||||
src = _single_file(target)
|
||||
out = ctx.actions.declare_file("%s/%s" % (ctx.label.name, src.basename))
|
||||
ctx.actions.run_shell(
|
||||
inputs = [src],
|
||||
outputs = [out],
|
||||
command = 'cp -f "$1" "$2"',
|
||||
arguments = [src.path, out.path],
|
||||
mnemonic = "ReleaseFile",
|
||||
)
|
||||
inputs.append(src)
|
||||
outputs.append(out)
|
||||
commands.append('cp -f "%s" "%s"' % (src.path, out.path))
|
||||
|
||||
# Sidecar binaries
|
||||
gvisor_bin = ctx.actions.declare_directory("%s/gvisor-bin" % ctx.label.name)
|
||||
sidecar_files = []
|
||||
commands = ['mkdir -p "%s"' % (gvisor_bin.path,)]
|
||||
for target, name in ctx.attr.sidecars.items():
|
||||
src = _single_file(target)
|
||||
sidecar_files.append(src)
|
||||
commands.append('cp -f "%s" "%s/%s"' % (src.path, gvisor_bin.path, name))
|
||||
ctx.actions.run_shell(
|
||||
inputs = sidecar_files,
|
||||
outputs = [gvisor_bin],
|
||||
command = "\n".join(commands),
|
||||
mnemonic = "ReleaseSidecars",
|
||||
)
|
||||
outputs.append(gvisor_bin)
|
||||
out = ctx.actions.declare_file("%s/gvisor-bin/%s" % (ctx.label.name, name))
|
||||
inputs.append(src)
|
||||
outputs.append(out)
|
||||
commands.append('mkdir -p "$(dirname "%s")" && cp -f "%s" "%s"' % (out.path, src.path, out.path))
|
||||
|
||||
return [DefaultInfo(files = depset(outputs))]
|
||||
ctx.actions.run_shell(
|
||||
inputs = inputs,
|
||||
outputs = outputs,
|
||||
command = "\n".join(commands),
|
||||
mnemonic = "ReleaseFiles",
|
||||
)
|
||||
|
||||
runfiles = ctx.runfiles(files = outputs)
|
||||
return [DefaultInfo(
|
||||
files = depset(outputs),
|
||||
default_runfiles = runfiles,
|
||||
data_runfiles = runfiles,
|
||||
)]
|
||||
|
||||
release_files = rule(
|
||||
implementation = _release_files_impl,
|
||||
|
||||
Reference in New Issue
Block a user