/* Route-Critical (index, V6 §3.6.3 — erzeugt von tools/generate_critical_css.mjs) */ .collapse:not(.show){display:none}.navbar{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between}.navbar>.container-fluid{display:flex;flex-wrap:inherit;align-items:center;justify-content:space-between}.mydraft-svg-icon{display:inline-block;vertical-align:middle;color:currentcolor;flex-shrink:0;transition:color var(--duration-fast, 150ms) ease,stroke var(--duration-fast, 150ms) ease,fill var(--duration-fast, 150ms) ease}.mydraft-svg-icon circle,.mydraft-svg-icon line,.mydraft-svg-icon path,.mydraft-svg-icon polygon,.mydraft-svg-icon polyline,.mydraft-svg-icon rect{transition:stroke var(--duration-fast, 150ms) ease,fill var(--duration-fast, 150ms) ease}@layer components{.live-pulse-dot{width:8px;height:8px}}.min-w-0{min-width:0}@layer overrides{.live-radar-header-badge{background:rgba(239,68,68,.12);border:1px solid rgba(239,68,68,.35);color:#fca5a5;font-size:.75rem;font-weight:700;transition:.25s;cursor:pointer}}@layer overrides{.live-pulse-dot{width:8px;height:8px;background-color:var(--threat-critical,#ef4444);border-radius:50%;display:inline-block;box-shadow:rgba(239,68,68,.7) 0 0 0 0}}@layer overrides{body.light .live-radar-header-badge,html[data-bs-theme=light] .live-radar-header-badge{background:#fbd9dd;border-color:#eeb2b8;color:#8a1723!important}}.isharestuff-live-ticker{color:var(--color-text-body,#f8fafc);backdrop-filter:none;flex-wrap:wrap;row-gap:.3rem;background:var(--color-surface,rgba(15,23,42,.85))!important;border:1px solid var(--color-border,rgba(255,255,255,.12))!important;padding:.45rem .5rem!important;margin-bottom:.75rem!important}.ticker-content{mask-image:linear-gradient(to right,transparent 0px,rgb(0,0,0) 14px,rgb(0,0,0) calc(100% - 14px),transparent 100%);touch-action:pan-y;cursor:grab;user-select:none;-webkit-tap-highlight-color:transparent;order:1;width:100%;max-width:100%;flex:0 0 100%!important}.isharestuff-live-ticker .ticker-lead-group{order:2;width:100%;justify-content:space-between;margin-right:0!important}.ticker-track{display:flex;align-items:center;white-space:nowrap;will-change:transform;transform:translate3d(0,0,0);--ticker-speed:1;animation-name:isharestuffTicker;animation-timing-function:linear;animation-iteration-count:infinite;animation-duration:calc(26s / var(--ticker-speed, 1))}.ticker-link{max-width:min(78vw,340px);font-size:.82rem;display:inline-block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:baseline;transition:color .2s;color:var(--color-text-heading,#f8fafc)!important}.isharestuff-live-ticker .ticker-ctrl-btn{min-width:44px;height:44px;font-size:.78rem;-webkit-tap-highlight-color:transparent}.ticker-controls-bar{gap:.4rem}.ticker-counter{font-family:var(--bs-font-monospace, ui-monospace, monospace);font-size:.72rem;line-height:1;color:var(--color-text-secondary,#94a3b8);min-width:3.4em;text-align:center;user-select:none;white-space:nowrap;margin-left:auto}.ticker-item{font-size:.82rem}.ticker-item .ticker-date{font-size:.72rem;vertical-align:baseline}.ticker-text-group{display:inline-flex;align-items:baseline}.ticker-badge-live-btn{border:0;cursor:pointer;text-decoration:none!important}.ticker-badge{font-size:.78rem!important;min-height:44px!important;min-width:44px!important;padding:6px 12px!important;display:inline-flex!important;align-items:center!important;justify-content:center!important}@media (min-width:992px){.navbar-expand-lg{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-lg .navbar-toggler{display:none}}@media (prefers-reduced-motion:reduce){@layer overrides{#news-popup,#news-toast-banner,.animate-pulse,.live-pulse-dot,.live-pulsing-dot,.news-item-card{animation:none;transition:none}}.ticker-track{animation:none!important}.ticker-content{cursor:default}}@media (min-width:768px){.isharestuff-live-ticker{flex-wrap:nowrap;row-gap:0;backdrop-filter:blur(12px);padding:.5rem!important;margin-bottom:1rem!important}.isharestuff-live-ticker .ticker-content{order:0;width:auto;max-width:none;flex:1 1 auto!important}.isharestuff-live-ticker .ticker-lead-group{order:0;width:auto;justify-content:flex-start;margin-right:1rem!important}.ticker-controls-bar{gap:.3rem}.isharestuff-live-ticker .ticker-ctrl-btn{min-width:30px;height:auto;font-size:inherit}.ticker-counter{font-size:.66rem;margin-left:0}.ticker-track{animation-duration:calc(40s / var(--ticker-speed, 1))}.ticker-link{max-width:340px;font-size:.85rem}.ticker-item{font-size:.85rem}.ticker-badge{font-size:inherit!important;min-height:0!important;min-width:0!important;padding:.5rem 1rem!important}}@media (max-width:359.98px){.ticker-counter{display:none}.ticker-controls-bar{gap:.3rem}.ticker-badge{padding:6px 9px!important}} 🔒 Building a Secure Code Execution Sandbox in Rust | Sichere Programmierung – TSECURITY.DE Zum Hauptinhalt springen
Sichere Programmierung🚀 Introducing SellSmart AI — Generate Product Listings with Gemma(19.10.2026 um 06:33 Uhr)
•••
Sichere ProgrammierungWITHIN: turning a painting into a place you can explore with Three.js(19.10.2026 um 06:34 Uhr)
••
Sichere ProgrammierungHow to Compress a PDF Without Making It Hard to Read(19.10.2026 um 06:34 Uhr)
••••
Sichere ProgrammierungMultiagentic Chatbot(19.10.2026 um 06:37 Uhr)
•
Sichere Programmierung🚀 Introducing SellSmart AI — Generate Product Listings with Gemma(19.10.2026 um 06:33 Uhr)
•••
Sichere ProgrammierungWITHIN: turning a painting into a place you can explore with Three.js(19.10.2026 um 06:34 Uhr)
••
Sichere ProgrammierungHow to Compress a PDF Without Making It Hard to Read(19.10.2026 um 06:34 Uhr)
••••
Sichere ProgrammierungMultiagentic Chatbot(19.10.2026 um 06:37 Uhr)
•
Intelligence View
⚡ tsecurity.de Intelligence

Building a Secure Code Execution Sandbox in Rust

I got annoyed. I was looking at how most code execution platforms handle sandboxing and kept seeing the same pattern: throw it in a Docker container, set a…

Beitrag
0
Seite
0
↗ Quelle (dev.to)
Social ReaktionenReagiere als Erste:r — dein Feedback zählt!

I got annoyed.



I was looking at how most code execution platforms handle sandboxing and kept seeing the same pattern: throw it in a Docker container, set a timeout, call it secure. That's not sandboxing, that's hoping nothing goes wrong. So I built Custody to figure out what real isolation actually looks like when you have to think about every layer.






This isn't a tutorial. It's more of a breakdown of the decisions I made, why I made them, and what I'd change if I started over.



What I was defending against

Before touching any code I wrote out the threat model. Not because I was being formal about it, but because "secure code execution" means nothing unless you're specific about what you're securing against. My list:




  • Fork bombs killing the host by exhausting PIDs

  • Memory exhaustion crashing everything outside the sandbox

  • Code trying to phone home over the network

  • Filesystem escapes reaching host data

  • Syscall abuse through dangerous kernel interfaces

  • Infinite loops sitting there consuming CPU forever



The important thing I realized early is that no single mechanism stops all of these. Docker alone doesn't. gVisor alone doesn't. You have to layer them, and each layer has to be doing something the others aren't.






The outer wall: gVisor



gVisor sits between the untrusted code and the Linux kernel. Instead of syscalls going straight to the kernel, they hit gVisor's Sentry first, which is a user-space kernel reimplementation in Go. It's slower than native execution, but for a sandbox that's actually a reasonable tradeoff.



Every job in Custody spawns through runsc with two flags I care about:



"--network=none".to_string(),

"--no-new-privs".to_string(),



--network=none is self-explanatory. No network, no exfiltration, no DNS tricks, nothing. --no-new-privs is the less obvious one. It stops any process inside the sandbox from gaining elevated privileges through setuid binaries. Without it, a clever attacker finds a setuid binary in your rootfs and suddenly your sandbox assumptions fall apart.






Resource limits: cgroups v2



gVisor handles isolation. It does nothing about resource consumption. A sandboxed process can still fork bomb itself into consuming every PID on the system if you don't enforce limits separately.



I write directly to the cgroup filesystem instead of going through a container runtime:



fs::write(

self.cgroup_path.join("cpu.max"),

format!("{} {}", quota_us, period_us),

)?;



Four limits per job: CPU quota, memory ceiling, max PIDs, and wall clock timeout. The wall timeout is separate from CPU quota on purpose. A process that just sleeps uses zero CPU but sits there forever. cgroups won't catch that. So the wall timeout is enforced at the application layer with a polling loop that kills the process if it runs past the deadline.



Cleanup happens in the Drop impl so the cgroup gets torn down even if something panics mid-execution. That one took me longer than I'd like to admit to think of.






Syscall filtering: seccomp



This is the innermost layer. Even if someone escapes gVisor somehow, seccomp is a kernel-level allowlist of syscalls the process is permitted to make. A Python script needs maybe 30 syscalls to run normally. It does not need ptrace, mount, or socket. Block everything else at the kernel level and a lot of attack surface just disappears.



I detect violations by watching for exit code 159 or SIGSYS in stderr:



if code == 159 || stderr_str.contains("seccomp") {

kill_reason = Some("seccomp_violation".to_string());

}






Two things most sandboxes miss



Output floods and OOM detection. If a job writes gigabytes to stdout it can exhaust host memory even if execution itself is isolated. Custody caps output per job and truncates explicitly so the caller knows why output ended early rather than just getting a mystery empty response.



OOM detection reads memory.current from the cgroup after execution finishes. Not perfect, but good enough to catch the obvious cases and label them correctly in the audit log.






The kill reason taxonomy



Every job exits with a labeled reason: timeout_wall, output_limit, oom, seccomp_violation, or clean. This sounds like a small thing but it matters a lot in practice. When you're looking at audit logs and trying to understand whether you're seeing a bug, a resource tuning problem, or an actual attack attempt, vague "job failed" entries are useless. Named failure modes make the logs actually tell you something.






What I'd do differently



Three things I'd change in a v2.



The rootfs preparation copies binaries from the host at runtime. That's fragile. A proper implementation uses pre-built OCI images per language that are version-pinned and auditable.



The wall timeout uses a polling loop with 100ms sleeps. For short-lived jobs this adds real latency. The right approach is pidfd_open plus epoll so you get notified the instant the process exits instead of waiting for the next poll interval.



The seccomp profiles are handwritten per language. That works but it's maintenance-heavy and easy to get wrong. Better to generate them from actual runtime behavior using eBPF or strace analysis, then tighten from there.






How it held up



100% containment across 8 attack scenarios including fork bombs, network exfiltration attempts, and OOM conditions. The architecture is deliberately simple. For security-critical code I'd rather have something I can fully audit in an afternoon than something clever I have to trust blindly.

🔍 CTI & Forensik

Cyber Threat Intelligence & Forensik

ATT&CK-Navigator · IoC-Radar · Exploit-Belege
MITRE ATT&CK Matrix Navigator
1 belegte Technik
T1041TA0010 · Exfiltration
Exfiltration Over C2 Channel
Mitigation: M1057 Data Loss Prevention
Quelle: Kontext-Klassifikation des Artikeltextes
Exfiltration
CTI Threat Relationship Graph
Akteure · Techniken · Beziehungen
5 Knoten · 4 Relationen
CVE / Incident Threat Actor Software MITRE ATT&CK CWE Weakness IoC
Intelligence Digest — kostenlos Täglich die wichtigsten Security-News · jederzeit abbestellbar
Community Rating & Social Proof
⭐ Leser-Wertung
–
Ø / 5.0
Noch keine Leser-Bewertungen
War diese Seite hilfreich?
0
Jetzt Bewertung abgeben
🟢 Niedrig (1-3.9) 🟡 Mittel (4.0-6.9) 🔴 Hoch (7.0-8.9) Kritisch (9.0-10.0)

Verwandte Story-Cluster & Quellen (Vektor-KI)

Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Building a Secure Code Execution Sandbox in Rust

Thematisch verwandte Begriffe: Building, Secure, Code, Execution · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

💬 Reacties worden geladen…
Trek om te vernieuwen
Nächster Beitrag