mirror of
https://github.com/NVIDIA/OpenShell.git
synced 2026-10-31 07:44:57 +08:00
test(conformance): use Alpine workload image
Signed-off-by: Evan Lezar <elezar@nvidia.com>
This commit is contained in:
@@ -141,8 +141,6 @@ async fn create_running_sandbox(
|
||||
"create",
|
||||
"--name",
|
||||
sandbox_name,
|
||||
"--from",
|
||||
"base",
|
||||
"--detach",
|
||||
"--no-tty",
|
||||
"--",
|
||||
|
||||
@@ -55,15 +55,7 @@ async fn run_smoke_inner(runner: &mut OpenShellRunner) -> Result<(), String> {
|
||||
.step("create")
|
||||
.description("sandbox creation succeeds")
|
||||
.with_timeout(CREATE_TIMEOUT)
|
||||
.run(&[
|
||||
"sandbox",
|
||||
"create",
|
||||
"--name",
|
||||
&sandbox_name,
|
||||
"--from",
|
||||
"base",
|
||||
"--detach",
|
||||
])
|
||||
.run(&["sandbox", "create", "--name", &sandbox_name, "--detach"])
|
||||
.await
|
||||
.map_err(|error| error.to_string())?;
|
||||
create.require_success()?;
|
||||
|
||||
@@ -988,6 +988,7 @@ pub fn restrictive_default_policy() -> SandboxPolicy {
|
||||
filesystem: Some(FilesystemPolicy {
|
||||
include_workdir: true,
|
||||
read_only: vec![
|
||||
"/bin".into(),
|
||||
"/usr".into(),
|
||||
"/lib".into(),
|
||||
"/proc".into(),
|
||||
@@ -2128,6 +2129,10 @@ network_policies:
|
||||
let policy = restrictive_default_policy();
|
||||
let fs = policy.filesystem.expect("must have filesystem policy");
|
||||
assert!(fs.include_workdir);
|
||||
assert!(
|
||||
fs.read_only.iter().any(|p| p == "/bin"),
|
||||
"read_only should contain /bin"
|
||||
);
|
||||
assert!(
|
||||
fs.read_only.iter().any(|p| p == "/usr"),
|
||||
"read_only should contain /usr"
|
||||
|
||||
@@ -69,12 +69,13 @@ impl LocalBoundaryExec {
|
||||
let (session_user, session_home) =
|
||||
crate::process::session_user_and_home(&self.policy, effective_workdir);
|
||||
let path = std::env::var("PATH").unwrap_or_else(|_| "/usr/local/bin:/usr/bin:/bin".into());
|
||||
let shell = openshell_core::shell::detect_login_shell();
|
||||
command
|
||||
.env_clear()
|
||||
.env(openshell_core::sandbox_env::SANDBOX, "1")
|
||||
.env("HOME", session_home)
|
||||
.env("USER", session_user)
|
||||
.env("SHELL", "/bin/bash")
|
||||
.env("SHELL", shell)
|
||||
.env("PATH", path)
|
||||
.env("TERM", if spec.pty { "xterm-256color" } else { "dumb" });
|
||||
for (key, value) in &self.user_environment {
|
||||
|
||||
@@ -302,7 +302,6 @@ fn prepare_with_path_open_mode(
|
||||
|
||||
let result: Result<PreparedRuleset> = (|| {
|
||||
let access_all = AccessFs::from_all(abi);
|
||||
let access_read = AccessFs::from_read(abi);
|
||||
|
||||
let mut ruleset = Ruleset::default();
|
||||
ruleset = ruleset
|
||||
@@ -315,7 +314,8 @@ fn prepare_with_path_open_mode(
|
||||
|
||||
for path in &read_only {
|
||||
if let Some(path_fd) = try_open_path(path, compatibility, path_open_mode)? {
|
||||
let allowed_access = access_for_path_fd(&path_fd, access_read, abi)?;
|
||||
let allowed_access =
|
||||
access_for_path_fd(&path_fd, read_only_access(path, abi), abi)?;
|
||||
debug!(path = %path.display(), "Landlock allow read-only");
|
||||
ruleset = ruleset
|
||||
.add_rule(PathBeneath::new(path_fd, allowed_access))
|
||||
@@ -434,6 +434,17 @@ pub fn enforce(prepared: PreparedRuleset) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// The baseline permits execution only from Alpine's `/bin`. Other read-only
|
||||
/// paths, including `/proc` and `/etc`, remain non-executable.
|
||||
fn read_only_access(path: &Path, abi: ABI) -> BitFlags<AccessFs> {
|
||||
let access = AccessFs::from_read(abi);
|
||||
if path == Path::new("/bin") {
|
||||
access | AccessFs::Execute
|
||||
} else {
|
||||
access
|
||||
}
|
||||
}
|
||||
|
||||
/// Tailor a rule's access mask to the inode referenced by its already-open FD.
|
||||
///
|
||||
/// Landlock directory-only rights such as `ReadDir` are invalid for regular
|
||||
@@ -727,6 +738,12 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn only_bin_read_only_access_includes_execute() {
|
||||
assert!(read_only_access(Path::new("/bin"), ABI::V3).contains(AccessFs::Execute));
|
||||
assert!(!read_only_access(Path::new("/usr"), ABI::V3).contains(AccessFs::Execute));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn access_for_path_fd_limits_regular_file_access() {
|
||||
let file = tempfile::NamedTempFile::new().unwrap();
|
||||
|
||||
@@ -892,21 +892,11 @@ impl SshHandler {
|
||||
let no_login_shell = state.no_login_shell;
|
||||
let pty = state.pty_request.take();
|
||||
let pty_requested = pty.is_some();
|
||||
let (program, args) = command.map_or_else(
|
||||
|| {
|
||||
if pty_requested {
|
||||
("/bin/bash".to_string(), vec!["-i".to_string()])
|
||||
} else {
|
||||
("/bin/bash".to_string(), vec![])
|
||||
}
|
||||
},
|
||||
|command| {
|
||||
(
|
||||
"/bin/bash".to_string(),
|
||||
vec![login_shell_flag(no_login_shell).to_string(), command],
|
||||
)
|
||||
},
|
||||
);
|
||||
// The supervisor shares the workload filesystem, so select a shell
|
||||
// that actually exists in the image. Alpine and other minimal images
|
||||
// provide `/bin/sh` but not `/bin/bash`.
|
||||
let shell = openshell_core::shell::detect_login_shell();
|
||||
let (program, args) = shell_command(shell, command, pty_requested, no_login_shell);
|
||||
let env = pty
|
||||
.as_ref()
|
||||
.map(|request| vec![("TERM".to_string(), request.term.clone())])
|
||||
@@ -1084,6 +1074,25 @@ const fn login_shell_flag(no_login_shell: bool) -> &'static str {
|
||||
if no_login_shell { "-c" } else { "-lc" }
|
||||
}
|
||||
|
||||
fn shell_command(
|
||||
shell: String,
|
||||
command: Option<String>,
|
||||
pty_requested: bool,
|
||||
no_login_shell: bool,
|
||||
) -> (String, Vec<String>) {
|
||||
let args = command.map_or_else(
|
||||
|| {
|
||||
if pty_requested {
|
||||
vec!["-i".to_string()]
|
||||
} else {
|
||||
Vec::new()
|
||||
}
|
||||
},
|
||||
|command| vec![login_shell_flag(no_login_shell).to_string(), command],
|
||||
);
|
||||
(shell, args)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Clone)]
|
||||
struct PtyRequest {
|
||||
@@ -1400,6 +1409,27 @@ mod tests {
|
||||
main_session.end_terminal_attachment();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shell_command_uses_the_resolved_image_shell() {
|
||||
let (program, args) = shell_command(
|
||||
"/bin/sh".to_string(),
|
||||
Some("printf ready".to_string()),
|
||||
false,
|
||||
false,
|
||||
);
|
||||
|
||||
assert_eq!(program, "/bin/sh");
|
||||
assert_eq!(args, ["-lc", "printf ready"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interactive_shell_uses_the_resolved_image_shell() {
|
||||
let (program, args) = shell_command("/bin/sh".to_string(), None, true, false);
|
||||
|
||||
assert_eq!(program, "/bin/sh");
|
||||
assert_eq!(args, ["-i"]);
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn file_mode(path: &Path) -> u32 {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
{% block driver %}
|
||||
[openshell.drivers.docker]
|
||||
socket_path = "{{ openshell_runtime_socket }}"
|
||||
default_image = "ghcr.io/nvidia/openshell-community/sandboxes/base:latest"
|
||||
image_pull_policy = "if_not_present"
|
||||
sandbox_label = "tmachine"
|
||||
grpc_endpoint = "http://127.0.0.1:17670"
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
{% block driver %}
|
||||
[openshell.drivers.podman]
|
||||
socket_path = "{{ openshell_runtime_socket }}"
|
||||
default_image = "ghcr.io/nvidia/openshell-community/sandboxes/base:latest"
|
||||
image_pull_policy = "if_not_present"
|
||||
sandbox_runtime_image = "docker.io/openshell/sandbox:tmachine"
|
||||
supervisor_image = "docker.io/openshell/supervisor:tmachine"
|
||||
|
||||
Reference in New Issue
Block a user