Harden launcher overrides and fix opencode backend regressions
- remove codex auth mounts from opencode run/shell paths - reject opencode login and invalid backend values - harden opencode config writes against symlink clobbering - fix opencode build args and packages_extra handling - enforce cap-drop and read-only rootfs in runtime commands - reject dangerous runtime/build env overrides - update README and test docs to match actual behavior - extend regression coverage for backend safety and hardening
This commit is contained in:
79
sloptrap
79
sloptrap
@@ -174,7 +174,7 @@ usage() {
|
||||
info_line "Example manifest entries:\n"
|
||||
comment_line " name=my-project\n"
|
||||
comment_line " packages_extra=kubectl helm\n"
|
||||
comment_line " capabilities=apt-install packet-capture\n"
|
||||
comment_line " agent=codex\n"
|
||||
info_line "\n"
|
||||
info_line "Example targets:\n"
|
||||
comment_line " run Build if needed, then launch Codex\n"
|
||||
@@ -328,6 +328,7 @@ FROM ${BASE_IMAGE}
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
ARG BASE_PACKAGES="curl bash ca-certificates libstdc++6 wget ripgrep xxd file procps util-linux binutils"
|
||||
ARG EXTRA_PACKAGES=""
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends apt-utils ${BASE_PACKAGES} ${EXTRA_PACKAGES} && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ARG CODEX_UID=1337
|
||||
@@ -1121,7 +1122,6 @@ SLOPTRAP_CODEX_BIN_NAME=""
|
||||
SLOPTRAP_CODEX_URL=""
|
||||
SLOPTRAP_CODEX_ARCHIVE=""
|
||||
SLOPTRAP_CODEX_HOME_CONT=""
|
||||
SLOPTRAP_SECURITY_OPTS_EXTRA=""
|
||||
SLOPTRAP_VOLUME_LABEL=""
|
||||
SLOPTRAP_WORKDIR=${SLOPTRAP_WORKDIR-}
|
||||
SLOPTRAP_NETWORK_NAME=""
|
||||
@@ -1131,8 +1131,6 @@ SLOPTRAP_LIMITS_SWP=""
|
||||
SLOPTRAP_LIMITS_SHM=""
|
||||
SLOPTRAP_LIMITS_CPU=""
|
||||
SLOPTRAP_TMPFS_PATHS=""
|
||||
SLOPTRAP_ROOTFS_READONLY=""
|
||||
SLOPTRAP_ROOTFS_READONLY_DEFAULT=""
|
||||
SLOPTRAP_RUN_AS_ROOT=false
|
||||
get_env_default() {
|
||||
local var=$1
|
||||
@@ -1296,6 +1294,12 @@ ensure_opencode_config() {
|
||||
return 0
|
||||
fi
|
||||
ensure_codex_directory "$config_dir" "project Opencode config"
|
||||
if [[ -L $OPENCODE_CONFIG_HOST ]]; then
|
||||
error "Opencode config '$OPENCODE_CONFIG_HOST' must not be a symlink"
|
||||
fi
|
||||
if [[ -e $OPENCODE_CONFIG_HOST && ! -f $OPENCODE_CONFIG_HOST ]]; then
|
||||
error "expected Opencode config '$OPENCODE_CONFIG_HOST' to be a regular file"
|
||||
fi
|
||||
if ! jq -n \
|
||||
--arg schema "https://opencode.ai/config.json" \
|
||||
--arg provider_id "$provider_id" \
|
||||
@@ -1541,6 +1545,14 @@ normalize_package_list() {
|
||||
printf '%s' "${normalized[*]}"
|
||||
}
|
||||
|
||||
reject_removed_env_override() {
|
||||
local var=$1
|
||||
local reason=$2
|
||||
if printenv "$var" >/dev/null 2>&1; then
|
||||
error "environment override '$var' has been removed for security reasons (${reason})"
|
||||
fi
|
||||
}
|
||||
|
||||
targets_need_build() {
|
||||
local -a targets=("$@")
|
||||
if [[ ${#targets[@]} -eq 0 ]]; then
|
||||
@@ -1641,7 +1653,6 @@ prepare_container_runtime() {
|
||||
fi
|
||||
SLOPTRAP_CODEX_UID=$(get_env_default "SLOPTRAP_CODEX_UID" "1337")
|
||||
SLOPTRAP_CODEX_GID=$(get_env_default "SLOPTRAP_CODEX_GID" "1337")
|
||||
SLOPTRAP_SECURITY_OPTS_EXTRA=$(get_env_default "SLOPTRAP_SECURITY_OPTS_EXTRA" "")
|
||||
local default_network="bridge"
|
||||
if [[ $CONTAINER_ENGINE == "podman" ]]; then
|
||||
if command -v slirp4netns >/dev/null 2>&1; then
|
||||
@@ -1650,11 +1661,9 @@ prepare_container_runtime() {
|
||||
warn "podman detected but 'slirp4netns' is missing; falling back to 'bridge' networking"
|
||||
fi
|
||||
fi
|
||||
SLOPTRAP_NETWORK_NAME=$(get_env_default "SLOPTRAP_NETWORK_NAME" "$default_network")
|
||||
SLOPTRAP_NETWORK_NAME="$default_network"
|
||||
if $ALLOW_HOST_NETWORK; then
|
||||
SLOPTRAP_NETWORK_NAME="host"
|
||||
elif [[ $SLOPTRAP_NETWORK_NAME == "host" ]]; then
|
||||
error "$MANIFEST_PATH: host networking requires allow_host_network=true"
|
||||
fi
|
||||
if [[ "$BACKEND" == "opencode" && $ALLOW_HOST_NETWORK == false ]]; then
|
||||
ensure_host_loopback_network_access
|
||||
@@ -1665,8 +1674,6 @@ prepare_container_runtime() {
|
||||
SLOPTRAP_LIMITS_SHM=$(get_env_default "SLOPTRAP_LIMITS_SHM" "4096m")
|
||||
SLOPTRAP_LIMITS_CPU=$(get_env_default "SLOPTRAP_LIMITS_CPU" "8")
|
||||
SLOPTRAP_TMPFS_PATHS=$(get_env_default "SLOPTRAP_TMPFS_PATHS" "/tmp:exec /run /run/lock")
|
||||
SLOPTRAP_ROOTFS_READONLY_DEFAULT=$(get_env_default "SLOPTRAP_ROOTFS_READONLY" "1")
|
||||
SLOPTRAP_ROOTFS_READONLY=$SLOPTRAP_ROOTFS_READONLY_DEFAULT
|
||||
SLOPTRAP_IMAGE_NAME=$(get_env_default "SLOPTRAP_IMAGE_NAME" "${PROJECT_NAME}-sloptrap-image")
|
||||
SLOPTRAP_CONTAINER_NAME=$(get_env_default "SLOPTRAP_CONTAINER_NAME" "${PROJECT_NAME}-sloptrap-container")
|
||||
SLOPTRAP_IMAGE_NAME=$(sanitize_engine_name "$SLOPTRAP_IMAGE_NAME")
|
||||
@@ -1677,11 +1684,6 @@ prepare_container_runtime() {
|
||||
ensure_opencode_storage_paths
|
||||
fi
|
||||
|
||||
if [[ -n $SLOPTRAP_SECURITY_OPTS_EXTRA ]]; then
|
||||
local -a extra_opts=()
|
||||
read -r -a extra_opts <<< "$SLOPTRAP_SECURITY_OPTS_EXTRA"
|
||||
security_opts+=("${extra_opts[@]}")
|
||||
fi
|
||||
local -a resource_opts=(
|
||||
--pids-limit "$SLOPTRAP_LIMITS_PID"
|
||||
--memory "$SLOPTRAP_LIMITS_RAM"
|
||||
@@ -1699,13 +1701,8 @@ prepare_container_runtime() {
|
||||
done
|
||||
fi
|
||||
|
||||
security_opts+=(--security-opt no-new-privileges)
|
||||
local rootfs_flag=()
|
||||
case "${SLOPTRAP_ROOTFS_READONLY,,}" in
|
||||
1|true|yes)
|
||||
rootfs_flag=(--read-only)
|
||||
;;
|
||||
esac
|
||||
security_opts+=(--cap-drop ALL --security-opt no-new-privileges)
|
||||
local -a rootfs_flag=(--read-only)
|
||||
|
||||
if [[ $CONTAINER_ENGINE == "podman" ]]; then
|
||||
SLOPTRAP_VOLUME_LABEL=":Z"
|
||||
@@ -1813,6 +1810,10 @@ build_image() {
|
||||
if ! $DRY_RUN; then
|
||||
status_line "Building %s\n" "$SLOPTRAP_IMAGE_NAME"
|
||||
fi
|
||||
local binary_build_arg_name="CODEX_BIN"
|
||||
if [[ "$BACKEND" == "opencode" ]]; then
|
||||
binary_build_arg_name="OPENCODE_BIN"
|
||||
fi
|
||||
local -a cmd=(
|
||||
"$CONTAINER_ENGINE" build --quiet
|
||||
-t "$SLOPTRAP_IMAGE_NAME"
|
||||
@@ -1820,7 +1821,7 @@ build_image() {
|
||||
--network "$SLOPTRAP_NETWORK_NAME"
|
||||
--label "$SLOPTRAP_IMAGE_LABEL"
|
||||
--build-arg "BASE_PACKAGES=$SLOPTRAP_PACKAGES_BASE"
|
||||
--build-arg "CODEX_BIN=$SLOPTRAP_CODEX_BIN_NAME"
|
||||
--build-arg "$binary_build_arg_name=$SLOPTRAP_CODEX_BIN_NAME"
|
||||
--build-arg "CODEX_UID=$SLOPTRAP_CODEX_UID"
|
||||
--build-arg "CODEX_GID=$SLOPTRAP_CODEX_GID"
|
||||
)
|
||||
@@ -1859,6 +1860,10 @@ rebuild_image() {
|
||||
fi
|
||||
local extra_packages_arg
|
||||
extra_packages_arg=$(normalize_package_list "$SLOPTRAP_PACKAGES_EXTRA_RESOLVED")
|
||||
local binary_build_arg_name="CODEX_BIN"
|
||||
if [[ "$BACKEND" == "opencode" ]]; then
|
||||
binary_build_arg_name="OPENCODE_BIN"
|
||||
fi
|
||||
local -a cmd=(
|
||||
"$CONTAINER_ENGINE" build --no-cache --quiet
|
||||
-t "$SLOPTRAP_IMAGE_NAME"
|
||||
@@ -1866,7 +1871,7 @@ rebuild_image() {
|
||||
--network "$SLOPTRAP_NETWORK_NAME"
|
||||
--label "$SLOPTRAP_IMAGE_LABEL"
|
||||
--build-arg "BASE_PACKAGES=$SLOPTRAP_PACKAGES_BASE"
|
||||
--build-arg "CODEX_BIN=$SLOPTRAP_CODEX_BIN_NAME"
|
||||
--build-arg "$binary_build_arg_name=$SLOPTRAP_CODEX_BIN_NAME"
|
||||
--build-arg "CODEX_UID=$SLOPTRAP_CODEX_UID"
|
||||
--build-arg "CODEX_GID=$SLOPTRAP_CODEX_GID"
|
||||
)
|
||||
@@ -1963,8 +1968,8 @@ run_codex_command() {
|
||||
ensure_opencode_config
|
||||
else
|
||||
ensure_codex_storage_paths
|
||||
append_auth_mount_arg false auth_mount
|
||||
fi
|
||||
append_auth_mount_arg false auth_mount
|
||||
local -a cmd=("${BASE_CONTAINER_CMD[@]}" "${auth_mount[@]}" "${source_args[@]}")
|
||||
if [[ "$BACKEND" == "opencode" ]]; then
|
||||
true
|
||||
@@ -1993,6 +1998,9 @@ run_codex() {
|
||||
}
|
||||
|
||||
run_login_target() {
|
||||
if [[ "$BACKEND" == "opencode" ]]; then
|
||||
error "target 'login' is only supported for the codex backend"
|
||||
fi
|
||||
ensure_codex_storage_paths
|
||||
local -a source_args=("$SLOPTRAP_IMAGE_NAME")
|
||||
local -a auth_mount=()
|
||||
@@ -2015,7 +2023,9 @@ run_shell_target() {
|
||||
if ! $DRY_RUN; then
|
||||
status_line "Shell %s\n" "$SLOPTRAP_IMAGE_NAME"
|
||||
fi
|
||||
append_auth_mount_arg false auth_mount
|
||||
if [[ "$BACKEND" != "opencode" ]]; then
|
||||
append_auth_mount_arg false auth_mount
|
||||
fi
|
||||
local -a cmd=("${BASE_CONTAINER_CMD[@]}" --entrypoint /bin/bash "${auth_mount[@]}" "${source_args[@]}")
|
||||
run_runtime_container_cmd "${cmd[@]}"
|
||||
}
|
||||
@@ -2227,16 +2237,18 @@ select_backend() {
|
||||
|
||||
case "${manifest_agent,,}" in
|
||||
opencode) BACKEND="opencode" ;;
|
||||
codex|*) BACKEND="codex" ;;
|
||||
codex) BACKEND="codex" ;;
|
||||
*) error "$MANIFEST_PATH: agent must be 'codex' or 'opencode' (got '$manifest_agent')" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
select_backend
|
||||
|
||||
if [[ "$BACKEND" == "opencode" ]]; then
|
||||
OPENCODE_SERVER="${MANIFEST[opencode_server]:-http://localhost:8080}"
|
||||
OPENCODE_MODEL="${MANIFEST[opencode_model]:-bartowski/Qwen_Qwen3.5-9B-GGUF:Q8_0}"
|
||||
OPENCODE_CONTEXT="${MANIFEST[opencode_context]:-256K}"
|
||||
OPENCODE_SERVER=$(get_env_default "SLOPTRAP_OPENCODE_SERVER" "${MANIFEST[opencode_server]:-http://localhost:8080}")
|
||||
OPENCODE_MODEL=$(get_env_default "SLOPTRAP_OPENCODE_MODEL" "${MANIFEST[opencode_model]:-bartowski/Qwen_Qwen3.5-9B-GGUF:Q8_0}")
|
||||
OPENCODE_CONTEXT=$(get_env_default "SLOPTRAP_OPENCODE_CONTEXT" "${MANIFEST[opencode_context]:-256K}")
|
||||
parse_opencode_context "$OPENCODE_CONTEXT" >/dev/null
|
||||
OPENCODE_STATE_HOME_HOST="$CODEX_STATE_HOME_HOST/opencode"
|
||||
OPENCODE_CONFIG_HOST="$CODEX_STATE_HOME_HOST/config/opencode/opencode.json"
|
||||
OPENCODE_CONFIG_CONT=""
|
||||
@@ -2255,6 +2267,13 @@ if [[ "$BACKEND" != "opencode" && ! -s "$CODEX_AUTH_FILE_HOST" ]]; then
|
||||
fi
|
||||
|
||||
CONTAINER_ENGINE="$(detect_container_engine)"
|
||||
reject_removed_env_override "SLOPTRAP_SECURITY_OPTS_EXTRA" "arbitrary runtime security flags must not bypass launcher hardening"
|
||||
reject_removed_env_override "SLOPTRAP_ROOTFS_READONLY" "the runtime root filesystem is always mounted read-only"
|
||||
reject_removed_env_override "SLOPTRAP_NETWORK_NAME" "network selection is derived by the launcher and manifest only"
|
||||
reject_removed_env_override "SLOPTRAP_DOCKERFILE_PATH" "custom image recipes bypass the launcher hardening model"
|
||||
reject_removed_env_override "SLOPTRAP_CODEX_URL" "backend download URLs are fixed to the verified release source"
|
||||
reject_removed_env_override "SLOPTRAP_CODEX_ARCHIVE" "backend archive selection is fixed by the launcher"
|
||||
reject_removed_env_override "SLOPTRAP_CODEX_BIN" "backend binary naming is fixed by the launcher"
|
||||
CODEX_ARGS_ARRAY=("${DEFAULT_CODEX_ARGS[@]}")
|
||||
ensure_safe_sandbox "${CODEX_ARGS_ARRAY[@]}"
|
||||
CODEX_ARGS_DISPLAY=$DEFAULT_CODEX_ARGS_DISPLAY
|
||||
|
||||
Reference in New Issue
Block a user