nixos.users-groups: Set up subuid/subgid mappings for all normal users

This is required by (among others) Podman to run containers in rootless mode.

Other distributions such as Fedora and Ubuntu already set up these mappings.

The scheme with a start UID/GID offset starting at 100000 and increasing in 65536 increments is copied from Fedora.
This commit is contained in:
adisbladis 2020-04-28 22:30:34 +01:00
parent 0963cebfc5
commit 5733967290
No known key found for this signature in database
GPG key ID: 110BFAD44C6249B7
4 changed files with 66 additions and 54 deletions

View file

@ -281,3 +281,58 @@ foreach my $u (values %usersOut) {
}
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");

View file

@ -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 }:
let
id = builtins.toString (builtins.getAttr idAttr (builtins.getAttr name set));
@ -406,6 +394,7 @@ let
{ inherit (u)
name uid group description home createHome isSystemUser
password passwordFile hashedPassword
isNormalUser subUidRanges subGidRanges
initialPassword initialHashedPassword;
shell = utils.toShellPath u.shell;
}) cfg.users;
@ -567,16 +556,7 @@ in {
# Install all the user shells
environment.systemPackages = systemShells;
environment.etc = {
subuid = {
text = subuidFile;
mode = "0644";
};
subgid = {
text = subgidFile;
mode = "0644";
};
} // (mapAttrs' (name: { packages, ... }: {
environment.etc = (mapAttrs' (name: { packages, ... }: {
name = "profiles/per-user/${name}";
value.source = pkgs.buildEnv {
name = "user-environment";

View file

@ -23,6 +23,15 @@ in
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 = {
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 {
@ -122,26 +122,6 @@ in
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 =
if cfg.policy != {} then pkgs.writeText "policy.json" (builtins.toJSON cfg.policy)
else copyFile "${pkgs.skopeo.src}/default-policy.json";

View file

@ -12,9 +12,6 @@ import ./make-test-python.nix (
{ pkgs, ... }:
{
virtualisation.podman.enable = true;
virtualisation.containers.users = [
"alice"
];
users.users.alice = {
isNormalUser = true;