Skip to content
Closed
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
3 changes: 2 additions & 1 deletion crates/openshell-core/src/driver_mounts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,8 @@ pub fn path_is_or_under(path: &Path, parent: &Path) -> bool {
path == parent || path.starts_with(parent)
}

fn paths_overlap(left: &Path, right: &Path) -> bool {
/// Return true when either path is the other or contains it.
pub fn paths_overlap(left: &Path, right: &Path) -> bool {
path_is_or_under(left, right) || path_is_or_under(right, left)
}

Expand Down
1 change: 1 addition & 0 deletions crates/openshell-driver-vm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ Select the VM driver with `--compute-driver vm`, `OPENSHELL_COMPUTE_DRIVER=vm`,
| `proxy_connect_by_hostname` | unset | Send hostnames rather than validated IPs in CONNECT. Last resort for proxies whose ACLs reject IP CONNECT targets. |
| `proxy_ca_bundle` | unset | Gateway-host PEM CA bundle trusted for the corporate proxy and TLS-intercepted server certificates. The driver validates it and stages it at a fixed non-secret guest path in the protected overlay. Requires `https_proxy`. |
| `provider_spiffe_workload_api_tcp_endpoint` | unset | Explicit guest-reachable `tcp:IP:port` SPIFFE Workload API listener for provider token exchange. It requires `provider_spiffe_allow_guest_tcp = true`; a host UNIX socket is never silently exposed to a VM guest. |
| `enable_bind_mounts` | `false` | Allow per-sandbox `driver_config.vm.mounts` host directory shares (libkrun virtiofs only). The driver writes a `tag\ttarget\tmode` manifest to `/.openshell/mounts.manifest` in the overlay, and guest init mounts each share after `/sandbox` ownership fixups. Rejected while resource admission is enabled. |

The proxy settings are operator-owned and deployment-level: they are not accepted through `template.driver_config.vm`, and the driver passes them only to native host control. Every present-but-invalid value is fatal at gateway or sandbox startup rather than degrading to a direct dial.

Expand Down
4 changes: 4 additions & 0 deletions crates/openshell-driver-vm/runtime/kernel/openshell.kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ CONFIG_VIRTIO_BLK=y
CONFIG_EXT4_FS=y
CONFIG_EXT4_USE_FOR_EXT2=y

# Host-directory sharing via virtiofs (used for VM driver bind mounts).
CONFIG_FUSE_FS=y
CONFIG_VIRTIO_FS=y

# Cgroups used for process supervision and resource limits.
CONFIG_CGROUPS=y
CONFIG_CGROUP_DEVICE=y
Expand Down
69 changes: 69 additions & 0 deletions crates/openshell-driver-vm/scripts/openshell-vm-sandbox-init.sh
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,73 @@ run_openshell_init_dropins() {
done < <(LC_ALL=C sort -u "$manifest")
}

# Create a virtiofs mount point under ROOT_PREFIX without following symlinks,
# so an image cannot redirect a validated target onto another guest path.
# Directories created under /sandbox are handed to the sandbox user.
prepare_virtiofs_target() {
local target="$1" owner="$2"
local path="${ROOT_PREFIX:-}" component
local -a components
IFS=/ read -r -a components <<<"${target#/}"
for component in "${components[@]}"; do
[ -n "$component" ] || continue
path="${path}/${component}"
if [ -L "$path" ]; then
ts >&2 "FATAL: virtiofs mount target ${target} traverses symlink ${path#"${ROOT_PREFIX:-}"}"
exit 1
fi
if [ ! -e "$path" ]; then
if ! mkdir "$path"; then
ts >&2 "FATAL: failed to create virtiofs mount point ${target}"
exit 1
fi
case "${path#"${ROOT_PREFIX:-}"}" in
/sandbox/*)
if ! chown "$owner" "$path"; then
ts >&2 "FATAL: failed to hand virtiofs mount point parent ${path#"${ROOT_PREFIX:-}"} to ${owner}"
exit 1
fi
;;
esac
elif [ ! -d "$path" ]; then
ts >&2 "FATAL: virtiofs mount target ${target} is not a directory"
exit 1
fi
done
printf '%s\n' "$path"
}

# Runs after every /sandbox ownership fixup and the root-run init drop-ins so
# nothing in init walks into host-backed shares.
mount_virtiofs_shares() {
local manifest
manifest="$(root_path /.openshell/mounts.manifest)"
[ -f "$manifest" ] || return 0

ts "mounting virtiofs shares"
local tag target mode mount_opts guest_target owner
owner="$(sandbox_owner)"
while IFS=$'\t' read -r tag target mode; do
[ -n "$tag" ] || continue
case "$mode" in
ro) mount_opts="-o ro" ;;
rw) mount_opts="" ;;
*)
ts "FATAL: unknown virtiofs mount mode '${mode}' for tag ${tag}"
exit 1
;;
esac
guest_target="$(prepare_virtiofs_target "$target" "$owner")" || exit 1
# shellcheck disable=SC2086
if mount -t virtiofs $mount_opts "$tag" "$guest_target"; then
ts " mounted virtiofs ${tag} -> ${target} (${mode})"
else
ts "FATAL: failed to mount virtiofs ${tag} at ${target}"
exit 1
fi
done < "$manifest"
}

run_post_overlay_setup() {
# Source QEMU-injected environment variables if present. The file lives in
# the overlay upperdir so the cached bootstrap rootfs remains immutable.
Expand Down Expand Up @@ -617,6 +684,8 @@ fi

run_openshell_init_dropins

mount_virtiofs_shares

if [ -n "${OPENSHELL_SANDBOX_ID:-}" ]; then
ts "OPENSHELL_SANDBOX_ID=${OPENSHELL_SANDBOX_ID}"
fi
Expand Down
Loading
Loading