Skip to content

Commit ddc76fd

Browse files
domenkozarclaude
andcommitted
feat(build/oci): make sandbox shell configurable via query parameter
Previously, the sandbox shell path was hardcoded at compile time using the SNIX_BUILD_SANDBOX_SHELL environment variable. This change allows configuring the sandbox shell path at runtime via a query parameter in the OCI build service URL. The sandbox_shell can now be specified as: oci:///path/to/bundle?sandbox_shell=/path/to/shell If not specified, it defaults to /bin/sh. This makes the build service more flexible and removes the need for compile-time configuration. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> Change-Id: Idf14ad2d1ddf111fb3173d6aa13b4049a2a89070
1 parent abb2daa commit ddc76fd

File tree

5 files changed

+62
-14
lines changed

5 files changed

+62
-14
lines changed

snix/build/src/buildservice/from_addr.rs

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,57 @@
11
use super::{BuildService, DummyBuildService, grpc::GRPCBuildService};
2+
use serde::Deserialize;
23
use snix_castore::{blobservice::BlobService, directoryservice::DirectoryService};
4+
use std::path::PathBuf;
35
use url::Url;
46

57
#[cfg(target_os = "linux")]
68
use super::oci::OCIBuildService;
79

10+
/// Configuration for OCIBuildService
11+
#[cfg(target_os = "linux")]
12+
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
13+
#[serde(deny_unknown_fields)]
14+
pub struct OCIBuildServiceConfig {
15+
/// Root path in which all bundles are created
16+
pub bundle_root: PathBuf,
17+
18+
/// Path to the sandbox shell to use.
19+
/// This needs to be a statically linked binary, or you must ensure all
20+
/// dependencies are part of the build (which they usually are not).
21+
#[serde(default = "default_sandbox_shell")]
22+
pub sandbox_shell: PathBuf,
23+
// TODO: make rootless_uid_gid configurable
24+
}
25+
26+
#[cfg(target_os = "linux")]
27+
fn default_sandbox_shell() -> PathBuf {
28+
PathBuf::from("/bin/sh")
29+
}
30+
31+
#[cfg(target_os = "linux")]
32+
impl TryFrom<Url> for OCIBuildServiceConfig {
33+
type Error = Box<dyn std::error::Error + Send + Sync>;
34+
35+
fn try_from(url: Url) -> Result<Self, Self::Error> {
36+
// oci wants a path in which it creates bundles
37+
if url.path().is_empty() {
38+
return Err("oci needs a bundle dir as path".into());
39+
}
40+
41+
// Parse sandbox_shell from query parameters
42+
let query_pairs: std::collections::HashMap<_, _> = url.query_pairs().collect();
43+
let sandbox_shell = query_pairs
44+
.get("sandbox_shell")
45+
.map(|s| PathBuf::from(s.as_ref()))
46+
.unwrap_or_else(default_sandbox_shell);
47+
48+
Ok(OCIBuildServiceConfig {
49+
bundle_root: url.path().into(),
50+
sandbox_shell,
51+
})
52+
}
53+
}
54+
855
/// Constructs a new instance of a [BuildService] from an URI.
956
///
1057
/// The following schemes are supported by the following services:
@@ -32,17 +79,14 @@ where
3279
"dummy" => Box::<DummyBuildService>::default(),
3380
#[cfg(target_os = "linux")]
3481
"oci" => {
35-
// oci wants a path in which it creates bundles.
36-
if url.path().is_empty() {
37-
Err(std::io::Error::other("oci needs a bundle dir as path"))?
38-
}
39-
40-
// TODO: make sandbox shell and rootless_uid_gid
82+
let config = OCIBuildServiceConfig::try_from(url)
83+
.map_err(|e| std::io::Error::other(format!("invalid oci config: {}", e)))?;
4184

4285
Box::new(OCIBuildService::new(
43-
url.path().into(),
86+
config.bundle_root,
4487
blob_service,
4588
directory_service,
89+
config.sandbox_shell,
4690
))
4791
}
4892
scheme => {

snix/build/src/buildservice/oci.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ use std::{ffi::OsStr, path::PathBuf, process::Stdio};
1818

1919
use super::BuildService;
2020

21-
const SANDBOX_SHELL: &str = env!("SNIX_BUILD_SANDBOX_SHELL");
2221
const MAX_CONCURRENT_BUILDS: usize = 2; // TODO: make configurable
2322

2423
pub struct OCIBuildService<BS, DS> {
@@ -30,13 +29,21 @@ pub struct OCIBuildService<BS, DS> {
3029
/// Handle to a [DirectoryService], used by filesystems spawned during builds.
3130
directory_service: DS,
3231

32+
/// Path to the sandbox shell to use
33+
sandbox_shell: PathBuf,
34+
3335
// semaphore to track number of concurrently running builds.
3436
// this is necessary, as otherwise we very quickly run out of open file handles.
3537
concurrent_builds: tokio::sync::Semaphore,
3638
}
3739

3840
impl<BS, DS> OCIBuildService<BS, DS> {
39-
pub fn new(bundle_root: PathBuf, blob_service: BS, directory_service: DS) -> Self {
41+
pub fn new(
42+
bundle_root: PathBuf,
43+
blob_service: BS,
44+
directory_service: DS,
45+
sandbox_shell: PathBuf,
46+
) -> Self {
4047
// We map root inside the container to the uid/gid this is running at,
4148
// and allocate one for uid 1000 into the container from the range we
4249
// got in /etc/sub{u,g}id.
@@ -45,6 +52,7 @@ impl<BS, DS> OCIBuildService<BS, DS> {
4552
bundle_root,
4653
blob_service,
4754
directory_service,
55+
sandbox_shell,
4856
concurrent_builds: tokio::sync::Semaphore::new(MAX_CONCURRENT_BUILDS),
4957
}
5058
}
@@ -66,7 +74,7 @@ where
6674
let span = Span::current();
6775
span.record("bundle_name", bundle_name.to_string());
6876

69-
let mut runtime_spec = make_spec(&request, true, SANDBOX_SHELL)
77+
let mut runtime_spec = make_spec(&request, true, self.sandbox_shell.to_str().unwrap())
7078
.context("failed to create spec")
7179
.map_err(std::io::Error::other)?;
7280

snix/default.nix

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@ in
6767
inherit cargoDeps src;
6868
name = "snix-rust-docs";
6969
PROTO_ROOT = protos;
70-
SNIX_BUILD_SANDBOX_SHELL = "/homeless-shelter";
7170

7271
nativeBuildInputs = with pkgs; [
7372
cargo
@@ -93,7 +92,6 @@ in
9392
inherit cargoDeps src;
9493
name = "snix-clippy";
9594
PROTO_ROOT = protos;
96-
SNIX_BUILD_SANDBOX_SHELL = "/homeless-shelter";
9795

9896
buildInputs = [
9997
pkgs.fuse

snix/shell.nix

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ pkgs.mkShell {
5151
# should also benchmark with a more static nixpkgs checkout, so nixpkgs
5252
# refactorings are not observed as eval perf changes.
5353
shellHook = ''
54-
export SNIX_BUILD_SANDBOX_SHELL=${if pkgs.stdenv.isLinux then pkgs.busybox-sandbox-shell + "/bin/busybox" else "/bin/sh"}
5554
export SNIX_BENCH_NIX_PATH=nixpkgs=${pkgs.path}
5655
'';
5756
}

snix/utils.nix

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@ in
8080
};
8181
PROTO_ROOT = depot.snix.build.protos.protos;
8282
nativeBuildInputs = [ pkgs.protobuf ];
83-
SNIX_BUILD_SANDBOX_SHELL = if pkgs.stdenv.isLinux then pkgs.pkgsStatic.busybox + "/bin/sh" else "/bin/sh";
8483
};
8584

8685
snix-castore = prev: {

0 commit comments

Comments
 (0)