nixos/home/profile.nix
2025-01-13 11:52:09 +08:00

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))
];
}