Skip to content

Instantly share code, notes, and snippets.

@jkachmar
Last active May 30, 2022 10:21
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jkachmar/15edbfd47c95a27fdee10dd27e020e7d to your computer and use it in GitHub Desktop.
Save jkachmar/15edbfd47c95a27fdee10dd27e020e7d to your computer and use it in GitHub Desktop.
#########################
# Machine Configuration #
#########################
{ config, inputs, lib, pkgs, unstable, ... }:
{
imports = [
./primary-user-nixos.nix
./desktop-apps.nix
./desktop-kde.nix
./hardware.nix
];
###################################
# Machine & Primary User Identity #
###################################
networking.hostName = "star-platinum";
networking.hostId = "68210a0b"; # Required for ZFS.
environment.etc."machine-id".text = "0aaab60e7c4a49d49c953e4972dbe443";
# System config for the primary user.
primary-user = {
home-manager.imports = [ "${inputs.impermanence}/home-manager.nix" ];
name = "jkachmar";
git.user.name = "jkachmar";
git.user.email = " ... ";
initialHashedPassword = " ... ";
# Declare base directories under which the primary account holder's
# persistent content shall be stored.
#
# TODO: Should there only be one base directory, with the "local" state
# automatically placed under `${config.primary-user.name}/home`?
persistence.base-directories = {
global = "/state/jkachmar";
home = "/state/jkachmar/home";
};
# System-level persistent state.
#
# TODO: Consider factoring these out... somehwere else?
persistence.global = {
logs.directories = [
"/var/log" # `journald` (et al.) logs.
];
misc.directories = [
"/etc/nixos" # NixOS system configuration files.
"/etc/fail2ban" # fail2ban state directory.
];
};
# User-level persistent state.
persistence.home.misc.directories = [
"code" # Misc. software projects.
];
};
}
#####################################
# Misc. NixOS desktop applications. #
#####################################
{ pkgs, ... }:
let
# # TODO: Move this out to an overlay (i.e. figure out how overlays are going
# # to work with a Flakes-based configuration).
# updated-signal-desktop = pkgs.signal-desktop.overrideAttrs (_oldAttrs: rec {
# version = "1.40.0";
# src = pkgs.fetchurl {
# url = "https://updates.signal.org/desktop/apt/pool/main/s/signal-desktop/signal-desktop_${version}_amd64.deb";
# sha256 = "1xd38a9mi23c4r873k37rzip68hfk3a4bk9j4j24v2kb3yvixrpp";
# };
# });
updated-signal-desktop = pkgs.signal-desktop.override {
spellcheckerLanguage = "en_US";
};
in
{
primary-user = {
home-manager.home.packages = with pkgs; [
# From `pkgs`.
discord
firefox
slack
updated-signal-desktop
];
persistence.home.misc.directories = [
# XDG config home directories.
".config/discord" # Discord config/local state.
".config/Signal" # Signal config/local state.
".config/Slack" # Slack config/local state.
# XXX: Is this really necessary to persist?
".cache/mozilla" # Firefox local cache.
".mozilla" # Firefox config/local state.
];
};
}
############################################
# NixOS desktop environment configuration. #
############################################
{ pkgs, ... }:
{
services.xserver = {
enable = true;
layout = "us";
# Use KDE as the desktop environment.
desktopManager.plasma5.enable = true;
displayManager.sddm.enable = true;
};
primary-user.home-manager.home.packages = with pkgs.kdeApplications; [
ark
kate
kcalc
pkgs.kdeconnect
pkgs.kdeplasma-addons
spectacle
];
# KDE's stateful components, which should be persisted across reboots.
#
# Since KDE clutters things up quite a bit, all of its state is organized
# under a dedicated subdirectory within the primary user's normal
# local persistence directory.
#
# TODO: Figure out a better abstraction for this...
primary-user.persistence.home.kde = {
directories = [
".config/glib-2.0"
".config/gtk-3.0"
".config/kdeconnect"
".config/session"
".config/xsettingsd"
".local/share/konsole"
];
files = [
".config/akregatorrc"
".config/baloofilerc"
".config/gtkrc"
".config/gtkrc-2.0"
".config/kactivitymanagerdrc"
".config/kactivitymanagerd-statsrc"
".config/kateschemarc"
".config/kcminputrc"
".config/kconf_updaterc"
".config/kded5rc"
".config/kdeglobals"
".config/kglobalshortcutsrc"
".config/khotkeysrc"
".config/kmixrc"
".config/konsolerc"
".config/kscreenlockerrc"
".config/ksmserverrc"
".config/ktimezonedrc"
".config/kwinrc"
".config/kwinrulesrc"
".config/kxkbrc"
".config/plasma-localerc"
".config/plasmanotifyrc"
".config/plasma-org.kde.plasma.desktop-appletsrc"
".config/plasmashellrc"
".config/powermanagementprofilesrc"
".config/spectaclerc"
# XXX: Looks like KDE will always recreate this...
# ".config/Trolltech.conf"
];
};
}
##############################################################################
# OS-agnostic configuration options for the system's primary account holder. #
##############################################################################
{ config, lib, options, pkgs, ... }:
let
inherit (lib) mkAliasDefinitions mkAliasOptionModule mkIf mkOption types;
cfg = config.primary-user;
in
{
options.primary-user.name = mkOption {
type = types.nullOr types.str;
default = null;
description = "The system primary account holder's username.";
};
options.primary-user.email = mkOption {
type = types.nullOr types.str;
default = null;
description = "The system primary account holder's main email address.";
};
# `mkAliasOptionModule` won't work here, as `home-manager` takes a `pkgs`
# argument which cannot be "mapped over".
#
# TODO: See if there isn't a better place to put this sort of thing...
options.primary-user.git.user.name = mkOption {
type = types.nullOr types.str;
default = null;
description = "The primary account holder's global git user name.";
};
options.primary-user.git.user.email = mkOption {
type = types.nullOr types.str;
default = null;
description = "The primary account holder's global git email address.";
};
# Convenience aliases for various configuration options relating to the
# system's primary account holder.
#
# NOTE: These aliases _must_ be OS-agnostic; this module will be imported by
# OS-specific modules which will provide the config implementation as well as
# any additional OS-specific options/aliases.
imports = [
# OS-agnostic option aliases.
(mkAliasOptionModule [ "primary-user" "user" ] [ "users" "users" cfg.name ])
(mkAliasOptionModule [ "primary-user" "description" ] [ "users" "users" cfg.name "description" ])
(mkAliasOptionModule [ "primary-user" "home" "directory" ] [ "users" "users" cfg.name "home" ])
(mkAliasOptionModule [ "primary-user" "uid" ] [ "users" "users" cfg.name "uid" ])
(mkAliasOptionModule [ "primary-user" "shell" ] [ "users" "users" cfg.name "shell" ])
# OS-agnostic `home-manager` option aliases.
(mkAliasOptionModule [ "primary-user" "home-manager" ] [ "home-manager" "users" cfg.name ])
];
config = mkIf (cfg.name != null) {
home-manager.users.${cfg.name}.programs.git = {
userName = mkAliasDefinitions options.primary-user.git.user.name;
userEmail = mkAliasDefinitions options.primary-user.git.user.email;
};
};
}
########################################################################
# NixOS configuration options for the system's primary account holder. #
########################################################################
{ config, lib, options, pkgs, ... }:
let
inherit (lib) mkAliasDefinitions mkAliasOptionModule mkDefault mkIf mkMerge mkOption types;
inherit (lib.options) showOption;
inherit (pkgs.stdenv.targetPlatform) isLinux;
cfg = config.primary-user;
in
{
options.primary-user.persistence.base-directories.global = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
The base directory for "global" persistent state associated with the
primary account holder.
This is state which typically resides under top-level directories such as
`/etc` or `/var`.
'';
};
options.primary-user.persistence.global =
let
suboptions = types.submodule {
options = {
directories = mkOption {
type = types.listOf types.str;
default = [ ];
description = ''
A list of directories that shall be bind-mounted to paths within
the given subdirectory of the user's global persistent storage
directory.
'';
};
files = mkOption {
type = types.listOf types.str;
default = [ ];
description = ''
A list of files that shall be symlinked to paths within the
given subdirectory of the user's global persistent storage
directory.
'';
};
};
};
in
mkOption {
default = { };
type = types.attrsOf suboptions;
description = ''
Persistent storage locations for "global" state declared by the primary
account holder.
Each attribute should be the relative path to a subdirectory within
the "global" persistent state directory.
'';
};
options.primary-user.persistence.base-directories.home = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
The base directory for persistent state associated with the primary
account holder's personal home directory.
This is state which typically resides under top-level directories such as
`/home/alice/.bash_history` or `/home/bob/.config/Signal`.
'';
};
options.primary-user.persistence.home =
let
suboptions = types.submodule {
options = {
directories = mkOption {
type = types.listOf types.str;
default = [ ];
description = ''
A list of directories under the primary user's home directory
that shall be bind-mounted to paths within the persistent state
directory.
'';
};
files = mkOption {
type = types.listOf types.str;
default = [ ];
description = ''
A list of files under the primary user's home directory that
shall be symlinked to paths within the persistent state
directory.
'';
};
};
};
in
mkOption {
default = { };
type = types.attrsOf suboptions;
description = ''
Persistent storage locations for state within the primary account
holder's personal home directory.
Each attribute should be the relative path to a subdirectory within
the "home" persistent state directory.
'';
};
imports = [
# OS-agnostic option aliases.
./primary-user-common.nix
# NixOS-specific option aliases.
(mkAliasOptionModule [ "primary-user" "initialHashedPassword" ] [ "users" "users" cfg.name "initialHashedPassword" ])
(mkAliasOptionModule [ "primary-user" "extraGroups" ] [ "users" "users" cfg.name "extraGroups" ])
(mkAliasOptionModule [ "primary-user" "isNormalUser" ] [ "users" "users" cfg.name "isNormalUser" ])
(mkAliasOptionModule [ "primary-user" "openssh" ] [ "users" "users" cfg.name "openssh" ])
];
# Define NixOS-specific `primary-user` configuration.
config = mkIf (cfg.name != null) (mkMerge [
# General primary-user configuration settings.
({
users.users.${cfg.name} = {
name = cfg.name;
uid = mkDefault 1000;
home = "/home/${cfg.name}";
isNormalUser = true;
extraGroups = [ "wheel" ];
};
# TODO: Should this use `lib.mkMerge` and _only_ specify the primary user
# as an allowed and/or trusted user?
nix = {
allowedUsers = [ "root" cfg.name ];
trustedUsers = [ "root" cfg.name ];
};
})
# Persistence for global state associated with the primary account holder.
(mkIf (cfg.persistence.base-directories.global != null) (
let
baseDir = cfg.persistence.base-directories.global;
subDirs = lib.attrNames cfg.persistence.global;
mkPersistDirs = subDir: {
"${baseDir}/${subDir}" = {
inherit (cfg.persistence.global.${subDir})
directories
files;
};
};
persistDirs = builtins.map mkPersistDirs subDirs;
in
{
environment.persistence =
lib.foldl' lib.recursiveUpdate { } persistDirs;
}
))
# Persistent state associated with the primary account holder's home
# directory
(mkIf (cfg.persistence.base-directories.home != null) (
let
baseDir = cfg.persistence.base-directories.home;
subDirs = lib.attrNames cfg.persistence.home;
mkPersistDirs = subDir: {
"${baseDir}/${subDir}" = {
inherit (cfg.persistence.home.${subDir})
directories
files;
# Allow other users (e.g. root) to access these directories/files.
allowOther = true;
};
};
persistDirs = builtins.map mkPersistDirs subDirs;
in
{
# Set up the user-level persistent state mount points and allow other
# users (e.g. root) to access files via the bind-mounted directories.
programs.fuse.userAllowOther = true;
primary-user.home-manager.home.persistence =
lib.foldl' lib.recursiveUpdate { } persistDirs;
}
))
]);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment