Files
Shailend ChandandgVisor bot 2c6185b9d1 Support privileged executables inside the sandbox
There is already some support for privileged executables before this change:
file capabilities were taken into account when creating new processes inside
the sandbox from external instigation (but not during execve(2)). set-user-id
and set-group-id bits were never taken into account, and the Sentry behaved
as if NO_NEW_PRIVS is always true. After this change, execve(2) and the
external Exec entry point both take the set-user(group)-id bits and file
capabilities into account.

Because NO_NEW_PRIVS can no longer be assumed to be always set, a couple of
extra checks to prevent privilege elevation during execve(2) are needed:
the presence of a ptracer and the use of a shared fsContext. Moreover,
execve had to be serialized with seccomp(2) when it is syncing filters to
all threads in a group. Serializing execve with PTRACE_ATTACH and seccomp
required some Task runState gymnastics.

Task dumpability is reset when privilege elevation takes place and privilege
elevation is denied for binaries living on nosuid mounts. To fix an old bug
where some aux vectors were populated using data from the old pre-execve
creds, the computation of the (file-privilege-augmented) creds during execve
now takes place before the stack for the new program is set up.

gvisor users who thus far have exec'd into the sandbox with UID as (sandbox)
root will lose the caps an all-powerful namespace root'd have if: 1) the binary
is a suid/guid binary 2) the owner of the binary is host root and there is
no mapping for host root in the host user namespace of the sandbox. To avoid
this surprise, the suid/sgid bits are ignored by default. Users can opt-in
via the --allow-suid flag.

One result of this change is that sudo now works inside the sandbox.

PiperOrigin-RevId: 794414101
2025-08-12 23:07:38 -07:00

98 lines
2.6 KiB
Bash

#!/bin/bash
# Copyright 2024 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.
set -xeo pipefail
fail() {
echo "FAIL: $*"
exit 1
}
# Don't report any test cases.
echo "$@" | grep list_tests && exit 1
# When running this test inside a user namespace without host root mapped, like bazel is wont to
# do, /bin/mount appears as a setuid binary owned by (host) overflow-uid inside the container,
# and thus would rob the execing process of its exalted (sandbox) root EUID. So we make a copy if
# mount has the setuid bit set.
hostMount="$(which mount)"
if [[ -u ${hostMount} ]]; then
cp "${hostMount}" /tmp/mount
fi
mount() {
if [[ -x /tmp/mount ]]; then
/tmp/mount "$@"
else
"${hostMount}" "$@"
fi
}
if [[ -z "$TEST_ON_GVISOR" ]]; then
if [[ ! -d /var/run ]]; then
echo "SKIP: /var/run doesn't exist but it is required for the ip tool."
exit 0
fi
mount -t tmpfs test /var/run
else
mkdir -p /var/run
mount -t tmpfs test /var/run
fi
# check_connectivity checks that a TCP connection can be established between two
# specified namespaces.
# Arguments:
# * network namespace where a server will be started
# * IP address in the server network namespace.
# * port for a test TCP connection
# * network namespace where a client will be started
# * test message that will be send from server to client
check_connectivity() {
local srv_netns="$1"
local srv_ip="$2"
local port="$3"
local clt_netns="$4"
local test_msg="$5"
# Create a sync pipe that will be closed when TCP_SRV creates a listen socket.
exec {pipe}<> <(:)
exec {pipe_r}</proc/self/fd/$pipe
exec {pipe_w}>/proc/self/fd/$pipe
exec {pipe}>&-
echo "$test_msg" | ip netns exec "$srv_netns" "$TCP_SRV" "--port=$port" "--sync-fd=$pipe_w" 1>&2 &
pid=$!
exec {pipe_w}>&-
cat <&$pipe_r
exec {pipe_r}>&-
# The server has been started.
out=$(ip netns exec "$clt_netns" nc -nvd "$srv_ip" "$port")
if [[ "$out" != "$test_msg" ]]; then
fail "unexpected output '$out' (expected '$test_msg'"
fi
wait "$pid"
}
wait_for() {
for _ in $(seq 50); do
if eval "$@"; then
return 0
fi
sleep 0.1
done
return 1
}