246 lines
6.8 KiB
Nix
246 lines
6.8 KiB
Nix
{
|
|
pkgs,
|
|
lib,
|
|
config,
|
|
inputs,
|
|
...
|
|
}:
|
|
with lib;
|
|
let
|
|
cfg = config.users;
|
|
in
|
|
{
|
|
options.users = {
|
|
profiles = mkOption {
|
|
type =
|
|
with types;
|
|
attrsOf (submodule {
|
|
options = {
|
|
uid = mkOption {
|
|
type = with types; nullOr int;
|
|
default = null;
|
|
description = "uid passthrough to base user configuration";
|
|
};
|
|
description = mkOption {
|
|
type = with types; nullOr str;
|
|
default = null;
|
|
description = "description passthrough to base user configuration";
|
|
};
|
|
admin = mkOption {
|
|
type = with types; bool;
|
|
default = false;
|
|
description = "add user to privileged groups";
|
|
};
|
|
sshLogin = mkOption {
|
|
type = with types; bool;
|
|
default = false;
|
|
description = "enable ssh authorized keys for user";
|
|
};
|
|
picture = mkOption {
|
|
type = with types; nullOr path;
|
|
default = null;
|
|
description = "path to user profile picture";
|
|
};
|
|
};
|
|
});
|
|
description = "preconfigured users with profile options";
|
|
};
|
|
|
|
adminGroups = mkOption {
|
|
type = with types; listOf str;
|
|
description = "groups to add privileged users to";
|
|
};
|
|
|
|
homeModules = mkOption {
|
|
type = with types; listOf anything;
|
|
description = "home manager modules imported into every profile";
|
|
};
|
|
|
|
home = {
|
|
size = mkOption {
|
|
type = with types; str;
|
|
default = "1G";
|
|
description = "default home tmpfs size, mounted to prevent accidentally filling up root";
|
|
};
|
|
persist = {
|
|
files = mkOption {
|
|
type =
|
|
with types;
|
|
listOf (oneOf [
|
|
str
|
|
(attrsOf str)
|
|
]);
|
|
default = [ ];
|
|
};
|
|
directories = mkOption {
|
|
type =
|
|
with types;
|
|
listOf (oneOf [
|
|
str
|
|
(attrsOf str)
|
|
]);
|
|
default = [ ];
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
config = {
|
|
users = {
|
|
users = mapAttrs (name: opts: {
|
|
inherit (opts) uid;
|
|
description = with opts; mkIf (description != null) description;
|
|
extraGroups = [ "dialout" ] ++ optionals opts.admin cfg.adminGroups;
|
|
openssh.authorizedKeys.keys = mkIf (
|
|
opts.sshLogin && config.services.openssh.enable
|
|
) config.global.auth.openssh.publicKeys;
|
|
hashedPasswordFile = "/nix/persist/shadow/${name}";
|
|
shell = pkgs.zsh;
|
|
isNormalUser = mkIf (name != "root") true;
|
|
}) cfg.profiles;
|
|
|
|
mutableUsers = false;
|
|
|
|
# base groups
|
|
adminGroups = [
|
|
"wheel"
|
|
"kvm"
|
|
"systemd-journal"
|
|
"networkmanager"
|
|
];
|
|
|
|
# base home modules in current directory
|
|
homeModules =
|
|
pipe ./. [
|
|
builtins.readDir
|
|
(filterAttrs (n: ty: ty == "directory" && builtins.pathExists ./${n}/home.nix))
|
|
(mapAttrsToList (n: _: ./${n}/home.nix))
|
|
]
|
|
++ [
|
|
{
|
|
options.passthrough = mkOption {
|
|
type = with types; attrsOf anything;
|
|
description = "passthrough values from nixos configuration";
|
|
};
|
|
}
|
|
];
|
|
|
|
# basic persistence
|
|
home.persist = {
|
|
directories = [
|
|
"src"
|
|
{
|
|
directory = ".gnupg";
|
|
mode = "0700";
|
|
}
|
|
{
|
|
directory = ".ssh";
|
|
mode = "0700";
|
|
}
|
|
{
|
|
directory = ".local/share/keyrings";
|
|
mode = "0700";
|
|
}
|
|
];
|
|
};
|
|
};
|
|
|
|
# mount tmpfs on each user's home directory with appropriate ownership
|
|
fileSystems = mapAttrs' (
|
|
name: opts:
|
|
nameValuePair
|
|
# nixpkgs quirk: accessing user configuration here causes infinite recursion
|
|
# this workaround ensures proper home directory path unless overridden elsewhere
|
|
(if name != "root" then "/home/${name}" else "/root")
|
|
{
|
|
device = "homefs";
|
|
fsType = "tmpfs";
|
|
options = [
|
|
"size=${cfg.home.size}"
|
|
"uid=${builtins.toString opts.uid}"
|
|
"gid=${builtins.toString cfg.groups.${cfg.users.${name}.group}.gid}"
|
|
"mode=700"
|
|
];
|
|
|
|
# impermanence sets permissions before filesystems are mounted
|
|
# this mounts filesystem in initrd therefore working around that bug
|
|
neededForBoot = true;
|
|
}
|
|
) cfg.profiles;
|
|
|
|
global.fs.zfs.mountpoints = mapAttrs' (
|
|
name: opts: nameValuePair "/nix/persist/home/${name}" "home/${name}"
|
|
) (filterAttrs (n: _: n != "root") config.users.profiles);
|
|
|
|
home-manager.users = mapAttrs (name: opts: {
|
|
imports =
|
|
with inputs;
|
|
cfg.homeModules
|
|
++ [
|
|
impermanence.homeManagerModules.impermanence
|
|
catppuccin.homeManagerModules.catppuccin
|
|
];
|
|
home.file.".face" = mkIf (opts.picture != null) {
|
|
source = opts.picture;
|
|
};
|
|
home.stateVersion = "23.11";
|
|
}) cfg.profiles;
|
|
|
|
system.activationScripts = mapAttrs' (
|
|
name: opts:
|
|
nameValuePair "${name}-profile-icon" {
|
|
deps = [ "users" ];
|
|
text =
|
|
let
|
|
iconDest = "/var/lib/AccountsService/icons/${name}";
|
|
userConf = pkgs.writeText "${name}-config" ''
|
|
[User]
|
|
Session=
|
|
Icon=${iconDest}
|
|
SystemAccount=false
|
|
'';
|
|
in
|
|
''
|
|
install -Dm 0444 ${opts.picture} ${iconDest}
|
|
install -Dm 0400 ${userConf} /var/lib/AccountsService/users/${name}
|
|
'';
|
|
}
|
|
) (filterAttrs (n: _: n != "root") config.users.profiles);
|
|
|
|
# set up standard persistence for users
|
|
# this is registered internally for each software's configuration
|
|
environment.persistence."/nix/persist" = {
|
|
users = (
|
|
mapAttrs (
|
|
name: _:
|
|
cfg.home.persist
|
|
// {
|
|
# root workaround, ugly but necessary
|
|
# cannot get it properly for the same reason
|
|
# mentioned above in fileSystems
|
|
home = mkIf (name == "root") "/root";
|
|
}
|
|
) cfg.profiles
|
|
);
|
|
|
|
hideMounts = true;
|
|
};
|
|
|
|
# enable passwordless sudo
|
|
security.sudo.wheelNeedsPassword = false;
|
|
|
|
# enable access in build-vm
|
|
virtualisation.vmVariant = {
|
|
users.users.koishi.password = "passwd";
|
|
users.users.koishi.hashedPasswordFile = mkForce null;
|
|
};
|
|
};
|
|
|
|
# this is for home components that need to extend nixos
|
|
imports = pipe ./. [
|
|
builtins.readDir
|
|
(filterAttrs (n: ty: ty == "directory" && builtins.pathExists ./${n}/nixos.nix))
|
|
(mapAttrsToList (n: _: ./${n}/nixos.nix))
|
|
];
|
|
}
|