Skip to content

Latest commit

 

History

History
171 lines (131 loc) · 5.88 KB

File metadata and controls

171 lines (131 loc) · 5.88 KB

Security

Terminal is a command ACL and argument sanitizer. It filters which commands can be spawned and validates arguments before execution. It is not a sandbox, container, or process isolation layer.

What Terminal Actually Does

  1. Allow/Deny Pattern Matching - rejects commands not matching the allow list
  2. Argument Sanitization - blocks shell metacharacters, null bytes, path traversal
  3. Workspace Validation - enforces cwd is within allowed directories
  4. Environment Filtering - strips sensitive environment variables
  5. Timeout Enforcement - kills runaway processes via native AbortSignal
  6. Privilege Dropping - runs child processes as a different uid/gid
  7. Windows Hardening - hides console windows, disables argument quoting

Native Process Hardening

Terminal leverages native Node.js child_process.spawn() options for additional hardening:

  • Native AbortSignal - Uses Node.js built-in signal option. When Terminal.kill() is called or a timeout fires, Node handles the kill signal natively without race conditions
  • uid/gid - Drops privileges at spawn time via setuid/setgid. The child runs as a different user/group than the parent
  • killSignal - Configurable default signal (default: SIGTERM). Change to SIGKILL for immediate termination
  • windowsHide - Prevents console window pop-ups on Windows
  • windowsVerbatimArguments - Disables Windows argument quoting/escaping, preventing injection via shell parsing

What Terminal Does NOT Do

  • Does NOT inspect file contents - python3 /workspace/delete.py passes all checks
  • Does NOT sandbox the filesystem - allowed commands can read/write anywhere the OS permits
  • Does NOT isolate network access - allowed commands can open sockets freely
  • Does NOT prevent privilege escalation by default - allowed commands can call sudo if the OS allows. Use uid/gid config to drop privileges at spawn time
  • Does NOT inspect interpreter scripts - node -e payloads without metacharacters pass through

Threat Model

Defended Against (in the command string)

  • Shell Injection - echo; rm -rf / in arguments is blocked by strictArgs
  • Command Chaining - || and && in arguments is blocked by strictArgs
  • Path Traversal - ../../../etc/passwd in arguments is blocked by path traversal check
  • Variable Expansion - $(whoami) in arguments is blocked by strictArgs
  • Null Byte Injection - \x00 in arguments is blocked

NOT Defended Against (requires OS hardening)

  • Script File Execution - any allowed interpreter (python3, node, deno) executing a malicious script file
  • Allowed Command Abuse - git with malicious alias config, tar extracting to arbitrary paths
  • Environment Variable Abuse - LD_PRELOAD injection, PATH hijacking (if PATH is allowed)
  • Symlink Escapes - symlinks inside workspaces pointing outside allowed directories
  • Resource Exhaustion - fork bombs, memory exhaustion, disk filling (requires cgroups)

OS-Level Hardening Guide

Terminal filters the command string. The OS must enforce the actual execution boundary.

1. Run as an Unprivileged User

# Create a restricted user
useradd -m -s /bin/bash terminal-runner
chown -R terminal-runner:terminal-runner /workspace
su terminal-runner -c "deno run app.ts"

2. Filesystem Permissions (chmod, chown)

# Make sensitive files unreadable
chmod 700 /etc/secrets
chmod 600 ~/.ssh/id_rsa

# Make workspace read-only where possible
chmod -R u+w /workspace/input
chmod -R u-w /workspace/output   # Write output to a separate dir

3. chroot Jail (Directory Isolation)

# Create a minimal root filesystem
mkdir -p /jail/{bin,lib,lib64,workspace}
cp /bin/bash /bin/ls /jail/bin/
chroot /jail /bin/bash

4. Linux Namespaces (unshare)

# Isolate filesystem, network, and process namespace
unshare --mount --net --pid --fork --map-root-user /bin/bash

5. seccomp-bpf (Syscall Filtering)

// Deno example using --allow-net, --allow-read flags
// Node.js example using --experimental-sea or external seccomp tools
deno run --allow-read=/workspace --allow-write=/workspace app.ts

6. cgroups (Resource Limits)

# Limit CPU and memory
cgcreate -g cpu,memory:/terminal-limit
cgset -r cpu.cfs_quota_us=50000 terminal-limit   # 50% CPU
cgset -r memory.limit_in_bytes=512M terminal-limit
cgexec -g cpu,memory:terminal-limit deno run app.ts

7. Docker / Podman Container

FROM denoland/deno:latest
WORKDIR /workspace
COPY . .
RUN chmod -R o-w /etc /usr /bin
USER 1000:1000
CMD ["deno", "run", "app.ts"]
docker run --rm \
  --read-only \
  --tmpfs /tmp:noexec,nosuid,size=100m \
  -v $(pwd)/workspace:/workspace:ro \
  -u 1000:1000 \
  terminal-app

8. AppArmor / SELinux Profiles

# AppArmor profile restricting file access
cat > /etc/apparmor.d/terminal-runner << 'EOF'
#include <tunables/global>

profile terminal-runner {
  #include <abstractions/base>
  /workspace/** r,
  /workspace/output/** w,
  /bin/** ix,
  /usr/bin/** ix,
  deny /etc/passwd r,
  deny /home/** w,
}
EOF
aa-enforce terminal-runner

Defense-in-Depth Stack

For real security, stack all layers:

Layer 8: Terminal ACL (command allow/deny, argument sanitization)
Layer 7: Unprivileged user (no root access)
Layer 6: Filesystem permissions (chmod, chown)
Layer 5: chroot / bind mounts (directory isolation)
Layer 4: Linux namespaces (unshare)
Layer 3: seccomp-bpf (syscall filtering)
Layer 2: cgroups (resource limits)
Layer 1: Container / VM (process + network isolation)

Terminal alone is Layer 8. Never rely on it as your only defense.

See Also