add the agent option privateUsers, with the uid = gid assertion
This commit is contained in:
parent
3ca660e558
commit
1fd591d28b
2 changed files with 76 additions and 0 deletions
|
|
@ -90,8 +90,47 @@ in
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
options.services.hyperhive.agent.user.privateUsers = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = false;
|
||||||
|
example = true;
|
||||||
|
description = ''
|
||||||
|
Run this agent's container in its own user namespace. Off by default.
|
||||||
|
|
||||||
|
**Approving the config PR that sets this authorises a host-level
|
||||||
|
change.** The swarm builder reads the option from the evaluated config
|
||||||
|
into the agent's closure record, and the hive then starts the container
|
||||||
|
with `--private-users=pick`, every hive bind mounted `owneridmap`, and
|
||||||
|
its root idmapped. The first such start has `hive-priv`, as root, chown
|
||||||
|
the agent's `state`, `harness`, `work` and `claude` dirs once to the
|
||||||
|
host's data owner
|
||||||
|
(`services.hyperhive.deploy.hive-controller.userns.dataOwner`). Nothing is
|
||||||
|
deleted. Turning it off afterwards needs the dirs chowned back by hand.
|
||||||
|
|
||||||
|
Needs `user.uid` and `user.gid` set to the same number: nspawn maps a
|
||||||
|
bind's gids with its uid map, so a group of another number has no
|
||||||
|
mapping and writes fail. Start the agent once with the pin before
|
||||||
|
turning this on; `hive-priv` refuses to move a `state` dir whose gid
|
||||||
|
differs from its uid. An agent that holds `ManageRootAgent` should not
|
||||||
|
set it, since the other agents' dirs it mounts are not remapped.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
assertions = [
|
assertions = [
|
||||||
|
{
|
||||||
|
assertion =
|
||||||
|
!config.services.hyperhive.agent.user.privateUsers
|
||||||
|
|| (
|
||||||
|
config.services.hyperhive.agent.user.uid != null
|
||||||
|
&& config.services.hyperhive.agent.user.uid == config.services.hyperhive.agent.user.gid
|
||||||
|
);
|
||||||
|
message = ''
|
||||||
|
services.hyperhive.agent.user.privateUsers needs services.hyperhive.agent.user.uid
|
||||||
|
and services.hyperhive.agent.user.gid set to the same number: a user namespace
|
||||||
|
maps a bind's gids with its uid map, so a group of another number has no mapping.
|
||||||
|
'';
|
||||||
|
}
|
||||||
{
|
{
|
||||||
assertion =
|
assertion =
|
||||||
config.services.hyperhive.agent.user.uid == null
|
config.services.hyperhive.agent.user.uid == null
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,44 @@ let
|
||||||
.system.activationScripts.hive-agent-user-migrate.text;
|
.system.activationScripts.hive-agent-user-migrate.text;
|
||||||
# The tmpfiles rules of the same named agent.
|
# The tmpfiles rules of the same named agent.
|
||||||
rules = (agentWith { services.hyperhive.agent.user.name = "iris"; }).systemd.tmpfiles.rules;
|
rules = (agentWith { services.hyperhive.agent.user.name = "iris"; }).systemd.tmpfiles.rules;
|
||||||
|
# `privateUsers` with the given pins; the agent name is the one the cases
|
||||||
|
# above use, so only the pins differ.
|
||||||
|
withUsers =
|
||||||
|
user:
|
||||||
|
agentWith {
|
||||||
|
services.hyperhive.agent.user = {
|
||||||
|
name = "iris";
|
||||||
|
}
|
||||||
|
// user;
|
||||||
|
};
|
||||||
|
# Whether the module refuses the config over the user-namespace pin.
|
||||||
|
refusesPins =
|
||||||
|
cfg: lib.any (a: !a.assertion && lib.hasInfix "privateUsers" a.message) cfg.assertions;
|
||||||
cases = [
|
cases = [
|
||||||
|
{
|
||||||
|
# The control is the matching pair: it passes, so each refusal is the
|
||||||
|
# assertion's own and the default (off, no pins) passes too.
|
||||||
|
name = "privateUsers needs a uid and a gid that are the same number";
|
||||||
|
ok =
|
||||||
|
!refusesPins (withUsers { })
|
||||||
|
&& !refusesPins (withUsers {
|
||||||
|
privateUsers = true;
|
||||||
|
uid = 1000;
|
||||||
|
gid = 1000;
|
||||||
|
})
|
||||||
|
&& refusesPins (withUsers {
|
||||||
|
privateUsers = true;
|
||||||
|
})
|
||||||
|
&& refusesPins (withUsers {
|
||||||
|
privateUsers = true;
|
||||||
|
uid = 1000;
|
||||||
|
})
|
||||||
|
&& refusesPins (withUsers {
|
||||||
|
privateUsers = true;
|
||||||
|
uid = 1000;
|
||||||
|
gid = 1001;
|
||||||
|
});
|
||||||
|
}
|
||||||
{
|
{
|
||||||
# hive-c0re creates the host work dir as hive-core; this activation is
|
# hive-c0re creates the host work dir as hive-core; this activation is
|
||||||
# the only thing that lets the agent write to it.
|
# the only thing that lets the agent write to it.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue