Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 7 additions & 0 deletions src/Docker.jl
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,13 @@ function commit_previous_run(exe::DockerExecutor, image_name::String)
return image_name
end

function build_executor_command(exe::DockerExecutor, config::SandboxConfig, user_cmd::Base.CmdRedirect)
if user_cmd.stream_no > 2
error("DockerExecutor does not support redirection of streams other than stdin, stdout, and stderr")
end
return Base.CmdRedirect(build_executor_command(exe, confi, user_cmd.cmd), user_cmd.handle, user_cmd.stream_no, user_cmd.readable)
end

function build_executor_command(exe::DockerExecutor, config::SandboxConfig, user_cmd::Cmd)
# Build the docker image that corresponds to this rootfs
image_name = build_docker_image(config.mounts, config.uid, config.gid; verbose=config.verbose)
Expand Down
62 changes: 31 additions & 31 deletions src/Sandbox.jl
Original file line number Diff line number Diff line change
Expand Up @@ -104,40 +104,40 @@ function warn_priviledged(::PrivilegedUserNamespacesExecutor)
end
warn_priviledged(::SandboxExecutor) = nothing

for f in (:run, :success)
@eval begin
function $f(exe::SandboxExecutor, config::SandboxConfig, user_cmd::Cmd)
# This is for `stdin` because when precompiling, it is closed,
# which causes `run()` to throw an error.
open_or_devnull(io) = isopen(io) ? io : devnull

# Because Julia 1.8+ closes IOBuffers like `stdout` and `stderr`, we create temporary
# IOBuffers that get copied over to the persistent `stdin`/`stdout` after the run is complete.
temp_stdout = isa(config.stdout, IOBuffer) ? IOBuffer() : config.stdout
temp_stderr = isa(config.stderr, IOBuffer) ? IOBuffer() : config.stderr
cmd = pipeline(
build_executor_command(exe, config, user_cmd);
stdin=open_or_devnull(config.stdin),
stdout=temp_stdout,
stderr=temp_stderr,
)
if config.verbose
@info("Running sandboxed command", user_cmd.exec)
end
warn_priviledged(exe)
ret = $f(cmd)
function _run(run_or_success, exe::SandboxExecutor, config::SandboxConfig, user_cmd::Union{Cmd, Base.CmdRedirect})
# This is for `stdin` because when precompiling, it is closed,
# which causes `run()` to throw an error.
open_or_devnull(io) = isopen(io) ? io : devnull

# Because Julia 1.8+ closes IOBuffers like `stdout` and `stderr`, we create temporary
# IOBuffers that get copied over to the persistent `stdin`/`stdout` after the run is complete.
temp_stdout = isa(config.stdout, IOBuffer) ? IOBuffer() : config.stdout
temp_stderr = isa(config.stderr, IOBuffer) ? IOBuffer() : config.stderr
cmd = pipeline(
build_executor_command(exe, config, user_cmd);
stdin=open_or_devnull(config.stdin),
stdout=temp_stdout,
stderr=temp_stderr,
)
if config.verbose
base_cmd = user_cmd
while isa(base_cmd, Base.CmdRedirect); base_cmd = base_cmd.cmd; end
@info("Running sandboxed command", base_cmd.exec)
end
warn_priviledged(exe)
ret = run_or_success(cmd)

# If we were using temporary IOBuffers, write the result out to `config.std{out,err}`
if isa(temp_stdout, IOBuffer)
write(config.stdout, take!(temp_stdout))
end
if isa(temp_stderr, IOBuffer)
write(config.stderr, take!(temp_stderr))
end
return ret
end
# If we were using temporary IOBuffers, write the result out to `config.std{out,err}`
if isa(temp_stdout, IOBuffer)
write(config.stdout, take!(temp_stdout))
end
if isa(temp_stderr, IOBuffer)
write(config.stderr, take!(temp_stderr))
end
return ret
end
run(exe::SandboxExecutor, config::SandboxConfig, user_cmd::Union{Cmd, Base.CmdRedirect}) = _run(run, exe, config, user_cmd)
success(exe::SandboxExecutor, config::SandboxConfig, user_cmd::Union{Cmd, Base.CmdRedirect}) = _run(success, exe, config, user_cmd)

"""
with_executor(f::Function, ::Type{<:SandboxExecutor} = preferred_executor(); kwargs...)
Expand Down
4 changes: 4 additions & 0 deletions src/UserNamespaces.jl
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@ function check_overlayfs_loaded(;verbose::Bool = false)
return true
end

function build_executor_command(exe::UserNamespacesExecutor, config::SandboxConfig, user_cmd::Base.CmdRedirect)
return Base.CmdRedirect(build_executor_command(exe, config, user_cmd.cmd), user_cmd.handle, user_cmd.stream_no, user_cmd.readable)
end

function build_executor_command(exe::UserNamespacesExecutor, config::SandboxConfig, user_cmd::Cmd)
# While we would usually prefer to use the `executable_product()` function to get a
# `Cmd` object that has all of the `PATH` and `LD_LIBRARY_PATH` environment variables
Expand Down
9 changes: 9 additions & 0 deletions test/UserNamespaces.jl
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,15 @@ if executor_available(UnprivilegedUserNamespacesExecutor)
@test String(take!(stdout)) == "received SIGINT\nreceived SIGTERM\n"
end
end

@testset "Extra fds" begin
config = SandboxConfig(Dict("/" => Sandbox.debian_rootfs()))
with_executor(UnprivilegedUserNamespacesExecutor) do exe
buf = IOBuffer()
@test success(exe, config, Base.CmdRedirect(`/bin/sh -c "echo hello >&3"`, buf, 3))
@test String(take!(buf)) == "hello\n"
end
end
else
@error("Skipping Unprivileged tests, as it does not seem to be available")
end
Expand Down
Loading