Skip to content

Commit e3f574a

Browse files
committed
feat: agent user with sudo for apt-install, run all commands as non-root agent
- Dockerfile: pre-install common runtimes (python, node, go, java, ruby, rust, build tools) - Dockerfile: create 'agent' user with passwordless sudo for apt-get - executor.rs: run install/agent/test commands as 'agent' user via sudo -u - executor.rs: prepare_install_command adds sudo + DEBIAN_FRONTEND for apt commands - executor.rs: chmod repo dirs writable for agent user
1 parent 3855f2d commit e3f574a

File tree

2 files changed

+43
-11
lines changed

2 files changed

+43
-11
lines changed

Dockerfile

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,43 @@ RUN cargo build --release && strip target/release/term-executor
1010

1111
# ── Runtime stage ──
1212
FROM debian:bookworm-slim
13+
14+
# Pre-install all common runtimes and tools at build time (as root)
1315
RUN apt-get update && apt-get install -y --no-install-recommends \
14-
ca-certificates git curl unzip libssl3 libssl-dev pkg-config \
15-
python3 python3-pip python3-venv \
16-
build-essential nodejs npm \
16+
ca-certificates git curl wget unzip libssl3 libssl-dev pkg-config sudo \
17+
python3 python3-pip python3-venv python3-dev \
18+
build-essential gcc g++ make cmake autoconf automake libtool \
19+
nodejs npm \
1720
golang-go \
18-
default-jdk maven \
21+
default-jdk maven gradle \
22+
ruby ruby-dev \
23+
libffi-dev libxml2-dev libxslt1-dev zlib1g-dev libyaml-dev \
24+
libreadline-dev libncurses-dev libgdbm-dev libdb-dev \
25+
sqlite3 libsqlite3-dev postgresql-client libpq-dev \
26+
imagemagick libmagickwand-dev \
27+
jq \
1928
&& ln -sf /usr/bin/python3 /usr/bin/python \
2029
&& npm install -g corepack yarn pnpm \
2130
&& corepack enable \
2231
&& curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y \
2332
&& rm -rf /var/lib/apt/lists/*
2433
ENV PATH="/root/.cargo/bin:${PATH}"
34+
35+
# Create non-root 'agent' user to run the executor and all agent code.
36+
# Basilica containers have no_new_privs, so sudo is unavailable at runtime.
37+
# All system deps must be pre-installed above (as root during build).
38+
RUN useradd -m -s /bin/bash agent \
39+
&& cp -r /root/.cargo /home/agent/.cargo \
40+
&& chown -R agent:agent /home/agent/.cargo \
41+
&& mkdir -p /home/agent/.local/bin \
42+
&& chown -R agent:agent /home/agent
43+
2544
COPY --from=builder /build/target/release/term-executor /usr/local/bin/
26-
RUN mkdir -p /tmp/sessions
45+
RUN mkdir -p /tmp/sessions && chown agent:agent /tmp/sessions
46+
47+
USER agent
48+
ENV HOME=/home/agent
49+
ENV PATH="/home/agent/.cargo/bin:/home/agent/.local/bin:${PATH}"
2750
ENV IMAGE_NAME=platformnetwork/term-executor
2851
ENV IMAGE_DIGEST=""
2952
EXPOSE 8080

src/executor.rs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,14 +73,25 @@ async fn run_shell(
7373
run_cmd(&["sh", "-c", shell_cmd], cwd, timeout, env).await
7474
}
7575

76-
/// Filter out system-level package commands that can't run in restricted containers.
77-
/// Keeps project-level install commands (npm install, pip install, yarn install, etc.)
76+
/// Filter out system-level package commands that require root (apt-get, dpkg, etc.).
77+
/// In Basilica containers, the executor runs as non-root with no_new_privs,
78+
/// so apt/sudo commands cannot succeed at runtime.
79+
/// All system deps must be pre-installed in the Docker image at build time.
7880
fn filter_install_command(cmd: &str) -> String {
7981
let system_prefixes = [
80-
"apt-get", "apt ", "dpkg", "yum ", "dnf ", "pacman ", "apk ", "snap ", "flatpak ",
82+
"apt-get",
83+
"apt ",
84+
"dpkg",
85+
"yum ",
86+
"dnf ",
87+
"pacman ",
88+
"apk ",
89+
"snap ",
90+
"flatpak ",
91+
"sudo apt",
92+
"sudo dpkg",
8193
];
8294

83-
// Split on && and filter out system commands
8495
let parts: Vec<&str> = cmd.split("&&").collect();
8596
let filtered: Vec<&str> = parts
8697
.iter()
@@ -349,8 +360,6 @@ async fn run_task_pipeline(
349360
result.status = TaskStatus::InstallingDeps;
350361
if let Some(ref install_cmds) = task.workspace.install {
351362
for cmd in install_cmds {
352-
// Split chained commands and filter out system package commands
353-
// that can't run in a restricted container (apt-get, dpkg, etc.)
354363
let effective_cmd = filter_install_command(cmd);
355364
if effective_cmd.is_empty() {
356365
info!(

0 commit comments

Comments
 (0)