Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Users

{
  config,
  lib,
  ...
}:
let
  inherit (lib) mkOption types mdDoc;
  cfg = config.dr460nixed.users;

  ifTheyExist = groups: builtins.filter (group: builtins.hasAttr group config.users.groups) groups;
in
{
  options.dr460nixed.users = mkOption {
    description = mdDoc "User accounts to configure.";
    default = { };
    type = types.attrsOf (
      types.submodule (_: {
        options = {
          isNormalUser = mkOption {
            type = types.bool;
            default = true;
            description = mdDoc "Whether the user is a normal user.";
          };
          uid = mkOption {
            type = types.nullOr types.int;
            default = null;
            description = mdDoc "UID for the user.";
          };
          gid = mkOption {
            type = types.nullOr types.int;
            default = null;
            description = mdDoc "GID for the user.";
          };
          extraGroups = mkOption {
            type = types.listOf types.str;
            default = [
              "audio"
              "video"
              "wheel"
            ];
            description = mdDoc "Extra groups for the user.";
          };
          shellGroups = mkOption {
            type = types.listOf types.str;
            default = [ ];
            description = mdDoc "Groups to add only if they exist on the system.";
          };
          authorizedKeyFiles = mkOption {
            type = types.listOf types.path;
            default = [ ];
            description = mdDoc "SSH authorized key files.";
          };
          homeManager = mkOption {
            type = types.submodule (_: {
              options = {
                enable = lib.mkEnableOption "Enable home-manager for this user.";
                shellAliases = mkOption {
                  type = types.attrsOf types.str;
                  default = { };
                  description = mdDoc "User-specific shell aliases.";
                };
                fishAbbreviations = mkOption {
                  type = types.attrsOf types.str;
                  default = { };
                  description = mdDoc "User-specific fish abbreviations.";
                };
                git = {
                  userName = mkOption {
                    type = types.str;
                    default = "";
                    description = mdDoc "Git user name.";
                  };
                  userEmail = mkOption {
                    type = types.str;
                    default = "";
                    description = mdDoc "Git user email.";
                  };
                  signingKey = mkOption {
                    type = types.nullOr types.str;
                    default = null;
                    description = mdDoc "GPG signing key for git.";
                  };
                };
                stateVersion = mkOption {
                  type = types.str;
                  default = "26.05";
                  description = mdDoc "Home-manager state version.";
                };
              };
            });
            default = { };
            description = mdDoc "Home-manager configuration for this user.";
          };
        };
      })
    );
  };

  config = {
    users.deterministicIds =
      (lib.mapAttrs (_name: u: {
        inherit (u) uid gid;
      }) (lib.filterAttrs (_name: u: u.uid != null || u.gid != null) cfg))
      // {
        acme.uid = 999;
        acme.gid = 999;
        adbusers.uid = 998;
        adbusers.gid = 998;
        adguard.uid = 977;
        adguard.gid = 977;
        anubis.uid = 961;
        anubis.gid = 961;
        avahi.uid = 997;
        avahi.gid = 997;
        chaotic_op.uid = 996;
        chaotic_op.gid = 996;
        cloudflared.uid = 972;
        cloudflared.gid = 972;
        code-server.uid = 967;
        code-server.gid = 967;
        dhcpcd.uid = 976;
        dhcpcd.gid = 976;
        fwupd-refresh.uid = 975;
        fwupd-refresh.gid = 975;
        flatpak.uid = 974;
        flatpak.gid = 974;
        forgejo.uid = 963;
        forgejo.gid = 963;
        gamemode.uid = 969;
        gamemode.gid = 969;
        geoclue.uid = 959;
        geoclue.gid = 959;
        git.uid = 960;
        git.gid = 960;
        grafana.uid = 995;
        grafana.gid = 995;
        incus-admin.uid = 665;
        incus-admin.gid = 665;
        influxdb2.uid = 994;
        influxdb2.gid = 994;
        jellyfin.uid = 970;
        jellyfin.gid = 970;
        loki.uid = 993;
        loki.gid = 993;
        mandb.uid = 954;
        mandb.gid = 954;
        minecraft.uid = 973;
        minecraft.gid = 973;
        netdata.uid = 979;
        netdata.gid = 979;
        nixos.uid = 1001;
        nixos.gid = 1001;
        nm-iodine.uid = 992;
        nm-iodine.gid = 992;
        node-exporter.uid = 991;
        node-exporter.gid = 991;
        nscd.uid = 990;
        nscd.gid = 990;
        plocate.uid = 989;
        plocate.gid = 989;
        polkituser.uid = 988;
        polkituser.gid = 988;
        paperless.uid = 966;
        paperless.gid = 966;
        plasmalogin.uid = 958;
        plasmalogin.gid = 958;
        pcscd.uid = 953;
        pcscd.gid = 953;
        podman.uid = 968;
        podman.gid = 968;
        proc.uid = 971;
        proc.gid = 971;
        promtail.uid = 987;
        promtail.gid = 987;
        redis-paperless.uid = 965;
        redis-paperless.gid = 965;
        resolvconf.uid = 964;
        resolvconf.gid = 964;
        rtkit.uid = 986;
        rtkit.gid = 986;
        sshd.uid = 985;
        sshd.gid = 985;
        systemd-coredump.uid = 984;
        systemd-coredump.gid = 984;
        systemd-oom.uid = 983;
        systemd-oom.gid = 983;
        tailscale-tls.uid = 978;
        tailscale-tls.gid = 978;
        telegraf.uid = 982;
        telegraf.gid = 982;
        vnstatd.uid = 981;
        vnstatd.gid = 981;
        wakapi.uid = 962;
        wakapi.gid = 962;
        wireshark.uid = 957;
        wireshark.gid = 957;
        wpa_supplicant.uid = 956;
        wpa_supplicant.gid = 956;
        msr.gid = 955;
      };

    sops.secrets = {
      "passwords/root".neededForUsers = true;
    };

    users = {
      mutableUsers = false;
      users = lib.mkMerge [
        (lib.mapAttrs (name: u: {
          inherit (u) isNormalUser;
          extraGroups = u.extraGroups ++ ifTheyExist u.shellGroups;
          hashedPasswordFile = config.sops.secrets."passwords/${name}".path;
          home = "/home/${name}";
          openssh.authorizedKeys.keyFiles = u.authorizedKeyFiles;
          autoSubUidGidRange = false;
          subGidRanges = lib.mkIf config.virtualisation.podman.enable [
            {
              count = 65536;
              startGid = (if u.uid != null then u.uid else 1000) * 1000;
            }
          ];
          subUidRanges = [
            {
              count = 65536;
              startUid = (if u.uid != null then u.uid else 1000) * 1000;
            }
          ];
        }) cfg)
        {
          root = {
            hashedPassword = null;
            hashedPasswordFile = lib.mkForce config.sops.secrets."passwords/root".path;
          };
        }
      ];
    };

    security.sudo.extraRules = [
      {
        groups = [ "wheel" ];
        commands =
          let
            currentSystem = "/run/current-system/";
            storePath = "/nix/store/";
          in
          [
            {
              command = "${storePath}/*/bin/switch-to-configuration";
              options = [
                "SETENV"
                "NOPASSWD"
              ];
            }
            {
              command = "${currentSystem}/sw/bin/nix-store";
              options = [
                "SETENV"
                "NOPASSWD"
              ];
            }
            {
              command = "${currentSystem}/sw/bin/nixos-rebuild";
              options = [ "NOPASSWD" ];
            }
            {
              command = "${currentSystem}/sw/bin/nix-collect-garbage";
              options = [
                "SETENV"
                "NOPASSWD"
              ];
            }
            {
              command = "${currentSystem}/sw/bin/systemctl";
              options = [ "NOPASSWD" ];
            }
          ];
      }
    ];
  };
}