Add --hostname to set the sandbox's hostname under -r/--run
Long-option only. Threaded through run_container() into build_bwrap_args(), which passes it as bwrap's own --hostname only when --unshare-uts is actually among the flags being given to bwrap (bwrap itself refuses --hostname without it) -- otherwise logs a warning and leaves the hostname alone, since a stock Android kernel in degraded mode may not support a UTS namespace at all. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Gv3s5jckJKzh6JkMoi2Akz
This commit is contained in:
+20
-3
@@ -179,7 +179,8 @@ std::vector<std::string> detect_bwrap_unshare_args() {
|
||||
std::vector<std::string> build_bwrap_args(const std::string& root,
|
||||
const std::vector<std::string>& command,
|
||||
const std::vector<ResolvedVolumeMount>& volumes,
|
||||
std::optional<ResolvedUser> user) {
|
||||
std::optional<ResolvedUser> user,
|
||||
const std::optional<std::string>& hostname) {
|
||||
// --new-session detaches from the controlling terminal, which breaks job
|
||||
// control for an interactive foreground shell ("can't access tty"). Re-enable
|
||||
// once background/daemonized runs are implemented, where that's the point.
|
||||
@@ -187,6 +188,7 @@ std::vector<std::string> build_bwrap_args(const std::string& root,
|
||||
|
||||
auto unshare_args = detect_bwrap_unshare_args();
|
||||
bool has_pid_ns = false;
|
||||
bool has_uts_ns = false;
|
||||
for (const auto& arg : unshare_args) {
|
||||
// Not requested yet: without any network setup (slirp4netns or similar),
|
||||
// unsharing it just leaves the sandbox with no network at all. Re-add once
|
||||
@@ -199,12 +201,26 @@ std::vector<std::string> build_bwrap_args(const std::string& root,
|
||||
args.push_back(arg);
|
||||
if (arg == "--unshare-pid") {
|
||||
has_pid_ns = true;
|
||||
} else if (arg == "--unshare-uts") {
|
||||
has_uts_ns = true;
|
||||
}
|
||||
}
|
||||
if (has_pid_ns) {
|
||||
args.push_back("--as-pid-1");
|
||||
}
|
||||
|
||||
if (hostname) {
|
||||
// bwrap itself refuses --hostname without --unshare-uts, so this must be
|
||||
// guarded the same way rather than always passed through -- the kernel may
|
||||
// not support a UTS namespace at all (degraded-mode Android kernels).
|
||||
if (has_uts_ns) {
|
||||
args.insert(args.end(), {"--hostname", *hostname});
|
||||
} else {
|
||||
spdlog::warn("--hostname requires an unshared UTS namespace, which the running "
|
||||
"kernel doesn't support here; ignoring --hostname");
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> filesystem_args = {
|
||||
"--bind",
|
||||
root,
|
||||
@@ -288,14 +304,15 @@ std::optional<std::vector<std::string>> wrap_for_root_namespace(const std::strin
|
||||
}
|
||||
|
||||
int run_bwrap(const std::string& root, const std::vector<std::string>& command, bool use_nsenter,
|
||||
const std::vector<ResolvedVolumeMount>& volumes, std::optional<ResolvedUser> user) {
|
||||
const std::vector<ResolvedVolumeMount>& volumes, std::optional<ResolvedUser> user,
|
||||
const std::optional<std::string>& hostname) {
|
||||
if (user && !find_priv_drop_helper()) {
|
||||
spdlog::error("could not find the {} helper next to this binary; --user/--group requires it",
|
||||
kPrivDropHelperName);
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto bwrap_args = build_bwrap_args(root, command, volumes, user);
|
||||
auto bwrap_args = build_bwrap_args(root, command, volumes, user, hostname);
|
||||
auto argv = wrap_for_root_namespace(root, use_nsenter, bwrap_args);
|
||||
if (!argv) {
|
||||
return -1;
|
||||
|
||||
+10
-4
@@ -55,11 +55,14 @@ struct ResolvedUser {
|
||||
// in volume_mount.h). If `user` is set, the command is wrapped so it drops to that
|
||||
// uid/gid before running -- see run_bwrap() for how, since bwrap's own --uid/--gid
|
||||
// require --unshare-user, which isn't requested when running as root (see
|
||||
// detect_bwrap_unshare_args()).
|
||||
// detect_bwrap_unshare_args()). If `hostname` is set and the kernel supports
|
||||
// --unshare-uts (bwrap refuses --hostname without it), passes it as bwrap's own
|
||||
// --hostname; otherwise logs a warning and leaves the sandbox's hostname alone.
|
||||
std::vector<std::string> build_bwrap_args(const std::string& root,
|
||||
const std::vector<std::string>& command,
|
||||
const std::vector<ResolvedVolumeMount>& volumes,
|
||||
std::optional<ResolvedUser> user);
|
||||
std::optional<ResolvedUser> user,
|
||||
const std::optional<std::string>& hostname);
|
||||
|
||||
// Runs bwrap against `root` (the merged mount path from mount_layer()) in the
|
||||
// foreground and waits for it to exit. If `use_nsenter` is true, first locates the
|
||||
@@ -71,6 +74,9 @@ std::vector<std::string> build_bwrap_args(const std::string& root,
|
||||
// (observed on kernels older than 4.18, per fuse-overlayfs's own release notes).
|
||||
// If `user` is set, this process's own binary is bind-mounted into the sandbox and
|
||||
// used to drop privileges to that uid/gid before running `command` -- see
|
||||
// build_bwrap_args(). Returns bwrap's exit code, or -1 on failure to launch.
|
||||
// build_bwrap_args(). `hostname`, if set, is forwarded to build_bwrap_args() --
|
||||
// see there for when it does/doesn't take effect. Returns bwrap's exit code, or -1
|
||||
// on failure to launch.
|
||||
int run_bwrap(const std::string& root, const std::vector<std::string>& command, bool use_nsenter,
|
||||
const std::vector<ResolvedVolumeMount>& volumes, std::optional<ResolvedUser> user);
|
||||
const std::vector<ResolvedVolumeMount>& volumes, std::optional<ResolvedUser> user,
|
||||
const std::optional<std::string>& hostname);
|
||||
|
||||
+19
-7
@@ -58,18 +58,19 @@ enum class Mode {
|
||||
kInspect
|
||||
};
|
||||
|
||||
// --log-level/--user/--group/--list-volumes/--delete-volume[-full] have no short
|
||||
// form (--log-level's was freed up so -l could become --list-images; -u is already
|
||||
// --umount; the rest have no natural free letter left, or don't need one), so they
|
||||
// need long-option vals outside the printable-char range short options use.
|
||||
// --log-level/--user/--group/--list-volumes/--delete-volume[-full]/--hostname have
|
||||
// no short form (--log-level's was freed up so -l could become --list-images; -u is
|
||||
// already --umount; the rest have no natural free letter left, or don't need one),
|
||||
// so they need long-option vals outside the printable-char range short options use.
|
||||
constexpr int kLogLevelOpt = 256;
|
||||
constexpr int kUserOpt = 257;
|
||||
constexpr int kGroupOpt = 258;
|
||||
constexpr int kListVolumesOpt = 259;
|
||||
constexpr int kDeleteVolumeOpt = 260;
|
||||
constexpr int kDeleteVolumeFullOpt = 261;
|
||||
constexpr int kHostnameOpt = 262;
|
||||
|
||||
constexpr std::array<struct option, 18> kLongOptions = {{
|
||||
constexpr std::array<struct option, 19> kLongOptions = {{
|
||||
{"help", no_argument, nullptr, 'h'},
|
||||
{"version", no_argument, nullptr, 'V'},
|
||||
{"test", no_argument, nullptr, 't'},
|
||||
@@ -87,6 +88,7 @@ constexpr std::array<struct option, 18> kLongOptions = {{
|
||||
{"delete-volume", required_argument, nullptr, kDeleteVolumeOpt},
|
||||
{"delete-volume-full", required_argument, nullptr, kDeleteVolumeFullOpt},
|
||||
{"inspect", required_argument, nullptr, 'i'},
|
||||
{"hostname", required_argument, nullptr, kHostnameOpt},
|
||||
{nullptr, 0, nullptr, 0},
|
||||
}};
|
||||
|
||||
@@ -131,6 +133,10 @@ void print_usage(const char* prog) {
|
||||
" (no user namespace involved)\n"
|
||||
" --group <group> with --user, use this group (name or numeric\n"
|
||||
" gid) instead of the user's own primary group\n"
|
||||
" --hostname <name> with --run, set the sandbox's hostname (only\n"
|
||||
" takes effect if the running kernel supports\n"
|
||||
" --unshare-uts; otherwise ignored with a\n"
|
||||
" warning)\n"
|
||||
" -l, --list-images <dir> list OCI Image Layout tars (*.tar, *.tar.*) found\n"
|
||||
" directly in <dir>, with their name:tag\n"
|
||||
" -i, --inspect <image.tar> print an image's declared user, exposed ports,\n"
|
||||
@@ -453,6 +459,7 @@ int delete_volume_command(const std::string& name, const std::filesystem::path&
|
||||
int run_container(const std::filesystem::path& image_tar,
|
||||
const std::vector<std::string>& requested_command, bool use_nsenter,
|
||||
const std::optional<std::string>& user, const std::optional<std::string>& group,
|
||||
const std::optional<std::string>& hostname,
|
||||
const std::vector<std::pair<std::string, std::string>>& volume_specs,
|
||||
const AppConfig& app_config) {
|
||||
auto mounted = mount_image(image_tar);
|
||||
@@ -517,7 +524,7 @@ int run_container(const std::filesystem::path& image_tar,
|
||||
|
||||
int exit_code = -1;
|
||||
if (ok) {
|
||||
exit_code = run_bwrap(mounted->merged_path, command, use_nsenter, volume_mounts, resolved_user);
|
||||
exit_code = run_bwrap(mounted->merged_path, command, use_nsenter, volume_mounts, resolved_user, hostname);
|
||||
if (exit_code < 0) {
|
||||
spdlog::error("failed to run bwrap");
|
||||
}
|
||||
@@ -550,6 +557,7 @@ int main(int argc, char* argv[]) {
|
||||
bool disable_nsenter = false;
|
||||
std::optional<std::string> user_flag;
|
||||
std::optional<std::string> group_flag;
|
||||
std::optional<std::string> hostname_flag;
|
||||
std::vector<std::pair<std::string, std::string>> volume_specs;
|
||||
|
||||
opterr = 0;
|
||||
@@ -645,6 +653,9 @@ int main(int argc, char* argv[]) {
|
||||
case kGroupOpt:
|
||||
group_flag = optarg;
|
||||
break;
|
||||
case kHostnameOpt:
|
||||
hostname_flag = optarg;
|
||||
break;
|
||||
case ':':
|
||||
spdlog::error("option requires an argument: -{}", static_cast<char>(optopt));
|
||||
print_usage(argv[0]);
|
||||
@@ -720,7 +731,8 @@ int main(int argc, char* argv[]) {
|
||||
if (geteuid() == 0 && !disable_nsenter) {
|
||||
spdlog::debug("running as root; skipping nsenter (the mount is already directly visible)");
|
||||
}
|
||||
return run_container(mode_arg, command, use_nsenter, user_flag, group_flag, volume_specs, *config);
|
||||
return run_container(mode_arg, command, use_nsenter, user_flag, group_flag, hostname_flag, volume_specs,
|
||||
*config);
|
||||
}
|
||||
|
||||
auto mounted = mount_image(mode_arg);
|
||||
|
||||
Reference in New Issue
Block a user