6 ms·
Cargo desperately needs sandboxing for build.rs scripts. It’s been attempted before, but didn’t go very far¹. ¹ https://rust-lang.github.io/goals/2024h2/sandbo
by jakubadamw 2mo ago
Cargo desperately needs sandboxing for build.rs scripts. It’s been attempted before, but didn’t go very far¹.
¹ https://rust-lang.github.io/goals/2024h2/sandboxed-build-script.html https://rust-lang.github.io/goals/2024h2/sandboxed-build-scr...
- Panzerschrek 2mo agoSandboxing for build scripts can't work properly. If you sandbox too much, some necessary stuff can't be done. If you sandbox too little, it has no practical value.
- lobofta 2mo agoSo let each build script define its own level of sandboxing and then users can determine whether they are okay with that level or not, e.g. `cargo build --sandbox-level=...`
- jurgenburgen 2mo agoThat’s not solving the problem, that’s avoiding it by making it the users fault if they make a mistake.
- kibwen 2mo agoRust, like C, C++, and every other systems programming language, is all about giving users the power to make mistakes. The philosophical difference when it comes to Rust is simply that it tries to force the user to flip off the safety on the gun before letting you shoot yourself in the foot. A Cargo config option letting people opt-out of sandboxing would be fully in line with Rust's philosophy.
- jurgenburgen 2mo agoHaving a dangerous flag as a backwards compatibility flag is okay. I don’t think making users decide between multiple levels of sandboxing is constructive, they will just be trained to ignore it. This is the kind of decision users likely don’t understand without looking at the source code of a crate and it’s bad UX to push it to be their responsibility.
- kibwen 1mo agoNo, the flag would not exist simply for backwards compatibility, it would exist because build scripts are occasionally necessary and there are plenty of legitimate uses for them, even if they should be opt-in.
- amluto 2mo agoAs an easy start, how about letting build scripts read /usr, read and write a temporary build directory, have some /tmp scratch space, and be allowed to write its final output artifact. No network and otherwise isolated from the rest of the system. I would argue that, if a build script doesn’t work in the setting, then it doesn’t deserve to be installable by a default cargo command.
- kibwen 2mo agoCargo is a cross-platform tool, so when it ships a sandboxing solution it will need to be a cross-platform solution, and because this is a security feature it needs to be bulletproof, so no half-measures like Docker. Something like a WASM runtime might fit the bill, though that will be much easier to get working for typical proc macros than for typical build scripts. If you only care about Unix, then you can do this yourself today by building code in your sandbox of choice.
- amluto 2mo ago> Cargo is a cross-platform tool, so when it ships a sandboxing solution it will need to be a cross-platform solution This seems like an excuse, not an actual objection. Linux can do seccomp or Landlock or gVisor or a combination. Seccomp and gVisor need no privileges. Windows has its internal weird mechanisms. Mac has sandbox-exec. Cargo could easily pick an appropriate sandbox for each major platform and ship it by default. > If you only care about Unix, then you can do this yourself today by building code in your sandbox of choice. This is ridiculous. The sandbox should not have network access, but cargo needs network access to download the package in the first place.
- kibwen 2mo ago> Windows has its internal weird mechanisms. If you have a serious proposal, then I encourage someone to seriously propose it. Cargo is an understaffed open source project that, like the rest of the Rust project, relies largely on volunteers. However, gesturing to unspecified internal weird mechanisms does not strike me as a serious proposal worthy of consideration by anyone, so I'd suggest working on that first. > The sandbox should not have network access, but cargo needs network access to download the package in the first place. Naturally. Use `cargo fetch` to download a package locally without invoking any build step: https://doc.rust-lang.org/cargo/commands/cargo-fetch.html https://doc.rust-lang.org/cargo/commands/cargo-fetch.html
- krautsauer 2mo agohttps://news.ycombinator.com/item?id=49374811 https://news.ycombinator.com/item?id=49374811 (Oh and btw, proc macros also run arbitrary code.)
- quotemstr 2mo agoThere's no good reason a proc macro can't run in a no-IO sandbox by default. None. Doesn't require a language change. Doesn't require some microvmcapabilityeffect BS. It requires looking people straight in the eye and saying "no" when they complain about needing to prompt for privileges.
- krautsauer 1mo agoNo good reason? Here's a list: https://github.com/dtolnay/watt#remaining-work https://github.com/dtolnay/watt#remaining-work (It mostly boils down to "somebody needs to do it". I'd really like proc macros precompiled to wasm by crates.io…)
- quotemstr 1mo agoWASM is totally unnecessary. Vanilla seccomp is sufficient and runs at full performance. What is it with people trying to stick WASM in places it's not needed?
- krautsauer 1mo agoI'd argue that this kind of thing is easier to implement securely and cross-platform with wasm (of course, a performant wasm engine is its own source of complexity). wasm has the additional benefits of allowing something like pre-compiling binaries once on a central server and thus skipping the CPU cycles for compiling all the macro crate dependencies on every crate compile. So even the people who don't care much about the security benefits have a reason. I'm kind of the "don't care about security" side because (as said elsewhere) it's trivial (https://docs.rs/ctor/latest/ctor/ https://docs.rs/ctor/latest/ctor/) for any macro to insert code that will run when compiled binaries/tests are run. You need the entire thing in a sandbox anyway, not just the macros, and that's something that can't be provided by cargo by default.
- weinzierl 2mo agoSandboxing just build.rs would only be be a minor inconvenience for the attacker, nothing more. The attacker can always as easily compromise the binary you build and as soon as you run it (e.g. in a test) you are owned. It would be a big pain for many that are in the unfortunate position to really need build scripts, though.
- insanitybit 2mo agoIt would be more than a minor inconvenience. I can handle sandboxing my tests and production infra, but I can't handle sandboxing build scripts because I don't own that code in any sense.
- Aurornis 2mo agoI imagine it would be sandboxed by default with an escape hatch to run build scripts outside of the sandbox with user verification. It makes people stop and think about what’s happening. Not perfect, but it does help. When working on JS ecosystem projects I manually approve build scripts and spend some time researching dependencies with build scripts to see if I can avoid running the build script. Some people will ignore it and run everything, but it’s a huge step in the right direction to make it operator-decided.
- kibwen 2mo agoAt the very least, it wouldn't be overly onerous when adding a dependency that requires a build script to require an opt-in via Cargo.toml, e.g. `build-script = true`. You'd make it viral so that any transitive dependency that requires a build script would affect its parent, then add the key as defaulting to `true` so as to not break backwards-compatibility, then switch the default to be more restrictive over a new edition. (This same key could be used to prevent proc-macros from having arbitrary system access as well, where by default proc macros could be compiled to WASM and run in a WASM sandbox and treated as pure functions.)
- klabb3 2mo ago100% agree. Wow I can’t believe how many think sandbox builds is an actually good idea, on a per language basis too. There are completely standard QoL issues in cargo that have been open for years and nobody is working on. Maintaining a sandbox for idk 3 operating systems minimum that have virtually no sandboxing support? For extremely diverse workloads that typically invoke commands? I mean.. good luck. There are still elephants (or rather mammoths) in the room for supply chain security, such as having 1500 nested deps for a standard project. It’s like we never woke up from the nightmare of leftpad. If your project had code from 100s of individuals, new versions can be pushed instantly, and nobody wants to review the code, then you have a time bomb. And also other problems.
- burnt-resistor 2mo agoNever going to work. Crates must be audited for behavior before use.
- Aeolos 2mo agocargo add + rust-analyzer instantly executes build.rs before you have a chance to audit the code. Cargo, please PLEASE give me a way to disable third-party build.rs and whitelist the ones I need. And please loudly mark any update that adds a build.rs where there was none before.
- praseodym 2mo agocargo-deny can audit build scripts, but unfortunately not prevent execution of malicious build scripts exactly for the reason you gave. It could still help if you only ever use cargo add and update in a sandbox. See https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html#the-build-field-optional https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.h...
- bigstrat2003 2mo agoSo, don't add dependencies before you audit the code? That seems like a pretty reasonable ask to me.
- Aeolos 2mo agoYou audit the code, then you run cargo update and you are pwned. Asking the user to not make mistakes is the c++ approach to security - it doesn’t work.
- burnt-resistor 1mo agoYou're creating a strawman. Don't run cargo update without verification, duh. > Asking the user to not make mistakes is the c++ approach to security Then demand crates.io do better by actually curating every version of every published crate.
- 2mo ago
- swiftcoder 2mo agobuild.rs by design can run absolutely anything. There tons of build.rs scripts that invoke a whole-ass C compiler toolchain to build and link C dependencies... It isn't so much a question of sandboxing build.rs, as fundamentally changing the way that foreign dependencies are integrated into the rust toolchain (i.e. moving from a rust-centric system like Cargo to something more general like buck2)
- kibwen 2mo agoThe vast and overwhelming majority of build scripts are building C code, so the other solution is to move away from integrating with C dependencies to native Rust dependencies, in which case adding friction to build scripts would be less noticeable.
- thayne 2mo agoOne of rust's strengths is it's ability to interface relatively easily with existing c code without having to rewrite absolutely everything in rust. I don't think that is something we want to give up.
- 0x457 2mo agobuild.rs changes nothing about how easy it is to integrate with C. What does simplify: figuring out how to supply library you need at build time. Which is the result of how bad dependency managment is outside (i.e. DLL-hell). Pretty much all other use cases of build.rs can be sandboxed. Well, there is sqlx that wants to connect to database at expansion time to compile check-queries (yew).
- __david__ 2mo agosqlx at least has the (optional) offline mode, where you "cargo sqlx prepare" once (which wants access to a db) and then you can build in offline mode which typechecks your queries against local files. Although I suppose that's still doing a lot of shenanigans at compile time. It could be sandboxed pretty well (theoretically). I'd hate to give it up completely though, getting a compile time error when SELECT query params or return values have type mismatches is extremely nice.
- somat 2mo agoI mean sure, but anything the build script could do, the build artifact could also do, That is to say, if you don't trust your source why do you trust the thing it compiles into?
- abhisek 2mo agoThis is exactly what PMG is designed for ie. install/build time process level sandboxing. It currently doesn't support cargo, but I believe the challenges are same. Here is my learning building PMG: Sandboxing is good when the workload is predictable, and the goal of sandbox is to guard against exploitation of vulnerabilities, like sandbox protecting chrome tabs (renderers). But unfortunately build scripts are not predictable, at least not in npm/pypi world and I have seen build scripts doing weirdest of the things which is no different from malware. When popular packages do weird things, build breaks and users end up turning off the sandbox. This is a perpetual problem to deal with while building sandbox (or any least privilege solution) to protect unbounded workloads. https://github.com/safedep/pmg https://github.com/safedep/pmg
- Asraelite 2mo agoThe "How PMG Works" section on Github does not actually explain how it works
- throwawayqqq11 2mo agoThen extend the list. We need isolated build envs AND deterministic builds, like nix does. Id like to add project provided runtime capabilities/permissions (eg. apparmor profiles) to the list to. Maybe the day will come, where projects not providing these things will be considered broken, like nix does.
- IshKebab 2mo agoI think it would be a good start if crates at least had to opt in to a build script, and adding one later would require permission from crates that depends on it. The vast majority of crates don't need build scripts, so it is vaguely feasible to audit the list of crates you use that might need them.
- bhickey 2mo agoI'd like to see a "no-build" option to blocks depending on crates using build.rs
- sempron64 2mo agoSandboxing is a mitigation but a very limited one. The library itself can contain a malicious payload -- rust code most often ends up as native code executed on the host. I think we may need to enter a world where there are fewer, heavily audited, libraries and dependency depth is limited. Allowing unvetted dependencies to be installed by default is not a good way forward. App stores have a similar problem and I think a lot of lessons have been learned there that can be applied.
- 7373737373 2mo agoThat's why languages need sandboxing at runtime as well
- josephg 2mo agoYes, or the compile time equivalents. Rust’s safety gets you most of the way there. We just need a capability model in the language, a more limited std and a way to ban untrusted 3rd party libraries from using unsafe code without explicit permission. I dream of a world where a function with the signature of add(u32, u32) -> u32 can’t burn my house down and steal my wife. Functions should only have access to their arguments. Nothing more. We need to end ambient authority.
- nh2 2mo agoYou have just reinvented "Safe Haskell" from 2012. It guarantees that pure functions are pure. https://www.microsoft.com/en-us/research/publication/safe-haskell/ https://www.microsoft.com/en-us/research/publication/safe-ha... https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/safe_haskell.html https://downloads.haskell.org/ghc/latest/docs/users_guide/ex...
- josephg 2mo agoOooh I didn't know that was a thing! Yes, I want this but in a fast, compiled systems language like rust.
- 2OEH8eoCRo0 2mo agoHow many times do we need to learn that sandboxing won't magically save us.