Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
5e96e31
feat(config): add optional port range for sandbox configuration
Leay15 Jan 18, 2026
d0d4f1b
feat(env): add environment variables for sandbox port range
Leay15 Jan 18, 2026
f77a66b
feat(handler): add missing line for sandbox configuration mapping
Leay15 Jan 18, 2026
3b6c91f
feat(port): initialize port manager with configurable port range
Leay15 Jan 18, 2026
68ad8ee
feat(port): add optional port range for sandbox port allocation
Leay15 Jan 18, 2026
37335bd
feat(build): update Makefile to use bash for building libkrun
Leay15 Jan 18, 2026
f96282a
feat(build): switch build script to use bash for improved compatibility
Leay15 Jan 18, 2026
d4aedaa
feat(dependencies): update multiple crate versions in Cargo.lock
Leay15 Jan 18, 2026
93cb923
feat(dependencies): update multiple crate versions in Cargo.lock
Leay15 Jan 18, 2026
8a9d780
feat(cpu): allow fractional CPU values for sandbox and MicroVm config…
Leay15 Jan 23, 2026
ba8a42d
feat(cpu): allow decimal CPU values in sandbox configuration
Leay15 Feb 1, 2026
bf62a1c
feat(env): add environment variables for Docker registry configuration
Leay15 Feb 1, 2026
3d8a672
feat(error): add Serde JSON error variant to error handling
Leay15 Feb 1, 2026
fef4f69
feat(auth): implement login and logout functionality for Docker regis…
Leay15 Feb 1, 2026
138176f
feat(auth): implement registry authentication resolution for Docker i…
Leay15 Feb 1, 2026
c8c7fec
feat(docker): add modules for Docker configuration and registry authe…
Leay15 Feb 1, 2026
6a7cfba
feat(auth): enhance login and logout commands with registry and crede…
Leay15 Feb 1, 2026
9b905bc
feat(docker): update registry initialization to support anonymous aut…
Leay15 Feb 1, 2026
1e73323
feat(auth): extend login and logout commands with registry options an…
Leay15 Feb 1, 2026
0daea8c
feat(docker): integrate registry authentication into OciClient config…
Leay15 Feb 1, 2026
b8c4d4b
feat(docker): update CLI documentation for private image pulling and …
Leay15 Feb 1, 2026
3c339d5
feat(tests): add mutex locking to environment variable tests for thre…
Leay15 Feb 1, 2026
dca93b1
feat(docker): enhance registry authentication resolution with detaile…
Leay15 Feb 1, 2026
9688bba
feat(docker): implement Docker config reader for registry authentication
Leay15 Feb 1, 2026
e6c59bb
feat(docker): add registry authentication persistence helpers for sto…
Leay15 Feb 1, 2026
70c5785
feat(docker): add integration tests for registry authentication with …
Leay15 Feb 1, 2026
113c68a
feat(docker): add usage examples for Docker config and registry auth …
Leay15 Feb 1, 2026
3c8b24d
feat(docker): update logging for saved registry credentials to indica…
Leay15 Feb 1, 2026
24e8c3e
feat(docker): add TODO for inheriting auth sources in msb server runtime
Leay15 Feb 1, 2026
dd792f2
feat(docker): remove TODO for supporting credsStore and credHelpers i…
Leay15 Feb 1, 2026
337cb54
feat(docker): clean up code formatting and improve readability in con…
Leay15 Feb 1, 2026
997cf2e
feat(docker): add note about credential storage and validation for ms…
Leay15 Feb 1, 2026
59d2e15
feat(docker): reorder imports for improved organization in registry a…
Leay15 Feb 1, 2026
b41ef81
feat(docker): reorganize imports for better clarity in image and regi…
Leay15 Feb 1, 2026
d2541fd
feat(docker): add base64, serde, and serde_json dependencies to Cargo…
Leay15 Feb 1, 2026
c2095da
feat(docker): add configurable parameters for CPU, memory, and timeou…
Leay15 Feb 1, 2026
11e6352
feat(setup): update version to 0.1.17 and configure setuptools packag…
Leay15 Feb 1, 2026
7fa76c9
feat(sdk): add logging for fractional CPU handling in BaseSandbox
Leay15 Feb 1, 2026
ec963bd
feat(sdk): update warning message for fractional CPU support on non-L…
Leay15 Feb 1, 2026
48f1461
feat(handler): adjust CPU configuration for non-Linux systems to defa…
Leay15 Feb 1, 2026
6be112d
feat(handler): refactor CPU configuration handling for non-Linux systems
Leay15 Feb 1, 2026
876ba6a
feat(handler): add SandboxConfig to payload imports in handler.rs
Leay15 Feb 1, 2026
27c490b
feat(config): change CPU configuration type from u32 to f32 for impro…
Leay15 Feb 2, 2026
7a4ce6c
feat(builder): support fractional vCPU values in MicroVm configuration
Leay15 Feb 2, 2026
9250cd9
feat(docs): update CPU parameter examples to use fractional values
Leay15 Feb 2, 2026
006e35d
feat(cgroup): add cgroups v2 support for CPU quota management
Leay15 Feb 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 77 additions & 77 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ uninstall:
fi

build_libkrun:
./scripts/build_libkrun.sh --no-clean --build-dir "$(BUILD_DIR)"
bash ./scripts/build_libkrun.sh --no-clean --build-dir "$(BUILD_DIR)"

# Catch-all target to allow example names and arguments
%:
Expand Down
69 changes: 69 additions & 0 deletions docs/references/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,15 @@ msb pull [--image] [--image-group] <name> [options]
**Examples:**

```bash
# Pull a private image using env credentials (token)
export MSB_REGISTRY_TOKEN=token123
msb pull registry.example.com/org/app:1.0

# Pull a private image using env credentials (username/password)
export MSB_REGISTRY_USERNAME=user
export MSB_REGISTRY_PASSWORD=pass
msb pull registry.example.com/org/app:1.0

# Pull an image
msb pull --image python:3.11

Expand Down Expand Up @@ -686,6 +695,66 @@ msb push myapp:latest

===

==- `msb login`
Set registry credentials (persisted in microsandbox home).

```bash
msb login [registry] [--username <user>] [--password-stdin] [--token <token>]
```

| Option | Description |
| ----------------- | ---------------------------------- |
| `--username` | Registry username |
| `--password-stdin`| Read password from stdin |
| `--token` | Registry access token (bearer) |

**Examples:**

```bash
# Provide a token
msb login ghcr.io --token token123

# Provide username and password via stdin
echo "pass" | msb login docker.io --username user --password-stdin

# Use env fallback if CLI is invalid
export MSB_REGISTRY_TOKEN=token123
msb login ghcr.io --username user --password-stdin
```

!!!note
`msb login` stores credentials locally but does not validate them against the registry.
!!!

!!!warning Security
Credentials are stored in `~/.microsandbox/registry_auth.json`. Restrict file permissions and avoid sharing it.
!!!

===

==- `msb logout`
Remove stored registry credentials.

```bash
msb logout [registry] [--all]
```

| Option | Description |
| --------- | ---------------------------------------- |
| `--all` | Remove all stored registry credentials |

**Examples:**

```bash
# Remove credentials for a registry
msb logout ghcr.io

# Remove all stored credentials
msb logout --all
```

===

---

### Maintenance
Expand Down
165 changes: 156 additions & 9 deletions microsandbox-cli/bin/msb/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,12 @@ use microsandbox_core::{
oci::Reference,
};
use microsandbox_server::MicrosandboxServerResult;
use microsandbox_utils::{NAMESPACES_SUBDIR, env};
use microsandbox_utils::{
NAMESPACES_SUBDIR, StoredRegistryCredentials, clear_registry_credentials, env,
remove_registry_credentials, store_registry_credentials,
};
use std::{collections::HashMap, path::PathBuf};
use tokio::io::{self, AsyncReadExt};
use typed_path::Utf8UnixPathBuf;

//--------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -54,7 +58,7 @@ pub async fn add_subcommand(
names: Vec<String>,
image: String,
memory: Option<u32>,
cpus: Option<u32>,
cpus: Option<f32>,
volumes: Vec<String>,
ports: Vec<String>,
envs: Vec<String>,
Expand Down Expand Up @@ -219,7 +223,7 @@ pub async fn script_run_subcommand(
#[allow(clippy::too_many_arguments)]
pub async fn exe_subcommand(
name: String,
cpus: Option<u8>,
cpus: Option<f32>,
memory: Option<u32>,
volumes: Vec<String>,
ports: Vec<String>,
Expand Down Expand Up @@ -472,7 +476,7 @@ pub async fn self_subcommand(action: SelfAction) -> MicrosandboxCliResult<()> {
pub async fn install_subcommand(
name: String,
alias: Option<String>,
cpus: Option<u8>,
cpus: Option<f32>,
memory: Option<u32>,
volumes: Vec<String>,
ports: Vec<String>,
Expand Down Expand Up @@ -671,11 +675,60 @@ pub async fn server_status_subcommand(
Ok(())
}

pub async fn login_subcommand() -> MicrosandboxCliResult<()> {
println!(
"{} login functionality is not yet implemented",
"error:".error()
);
pub async fn login_subcommand(
registry: Option<String>,
username: Option<String>,
password_stdin: bool,
token: Option<String>,
) -> MicrosandboxCliResult<()> {
let registry = resolve_registry_host(registry);
let creds = resolve_login_credentials(username, password_stdin, token).await?;

match creds {
LoginCredentials::Basic { username, password } => {
store_registry_credentials(
&registry,
StoredRegistryCredentials::Basic { username, password },
)
.map_err(|err| MicrosandboxCliError::ConfigError(err.to_string()))?;
println!(
"info: credentials saved for registry {} (not validated)",
registry
);
}
LoginCredentials::Token { token } => {
store_registry_credentials(&registry, StoredRegistryCredentials::Token { token })
.map_err(|err| MicrosandboxCliError::ConfigError(err.to_string()))?;
println!(
"info: token saved for registry {} (not validated)",
registry
);
}
}

Ok(())
}

pub async fn logout_subcommand(registry: Option<String>, all: bool) -> MicrosandboxCliResult<()> {
if all {
clear_registry_credentials()
.map_err(|err| MicrosandboxCliError::ConfigError(err.to_string()))?;
println!("info: cleared all stored registry credentials");
return Ok(());
}

let registry = resolve_registry_host(registry);
let removed = remove_registry_credentials(&registry)
.map_err(|err| MicrosandboxCliError::ConfigError(err.to_string()))?;
if removed {
println!("info: removed stored credentials for registry {}", registry);
} else {
println!(
"info: no stored credentials found for registry {}",
registry
);
}

Ok(())
}

Expand Down Expand Up @@ -741,6 +794,100 @@ fn parse_name_and_script(name_and_script: &str) -> (&str, Option<&str>) {
(name, script)
}

//--------------------------------------------------------------------------------------------------
// Functions: Login Helpers
//--------------------------------------------------------------------------------------------------

enum LoginCredentials {
Basic { username: String, password: String },
Token { token: String },
}

fn resolve_registry_host(registry: Option<String>) -> String {
registry
.or_else(env::get_registry_host)
.unwrap_or_else(env::get_oci_registry)
}

async fn resolve_login_credentials(
username: Option<String>,
password_stdin: bool,
token: Option<String>,
) -> MicrosandboxCliResult<LoginCredentials> {
let cli_password = if password_stdin {
Some(read_password_from_stdin().await?)
} else {
None
};

let cli_provided = token.is_some() || username.is_some() || password_stdin;
if cli_provided {
let cli_result = if token.is_some() && (username.is_some() || cli_password.is_some()) {
Err(MicrosandboxCliError::InvalidArgument(
"token cannot be combined with username/password".to_string(),
))
} else if let Some(token) = token {
Ok(LoginCredentials::Token { token })
} else {
match (username, cli_password) {
(Some(username), Some(password)) => {
Ok(LoginCredentials::Basic { username, password })
}
(None, None) => Err(MicrosandboxCliError::InvalidArgument(
"no credentials provided; use flags or environment variables".to_string(),
)),
_ => Err(MicrosandboxCliError::InvalidArgument(
"both username and password are required".to_string(),
)),
}
};

if let Ok(creds) = cli_result {
return Ok(creds);
}

// CLI was provided but invalid; attempt env fallback.
tracing::debug!("login: CLI credentials invalid, falling back to environment variables");
}

let env_token = env::get_registry_token();
let env_username = env::get_registry_username();
let env_password = env::get_registry_password();

if env_token.is_some() && (env_username.is_some() || env_password.is_some()) {
return Err(MicrosandboxCliError::InvalidArgument(
"token cannot be combined with username/password".to_string(),
));
}

if let Some(token) = env_token {
return Ok(LoginCredentials::Token { token });
}

match (env_username, env_password) {
(Some(username), Some(password)) => Ok(LoginCredentials::Basic { username, password }),
(None, None) => Err(MicrosandboxCliError::InvalidArgument(
"no credentials provided; use flags or environment variables".to_string(),
)),
_ => Err(MicrosandboxCliError::InvalidArgument(
"both username and password are required".to_string(),
)),
}
}

async fn read_password_from_stdin() -> MicrosandboxCliResult<String> {
let mut input = String::new();
let mut stdin = io::stdin();
stdin.read_to_string(&mut input).await?;
let password = input.trim_end_matches(&['\n', '\r'][..]).to_string();
if password.is_empty() {
return Err(MicrosandboxCliError::InvalidArgument(
"password provided via stdin is empty".to_string(),
));
}
Ok(password)
}

/// Parse a file path into project path and config file name.
///
/// If the file path is a directory, it is treated as the project path.
Expand Down
12 changes: 10 additions & 2 deletions microsandbox-cli/bin/msb/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,8 +259,16 @@ async fn main() -> MicrosandboxCliResult<()> {
handlers::server_ssh_subcommand(namespace, sandbox, name).await?;
}
},
Some(MicrosandboxSubcommand::Login) => {
handlers::login_subcommand().await?;
Some(MicrosandboxSubcommand::Login {
registry,
username,
password_stdin,
token,
}) => {
handlers::login_subcommand(registry, username, password_stdin, token).await?;
}
Some(MicrosandboxSubcommand::Logout { registry, all }) => {
handlers::logout_subcommand(registry, all).await?;
}
Some(MicrosandboxSubcommand::Push { image, name }) => {
handlers::push_subcommand(image, name).await?;
Expand Down
5 changes: 3 additions & 2 deletions microsandbox-cli/bin/msbrun.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
//! --log-level=3 \
//! --native-rootfs=/path/to/rootfs \
//! --overlayfs-rootfs=/path/to/rootfs \
//! --num-vcpus=2 \
//! --num-vcpus=0.5 \
//! --memory-mib=1024 \
//! --workdir-path=/app \
//! --exec-path=/usr/bin/python3 \
Expand All @@ -41,7 +41,7 @@
//! --log-level=3 \
//! --native-rootfs=/path/to/rootfs \
//! --overlayfs-rootfs=/path/to/rootfs \
//! --num-vcpus=2 \
//! --num-vcpus=0.5 \
//! --memory-mib=1024 \
//! --workdir-path=/app \
//! --exec-path=/usr/bin/python3 \
Expand Down Expand Up @@ -256,6 +256,7 @@ async fn main() -> Result<()> {
log_dir.clone(),
rootfs.clone(),
forward_output,
num_vcpus,
)
.await?;

Expand Down
15 changes: 12 additions & 3 deletions microsandbox-cli/bin/msbserver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,20 @@ pub async fn main() -> MicrosandboxCliResult<()> {
args.dev_mode,
)?);

// Get namespace directory from config
// Get namespace directory and port range from config
let namespace_dir = config.get_namespace_dir().clone();
let port_range = (
config.get_port_range_min().as_ref().copied(),
config.get_port_range_max().as_ref().copied(),
);

// Initialize the port manager
let port_manager = PortManager::new(namespace_dir).await.map_err(|e| {
// Initialize the port manager with the configured port range
let port_manager = if let (Some(min), Some(max)) = port_range {
PortManager::new_with_range(namespace_dir, Some((min, max))).await
} else {
PortManager::new(namespace_dir).await
}
.map_err(|e| {
eprintln!("Error initializing port manager: {}", e);
e
})?;
Expand Down
Loading