1
0
Fork 0
mirror of https://github.com/NixOS/nix synced 2025-06-27 12:41:15 +02:00

start work on freebsd build isolation

More attempting

Finalize jailed building, I think

Additional setup and cleanup for freebsd jails

finish rebasing

Fix build on Linux

Conditionally build some FreeBSD code to only build on FreeBSD

Co-Authored-By: Artemis Tosini <me@artem.ist>
This commit is contained in:
Audrey Dutcher 2024-01-31 20:12:40 -07:00 committed by John Ericson
parent 2cecff28c7
commit 3a934f088e
3 changed files with 186 additions and 6 deletions

View file

@ -352,6 +352,11 @@ this_library = library(
install_headers(headers, subdir : 'nix/store', preserve_path : true) install_headers(headers, subdir : 'nix/store', preserve_path : true)
libraries_private = [] libraries_private = []
# `libraries_private` cannot contain ad-hoc dependencies (from
# `find_library), so we need to do this manually
if host_machine.system() == 'freebsd'
libraries_private += ['-ljail']
endif
extra_pkg_config_variables = { extra_pkg_config_variables = {
'storedir' : get_option('store-dir'), 'storedir' : get_option('store-dir'),

View file

@ -5,6 +5,7 @@
unixtools, unixtools,
darwin, darwin,
freebsd,
nix-util, nix-util,
boost, boost,
@ -67,6 +68,7 @@ mkMesonLibrary (finalAttrs: {
++ lib.optional stdenv.hostPlatform.isLinux libseccomp ++ lib.optional stdenv.hostPlatform.isLinux libseccomp
# There have been issues building these dependencies # There have been issues building these dependencies
++ lib.optional stdenv.hostPlatform.isDarwin darwin.apple_sdk.libs.sandbox ++ lib.optional stdenv.hostPlatform.isDarwin darwin.apple_sdk.libs.sandbox
++ lib.optional stdenv.hostPlatform.isFreeBSD freebsd.libjail
++ lib.optional withAWS aws-sdk-cpp; ++ lib.optional withAWS aws-sdk-cpp;
propagatedBuildInputs = [ propagatedBuildInputs = [

View file

@ -61,6 +61,16 @@
extern "C" int sandbox_init_with_parameters(const char *profile, uint64_t flags, const char *const parameters[], char **errorbuf); extern "C" int sandbox_init_with_parameters(const char *profile, uint64_t flags, const char *const parameters[], char **errorbuf);
#endif #endif
#ifdef __FreeBSD__
# include <sys/param.h>
# include <sys/jail.h>
# include <jail.h>
# include <stdlib.h>
# include <sys/mount.h>
# include <string.h>
# include "nix/util/freebsd-jail.hh"
#endif
#include <pwd.h> #include <pwd.h>
#include <grp.h> #include <grp.h>
#include <iostream> #include <iostream>
@ -149,7 +159,12 @@ private:
* On Linux, whether we're doing the build in its own user * On Linux, whether we're doing the build in its own user
* namespace. * namespace.
*/ */
bool usingUserNamespace = true; bool usingUserNamespace =
#ifdef __linux__
true;
#else
false;
#endif
/** /**
* Whether we're currently doing a chroot build. * Whether we're currently doing a chroot build.
@ -165,6 +180,11 @@ private:
* RAII object to delete the chroot directory. * RAII object to delete the chroot directory.
*/ */
std::shared_ptr<AutoDelete> autoDelChroot; std::shared_ptr<AutoDelete> autoDelChroot;
#ifdef __FreeBSD__
/* Destructors happen in reverse order from declaration */
std::shared_ptr<AutoRemoveJail> autoDelJail;
std::vector<std::shared_ptr<AutoUnmount>> autoDelMounts;
#endif
/** /**
* The sort of derivation we are building. * The sort of derivation we are building.
@ -398,7 +418,7 @@ const Path DerivationBuilderImpl::homeDir = "/homeless-shelter";
inline bool DerivationBuilderImpl::needsHashRewrite() inline bool DerivationBuilderImpl::needsHashRewrite()
{ {
#ifdef __linux__ #if defined(__linux__) || defined(__FreeBSD__)
return !useChroot; return !useChroot;
#else #else
/* Darwin requires hash rewriting even when sandboxing is enabled. */ /* Darwin requires hash rewriting even when sandboxing is enabled. */
@ -584,6 +604,11 @@ std::variant<std::pair<BuildResult::Status, Error>, SingleDrvOutputs> Derivation
for (auto & i : redirectedOutputs) for (auto & i : redirectedOutputs)
deletePath(store.Store::toRealPath(i.second)); deletePath(store.Store::toRealPath(i.second));
#ifdef __FreeBSD__
/* Unmount and free jail id, if in use */
autoDelMounts.clear();
autoDelJail.reset();
#endif
/* Delete the chroot (if we were using one). */ /* Delete the chroot (if we were using one). */
autoDelChroot.reset(); /* this runs the destructor */ autoDelChroot.reset(); /* this runs the destructor */
@ -636,10 +661,10 @@ static void replaceValidPath(const Path & storePath, const Path & tmpPath)
way first. We'd better not be interrupted here, because if way first. We'd better not be interrupted here, because if
we're repairing (say) Glibc, we end up with a broken system. */ we're repairing (say) Glibc, we end up with a broken system. */
Path oldPath; Path oldPath;
if (pathExists(storePath)) { if (pathExists(storePath)) {
// why do we loop here? // why do we loop here?
// although makeTempPath should be unique, we can't // although makeTempPath should be unique, we can't
// guarantee that. // guarantee that.
do { do {
oldPath = makeTempPath(storePath, ".old"); oldPath = makeTempPath(storePath, ".old");
@ -1021,12 +1046,28 @@ void DerivationBuilderImpl::startBuilder()
pathsInChroot[i] = {i, true}; pathsInChroot[i] = {i, true};
} }
#ifdef __linux__ #if defined(__linux__) || defined(__FreeBSD__)
/* Create a temporary directory in which we set up the chroot /* Create a temporary directory in which we set up the chroot
environment using bind-mounts. We put it in the Nix store environment using bind-mounts. We put it in the Nix store
so that the build outputs can be moved efficiently from the so that the build outputs can be moved efficiently from the
chroot to their final location. */ chroot to their final location. */
auto chrootParentDir = store.Store::toRealPath(drvPath) + ".chroot"; auto chrootParentDir = store.Store::toRealPath(drvPath) + ".chroot";
#ifdef __FreeBSD__
int count;
struct statfs *mntbuf;
if ((count = getmntinfo(&mntbuf, MNT_WAIT)) < 0) {
throw SysError("Couldn't get mount info for chroot");
}
for (int i = 0; i < count; i++) {
Path mounted(mntbuf[i].f_mntonname);
if (hasPrefix(mounted, chrootParentDir)) {
if (unmount(mounted.c_str(), 0) < 0) {
throw SysError("Failed to unmount path %1%", mounted);
}
}
}
#endif
deletePath(chrootParentDir); deletePath(chrootParentDir);
/* Clean up the chroot directory automatically. */ /* Clean up the chroot directory automatically. */
@ -1108,6 +1149,7 @@ void DerivationBuilderImpl::startBuilder()
pathsInChroot.erase(store.printStorePath(*i.second.second)); pathsInChroot.erase(store.printStorePath(*i.second.second));
} }
#ifdef __linux__
if (cgroup) { if (cgroup) {
if (mkdir(cgroup->c_str(), 0755) != 0) if (mkdir(cgroup->c_str(), 0755) != 0)
throw SysError("creating cgroup '%s'", *cgroup); throw SysError("creating cgroup '%s'", *cgroup);
@ -1116,6 +1158,80 @@ void DerivationBuilderImpl::startBuilder()
chownToBuilder(*cgroup + "/cgroup.threads"); chownToBuilder(*cgroup + "/cgroup.threads");
//chownToBuilder(*cgroup + "/cgroup.subtree_control"); //chownToBuilder(*cgroup + "/cgroup.subtree_control");
} }
#elif defined(__FreeBSD__)
auto devpath = chrootRootDir + "/dev";
mkdir(devpath.c_str(), 0555);
mkdir((chrootRootDir + "/bin").c_str(), 0555);
char errmsg[255] = "";
struct iovec iov[8] = {
{ .iov_base = (void*)"fstype", .iov_len = sizeof("fstype") },
{ .iov_base = (void*)"devfs", .iov_len = sizeof("devfs") },
{ .iov_base = (void*)"fspath", .iov_len = sizeof("fspath") },
{ .iov_base = (void*)devpath.c_str(), .iov_len = devpath.length() + 1 },
{ .iov_base = (void*)"errmsg", .iov_len = sizeof("errmsg") },
{ .iov_base = (void*)errmsg, .iov_len = sizeof(errmsg) },
};
if (nmount(iov, 6, 0) < 0) {
throw SysError("Failed to mount jail /dev: %1%", errmsg);
}
autoDelMounts.push_back(std::make_shared<AutoUnmount>(devpath));
/* Fixed-output derivations typically need to access the
network, so give them access to /etc/resolv.conf and so
on. */
if (!derivationType.isSandboxed()) {
// Only use nss functions to resolve hosts and
// services. Dont use it for anything else that may
// be configured for this system. This limits the
// potential impurities introduced in fixed-outputs.
writeFile(chrootRootDir + "/etc/nsswitch.conf", "hosts: files dns\nservices: files\n");
/* N.B. it is realistic that these paths might not exist. It
happens when testing Nix building fixed-output derivations
within a pure derivation. */
for (auto & path : { "/etc/resolv.conf", "/etc/services", "/etc/hosts" })
if (pathExists(path))
pathsInChroot.try_emplace(path, path, true);
if (settings.caFile != "")
pathsInChroot.try_emplace("/etc/ssl/certs/ca-certificates.crt", settings.caFile, true);
}
for (auto & i : pathsInChroot) {
char errmsg[255];
errmsg[0] = 0;
if (i.second.source == "/proc") continue; // backwards compatibility
auto path = chrootRootDir + i.first;
struct stat stat_buf;
if (stat(i.second.source.c_str(), &stat_buf) < 0) {
throw SysError("stat");
}
// mount points must exist and be the right type
if (S_ISDIR(stat_buf.st_mode)) {
mkdir(path.c_str(), 0555);
} else {
close(open(path.c_str(), O_CREAT | O_RDONLY, 0444));
}
struct iovec iov[8] = {
{ .iov_base = (void*)"fstype", .iov_len = sizeof("fstype") },
{ .iov_base = (void*)"nullfs", .iov_len = sizeof("nullfs") },
{ .iov_base = (void*)"fspath", .iov_len = sizeof("fspath") },
{ .iov_base = (void*)path.c_str(), .iov_len = path.length() + 1 },
{ .iov_base = (void*)"target", .iov_len = sizeof("target") },
{ .iov_base = (void*)i.second.source.c_str(), .iov_len = i.second.source.length() + 1 },
{ .iov_base = (void*)"errmsg", .iov_len = sizeof("errmsg") },
{ .iov_base = (void*)errmsg, .iov_len = sizeof(errmsg) },
};
if (nmount(iov, 8, 0) < 0) {
throw SysError("Failed to mount nullfs for %1% - %2%", path, errmsg);
}
autoDelMounts.push_back(std::make_shared<AutoUnmount>(path));
}
#endif
#else #else
if (drvOptions.useUidRange(drv)) if (drvOptions.useUidRange(drv))
@ -1383,6 +1499,63 @@ void DerivationBuilderImpl::startBuilder()
writeFull(userNamespaceSync.writeSide.get(), "1"); writeFull(userNamespaceSync.writeSide.get(), "1");
} else } else
#elif defined(__FreeBSD__)
if (useChroot) {
/* Now that we now the sandbox uid, we can write
/etc/passwd. */
writeFile(chrootRootDir + "/etc/passwd", fmt(
"root:x:0:0::::Nix build user:%3%:/noshell\n"
"nixbld:x:%1%:%2%::::Nix build user:%3%:/noshell\n"
"nobody:x:65534:65534::::Nobody:/:/noshell\n",
sandboxUid(), sandboxGid(), settings.sandboxBuildDir));
if (system(("pwd_mkdb -d " + chrootRootDir + "/etc " + chrootRootDir + "/etc/passwd 2>/dev/null").c_str()) != 0) {
throw SysError("Failed to set up isolated users");
}
int jid;
if (derivationType.isSandboxed()) {
jid = jail_setv(JAIL_CREATE,
"persist", "true",
"path", chrootRootDir.c_str(),
"devfs_ruleset", "4",
"vnet", "new",
"host.hostname", "nixbsd",
NULL
);
if (jid < 0) {
throw SysError("Failed to create jail (isolated network)");
}
autoDelJail = std::make_shared<AutoRemoveJail>(jid);
if (system(("ifconfig -j " + std::to_string(jid) + " lo0 inet 127.0.0.1/8 up").c_str()) != 0) {
throw SysError("Failed to set up isolated network");
}
} else {
jid = jail_setv(JAIL_CREATE,
"persist", "true",
"path", chrootRootDir.c_str(),
"devfs_ruleset", "4",
"ip4", "inherit",
"ip6", "inherit",
"allow.raw_sockets", "true",
"host.hostname", "nixbsd",
NULL
);
if (jid < 0) {
throw SysError("Failed to create jail (fixed-derivation)");
}
autoDelJail = std::make_shared<AutoRemoveJail>(jid);
}
pid = startProcess([&]() {
openSlave();
if (jail_attach(jid) < 0) {
throw SysError("Failed to attach to jail");
}
runChild();
});
} else
#endif #endif
{ {
pid = startProcess([&]() { pid = startProcess([&]() {
@ -1432,7 +1605,7 @@ void DerivationBuilderImpl::initTmpDir()
{ {
/* In a sandbox, for determinism, always use the same temporary /* In a sandbox, for determinism, always use the same temporary
directory. */ directory. */
#ifdef __linux__ #if defined(__linux__) || defined(__FreeBSD__)
tmpDirInSandbox = useChroot ? settings.sandboxBuildDir : tmpDir; tmpDirInSandbox = useChroot ? settings.sandboxBuildDir : tmpDir;
#else #else
tmpDirInSandbox = tmpDir; tmpDirInSandbox = tmpDir;