Skip to content

Commit 6df2401

Browse files
committed
cstorage: Add userns helper for rootless containers-storage access
When running as an unprivileged user, files in containers-storage may have restrictive permissions (e.g., /etc/shadow with mode 0600 owned by remapped UIDs). This commit adds a user namespace helper that enables reading these files by spawning a helper process via `podman unshare`. The helper runs as UID 0 inside the user namespace and can read any file. It communicates with the parent process via Unix socket using JSON-RPC 2.0 with SCM_RIGHTS file descriptor passing for zero-copy streaming. Key components: - userns.rs: User namespace detection (can_bypass_file_permissions(), should_enter_userns(), subuid/subgid parsing) - userns_helper.rs: Helper process with JSON-RPC protocol, StorageProxy client, and ProxiedLayerStream for streaming layer content The cstor.rs import code now automatically uses the proxy when running as an unprivileged user, falling back to direct access when running as root or with CAP_DAC_OVERRIDE. Ported from cgwalters/cstor-rs. Assisted-by: OpenCode (Opus 4.5) Signed-off-by: Colin Walters <walters@verbum.org>
1 parent b0b5b01 commit 6df2401

10 files changed

Lines changed: 2293 additions & 62 deletions

File tree

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ composefs-oci = { version = "0.3.0", path = "crates/composefs-oci", default-feat
2121
composefs-boot = { version = "0.3.0", path = "crates/composefs-boot", default-features = false }
2222
composefs-http = { version = "0.3.0", path = "crates/composefs-http", default-features = false }
2323

24+
# JSON-RPC with FD passing for userns helper
25+
jsonrpc-fdpass = { git = "https://github.com/cgwalters/jsonrpc-fdpass", rev = "b30fa1d" }
26+
2427
[profile.dev.package.sha2]
2528
# this is *really* slow otherwise
2629
opt-level = 3

crates/cfsctl/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ version.workspace = true
1414
default = ['pre-6.15', 'oci', 'containers-storage']
1515
http = ['composefs-http']
1616
oci = ['composefs-oci']
17-
containers-storage = ['composefs-oci/containers-storage']
17+
containers-storage = ['composefs-oci/containers-storage', 'cstorage']
1818
rhel9 = ['composefs/rhel9']
1919
'pre-6.15' = ['composefs/pre-6.15']
2020

@@ -25,6 +25,7 @@ composefs = { workspace = true }
2525
composefs-boot = { workspace = true }
2626
composefs-oci = { workspace = true, optional = true }
2727
composefs-http = { workspace = true, optional = true }
28+
cstorage = { path = "../cstorage", features = ["userns-helper"], optional = true }
2829
env_logger = { version = "0.11.0", default-features = false }
2930
hex = { version = "0.4.0", default-features = false }
3031
indicatif = { version = "0.17.0", default-features = false }

crates/cfsctl/src/main.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,8 +211,20 @@ where
211211
Ok(repo)
212212
}
213213

214-
#[tokio::main]
215-
async fn main() -> Result<()> {
214+
fn main() -> Result<()> {
215+
// If we were spawned as a userns helper process, handle that and exit.
216+
// This MUST be called before the tokio runtime is created.
217+
#[cfg(feature = "containers-storage")]
218+
cstorage::init_if_helper();
219+
220+
// Now we can create the tokio runtime for the main application
221+
tokio::runtime::Builder::new_multi_thread()
222+
.enable_all()
223+
.build()?
224+
.block_on(async_main())
225+
}
226+
227+
async fn async_main() -> Result<()> {
216228
env_logger::init();
217229

218230
let args = App::parse();

crates/composefs-oci/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ version.workspace = true
1212

1313
[features]
1414
default = ["containers-storage"]
15-
containers-storage = ["dep:cstorage"]
15+
containers-storage = ["dep:cstorage", "cstorage?/userns-helper"]
1616

1717
[dependencies]
1818
anyhow = { version = "1.0.87", default-features = false }

0 commit comments

Comments
 (0)