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
53 changes: 32 additions & 21 deletions src/libstore/include/nix/store/globals.hh
Original file line number Diff line number Diff line change
Expand Up @@ -178,9 +178,31 @@ public:
{"build-compress-log"}};
};

class Settings : public virtual Config, private GCSettings, private LogFileSettings
struct AutoAllocateUidSettings : public virtual Config
{
Setting<uint32_t> startId{
this,
#ifdef __linux__
0x34000000,
#else
56930,
#endif
"start-id",
"The first UID and GID to use for dynamic ID allocation."};

Setting<uint32_t> uidCount{
this,
#ifdef __linux__
maxIdsPerBuild * 128,
#else
128,
#endif
"id-count",
"The number of UIDs/GIDs to use for dynamic ID allocation."};
};

class Settings : public virtual Config, private AutoAllocateUidSettings, private GCSettings, private LogFileSettings
{
StringSet getDefaultSystemFeatures();

StringSet getDefaultExtraPlatforms();
Expand Down Expand Up @@ -219,6 +241,15 @@ public:
return *this;
}

/**
* Get AutoAllocateUidSettings if auto-allocate-uids is enabled.
* @return Pointer to settings if enabled, nullptr otherwise.
*/
const AutoAllocateUidSettings * getAutoAllocateUidSettings() const
{
return autoAllocateUids ? this : nullptr;
}

static unsigned int getDefaultCores();

/**
Expand Down Expand Up @@ -658,26 +689,6 @@ public:
true,
Xp::AutoAllocateUids};

Setting<uint32_t> startId{
this,
#ifdef __linux__
0x34000000,
#else
56930,
#endif
"start-id",
"The first UID and GID to use for dynamic ID allocation."};

Setting<uint32_t> uidCount{
this,
#ifdef __linux__
maxIdsPerBuild * 128,
#else
128,
#endif
"id-count",
"The number of UIDs/GIDs to use for dynamic ID allocation."};

#ifdef __linux__
Setting<bool> useCgroups{
this,
Expand Down
2 changes: 1 addition & 1 deletion src/libstore/unix/build/derivation-builder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ class DerivationBuilderImpl : public DerivationBuilder, public DerivationBuilder
*/
virtual std::unique_ptr<UserLock> getBuildUser()
{
return acquireUserLock(1, false);
return acquireUserLock(settings.buildUsersGroup, 1, false);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/libstore/unix/build/linux-derivation-builder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ struct ChrootLinuxDerivationBuilder : ChrootDerivationBuilder, LinuxDerivationBu

std::unique_ptr<UserLock> getBuildUser() override
{
return acquireUserLock(drvOptions.useUidRange(drv) ? 65536 : 1, true);
return acquireUserLock(settings.buildUsersGroup, drvOptions.useUidRange(drv) ? 65536 : 1, true);
}

void prepareUser() override
Expand Down
2 changes: 1 addition & 1 deletion src/libstore/unix/include/nix/store/user-lock.hh
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ struct UserLock
* Acquire a user lock for a UID range of size `nrIds`. Note that this
* may return nullptr if no user is available.
*/
std::unique_ptr<UserLock> acquireUserLock(uid_t nrIds, bool useUserNamespace);
std::unique_ptr<UserLock> acquireUserLock(const std::string & userGroup, uid_t nrIds, bool useUserNamespace);

bool useBuildUsers();

Expand Down
65 changes: 35 additions & 30 deletions src/libstore/unix/user-lock.cc
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,15 @@ struct SimpleUserLock : UserLock
return supplementaryGIDs;
}

static std::unique_ptr<UserLock> acquire()
static std::unique_ptr<UserLock>
acquire(const std::filesystem::path & userPoolDir, const std::string & buildUsersGroup)
{
assert(settings.buildUsersGroup != "");
createDirs(settings.nixStateDir + "/userpool");
assert(buildUsersGroup != "");

/* Get the members of the build-users-group. */
struct group * gr = getgrnam(settings.buildUsersGroup.get().c_str());
struct group * gr = getgrnam(buildUsersGroup.c_str());
if (!gr)
throw Error("the group '%s' specified in 'build-users-group' does not exist", settings.buildUsersGroup);
throw Error("the group '%s' specified in 'build-users-group' does not exist", buildUsersGroup);

/* Copy the result of getgrnam. */
Strings users;
Expand All @@ -82,7 +82,7 @@ struct SimpleUserLock : UserLock
}

if (users.empty())
throw Error("the build users group '%s' has no members", settings.buildUsersGroup);
throw Error("the build users group '%s' has no members", buildUsersGroup);

/* Find a user account that isn't currently in use for another
build. */
Expand All @@ -91,13 +91,13 @@ struct SimpleUserLock : UserLock

struct passwd * pw = getpwnam(i.c_str());
if (!pw)
throw Error("the user '%s' in the group '%s' does not exist", i, settings.buildUsersGroup);
throw Error("the user '%s' in the group '%s' does not exist", i, buildUsersGroup);

auto fnUserLock = fmt("%s/userpool/%s", settings.nixStateDir, pw->pw_uid);
auto fnUserLock = userPoolDir / std::to_string(pw->pw_uid);

AutoCloseFD fd = open(fnUserLock.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, 0600);
if (!fd)
throw SysError("opening user lock '%s'", fnUserLock);
throw SysError("opening user lock %s", PathFmt(fnUserLock));

if (lockFile(fd.get(), ltWrite, false)) {
auto lock = std::make_unique<SimpleUserLock>();
Expand All @@ -108,7 +108,7 @@ struct SimpleUserLock : UserLock

/* Sanity check... */
if (lock->uid == getuid() || lock->uid == geteuid())
throw Error("the Nix user should not be a member of '%s'", settings.buildUsersGroup);
throw Error("the Nix user should not be a member of '%s'", buildUsersGroup);

#ifdef __linux__
/* Get the list of supplementary groups of this user. This is
Expand Down Expand Up @@ -158,36 +158,37 @@ struct AutoUserLock : UserLock
return {};
}

static std::unique_ptr<UserLock> acquire(uid_t nrIds, bool useUserNamespace)
static std::unique_ptr<UserLock> acquire(
const std::filesystem::path & userPoolDir,
const std::string & buildUsersGroup,
uid_t nrIds,
bool useUserNamespace,
const AutoAllocateUidSettings & uidSettings)
{
#if !defined(__linux__)
useUserNamespace = false;
#endif

experimentalFeatureSettings.require(Xp::AutoAllocateUids);
assert(settings.startId > 0);
assert(settings.uidCount % maxIdsPerBuild == 0);
assert((uint64_t) settings.startId + (uint64_t) settings.uidCount <= std::numeric_limits<uid_t>::max());
assert(uidSettings.startId > 0);
assert(uidSettings.uidCount % maxIdsPerBuild == 0);
assert((uint64_t) uidSettings.startId + (uint64_t) uidSettings.uidCount <= std::numeric_limits<uid_t>::max());
assert(nrIds <= maxIdsPerBuild);

createDirs(settings.nixStateDir + "/userpool2");

size_t nrSlots = settings.uidCount / maxIdsPerBuild;
size_t nrSlots = uidSettings.uidCount / maxIdsPerBuild;

for (size_t i = 0; i < nrSlots; i++) {
debug("trying user slot '%d'", i);

createDirs(settings.nixStateDir + "/userpool2");

auto fnUserLock = fmt("%s/userpool2/slot-%d", settings.nixStateDir, i);
auto fnUserLock = userPoolDir / fmt("slot-%d", i);

AutoCloseFD fd = open(fnUserLock.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, 0600);
if (!fd)
throw SysError("opening user lock '%s'", fnUserLock);
throw SysError("opening user lock %s", PathFmt(fnUserLock));

if (lockFile(fd.get(), ltWrite, false)) {

auto firstUid = settings.startId + i * maxIdsPerBuild;
auto firstUid = uidSettings.startId + i * maxIdsPerBuild;

auto pw = getpwuid(firstUid);
if (pw)
Expand All @@ -199,10 +200,9 @@ struct AutoUserLock : UserLock
if (useUserNamespace)
lock->firstGid = firstUid;
else {
struct group * gr = getgrnam(settings.buildUsersGroup.get().c_str());
struct group * gr = getgrnam(buildUsersGroup.c_str());
if (!gr)
throw Error(
"the group '%s' specified in 'build-users-group' does not exist", settings.buildUsersGroup);
throw Error("the group '%s' specified in 'build-users-group' does not exist", buildUsersGroup);
lock->firstGid = gr->gr_gid;
}
lock->nrIds = nrIds;
Expand All @@ -214,12 +214,17 @@ struct AutoUserLock : UserLock
}
};

std::unique_ptr<UserLock> acquireUserLock(uid_t nrIds, bool useUserNamespace)
std::unique_ptr<UserLock> acquireUserLock(const std::string & userGroup, uid_t nrIds, bool useUserNamespace)
{
if (settings.autoAllocateUids)
return AutoUserLock::acquire(nrIds, useUserNamespace);
else
return SimpleUserLock::acquire();
if (auto * uidSettings = settings.getAutoAllocateUidSettings()) {
auto userPoolDir = std::filesystem::path{settings.nixStateDir} / "userpool2";
createDirs(userPoolDir);
return AutoUserLock::acquire(userPoolDir, userGroup, nrIds, useUserNamespace, *uidSettings);
} else {
auto userPoolDir = std::filesystem::path{settings.nixStateDir} / "userpool";
createDirs(userPoolDir);
return SimpleUserLock::acquire(userPoolDir, userGroup);
}
}

bool useBuildUsers()
Expand Down
Loading