Repositories / jai.git
jai.git
Clone (read-only): git clone http://git.guha-anderson.com/git/jai.git
@@ -165,11 +165,11 @@ environment before running the command. For more complicated setup logic, you can use `setenv` to set the `BASH_ENV` environment variable to an initialization script to be sourced in non-interactive session. -The `--dir`, `--xdir`, `--mask`, `--unmask`, `--setenv`, and -`--storage` options will perform environment variable substitution for -variable names contained within `${`...`}`, but the braces are -required, unlike in the shell. You can quote a literal `$` by -preceding it with a backslash `\`. +The `dir`, `xdir`, `mask`, `unmask`, `setenv`, and `storage` options, +and `conf` options in configuration files will perform environment +variable substitution for variable names contained within `${`...`}`. +Note the braces are required, unlike in the shell. You can quote a +literal `$` or `\` by preceding it with a backslash `\`. # EXAMPLES @@ -385,6 +385,9 @@ opencode`): otherwise `$HOME/.jai`. However, if your home directory is on NFS you may wish to use storage on a local file system, as NFS does not support the extended attributes required by overlay file systems. + You can of course install a symbolic link for each individual home + directory, but `--storage` allows you to relocate the base directory + where all jails are located. `--command` *bash-command* : jai launches the jailed program you specify by running "`/bin/bash @@ -487,8 +490,9 @@ location can be changed by the `JAI_CONFIG_DIR` environment variable. directly changing this directory, tear down and recreate the sandboxed home directory with `jai -u`. The non-default version is used when you specify `-j` *name* on the command line. If you - specified `--storage=`*dir*, the changes directory will be in *dir* - instead of `$HOME/.jai`. + specified `--storage=`*dir*, the changes directory will looked up + under *dir* instead of `$HOME/.jai` (though in either case may be a + symbolic link elsewhere). `$HOME/.jai/default.work`, `$HOME/.jai/`*name*`.work` : This "work" directory is required by overlayfs, but does not contain
@@ -26,9 +26,9 @@ Config::parse_config_fd(int fd, Options *opts) auto ld = fdpath(fd, true); if (auto [_it, ok] = config_loop_detect_.insert(ld); !ok) err<Options::Error>("configuration loop"); - Defer _clear([this, ld, pch = parsing_config_file_] { + Defer _clear([this, ld, pcf = parsing_config_file_] { config_loop_detect_.erase(ld); - parsing_config_file_ = pch; + parsing_config_file_ = pcf; }); parsing_config_file_ = true; auto go = [&](Options *o) { o->parse_file(read_file(fd), ld); }; @@ -1036,7 +1036,8 @@ Config::opt_parser(bool dotjail) opts("-j", "--jail", [](path) { err<Options::Error>("cannot set name from a .jail file or include"); }); - opts("--conf", [this, opts = ret.get()](path file) { + opts("--conf", [this, opts = ret.get()](std::string_view arg) { + path file(expand(arg)); if (!parse_config_file(file, opts)) err<Options::Error>("{}: configuration file not found", file.string()); });
@@ -16,18 +16,15 @@ extern "C" char **environ; -inline const char * -env_or_empty(std::string_view var) -{ +inline const char *env_or_empty(std::string_view var) { const char *p = getenv(std::string(var).c_str()); return p ? p : ""; } // Calls exp("VAR"sv) to expand strings like "123${VAR}456". -template<typename Exp = decltype(env_or_empty)> -std::string -var_expand(std::string_view in, Exp &&exp = env_or_empty) - requires requires(std::string r) { r += exp(in); } +template <typename Exp = decltype(env_or_empty)> +std::string var_expand(std::string_view in, Exp &&exp = env_or_empty) + requires requires(std::string r) { r += exp(in); } { std::string ret; for (std::size_t i = 0, e = in.size(); i < e;) @@ -37,24 +34,19 @@ var_expand(std::string_view in, Exp &&exp = env_or_empty) in.substr(i, 2) == "${" && (j = in.find('}', i + 2)) != in.npos) { ret += exp(in.substr(i + 2, j - i - 2)); i = j + 1; - } - else + } else ret += in[i++]; return ret; } -inline pid_t -xfork(std::uint64_t flags = 0) -{ +inline pid_t xfork(std::uint64_t flags = 0) { clone_args ca{.flags = flags, .exit_signal = SIGCHLD}; if (auto ret = syscall(SYS_clone3, &ca, sizeof(ca)); ret != -1) return ret; syserr("clone3"); } -inline sigset_t -sigsingleton(int sig) -{ +inline sigset_t sigsingleton(int sig) { sigset_t ret; sigemptyset(&ret); sigaddset(&ret, sig); @@ -135,14 +127,12 @@ struct Config { void check_user(const struct stat &sb, std::string path_for_error = {}, bool untrusted_ok = false); void check_user(int fd, std::string path_for_error = {}, - bool untrusted_ok = false) - { + bool untrusted_ok = false) { check_user(xfstat(fd), path_for_error.empty() ? fdpath(fd) : path_for_error, untrusted_ok); } Fd ensure_udir(int dfd, const path &p, mode_t perm = 0700, - FollowLinks follow = kFollow) - { + FollowLinks follow = kFollow) { auto _restore = asuser(); Fd fd = ensure_dir(dfd, p, perm, follow); check_user(*fd); @@ -154,8 +144,7 @@ struct Config { int storage(); int run_jai(); int run_jai_user(); - const path &cwd() - { + const path &cwd() { if (cwd_.empty()) { auto restore = asuser(); cwd_ = canonical(std::filesystem::current_path()); @@ -169,25 +158,24 @@ struct Config { Fd make_private_run(); Fd make_private_passwd(); - const char *env_lookup(std::string_view var) - { + const char *env_lookup(std::string_view var) { if (auto it = setenv_.find(var); it != setenv_.end()) if (auto pos = it->second.find('='); pos != it->second.npos) return it->second.c_str() + pos + 1; return env_or_empty(var); } - std::string expand(std::string_view in) - { - return var_expand(in, [this](std::string_view v) { return env_lookup(v); }); + std::string expand(std::string_view in) { + return parsing_config_file_ + ? var_expand( + in, [this](std::string_view v) { return env_lookup(v); }) + : std::string(in); } - static bool name_ok(path p) - { + static bool name_ok(path p) { return p.is_relative() && std::ranges::distance(p.begin(), p.end()) == 1 && *p.c_str() != '.'; } - void mask_warn() - { + void mask_warn() { if (mask_warn_) { warn(R"(--mask ignored because {5}/{0}/{1}.home already mounted. {2:>{3}} Run "{4} -u" to unmount overlays.)", @@ -198,10 +186,9 @@ struct Config { } }; -template<> struct std::formatter<Config::Mode> : std::formatter<const char *> { +template <> struct std::formatter<Config::Mode> : std::formatter<const char *> { using super = std::formatter<const char *>; - auto format(Config::Mode m, auto &&ctx) const - { + auto format(Config::Mode m, auto &&ctx) const { using enum Config::Mode; switch (m) { case kStrict:
@@ -11,8 +11,8 @@ assert_path_exists "$CONFIG_DIR/default.jail" capture run_jai --version assert_status 0 -assert_contains "$CAPTURE_STDOUT" "jai 0.1" -assert_contains "$CAPTURE_STDOUT" "https://github.com/stanford-scs/jai" +assert_contains "$CAPTURE_STDOUT" "Untrusted user for strict mode:" +assert_contains "$CAPTURE_STDOUT" "This program comes with NO WARRANTY" capture run_jai --print-defaults assert_status 0
@@ -43,3 +43,17 @@ EOF capture_in_dir "$WORKDIR" run_jai_with_env SRC_VALUE=expanded -C expand assert_status 0 assert_output_line "EXPANDED=expanded" + +cat >"$CONFIG_DIR/expand-include.conf" <<'EOF' +conf .defaults +conf ${INCLUDE_FILE} +command /usr/bin/env +EOF + +capture_in_dir "$WORKDIR" run_jai_with_env INCLUDE_FILE=shared.conf -C expand-include +assert_status 0 +assert_output_line "FROM_SHARED=shared" + +capture_in_dir "$WORKDIR" run_jai_with_env INCLUDE_FILE=expand-include.conf -C '${INCLUDE_FILE}' +assert_status 1 +assert_contains "$CAPTURE_STDERR" '${INCLUDE_FILE}: no such configuration file'