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
54 changes: 20 additions & 34 deletions lib/hyper/node/fire_vmm/jailer.ex
Original file line number Diff line number Diff line change
@@ -1,27 +1,26 @@
defmodule Hyper.Node.FireVMM.Jailer do
@moduledoc """
Builds the firecracker
[jailer](https://github.com/firecracker-microvm/firecracker/blob/main/docs/jailer.md)
command for one VM.
Builds the `hyper-suidhelper jailer` command for one VM.

The jailer sets up the chroot, namespaces, cgroup (via `Hyper.Vm.Instance`
flags) and drops privileges, then exec's firecracker. We run the jailer (not
firecracker directly) under `MuonTrap.Daemon`; MuonTrap only supervises the OS
process, the jailer owns isolation.
The BEAM does not invoke the jailer directly. Instead it calls the setuid helper
with the `jailer` subcommand; the helper reads the firecracker binary path, chroot
base, parent cgroup, and cgroup version from its trusted `/etc/hyper/config.toml`,
re-acquires root, and `execve`s the jailer (same pid, so `MuonTrap.Daemon` keeps
supervising it).

This means the BEAM passes only untrusted-origin values: `--id`, `--uid`, `--gid`,
repeated `--cgroup KEY=VALUE`, and `--api-sock`. The helper derives and validates
everything else; it also inserts the `--` separator between its own flags and
firecracker's flags.

Because firecracker is chrooted to `<chroot_base>/<exec>/<id>/root`, the API
socket it opens at `/api.socket` lives at `host_socket` on the host - that's the
socket it opens at `/api.socket` lives at `host_socket` on the host that's the
path the controller connects to.

Host config: paths are derived from `config :hyper, work_dir: ...`. The
firecracker + jailer binaries are installed under `<work_dir>/redist/firecracker`
by `Hyper.Node.FireVMM.Provider`; the chroot base is `<work_dir>/jails`.
"""

use OpenTelemetryDecorator

alias Hyper.Node.FireVMM
alias Hyper.Node.FireVMM.Provider
alias Hyper.Vm.Instance

# firecracker's API socket path *inside* the chroot.
Expand All @@ -36,6 +35,8 @@ defmodule Hyper.Node.FireVMM.Jailer do
first failure.
"""

alias Hyper.Cfg.{Dirs, Jails}

@doc "Run every pre-requisite check, halting at the first failure."
@spec run() :: :ok | {:error, term()}
def run do
Expand All @@ -61,7 +62,7 @@ defmodule Hyper.Node.FireVMM.Jailer do
end

defp parent_cgroup_present do
if Sys.Linux.Cgroup.V2.named_exists?(Hyper.Cfg.Jails.cgroup()),
if Sys.Linux.Cgroup.V2.named_exists?(Jails.cgroup()),
do: :ok,
else: {:error, :missing_parent_cgroup}
end
Expand All @@ -77,7 +78,7 @@ defmodule Hyper.Node.FireVMM.Jailer do
end

defp chroot_writable do
case Sys.Posix.ensure_writable_dir(Hyper.Cfg.Dirs.chroot_base()) do
case Sys.Posix.ensure_writable_dir(Dirs.chroot_base()) do
{:ok} -> :ok
{:error, reason} -> {:error, {:chroot_base_unavailable, reason}}
end
Expand All @@ -92,26 +93,11 @@ defmodule Hyper.Node.FireVMM.Jailer do
@spec command(FireVMM.Opts.t()) :: t()
def command(opts) do
args =
[
"--id",
opts.vm_id,
"--exec-file",
Provider.firecracker_bin(),
"--uid",
to_string(opts.uid),
"--gid",
to_string(opts.gid),
"--chroot-base-dir",
Hyper.Cfg.Dirs.chroot_base(),
"--cgroup-version",
"2",
"--parent-cgroup",
Hyper.Cfg.Jails.cgroup()
] ++
["jailer", "--id", opts.vm_id, "--uid", to_string(opts.uid), "--gid", to_string(opts.gid)] ++
cgroup_flags(opts.type) ++
["--", "--api-sock", "/" <> @jail_socket]
["--api-sock", "/" <> @jail_socket]

%{binary: Provider.jailer_bin(), args: args, host_socket: host_socket(opts.vm_id)}
%{binary: Hyper.Cfg.Tools.suidhelper(), args: args, host_socket: host_socket(opts.vm_id)}
end

# Find the appropriate jailer cgroup flags for the given instance type.
Expand Down Expand Up @@ -180,5 +166,5 @@ defmodule Hyper.Node.FireVMM.Jailer do
])
end

defp exec_name, do: Path.basename(Provider.firecracker_bin())
defp exec_name, do: Path.basename(Hyper.Cfg.Tools.firecracker())
end
14 changes: 13 additions & 1 deletion native/suidhelper/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ path = "tests/util/safe_bin.rs"
name = "tools_dmsetup_parsers"
path = "tests/tools/dmsetup_parsers.rs"

[[test]]
name = "tools_jailer"
path = "tests/tools/jailer.rs"

[[test]]
name = "util_confinement"
path = "tests/util/confinement.rs"
Expand All @@ -72,14 +76,22 @@ path = "tests/e2e/config.rs"
name = "e2e_argv"
path = "tests/e2e/argv.rs"

[[test]]
name = "e2e_jailer"
path = "tests/e2e/jailer.rs"

[[test]]
name = "e2e_chroot_jail"
path = "tests/e2e/chroot_jail.rs"

[[test]]
name = "config_uid_gid_range"
path = "tests/config_uid_gid_range.rs"

[dependencies]
clap = { version = "4", features = ["derive"] }
hyper-suidhelper-meta = { path = "meta" }
nix = { version = "0.29", features = ["user", "fs", "dir"] }
nix = { version = "0.29", features = ["user", "fs", "dir", "process"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
thiserror = "1"
Expand Down
Loading
Loading