Merge pull request #86278 from adisbladis/subuid-subgid-normaluser
nixos.users-groups: Set up subuid/subgid mappings for all normal users
This commit is contained in:
commit
ceac51fdc4
@ -124,6 +124,12 @@ systemd.services.mysql.serviceConfig.ReadWritePaths = [ "/var/data" ];
|
|||||||
<varname>services.postfix.sslCACert</varname> was replaced by <varname>services.postfix.tlsTrustedAuthorities</varname> which now defaults to system certifcate authorities.
|
<varname>services.postfix.sslCACert</varname> was replaced by <varname>services.postfix.tlsTrustedAuthorities</varname> which now defaults to system certifcate authorities.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Subordinate GID and UID mappings are now set up automatically for all normal users.
|
||||||
|
This will make container tools like Podman work as non-root users out of the box.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
</itemizedlist>
|
</itemizedlist>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
@ -281,3 +281,58 @@ foreach my $u (values %usersOut) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateFile("/etc/shadow", \@shadowNew, 0600);
|
updateFile("/etc/shadow", \@shadowNew, 0600);
|
||||||
|
|
||||||
|
# Rewrite /etc/subuid & /etc/subgid to include default container mappings
|
||||||
|
|
||||||
|
my $subUidMapFile = "/var/lib/nixos/auto-subuid-map";
|
||||||
|
my $subUidMap = -e $subUidMapFile ? decode_json(read_file($subUidMapFile)) : {};
|
||||||
|
|
||||||
|
my (%subUidsUsed, %subUidsPrevUsed);
|
||||||
|
|
||||||
|
$subUidsPrevUsed{$_} = 1 foreach values %{$subUidMap};
|
||||||
|
|
||||||
|
sub allocSubUid {
|
||||||
|
my ($name, @rest) = @_;
|
||||||
|
|
||||||
|
# TODO: No upper bounds?
|
||||||
|
my ($min, $max, $up) = (100000, 100000 * 100, 1);
|
||||||
|
my $prevId = $subUidMap->{$name};
|
||||||
|
if (defined $prevId && !defined $subUidsUsed{$prevId}) {
|
||||||
|
$subUidsUsed{$prevId} = 1;
|
||||||
|
return $prevId;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $id = allocId(\%subUidsUsed, \%subUidsPrevUsed, $min, $max, $up, sub { my ($uid) = @_; getpwuid($uid) });
|
||||||
|
my $offset = $id - 100000;
|
||||||
|
my $count = $offset * 65536;
|
||||||
|
my $subordinate = 100000 + $count;
|
||||||
|
return $subordinate;
|
||||||
|
}
|
||||||
|
|
||||||
|
my @subGids;
|
||||||
|
my @subUids;
|
||||||
|
foreach my $u (values %usersOut) {
|
||||||
|
my $name = $u->{name};
|
||||||
|
|
||||||
|
foreach my $range (@{$u->{subUidRanges}}) {
|
||||||
|
my $value = join(":", ($name, $range->{startUid}, $range->{count}));
|
||||||
|
push @subUids, $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach my $range (@{$u->{subGidRanges}}) {
|
||||||
|
my $value = join(":", ($name, $range->{startGid}, $range->{count}));
|
||||||
|
push @subGids, $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($u->{isNormalUser}) {
|
||||||
|
my $subordinate = allocSubUid($name);
|
||||||
|
$subUidMap->{$name} = $subordinate;
|
||||||
|
my $value = join(":", ($name, $subordinate, 65536));
|
||||||
|
push @subUids, $value;
|
||||||
|
push @subGids, $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateFile("/etc/subuid", join("\n", @subUids) . "\n");
|
||||||
|
updateFile("/etc/subgid", join("\n", @subGids) . "\n");
|
||||||
|
updateFile($subUidMapFile, encode_json($subUidMap) . "\n");
|
||||||
|
@ -375,18 +375,6 @@ let
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
mkSubuidEntry = user: concatStrings (
|
|
||||||
map (range: "${user.name}:${toString range.startUid}:${toString range.count}\n")
|
|
||||||
user.subUidRanges);
|
|
||||||
|
|
||||||
subuidFile = concatStrings (map mkSubuidEntry (attrValues cfg.users));
|
|
||||||
|
|
||||||
mkSubgidEntry = user: concatStrings (
|
|
||||||
map (range: "${user.name}:${toString range.startGid}:${toString range.count}\n")
|
|
||||||
user.subGidRanges);
|
|
||||||
|
|
||||||
subgidFile = concatStrings (map mkSubgidEntry (attrValues cfg.users));
|
|
||||||
|
|
||||||
idsAreUnique = set: idAttr: !(fold (name: args@{ dup, acc }:
|
idsAreUnique = set: idAttr: !(fold (name: args@{ dup, acc }:
|
||||||
let
|
let
|
||||||
id = builtins.toString (builtins.getAttr idAttr (builtins.getAttr name set));
|
id = builtins.toString (builtins.getAttr idAttr (builtins.getAttr name set));
|
||||||
@ -406,6 +394,7 @@ let
|
|||||||
{ inherit (u)
|
{ inherit (u)
|
||||||
name uid group description home createHome isSystemUser
|
name uid group description home createHome isSystemUser
|
||||||
password passwordFile hashedPassword
|
password passwordFile hashedPassword
|
||||||
|
isNormalUser subUidRanges subGidRanges
|
||||||
initialPassword initialHashedPassword;
|
initialPassword initialHashedPassword;
|
||||||
shell = utils.toShellPath u.shell;
|
shell = utils.toShellPath u.shell;
|
||||||
}) cfg.users;
|
}) cfg.users;
|
||||||
@ -567,16 +556,7 @@ in {
|
|||||||
# Install all the user shells
|
# Install all the user shells
|
||||||
environment.systemPackages = systemShells;
|
environment.systemPackages = systemShells;
|
||||||
|
|
||||||
environment.etc = {
|
environment.etc = (mapAttrs' (name: { packages, ... }: {
|
||||||
subuid = {
|
|
||||||
text = subuidFile;
|
|
||||||
mode = "0644";
|
|
||||||
};
|
|
||||||
subgid = {
|
|
||||||
text = subgidFile;
|
|
||||||
mode = "0644";
|
|
||||||
};
|
|
||||||
} // (mapAttrs' (name: { packages, ... }: {
|
|
||||||
name = "profiles/per-user/${name}";
|
name = "profiles/per-user/${name}";
|
||||||
value.source = pkgs.buildEnv {
|
value.source = pkgs.buildEnv {
|
||||||
name = "user-environment";
|
name = "user-environment";
|
||||||
|
@ -23,6 +23,15 @@ in
|
|||||||
maintainers = [] ++ lib.teams.podman.members;
|
maintainers = [] ++ lib.teams.podman.members;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
imports = [
|
||||||
|
(
|
||||||
|
lib.mkRemovedOptionModule
|
||||||
|
[ "virtualisation" "containers" "users" ]
|
||||||
|
"All users with `isNormaUser = true` set now get appropriate subuid/subgid mappings."
|
||||||
|
)
|
||||||
|
];
|
||||||
|
|
||||||
options.virtualisation.containers = {
|
options.virtualisation.containers = {
|
||||||
|
|
||||||
enable =
|
enable =
|
||||||
@ -99,15 +108,6 @@ in
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
users = mkOption {
|
|
||||||
default = [];
|
|
||||||
type = types.listOf types.str;
|
|
||||||
description = ''
|
|
||||||
List of users to set up subuid/subgid mappings for.
|
|
||||||
This is a requirement for running rootless containers.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
@ -122,26 +122,6 @@ in
|
|||||||
registries = lib.mapAttrs (n: v: { registries = v; }) cfg.registries;
|
registries = lib.mapAttrs (n: v: { registries = v; }) cfg.registries;
|
||||||
};
|
};
|
||||||
|
|
||||||
users.extraUsers = builtins.listToAttrs (
|
|
||||||
(
|
|
||||||
builtins.foldl' (
|
|
||||||
acc: user: {
|
|
||||||
values = acc.values ++ [
|
|
||||||
{
|
|
||||||
name = user;
|
|
||||||
value = {
|
|
||||||
subUidRanges = [ { startUid = acc.offset; count = 65536; } ];
|
|
||||||
subGidRanges = [ { startGid = acc.offset; count = 65536; } ];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
];
|
|
||||||
offset = acc.offset + 65536;
|
|
||||||
}
|
|
||||||
)
|
|
||||||
{ values = []; offset = 100000; } (lib.unique cfg.users)
|
|
||||||
).values
|
|
||||||
);
|
|
||||||
|
|
||||||
environment.etc."containers/policy.json".source =
|
environment.etc."containers/policy.json".source =
|
||||||
if cfg.policy != {} then pkgs.writeText "policy.json" (builtins.toJSON cfg.policy)
|
if cfg.policy != {} then pkgs.writeText "policy.json" (builtins.toJSON cfg.policy)
|
||||||
else copyFile "${pkgs.skopeo.src}/default-policy.json";
|
else copyFile "${pkgs.skopeo.src}/default-policy.json";
|
||||||
|
@ -12,9 +12,6 @@ import ./make-test-python.nix (
|
|||||||
{ pkgs, ... }:
|
{ pkgs, ... }:
|
||||||
{
|
{
|
||||||
virtualisation.podman.enable = true;
|
virtualisation.podman.enable = true;
|
||||||
virtualisation.containers.users = [
|
|
||||||
"alice"
|
|
||||||
];
|
|
||||||
|
|
||||||
users.users.alice = {
|
users.users.alice = {
|
||||||
isNormalUser = true;
|
isNormalUser = true;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user