Add capabilities at build
This commit is contained in:
417
sloptrap
417
sloptrap
@@ -158,6 +158,7 @@ DEFAULT_CODEX_ARGS_DISPLAY=$(printf '%s ' "${DEFAULT_CODEX_ARGS[@]}")
|
||||
DEFAULT_CODEX_ARGS_DISPLAY=${DEFAULT_CODEX_ARGS_DISPLAY% }
|
||||
SLOPTRAP_IMAGE_LABEL_KEY="net.sk4nz.sloptrap.managed"
|
||||
SLOPTRAP_IMAGE_LABEL="${SLOPTRAP_IMAGE_LABEL_KEY}=1"
|
||||
SLOPTRAP_SUPPORTED_CAPABILITIES=(apt-install packet-capture nested-podman)
|
||||
|
||||
usage() {
|
||||
print_banner
|
||||
@@ -165,13 +166,15 @@ usage() {
|
||||
info_line "Options:\n"
|
||||
comment_line " --dry-run Show planned container command(s) and exit\n"
|
||||
comment_line " --print-config Display resolved manifest values\n"
|
||||
comment_line " --enable-capability <name> Enable a trusted runtime capability for this run\n"
|
||||
comment_line " --trust-capabilities Trust the manifest's requested capabilities for this build\n"
|
||||
comment_line " -h, --help Show this message\n"
|
||||
info_line "\n"
|
||||
comment_line "Each project supplies configuration via a %s file in its root.\n" "$MANIFEST_BASENAME"
|
||||
info_line "Example manifest entries:\n"
|
||||
comment_line " name=my-project\n"
|
||||
comment_line " packages_extra=kubectl helm\n"
|
||||
comment_line " codex_args=--sandbox danger-full-access --ask-for-approval never\n"
|
||||
comment_line " capabilities=apt-install packet-capture\n"
|
||||
info_line "\n"
|
||||
info_line "Example targets:\n"
|
||||
comment_line " run Build if needed, then launch Codex\n"
|
||||
@@ -221,6 +224,9 @@ resolve_path_strict() {
|
||||
declare -A MANIFEST=()
|
||||
declare -a SLOPTRAP_IGNORE_ENTRIES=()
|
||||
declare -a IGNORE_MOUNT_ARGS=()
|
||||
declare -a CODEX_ARGS_ARRAY=()
|
||||
declare -a DEFAULT_TARGETS=()
|
||||
declare -a ENABLED_CAPABILITIES_ARGS=()
|
||||
MANIFEST_PRESENT=false
|
||||
|
||||
CURRENT_IGNORE_FILE=""
|
||||
@@ -231,6 +237,13 @@ CODEX_AUTH_FILE_HOST=""
|
||||
CODEX_STATE_KEY=""
|
||||
CODEX_HOME_BOOTSTRAP=false
|
||||
NEED_LOGIN=false
|
||||
REQUESTED_CAPABILITIES=""
|
||||
ENABLED_CAPABILITIES=""
|
||||
CAPABILITY_MANIFEST_DIGEST=""
|
||||
CAPABILITY_TRUST_ROOT_HOST=""
|
||||
CAPABILITY_TRUST_FILE_HOST=""
|
||||
CAPABILITY_BUILD_STAMP_HOST=""
|
||||
CAPABILITY_STATE_HOST=""
|
||||
IGNORE_STUB_BASE=""
|
||||
IGNORE_HELPER_ROOT=""
|
||||
ALLOW_HOST_NETWORK=false
|
||||
@@ -299,10 +312,11 @@ FROM ${BASE_IMAGE}
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
ARG BASE_PACKAGES="curl bash ca-certificates libstdc++6 ripgrep xxd file procps"
|
||||
ARG BASE_PACKAGES="curl bash ca-certificates libstdc++6 ripgrep xxd file procps util-linux"
|
||||
ARG EXTRA_PACKAGES=""
|
||||
ARG CAPABILITY_PACKAGES=""
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends apt-utils ${BASE_PACKAGES} ${EXTRA_PACKAGES} \
|
||||
&& apt-get install -y --no-install-recommends apt-utils ${BASE_PACKAGES} ${EXTRA_PACKAGES} ${CAPABILITY_PACKAGES} \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ARG CODEX_UID=1337
|
||||
@@ -314,14 +328,20 @@ RUN groupadd --gid ${CODEX_GID} sloptrap \
|
||||
ARG CODEX_BIN=codex
|
||||
ARG CODEX_CONF=config/config.toml
|
||||
COPY ${CODEX_BIN} /usr/local/bin/codex
|
||||
COPY sloptrap-entrypoint /usr/local/bin/sloptrap-entrypoint
|
||||
COPY sloptrap-helperd /usr/local/bin/sloptrap-helperd
|
||||
COPY slop-apt /usr/local/bin/slop-apt
|
||||
COPY slopcap /usr/local/bin/slopcap
|
||||
COPY sloppodman /usr/local/bin/sloppodman
|
||||
|
||||
RUN chown -R sloptrap:sloptrap /home/sloptrap
|
||||
USER sloptrap
|
||||
RUN chmod 0755 /usr/local/bin/sloptrap-entrypoint /usr/local/bin/sloptrap-helperd \
|
||||
/usr/local/bin/slop-apt /usr/local/bin/slopcap /usr/local/bin/sloppodman \
|
||||
&& chown -R sloptrap:sloptrap /home/sloptrap
|
||||
|
||||
WORKDIR /workspace
|
||||
|
||||
ENV SHELL=/bin/bash HOME=/home/sloptrap
|
||||
ENTRYPOINT ["codex"]
|
||||
ENTRYPOINT ["/usr/local/bin/sloptrap-entrypoint"]
|
||||
EOF
|
||||
}
|
||||
|
||||
@@ -364,6 +384,13 @@ prepare_build_context() {
|
||||
populate_dockerfile "$SLOPTRAP_DOCKERFILE_PATH"
|
||||
validate_basename "$SLOPTRAP_CODEX_BIN_NAME"
|
||||
CODEX_BIN_PATH="$SLOPTRAP_BUILD_CONTEXT/$SLOPTRAP_CODEX_BIN_NAME"
|
||||
local helper
|
||||
for helper in sloptrap-entrypoint sloptrap-helperd slop-apt slopcap sloppodman; do
|
||||
if [[ ! -f "$SCRIPT_DIR/$helper" ]]; then
|
||||
error "required helper '$SCRIPT_DIR/$helper' not found"
|
||||
fi
|
||||
cp "$SCRIPT_DIR/$helper" "$SLOPTRAP_BUILD_CONTEXT/$helper"
|
||||
done
|
||||
}
|
||||
|
||||
select_codex_home() {
|
||||
@@ -394,6 +421,101 @@ select_codex_home() {
|
||||
fi
|
||||
}
|
||||
|
||||
compute_manifest_digest() {
|
||||
if [[ -f $MANIFEST_PATH ]]; then
|
||||
local digest
|
||||
digest=$(sha256sum "$MANIFEST_PATH")
|
||||
printf '%s' "${digest%% *}"
|
||||
return 0
|
||||
fi
|
||||
printf 'no-manifest'
|
||||
}
|
||||
|
||||
select_capability_state_paths() {
|
||||
local capability_root="$CODEX_ROOT_HOST/sloptrap/capabilities"
|
||||
CAPABILITY_TRUST_ROOT_HOST="$capability_root/trust"
|
||||
CAPABILITY_TRUST_FILE_HOST="$CAPABILITY_TRUST_ROOT_HOST/$CODEX_STATE_KEY.trust"
|
||||
CAPABILITY_BUILD_STAMP_HOST="$capability_root/builds/$CODEX_STATE_KEY.stamp"
|
||||
CAPABILITY_STATE_HOST="$CODEX_STATE_HOME_HOST/capabilities"
|
||||
}
|
||||
|
||||
ensure_capability_state_paths() {
|
||||
local capability_root="$CODEX_ROOT_HOST/sloptrap/capabilities"
|
||||
ensure_codex_directory "$capability_root" "sloptrap capability namespace"
|
||||
ensure_codex_directory "$CAPABILITY_TRUST_ROOT_HOST" "sloptrap capability trust root"
|
||||
ensure_codex_directory "$(dirname "$CAPABILITY_BUILD_STAMP_HOST")" "sloptrap capability build stamp root"
|
||||
ensure_codex_directory "$CAPABILITY_STATE_HOST" "project capability state"
|
||||
}
|
||||
|
||||
capability_trust_matches_current() {
|
||||
[[ -f $CAPABILITY_TRUST_FILE_HOST ]] || return 1
|
||||
local trusted_digest trusted_caps
|
||||
trusted_digest=$(sed -n '1p' "$CAPABILITY_TRUST_FILE_HOST" 2>/dev/null || true)
|
||||
trusted_caps=$(sed -n '2p' "$CAPABILITY_TRUST_FILE_HOST" 2>/dev/null || true)
|
||||
[[ $trusted_digest == "$CAPABILITY_MANIFEST_DIGEST" && $trusted_caps == "$REQUESTED_CAPABILITIES" ]]
|
||||
}
|
||||
|
||||
record_capability_trust() {
|
||||
ensure_capability_state_paths
|
||||
if $DRY_RUN; then
|
||||
print_command mkdir -p "$(dirname "$CAPABILITY_TRUST_FILE_HOST")"
|
||||
print_command sh -c "printf '%s\\n%s\\n' '$CAPABILITY_MANIFEST_DIGEST' '$REQUESTED_CAPABILITIES' > '$CAPABILITY_TRUST_FILE_HOST'"
|
||||
return 0
|
||||
fi
|
||||
printf '%s\n%s\n' "$CAPABILITY_MANIFEST_DIGEST" "$REQUESTED_CAPABILITIES" >"$CAPABILITY_TRUST_FILE_HOST"
|
||||
}
|
||||
|
||||
prompt_capability_trust() {
|
||||
local tty_path="/dev/tty"
|
||||
info_line "Manifest requests privileged capabilities: %s\n" "$REQUESTED_CAPABILITIES"
|
||||
printf '%s' "$PREFIX_TEXT" >"$tty_path"
|
||||
printf '%b' "$COLOR_TEXT" >"$tty_path"
|
||||
printf 'Trust these capabilities for this project build? [y/N]: ' >"$tty_path"
|
||||
printf '%b' "$RESET" >"$tty_path"
|
||||
local input
|
||||
if ! IFS= read -r input <"$tty_path"; then
|
||||
error "capability trust requires an interactive terminal or --trust-capabilities"
|
||||
fi
|
||||
case "${input,,}" in
|
||||
y|yes)
|
||||
record_capability_trust
|
||||
;;
|
||||
*)
|
||||
error "capability trust not granted"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
ensure_capability_trust() {
|
||||
[[ -n $REQUESTED_CAPABILITIES ]] || return 0
|
||||
capability_trust_matches_current && return 0
|
||||
if $TRUST_CAPABILITIES; then
|
||||
record_capability_trust
|
||||
return 0
|
||||
fi
|
||||
if [[ ! -t 0 ]]; then
|
||||
error "requested capabilities require prior trust or --trust-capabilities"
|
||||
fi
|
||||
prompt_capability_trust
|
||||
}
|
||||
|
||||
write_capability_build_stamp() {
|
||||
ensure_capability_state_paths
|
||||
if $DRY_RUN; then
|
||||
print_command sh -c "printf '%s\\n%s\\n' '$CAPABILITY_MANIFEST_DIGEST' '$REQUESTED_CAPABILITIES' > '$CAPABILITY_BUILD_STAMP_HOST'"
|
||||
return 0
|
||||
fi
|
||||
printf '%s\n%s\n' "$CAPABILITY_MANIFEST_DIGEST" "$REQUESTED_CAPABILITIES" >"$CAPABILITY_BUILD_STAMP_HOST"
|
||||
}
|
||||
|
||||
capability_build_stamp_matches_current() {
|
||||
[[ -f $CAPABILITY_BUILD_STAMP_HOST ]] || return 1
|
||||
local stamp_digest stamp_caps
|
||||
stamp_digest=$(sed -n '1p' "$CAPABILITY_BUILD_STAMP_HOST" 2>/dev/null || true)
|
||||
stamp_caps=$(sed -n '2p' "$CAPABILITY_BUILD_STAMP_HOST" 2>/dev/null || true)
|
||||
[[ $stamp_digest == "$CAPABILITY_MANIFEST_DIGEST" && $stamp_caps == "$REQUESTED_CAPABILITIES" ]]
|
||||
}
|
||||
|
||||
assert_path_within_code_dir() {
|
||||
local candidate=$1
|
||||
local resolved
|
||||
@@ -638,6 +760,58 @@ validate_package_list() {
|
||||
done
|
||||
}
|
||||
|
||||
validate_capability_list() {
|
||||
local key=$1
|
||||
local raw=$2
|
||||
local source=${3:-$MANIFEST_PATH}
|
||||
[[ -z $raw ]] && return 0
|
||||
local token supported capability
|
||||
for token in $raw; do
|
||||
supported=false
|
||||
for capability in "${SLOPTRAP_SUPPORTED_CAPABILITIES[@]}"; do
|
||||
if [[ $token == "$capability" ]]; then
|
||||
supported=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [[ $supported != true ]]; then
|
||||
error "$source: invalid capability '$token' in '$key'"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
normalize_capability_list() {
|
||||
local raw=$1
|
||||
[[ -z $raw ]] && return 0
|
||||
local token
|
||||
for token in $raw; do
|
||||
printf '%s\n' "$token"
|
||||
done | sort -u | tr '\n' ' ' | sed 's/ $//'
|
||||
}
|
||||
|
||||
capability_list_contains() {
|
||||
local list=$1
|
||||
local needle=$2
|
||||
local token
|
||||
for token in $list; do
|
||||
if [[ $token == "$needle" ]]; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
validate_enabled_capabilities() {
|
||||
local enabled=$1
|
||||
local requested=$2
|
||||
local capability
|
||||
for capability in $enabled; do
|
||||
if ! capability_list_contains "$requested" "$capability"; then
|
||||
error "runtime capability '$capability' is not requested by $MANIFEST_PATH"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
detect_container_engine() {
|
||||
local override=${SLOPTRAP_CONTAINER_ENGINE-}
|
||||
if [[ -n $override ]]; then
|
||||
@@ -726,18 +900,6 @@ normalize_wizzard_allow_host_network() {
|
||||
esac
|
||||
}
|
||||
|
||||
validate_wizzard_codex_args() {
|
||||
local value=$1
|
||||
ensure_safe_for_make "codex_args" "$value"
|
||||
local -a args=("${DEFAULT_CODEX_ARGS[@]}")
|
||||
local -a tokens=()
|
||||
if [[ -n $value ]]; then
|
||||
read -r -a tokens <<< "$value"
|
||||
args+=("${tokens[@]}")
|
||||
fi
|
||||
ensure_safe_sandbox "${args[@]}"
|
||||
}
|
||||
|
||||
run_wizzard() {
|
||||
local manifest_path=$1
|
||||
if [[ -L $manifest_path ]]; then
|
||||
@@ -752,12 +914,12 @@ run_wizzard() {
|
||||
|
||||
local default_name
|
||||
local default_packages_extra
|
||||
local default_codex_args
|
||||
local default_capabilities
|
||||
local default_allow_host_network
|
||||
|
||||
default_name=$(manifest_default_value "name" "$(basename "$CODE_DIR")")
|
||||
default_packages_extra=$(manifest_default_value "packages_extra" "")
|
||||
default_codex_args=$(manifest_default_value "codex_args" "$DEFAULT_CODEX_ARGS_DISPLAY")
|
||||
default_capabilities=$(manifest_default_value "capabilities" "")
|
||||
default_allow_host_network=$(manifest_default_value "allow_host_network" "false")
|
||||
|
||||
local action="Creating"
|
||||
@@ -789,16 +951,6 @@ run_wizzard() {
|
||||
break
|
||||
done
|
||||
|
||||
while true; do
|
||||
info_line "codex_args: Extra CLI flags passed to Codex at runtime.\n"
|
||||
value=$(prompt_manifest_value "codex_args" "$default_codex_args")
|
||||
value=$(trim "$value")
|
||||
[[ -n $value ]] || value=$default_codex_args
|
||||
validate_wizzard_codex_args "$value"
|
||||
default_codex_args=$value
|
||||
break
|
||||
done
|
||||
|
||||
while true; do
|
||||
info_line "allow_host_network: Use host networking instead of an isolated bridge.\n"
|
||||
value=$(prompt_manifest_value "allow_host_network" "$default_allow_host_network")
|
||||
@@ -812,7 +964,7 @@ run_wizzard() {
|
||||
cat > "$manifest_path" <<EOF
|
||||
name=$default_name
|
||||
packages_extra=$default_packages_extra
|
||||
codex_args=$default_codex_args
|
||||
capabilities=$default_capabilities
|
||||
allow_host_network=$default_allow_host_network
|
||||
EOF
|
||||
info_line "Wrote %s\n" "$manifest_path"
|
||||
@@ -842,7 +994,10 @@ print_config() {
|
||||
info_line "codex_archive=%s\n" "$SLOPTRAP_CODEX_ARCHIVE"
|
||||
info_line "codex_url=%s\n" "$SLOPTRAP_CODEX_URL"
|
||||
info_line "needs_login=%s\n" "$NEED_LOGIN"
|
||||
info_line "codex_args=%s\n" "$CODEX_ARGS_DISPLAY"
|
||||
info_line "runtime_flags=%s\n" "$CODEX_ARGS_DISPLAY"
|
||||
info_line "requested_capabilities=%s\n" "$REQUESTED_CAPABILITIES"
|
||||
info_line "enabled_capabilities=%s\n" "$ENABLED_CAPABILITIES"
|
||||
info_line "capability_trust=%s\n" "$(capability_trust_matches_current && printf true || printf false)"
|
||||
info_line "ignore_stub_base=%s\n" "$IGNORE_STUB_BASE"
|
||||
if [[ ${#SLOPTRAP_IGNORE_ENTRIES[@]} -gt 0 ]]; then
|
||||
local ignore_paths
|
||||
@@ -867,10 +1022,53 @@ print_manifest_summary() {
|
||||
comment_line " manifest_path=%s\n" "$MANIFEST_PATH"
|
||||
comment_line " name=%s\n" "$PROJECT_NAME"
|
||||
comment_line " packages_extra=%s\n" "$PACKAGES_EXTRA"
|
||||
comment_line " codex_args=%s\n" "$CODEX_ARGS_DISPLAY"
|
||||
comment_line " capabilities=%s\n" "$REQUESTED_CAPABILITIES"
|
||||
comment_line " runtime_flags=%s\n" "$CODEX_ARGS_DISPLAY"
|
||||
comment_line " allow_host_network=%s\n" "$ALLOW_HOST_NETWORK"
|
||||
}
|
||||
|
||||
build_runtime_context_prompt() {
|
||||
local manifest_present prompt requested enabled network_mode
|
||||
manifest_present="false"
|
||||
if [[ -f $MANIFEST_PATH ]]; then
|
||||
manifest_present="true"
|
||||
fi
|
||||
requested=${REQUESTED_CAPABILITIES:-none}
|
||||
enabled=${ENABLED_CAPABILITIES:-none}
|
||||
network_mode="isolated"
|
||||
if [[ $SLOPTRAP_NETWORK_NAME == "host" ]]; then
|
||||
network_mode="host"
|
||||
fi
|
||||
prompt=$(cat <<EOF
|
||||
You are running inside sloptrap, which confines Codex inside a container.
|
||||
This startup note describes the sloptrap runtime only; it does not replace higher-priority instructions from AGENTS.md or the system.
|
||||
|
||||
Container layout:
|
||||
- /workspace is the project mount.
|
||||
- /codex is persistent Codex state for this project.
|
||||
- The project manifest path is /workspace/.sloptrap and it may be absent.
|
||||
|
||||
Manifest key meanings:
|
||||
- name: labels the sloptrap project/image/container names.
|
||||
- packages_extra: Debian packages added when the image was built.
|
||||
- capabilities: privileged features requested by the manifest; only capabilities enabled for this run are currently usable.
|
||||
- allow_host_network: enables host networking when true; otherwise networking is isolated.
|
||||
|
||||
Current resolved sloptrap state:
|
||||
- manifest_present=$manifest_present
|
||||
- project_name=$PROJECT_NAME
|
||||
- packages_extra=${PACKAGES_EXTRA:-none}
|
||||
- requested_capabilities=$requested
|
||||
- enabled_capabilities=$enabled
|
||||
- network_mode=$network_mode
|
||||
- runtime_flags=$CODEX_ARGS_DISPLAY
|
||||
|
||||
If you need exact project configuration, inspect /workspace/.sloptrap directly.
|
||||
EOF
|
||||
)
|
||||
printf '%s' "$prompt"
|
||||
}
|
||||
|
||||
declare -a CONTAINER_SHARED_OPTS=()
|
||||
declare -a BASE_CONTAINER_CMD=()
|
||||
SLOPTRAP_IMAGE_NAME=""
|
||||
@@ -882,6 +1080,7 @@ CODEX_BIN_PATH=""
|
||||
SLOPTRAP_SHARED_DIR_ABS=""
|
||||
SLOPTRAP_PACKAGES_BASE=""
|
||||
SLOPTRAP_PACKAGES_EXTRA_RESOLVED=""
|
||||
SLOPTRAP_PACKAGES_CAPABILITY=""
|
||||
SLOPTRAP_CODEX_BIN_NAME=""
|
||||
SLOPTRAP_CODEX_URL=""
|
||||
SLOPTRAP_CODEX_ARCHIVE=""
|
||||
@@ -897,6 +1096,8 @@ 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
|
||||
@@ -985,6 +1186,7 @@ ensure_codex_storage_paths() {
|
||||
ensure_codex_directory "$state_root" "sloptrap Codex namespace"
|
||||
ensure_codex_directory "$state_bucket" "sloptrap Codex state root"
|
||||
ensure_codex_directory "$CODEX_STATE_HOME_HOST" "project Codex state"
|
||||
ensure_capability_state_paths
|
||||
if [[ -L $CODEX_AUTH_FILE_HOST ]]; then
|
||||
error "Codex auth file '$CODEX_AUTH_FILE_HOST' must not be a symlink"
|
||||
fi
|
||||
@@ -1062,20 +1264,20 @@ ensure_safe_sandbox() {
|
||||
while [[ $i -lt ${#args[@]} ]]; do
|
||||
if [[ ${args[$i]} == "--sandbox" ]]; then
|
||||
if (( i + 1 >= ${#args[@]} )); then
|
||||
error "$MANIFEST_PATH: '--sandbox' flag requires a mode (workspace-write, workspace-read-only, or danger-full-access)"
|
||||
error "runtime '--sandbox' flag requires a mode (workspace-write, workspace-read-only, or danger-full-access)"
|
||||
fi
|
||||
sandbox_mode="${args[$((i + 1))]}"
|
||||
fi
|
||||
((i+=1))
|
||||
done
|
||||
if [[ -z $sandbox_mode ]]; then
|
||||
error "$MANIFEST_PATH: codex_args must include '--sandbox <mode>' (workspace-write, workspace-read-only, or danger-full-access)"
|
||||
error "runtime flags must include '--sandbox <mode>' (workspace-write, workspace-read-only, or danger-full-access)"
|
||||
fi
|
||||
case "$sandbox_mode" in
|
||||
workspace-write|workspace-read-only|danger-full-access)
|
||||
;;
|
||||
*)
|
||||
error "$MANIFEST_PATH: sandbox mode '$sandbox_mode' is not allowed (expected workspace-write, workspace-read-only, or danger-full-access)"
|
||||
error "sandbox mode '$sandbox_mode' is not allowed (expected workspace-write, workspace-read-only, or danger-full-access)"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
@@ -1108,6 +1310,8 @@ targets_need_build() {
|
||||
prepare_container_runtime() {
|
||||
resolve_container_workdir
|
||||
SLOPTRAP_PACKAGES_EXTRA_RESOLVED=$PACKAGES_EXTRA
|
||||
SLOPTRAP_PACKAGES_CAPABILITY=""
|
||||
SLOPTRAP_RUN_AS_ROOT=false
|
||||
SLOPTRAP_SHARED_DIR_ABS="$CODE_DIR"
|
||||
if [[ ! -d $SLOPTRAP_SHARED_DIR_ABS ]]; then
|
||||
error "shared directory '$SLOPTRAP_SHARED_DIR_ABS' does not exist"
|
||||
@@ -1129,9 +1333,19 @@ prepare_container_runtime() {
|
||||
else
|
||||
SLOPTRAP_DOCKERFILE_SOURCE=""
|
||||
fi
|
||||
if [[ -n $REQUESTED_CAPABILITIES && -n $SLOPTRAP_DOCKERFILE_SOURCE ]]; then
|
||||
error "capabilities require the embedded Dockerfile; custom Dockerfile overrides are not supported"
|
||||
fi
|
||||
|
||||
SLOPTRAP_PACKAGES_BASE=$(get_env_default "SLOPTRAP_PACKAGES" "curl bash ca-certificates libstdc++6 git ripgrep xxd file procps")
|
||||
SLOPTRAP_PACKAGES_BASE=$(get_env_default "SLOPTRAP_PACKAGES" "curl bash ca-certificates libstdc++6 git ripgrep xxd file procps util-linux")
|
||||
validate_package_list "SLOPTRAP_PACKAGES" "$SLOPTRAP_PACKAGES_BASE" "SLOPTRAP_PACKAGES"
|
||||
if capability_list_contains "$REQUESTED_CAPABILITIES" "packet-capture"; then
|
||||
SLOPTRAP_PACKAGES_CAPABILITY+=" tcpdump"
|
||||
fi
|
||||
if capability_list_contains "$REQUESTED_CAPABILITIES" "nested-podman"; then
|
||||
SLOPTRAP_PACKAGES_CAPABILITY+=" podman fuse-overlayfs slirp4netns"
|
||||
fi
|
||||
SLOPTRAP_PACKAGES_CAPABILITY=$(normalize_package_list "$SLOPTRAP_PACKAGES_CAPABILITY")
|
||||
local default_codex_archive
|
||||
default_codex_archive=$(detect_codex_archive_name)
|
||||
local env_codex_archive
|
||||
@@ -1178,7 +1392,8 @@ prepare_container_runtime() {
|
||||
SLOPTRAP_LIMITS_SHM=$(get_env_default "SLOPTRAP_LIMITS_SHM" "1024m")
|
||||
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=$(get_env_default "SLOPTRAP_ROOTFS_READONLY" "1")
|
||||
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")
|
||||
@@ -1186,6 +1401,7 @@ prepare_container_runtime() {
|
||||
|
||||
local -a network_opts=(--network "$SLOPTRAP_NETWORK_NAME" --init)
|
||||
local -a security_opts=(--cap-drop=ALL --security-opt no-new-privileges)
|
||||
local -a capability_opts=()
|
||||
if [[ -n $SLOPTRAP_SECURITY_OPTS_EXTRA ]]; then
|
||||
local -a extra_opts=()
|
||||
read -r -a extra_opts <<< "$SLOPTRAP_SECURITY_OPTS_EXTRA"
|
||||
@@ -1208,6 +1424,18 @@ prepare_container_runtime() {
|
||||
done
|
||||
fi
|
||||
|
||||
if capability_list_contains "$ENABLED_CAPABILITIES" "apt-install"; then
|
||||
SLOPTRAP_ROOTFS_READONLY=0
|
||||
SLOPTRAP_RUN_AS_ROOT=true
|
||||
fi
|
||||
if capability_list_contains "$ENABLED_CAPABILITIES" "packet-capture"; then
|
||||
capability_opts+=(--cap-add NET_RAW --cap-add NET_ADMIN)
|
||||
SLOPTRAP_RUN_AS_ROOT=true
|
||||
fi
|
||||
if capability_list_contains "$ENABLED_CAPABILITIES" "nested-podman"; then
|
||||
capability_opts+=(--device /dev/fuse)
|
||||
fi
|
||||
|
||||
local rootfs_flag=()
|
||||
case "${SLOPTRAP_ROOTFS_READONLY,,}" in
|
||||
1|true|yes)
|
||||
@@ -1226,6 +1454,13 @@ prepare_container_runtime() {
|
||||
-v "$CODEX_STATE_HOME_HOST:$SLOPTRAP_CODEX_HOME_CONT$SLOPTRAP_VOLUME_LABEL"
|
||||
-v "$CODEX_AUTH_FILE_HOST:$SLOPTRAP_CODEX_HOME_CONT/auth.json$SLOPTRAP_VOLUME_LABEL"
|
||||
)
|
||||
if capability_list_contains "$ENABLED_CAPABILITIES" "nested-podman"; then
|
||||
volume_opts+=(
|
||||
-v "$CAPABILITY_STATE_HOST/podman-storage:$SLOPTRAP_CODEX_HOME_CONT/capabilities/podman/storage$SLOPTRAP_VOLUME_LABEL"
|
||||
-v "$CAPABILITY_STATE_HOST/podman-run:$SLOPTRAP_CODEX_HOME_CONT/capabilities/podman/run$SLOPTRAP_VOLUME_LABEL"
|
||||
-v "$CAPABILITY_STATE_HOST/podman-runtime:$SLOPTRAP_CODEX_HOME_CONT/capabilities/podman/runtime$SLOPTRAP_VOLUME_LABEL"
|
||||
)
|
||||
fi
|
||||
|
||||
local -a env_args=(
|
||||
-e "HOME=$SLOPTRAP_CODEX_HOME_CONT"
|
||||
@@ -1233,7 +1468,18 @@ prepare_container_runtime() {
|
||||
-e "XDG_CACHE_HOME=$SLOPTRAP_CODEX_HOME_CONT/cache"
|
||||
-e "XDG_STATE_HOME=$SLOPTRAP_CODEX_HOME_CONT/state"
|
||||
-e "CODEX_HOME=$SLOPTRAP_CODEX_HOME_CONT"
|
||||
-e "SLOPTRAP_WORKDIR=$SLOPTRAP_WORKDIR"
|
||||
-e "SLOPTRAP_HELPER_DIR=/run/sloptrap-helper"
|
||||
-e "SLOPTRAP_ACTIVE_CAPABILITIES=$ENABLED_CAPABILITIES"
|
||||
-e "SLOPTRAP_CAPTURE_DIR=$SLOPTRAP_CODEX_HOME_CONT/state/captures"
|
||||
-e "SLOPTRAP_AUDIT_LOG=$SLOPTRAP_CODEX_HOME_CONT/state/capabilities.log"
|
||||
-e "SLOPTRAP_INNER_PODMAN_ROOT=$SLOPTRAP_CODEX_HOME_CONT/capabilities/podman/storage"
|
||||
-e "SLOPTRAP_INNER_PODMAN_RUNROOT=$SLOPTRAP_CODEX_HOME_CONT/capabilities/podman/run"
|
||||
-e "XDG_RUNTIME_DIR=$SLOPTRAP_CODEX_HOME_CONT/capabilities/podman/runtime"
|
||||
)
|
||||
if capability_list_contains "$ENABLED_CAPABILITIES" "nested-podman" && [[ $SLOPTRAP_NETWORK_NAME == "host" ]]; then
|
||||
env_args+=(-e "SLOPTRAP_INNER_PODMAN_HOST_NETWORK=1")
|
||||
fi
|
||||
|
||||
local uid gid
|
||||
uid=$(id -u)
|
||||
@@ -1242,10 +1488,14 @@ prepare_container_runtime() {
|
||||
if [[ $CONTAINER_ENGINE == "podman" ]]; then
|
||||
user_opts=(--userns="keep-id:uid=$uid,gid=$gid" "${user_opts[@]}")
|
||||
fi
|
||||
if $SLOPTRAP_RUN_AS_ROOT; then
|
||||
user_opts=()
|
||||
fi
|
||||
|
||||
CONTAINER_SHARED_OPTS=(
|
||||
"${network_opts[@]}"
|
||||
"${security_opts[@]}"
|
||||
"${capability_opts[@]}"
|
||||
"${resource_opts[@]}"
|
||||
"${rootfs_flag[@]}"
|
||||
"${tmpfs_opts[@]}"
|
||||
@@ -1264,13 +1514,16 @@ prepare_container_runtime() {
|
||||
}
|
||||
|
||||
build_image() {
|
||||
ensure_capability_trust
|
||||
ensure_codex_binary
|
||||
if [[ $SKIP_BUILD_BANNER != true ]]; then
|
||||
print_banner
|
||||
fi
|
||||
print_manifest_summary
|
||||
local extra_packages_arg
|
||||
local capability_packages_arg
|
||||
extra_packages_arg=$(normalize_package_list "$SLOPTRAP_PACKAGES_EXTRA_RESOLVED")
|
||||
capability_packages_arg=$(normalize_package_list "$SLOPTRAP_PACKAGES_CAPABILITY")
|
||||
if ! $DRY_RUN; then
|
||||
status_line "Building %s\n" "$SLOPTRAP_IMAGE_NAME"
|
||||
fi
|
||||
@@ -1280,6 +1533,7 @@ build_image() {
|
||||
-f "$SLOPTRAP_DOCKERFILE_PATH"
|
||||
--label "$SLOPTRAP_IMAGE_LABEL"
|
||||
--build-arg "BASE_PACKAGES=$SLOPTRAP_PACKAGES_BASE"
|
||||
--build-arg "CAPABILITY_PACKAGES=$capability_packages_arg"
|
||||
--build-arg "CODEX_BIN=$SLOPTRAP_CODEX_BIN_NAME"
|
||||
--build-arg "CODEX_UID=$SLOPTRAP_CODEX_UID"
|
||||
--build-arg "CODEX_GID=$SLOPTRAP_CODEX_GID"
|
||||
@@ -1301,9 +1555,11 @@ build_image() {
|
||||
if [[ -n $build_output ]]; then
|
||||
comment_line "Image %s\n" "$build_output"
|
||||
fi
|
||||
write_capability_build_stamp
|
||||
}
|
||||
|
||||
rebuild_image() {
|
||||
ensure_capability_trust
|
||||
ensure_codex_binary
|
||||
if [[ $SKIP_BUILD_BANNER != true ]]; then
|
||||
print_banner
|
||||
@@ -1313,13 +1569,16 @@ rebuild_image() {
|
||||
status_line "Rebuilding %s (no cache)\n" "$SLOPTRAP_IMAGE_NAME"
|
||||
fi
|
||||
local extra_packages_arg
|
||||
local capability_packages_arg
|
||||
extra_packages_arg=$(normalize_package_list "$SLOPTRAP_PACKAGES_EXTRA_RESOLVED")
|
||||
capability_packages_arg=$(normalize_package_list "$SLOPTRAP_PACKAGES_CAPABILITY")
|
||||
local -a cmd=(
|
||||
"$CONTAINER_ENGINE" build --no-cache --quiet
|
||||
-t "$SLOPTRAP_IMAGE_NAME"
|
||||
-f "$SLOPTRAP_DOCKERFILE_PATH"
|
||||
--label "$SLOPTRAP_IMAGE_LABEL"
|
||||
--build-arg "BASE_PACKAGES=$SLOPTRAP_PACKAGES_BASE"
|
||||
--build-arg "CAPABILITY_PACKAGES=$capability_packages_arg"
|
||||
--build-arg "CODEX_BIN=$SLOPTRAP_CODEX_BIN_NAME"
|
||||
--build-arg "CODEX_UID=$SLOPTRAP_CODEX_UID"
|
||||
--build-arg "CODEX_GID=$SLOPTRAP_CODEX_GID"
|
||||
@@ -1341,14 +1600,21 @@ rebuild_image() {
|
||||
if [[ -n $build_output ]]; then
|
||||
comment_line "Image %s\n" "$build_output"
|
||||
fi
|
||||
write_capability_build_stamp
|
||||
}
|
||||
|
||||
build_if_missing() {
|
||||
ensure_capability_trust
|
||||
if $DRY_RUN; then
|
||||
print_command "$CONTAINER_ENGINE" image inspect "$SLOPTRAP_IMAGE_NAME"
|
||||
return 0
|
||||
fi
|
||||
if "$CONTAINER_ENGINE" image inspect "$SLOPTRAP_IMAGE_NAME" >/dev/null 2>&1; then
|
||||
if ! capability_build_stamp_matches_current; then
|
||||
warn "image '$SLOPTRAP_IMAGE_NAME' capability stamp mismatch; rebuilding"
|
||||
rebuild_image
|
||||
return 0
|
||||
fi
|
||||
local created
|
||||
if created=$("$CONTAINER_ENGINE" image inspect --format '{{.Created}}' "$SLOPTRAP_IMAGE_NAME" 2>/dev/null); then
|
||||
local created_epoch
|
||||
@@ -1411,7 +1677,7 @@ prune_sloptrap_images() {
|
||||
run_codex_command() {
|
||||
local -a extra_args=("$@")
|
||||
ensure_codex_storage_paths
|
||||
local -a cmd=("${BASE_CONTAINER_CMD[@]}" "$SLOPTRAP_IMAGE_NAME")
|
||||
local -a cmd=("${BASE_CONTAINER_CMD[@]}" "$SLOPTRAP_IMAGE_NAME" "codex")
|
||||
if [[ ${#CODEX_ARGS_ARRAY[@]} -gt 0 ]]; then
|
||||
cmd+=("${CODEX_ARGS_ARRAY[@]}")
|
||||
fi
|
||||
@@ -1425,7 +1691,9 @@ run_codex() {
|
||||
if ! $DRY_RUN; then
|
||||
status_line "Running %s\n" "$SLOPTRAP_IMAGE_NAME"
|
||||
fi
|
||||
run_codex_command
|
||||
local runtime_prompt
|
||||
runtime_prompt=$(build_runtime_context_prompt)
|
||||
run_codex_command "$runtime_prompt"
|
||||
}
|
||||
|
||||
run_login_target() {
|
||||
@@ -1433,7 +1701,7 @@ run_login_target() {
|
||||
if ! $DRY_RUN; then
|
||||
status_line "Login %s\n" "$SLOPTRAP_IMAGE_NAME"
|
||||
fi
|
||||
local -a cmd=("${BASE_CONTAINER_CMD[@]}" "$SLOPTRAP_IMAGE_NAME" login)
|
||||
local -a cmd=("${BASE_CONTAINER_CMD[@]}" "$SLOPTRAP_IMAGE_NAME" "codex" login)
|
||||
run_or_print "${cmd[@]}"
|
||||
}
|
||||
|
||||
@@ -1442,7 +1710,7 @@ run_shell_target() {
|
||||
if ! $DRY_RUN; then
|
||||
status_line "Shell %s\n" "$SLOPTRAP_IMAGE_NAME"
|
||||
fi
|
||||
local -a cmd=("${BASE_CONTAINER_CMD[@]}" --entrypoint /bin/bash "$SLOPTRAP_IMAGE_NAME")
|
||||
local -a cmd=("${BASE_CONTAINER_CMD[@]}" "$SLOPTRAP_IMAGE_NAME" /bin/bash)
|
||||
run_or_print "${cmd[@]}"
|
||||
}
|
||||
|
||||
@@ -1509,6 +1777,7 @@ dispatch_target() {
|
||||
DRY_RUN=false
|
||||
PRINT_CONFIG=false
|
||||
SKIP_BUILD_BANNER=false
|
||||
TRUST_CAPABILITIES=false
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
@@ -1520,6 +1789,16 @@ while [[ $# -gt 0 ]]; do
|
||||
PRINT_CONFIG=true
|
||||
shift
|
||||
;;
|
||||
--enable-capability)
|
||||
shift
|
||||
[[ $# -gt 0 ]] || error "--enable-capability requires a capability name"
|
||||
ENABLED_CAPABILITIES_ARGS+=("$1")
|
||||
shift
|
||||
;;
|
||||
--trust-capabilities)
|
||||
TRUST_CAPABILITIES=true
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
@@ -1599,6 +1878,8 @@ if [[ ! $PROJECT_NAME =~ $VALID_NAME_REGEX ]]; then
|
||||
fi
|
||||
|
||||
select_codex_home "$CODE_DIR"
|
||||
select_capability_state_paths
|
||||
CAPABILITY_MANIFEST_DIGEST=$(compute_manifest_digest)
|
||||
ensure_ignore_helper_root
|
||||
IGNORE_STUB_BASE="$IGNORE_HELPER_ROOT/session-${BASHPID:-$$}"
|
||||
resolve_sloptrap_ignore "$CODE_DIR"
|
||||
@@ -1608,7 +1889,7 @@ if [[ ! -s "$CODEX_AUTH_FILE_HOST" ]]; then
|
||||
NEED_LOGIN=true
|
||||
fi
|
||||
|
||||
TARGETS=("$@")
|
||||
TARGETS=("${TARGETS_INPUT[@]}")
|
||||
if [[ ${#TARGETS[@]} -eq 0 ]]; then
|
||||
TARGETS=("run")
|
||||
fi
|
||||
@@ -1616,6 +1897,14 @@ fi
|
||||
DEFAULT_TARGETS=("${TARGETS[@]}")
|
||||
|
||||
PACKAGES_EXTRA=${MANIFEST[packages_extra]-}
|
||||
REQUESTED_CAPABILITIES=$(normalize_capability_list "${MANIFEST[capabilities]-}")
|
||||
if [[ -n $REQUESTED_CAPABILITIES ]]; then
|
||||
ensure_safe_for_make "capabilities" "$REQUESTED_CAPABILITIES"
|
||||
fi
|
||||
validate_capability_list "capabilities" "$REQUESTED_CAPABILITIES"
|
||||
ENABLED_CAPABILITIES=$(normalize_capability_list "${ENABLED_CAPABILITIES_ARGS[*]-}")
|
||||
validate_capability_list "--enable-capability" "$ENABLED_CAPABILITIES" "command line"
|
||||
validate_enabled_capabilities "$ENABLED_CAPABILITIES" "$REQUESTED_CAPABILITIES"
|
||||
if [[ -n ${MANIFEST[allow_host_network]-} ]]; then
|
||||
case "${MANIFEST[allow_host_network],,}" in
|
||||
1|true|yes)
|
||||
@@ -1630,9 +1919,12 @@ if [[ -n ${MANIFEST[allow_host_network]-} ]]; then
|
||||
esac
|
||||
fi
|
||||
|
||||
forbidden_keys=(container_opts_extra security_opts_extra env_extra env_passthrough default_targets default_target)
|
||||
forbidden_keys=(codex_args container_opts_extra security_opts_extra env_extra env_passthrough default_targets default_target)
|
||||
for forbidden_key in "${forbidden_keys[@]}"; do
|
||||
if [[ -n ${MANIFEST[$forbidden_key]-} ]]; then
|
||||
if [[ $forbidden_key == "codex_args" ]]; then
|
||||
error "$MANIFEST_PATH: key 'codex_args' has been deprecated; sloptrap now always uses '$DEFAULT_CODEX_ARGS_DISPLAY'"
|
||||
fi
|
||||
error "$MANIFEST_PATH: key '$forbidden_key' has been removed for security reasons"
|
||||
fi
|
||||
done
|
||||
@@ -1643,43 +1935,8 @@ if [[ -n $PACKAGES_EXTRA ]]; then
|
||||
fi
|
||||
CONTAINER_ENGINE="$(detect_container_engine)"
|
||||
CODEX_ARGS_ARRAY=("${DEFAULT_CODEX_ARGS[@]}")
|
||||
if [[ -n ${MANIFEST[codex_args]-} ]]; then
|
||||
ensure_safe_for_make "codex_args" "${MANIFEST[codex_args]}"
|
||||
manifest_codex_args_value=$(trim "${MANIFEST[codex_args]}")
|
||||
if [[ $manifest_codex_args_value != "$DEFAULT_CODEX_ARGS_DISPLAY" ]]; then
|
||||
declare -a manifest_codex_args=()
|
||||
read -r -a manifest_codex_args <<< "$manifest_codex_args_value"
|
||||
CODEX_ARGS_ARRAY+=("${manifest_codex_args[@]}")
|
||||
unset -v manifest_codex_args
|
||||
fi
|
||||
fi
|
||||
declare -a sanitized_codex_args=()
|
||||
declare -a sandbox_pair=()
|
||||
codex_args_index=0
|
||||
while [[ $codex_args_index -lt ${#CODEX_ARGS_ARRAY[@]} ]]; do
|
||||
if [[ ${CODEX_ARGS_ARRAY[$codex_args_index]} == "--sandbox" ]]; then
|
||||
if (( codex_args_index + 1 >= ${#CODEX_ARGS_ARRAY[@]} )); then
|
||||
error "$MANIFEST_PATH: '--sandbox' flag requires a mode (workspace-write, workspace-read-only, or danger-full-access)"
|
||||
fi
|
||||
sandbox_pair=(--sandbox "${CODEX_ARGS_ARRAY[$((codex_args_index + 1))]}")
|
||||
((codex_args_index+=2))
|
||||
continue
|
||||
fi
|
||||
sanitized_codex_args+=("${CODEX_ARGS_ARRAY[$codex_args_index]}")
|
||||
((codex_args_index+=1))
|
||||
done
|
||||
if [[ ${#sandbox_pair[@]} -gt 0 ]]; then
|
||||
sanitized_codex_args+=("${sandbox_pair[@]}")
|
||||
fi
|
||||
CODEX_ARGS_ARRAY=("${sanitized_codex_args[@]}")
|
||||
unset -v sanitized_codex_args sandbox_pair codex_args_index
|
||||
ensure_safe_sandbox "${CODEX_ARGS_ARRAY[@]}"
|
||||
if [[ ${#CODEX_ARGS_ARRAY[@]} -gt 0 ]]; then
|
||||
CODEX_ARGS_DISPLAY=$(printf '%s ' "${CODEX_ARGS_ARRAY[@]}")
|
||||
CODEX_ARGS_DISPLAY=${CODEX_ARGS_DISPLAY% }
|
||||
else
|
||||
CODEX_ARGS_DISPLAY=""
|
||||
fi
|
||||
CODEX_ARGS_DISPLAY=$DEFAULT_CODEX_ARGS_DISPLAY
|
||||
|
||||
prepare_ignore_mounts "$CODE_DIR"
|
||||
prepare_container_runtime
|
||||
|
||||
Reference in New Issue
Block a user