diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..072eb55 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +hosts/lithium/semi-secret-vars.nix filter=git-agecrypt diff=git-agecrypt diff --git a/.sops.yaml b/.sops.yaml new file mode 100644 index 0000000..b41d732 --- /dev/null +++ b/.sops.yaml @@ -0,0 +1,13 @@ +keys: + - &admin_jay age1mv8xtvkuuw3hphq5ytaekz7p8a4kht79uajyhy534uy9e5472fhqj5zpxu + - &server_lithium age148yre4vaxp6lm59rft24te46szawqyguf8znkrtpq7ud8tpteauqxkwyjl +creation_rules: + - path_regex: secrets/[^/]+\.(yaml|json|env|ini)$ + key_groups: + - age: + - *admin_jay + - *server_lithium + - path_regex: secrets/lithium/[^/]+\.(yaml|json|env|ini)$ + key_groups: + - age: + - *server_lithium diff --git a/README.md b/README.md new file mode 100644 index 0000000..2ec089f --- /dev/null +++ b/README.md @@ -0,0 +1,120 @@ +# nixos-config + +> Declarative, reproducible configuration for all my NixOS systems +> Covers workstation/gaming, laptop, and homelab/server use cases. + +## Overview + +This repository maanges **multiple NixOS systems** using a shared modular configuration. +It's designed to be **secure, composable, and automated** using modern Nix tooling. + +- **Laptop ("neon")**: Portable KVM/Swiss-Army Knife +- **Homelab Server ("lithium")**: Identity, Backups, Forgejo, Jellyfin +- **Workstation / Gaming ("titanium")**: Dev and Gaming /w Steam/Proton +- Secrets managed via `sops-nix` +- Deployable with `nixos-rebuild` (and soon `deploy-rs` or `nixos-anywhere`) + + +```mermaid +--- +title: How it all fits together +--- +graph TD + subgraph Entrypoint + flake + mkSystem + end + subgraph System Configuration + hosts + hosts_conf + hosts_hardware + end + subgraph User Configuration + users_default + users_home + end + subgraph Shared Modules + nixos_mods + home_mods + end + flake["flake.nix"] --> mkSystem["lib/mkSystem"] + + mkSystem --> hosts["hosts/{hostname}/default.nix"] + mkSystem --> users_default["users/{username}/default.nix"] + mkSystem -.->|if file exists| users_home["users/{username}/home.nix"] + + hosts --> nixos_mods@{ shape: docs, label: "modules/nixos/*"} + hosts --> hosts_conf["configuration.nix"] + hosts --> hosts_hardware["hardware.nix"] + + users_home --> home_mods@{ shape: docs, label: "modules/home/*"} +``` + +## How to use this? (Deployment) + +With [home-manager](https://github.com/nix-community/home-manager) included as +an input to the flake, and pulled into the hosts along with their users, this +will automatically apply updates to both the system and user environments. + + +```bash +# This will show what the flake has to offer. +nix flake show + +# Build a VM to test config +nixos-rebuild build-vm --flake .#hostname + +# Preview and apply changes on a nixOS system +nixos-rebuild dry-run --flake .#hostname +sudo nixos-rebuild switch --flake .#hostname + +# Preview and apply changes on a macOS system +darwin-rebuild dry-run --flake .#hostname +darwin-rebuild switch --flake .#hostname + +# Generate an Install ISO +nix build .#nixosConfigurations.installIso.config.system.build.images.iso + +# Verify the ISO contents +sudo mount -o loop result/iso/nixos-*.iso mnt +ls mnt +umount mnt +``` + +## Design Goals + +- **Reproducibility**: All systems can be rebuilt from this repo +- **Modularity**: Every services is a reusable module +- **Security**: Minimal trust, secrets managed explicitly +- **Composability**: Roles + services enable rapid provisioning + +## Directory Layout / Organization + +``` +├── flake.nix # sets inputs, imports lib functions, wires hosts and users +├── lib # functions to build flake outputs +├── hosts +│   ├── +│   │   ├── configuration.nix # imports from ../../modules/nixos +│   │   ├── hardware.nix # host specific hardware configuration +│   │   └── default.nix # entrypoint for host configuration +├── users +│   ├── +│   │   ├── default.nix # entrypoint for user configuration +│   │   └── home.nix # imports from ../../modules/home/ +├── modules # Reusable NixOS and Home-Manager Modules +│   ├── nixos # host configuration modules +│   └── home # home-manager modules +├── overlays # Custom Nixpkgs overlays that modify existing pacakges. +└── pkgs # Custom Nix packages (not in nixpkgs) + +``` + +## References + +- [@shazow](https://github.com/shazow/) and https://github.com/shazow/nixfiles/ +- [@ryan4yin] and the contributors and co-authors of [nixos-and-flakes-book](https://nixos-and-flakes.thiscute.world/) +- [@Mic92] for https://github.com/Mic92/sops-nix and https://blog.thalheim.io/ +- Various GitHub Projects found with searches [similar to this](https://github.com/search?q=language%3ANix+sops-nix.nixosModules.sops&type=code) +- https://nix.dev/ and https://search.nixos.org/ +- https://edolstra.github.io/pubs/phd-thesis.pdf diff --git a/configuration.nix b/configuration.nix deleted file mode 100644 index 3f05730..0000000 --- a/configuration.nix +++ /dev/null @@ -1,156 +0,0 @@ -# Edit this configuration file to define what should be installed on -# your system. Help is available in the configuration.nix(5) man page -# and in the NixOS manual (accessible by running ‘nixos-help’). - -{ config, pkgs, nixos-hardware, ... }: - -{ - imports = - [ # Include the results of the hardware scan. - #nixos-hardware.nixosModules.gpd-pocket-3 - ./hardware-configuration.nix - ]; - - # Bootloader. - boot.loader.systemd-boot.enable = true; - boot.loader.efi.canTouchEfiVariables = true; - - networking.hostName = "neon"; # Define your hostname. - # networking.wireless.enable = true; # Enables wireless support via wpa_supplicant. - - # Enable networking - networking.networkmanager.enable = true; - - # Set your time zone. - time.timeZone = "America/Chicago"; - - # Select internationalisation properties. - i18n.defaultLocale = "en_US.UTF-8"; - - i18n.extraLocaleSettings = { - LC_ADDRESS = "en_US.UTF-8"; - LC_IDENTIFICATION = "en_US.UTF-8"; - LC_MEASUREMENT = "en_US.UTF-8"; - LC_MONETARY = "en_US.UTF-8"; - LC_NAME = "en_US.UTF-8"; - LC_NUMERIC = "en_US.UTF-8"; - LC_PAPER = "en_US.UTF-8"; - LC_TELEPHONE = "en_US.UTF-8"; - LC_TIME = "en_US.UTF-8"; - }; - - # Enable the X11 windowing system. - services.xserver.enable = true; - - # Enable the GNOME Desktop Environment. - services.xserver.displayManager.gdm.enable = true; - services.xserver.desktopManager.gnome.enable = true; - - # Configure keymap in X11 - services.xserver.xkb.layout = "us"; - - # Enable CUPS to print documents. - # services.printing.enable = true; - - # Enable sound with pipewire. - # sound.enable = true; - hardware.pulseaudio.enable = false; - security.rtkit.enable = true; - services.pipewire = { - enable = true; - alsa.enable = true; - alsa.support32Bit = true; - pulse.enable = true; - # If you want to use JACK applications, uncomment this - #jack.enable = true; - - # use the example session manager (no others are packaged yet so this is enabled by default, - # no need to redefine it in your config for now) - #media-session.enable = true; - }; - - # Enable touchpad support (enabled default in most desktopManager). - # services.xserver.libinput.enable = true; - - # Define a user account. Don't forget to set a password with ‘passwd’. - users.users.jml = { - isNormalUser = true; - description = "Jay Looney"; - extraGroups = [ "networkmanager" "wheel" ]; - packages = with pkgs; [ - firefox - ]; - }; - - # Enable automatic login for the user. - services.displayManager.autoLogin.enable = true; - services.displayManager.autoLogin.user = "jml"; - - # Workaround for GNOME autologin: https://github.com/NixOS/nixpkgs/issues/103746#issuecomment-945091229 - systemd.services."getty@tty1".enable = false; - systemd.services."autovt@tty1".enable = false; - - # Allow unfree packages - nixpkgs.config.allowUnfree = true; - - nix.settings = { - experimental-features = [ "nix-command" "flakes" ]; - # substituters = ["https://hyprland.cachix.org"]; - # trusted-public-keys = ["hyprland.cachix.org-1:a7pgxzMz7+chwVL3/pzj6jIBMioiJM7ypFP8PwtkuGc="]; - }; - - # Do not forget to add an editor to edit configuration.nix! The Nano editor is also installed by default. - environment.systemPackages = with pkgs; [ - home-manager - neovim - git - yubikey-personalization - kitty # hyprland default term - swww # wallpaper - xdg-desktop-portal-gtk - xdg-desktop-portal-hyprland - xwayland - ]; - - services.udev.packages = with pkgs; [ - ]; - - programs.neovim.enable = true; - programs.neovim.defaultEditor = true; - environment.variables = { - EDITOR = "nvim"; - VISUAL = "nvim"; - }; - - # Some programs need SUID wrappers, can be configured further or are - # started in user sessions. - programs.hyprland = { - enable = true; - # following along with stackademic installing nixos with hyprland - xwayland.enable = true; - #xwayland.hidpi = true; - }; - # Hint electron apps to use wayland - environment.sessionVariables = { - NIXOS_OZONE_WL = "1"; - }; - # screen sharing /w hyp - services.dbus.enable = true; - - # Enable the OpenSSH daemon. - services.openssh.enable = true; - - # Open ports in the firewall. - # networking.firewall.allowedTCPPorts = [ ... ]; - # networking.firewall.allowedUDPPorts = [ ... ]; - # Or disable the firewall altogether. - # networking.firewall.enable = false; - - # This value determines the NixOS release from which the default - # settings for stateful data, like file locations and database versions - # on your system were taken. It‘s perfectly fine and recommended to leave - # this value at the release version of the first install of this system. - # Before changing this value read the documentation for this option - # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). - system.stateVersion = "23.05"; # Did you read the comment? -} diff --git a/flake.lock b/flake.lock index c642017..a50ca1f 100644 --- a/flake.lock +++ b/flake.lock @@ -1,15 +1,111 @@ { "nodes": { - "home-manager": { + "crane": { + "locked": { + "lastModified": 1731098351, + "narHash": "sha256-HQkYvKvaLQqNa10KEFGgWHfMAbWBfFp+4cAgkut+NNE=", + "owner": "ipetkov", + "repo": "crane", + "rev": "ef80ead953c1b28316cc3f8613904edc2eb90c28", + "type": "github" + }, + "original": { + "owner": "ipetkov", + "repo": "crane", + "type": "github" + } + }, + "disko": { "inputs": { - "nixpkgs": "nixpkgs" + "nixpkgs": [ + "nixpkgs" + ] }, "locked": { - "lastModified": 1731535640, - "narHash": "sha256-2EckCJn4wxran/TsRiCOFcmVpep2m9EBKl99NBh2GnM=", + "lastModified": 1757508292, + "narHash": "sha256-7lVWL5bC6xBIMWWDal41LlGAG+9u2zUorqo3QCUL4p4=", + "owner": "nix-community", + "repo": "disko", + "rev": "146f45bee02b8bd88812cfce6ffc0f933788875a", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "disko", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": [ + "lanzaboote", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1730504689, + "narHash": "sha256-hgmguH29K2fvs9szpq2r3pz2/8cJd2LPS+b4tfNFCwE=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "506278e768c2a08bec68eb62932193e341f55c90", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "lanzaboote", + "pre-commit-hooks-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "home-manager": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1749499854, + "narHash": "sha256-V1BgwiX8NjbRreU6LC2EzmuqFSQAHhoSeNlYJyZ40NE=", "owner": "nix-community", "repo": "home-manager", - "rev": "35b055009afd0107b69c286fca34d2ad98940d57", + "rev": "1df816c407d3a5090c8496c9b00170af7891f021", "type": "github" }, "original": { @@ -18,13 +114,39 @@ "type": "github" } }, + "lanzaboote": { + "inputs": { + "crane": "crane", + "flake-compat": "flake-compat", + "flake-parts": "flake-parts", + "nixpkgs": [ + "nixpkgs" + ], + "pre-commit-hooks-nix": "pre-commit-hooks-nix", + "rust-overlay": "rust-overlay" + }, + "locked": { + "lastModified": 1737639419, + "narHash": "sha256-AEEDktApTEZ5PZXNDkry2YV2k6t0dTgLPEmAZbnigXU=", + "owner": "nix-community", + "repo": "lanzaboote", + "rev": "a65905a09e2c43ff63be8c0e86a93712361f871e", + "type": "github" + }, + "original": { + "owner": "nix-community", + "ref": "v0.4.2", + "repo": "lanzaboote", + "type": "github" + } + }, "nixos-hardware": { "locked": { - "lastModified": 1731403644, - "narHash": "sha256-T9V7CTucjRZ4Qc6pUEV/kpgNGzQbHWfGcfK6JJLfUeI=", + "lastModified": 1749195551, + "narHash": "sha256-W5GKQHgunda/OP9sbKENBZhMBDNu2QahoIPwnsF6CeM=", "owner": "nixos", "repo": "nixos-hardware", - "rev": "f6581f1c3b137086e42a08a906bdada63045f991", + "rev": "4602f7e1d3f197b3cb540d5accf5669121629628", "type": "github" }, "original": { @@ -35,41 +157,112 @@ }, "nixpkgs": { "locked": { - "lastModified": 1731139594, - "narHash": "sha256-IigrKK3vYRpUu+HEjPL/phrfh7Ox881er1UEsZvw9Q4=", - "owner": "NixOS", + "lastModified": 1749285348, + "narHash": "sha256-frdhQvPbmDYaScPFiCnfdh3B/Vh81Uuoo0w5TkWmmjU=", + "owner": "nixos", "repo": "nixpkgs", - "rev": "76612b17c0ce71689921ca12d9ffdc9c23ce40b2", + "rev": "3e3afe5174c561dee0df6f2c2b2236990146329f", "type": "github" }, "original": { - "owner": "NixOS", + "owner": "nixos", "ref": "nixos-unstable", "repo": "nixpkgs", "type": "github" } }, - "nixpkgs_2": { + "nixpkgs-stable": { "locked": { - "lastModified": 1731139594, - "narHash": "sha256-IigrKK3vYRpUu+HEjPL/phrfh7Ox881er1UEsZvw9Q4=", - "owner": "nixos", + "lastModified": 1730741070, + "narHash": "sha256-edm8WG19kWozJ/GqyYx2VjW99EdhjKwbY3ZwdlPAAlo=", + "owner": "NixOS", "repo": "nixpkgs", - "rev": "76612b17c0ce71689921ca12d9ffdc9c23ce40b2", + "rev": "d063c1dd113c91ab27959ba540c0d9753409edf3", "type": "github" }, "original": { - "owner": "nixos", - "ref": "nixos-unstable", + "owner": "NixOS", + "ref": "nixos-24.05", "repo": "nixpkgs", "type": "github" } }, + "pre-commit-hooks-nix": { + "inputs": { + "flake-compat": [ + "lanzaboote", + "flake-compat" + ], + "gitignore": "gitignore", + "nixpkgs": [ + "lanzaboote", + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable" + }, + "locked": { + "lastModified": 1731363552, + "narHash": "sha256-vFta1uHnD29VUY4HJOO/D6p6rxyObnf+InnSMT4jlMU=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "cd1af27aa85026ac759d5d3fccf650abe7e1bbf0", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, "root": { "inputs": { + "disko": "disko", "home-manager": "home-manager", + "lanzaboote": "lanzaboote", "nixos-hardware": "nixos-hardware", - "nixpkgs": "nixpkgs_2" + "nixpkgs": "nixpkgs", + "sops-nix": "sops-nix" + } + }, + "rust-overlay": { + "inputs": { + "nixpkgs": [ + "lanzaboote", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1731897198, + "narHash": "sha256-Ou7vLETSKwmE/HRQz4cImXXJBr/k9gp4J4z/PF8LzTE=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "0be641045af6d8666c11c2c40e45ffc9667839b5", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "sops-nix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1747603214, + "narHash": "sha256-lAblXm0VwifYCJ/ILPXJwlz0qNY07DDYdLD+9H+Wc8o=", + "owner": "Mic92", + "repo": "sops-nix", + "rev": "8d215e1c981be3aa37e47aeabd4e61bb069548fd", + "type": "github" + }, + "original": { + "owner": "Mic92", + "repo": "sops-nix", + "type": "github" } } }, diff --git a/flake.nix b/flake.nix index f39bf46..b4a67f8 100644 --- a/flake.nix +++ b/flake.nix @@ -4,18 +4,60 @@ nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; nixos-hardware.url = "github:nixos/nixos-hardware"; home-manager.url = "github:nix-community/home-manager"; + home-manager.inputs.nixpkgs.follows = "nixpkgs"; + lanzaboote.url = "github:nix-community/lanzaboote/v0.4.2"; + lanzaboote.inputs.nixpkgs.follows = "nixpkgs"; + sops-nix.url = "github:Mic92/sops-nix"; + sops-nix.inputs.nixpkgs.follows = "nixpkgs"; + disko.url = "github:nix-community/disko"; + disko.inputs.nixpkgs.follows = "nixpkgs"; }; - outputs = inputs@{self, nixpkgs, nixos-hardware, ...}: + # https://nix.dev/tutorials/nix-language.html#named-attribute-set-argument + outputs = inputs@{self, nixpkgs, nixos-hardware, home-manager, sops-nix, lanzaboote, disko, ...}: + let + mkSystem = (import ./lib { + inherit nixpkgs home-manager inputs; + }).mkSystem; + in { + # NOTE: Run `nix flake show` to see what this flake has to offer. + # TODO: Enable automated formatting with something like numtide/treefmt-nix nixosConfigurations = { - neon = nixpkgs.lib.nixosSystem { - system = "x86_64-linux"; - modules = [ - # https://github.com/NixOS/nixos-hardware/blob/master/README.md#using-nix-flakes-support - nixos-hardware.nixosModules.gpd-pocket-3 - ./configuration.nix + neon = mkSystem { + hostname = "neon"; + users = [ "jml" ]; + }; + lithium = mkSystem { + hostname = "lithium"; + # extraModules = [ inputs.sops-nix.nixosModules.sops ]; + users = [ + "jml" + "breakglass" ]; }; + titanium = mkSystem { + hostname = "titanium"; + users = [ + "jml" + ]; + extraModules = []; + }; + # `nix build .#nixosConfigurations.installIso.config.system.build.isoImage` + # https://github.com/nix-community/nixos-generators + installIso = nixpkgs.lib.nixosSystem { + system = "x86_64-linux"; + modules = [ + "${nixpkgs}/nixos/modules/installer/cd-dvd/installation-cd-minimal.nix" + ]; + specialArgs = {inherit inputs;}; + }; }; + homeConfigurations = { + "jml" = home-manager.lib.homeManagerConfiguration { + modules = [ + ./users/jml/home.nix + ]; + }; + }; }; } diff --git a/git-agecrypt.toml b/git-agecrypt.toml new file mode 100644 index 0000000..b2ff6dc --- /dev/null +++ b/git-agecrypt.toml @@ -0,0 +1,2 @@ +[config] +"hosts/lithium/semi-secret-vars.nix" = ["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIP2SVmZ3iJF/rviKhTgkZOvu1fWr6G29K4u6yaxjZn4H jay@lithium"] diff --git a/hosts/lithium/README.md b/hosts/lithium/README.md new file mode 100644 index 0000000..1cdad59 --- /dev/null +++ b/hosts/lithium/README.md @@ -0,0 +1,26 @@ +# lithium + +This is my primary homelab host/NAS, previously powered by TrueNAS Scale/k3s. + +## Manual Actions + +Even with fully declarative Nix/Nixpkgs/NixOS at the end of the day there are +still some actions that need to be taken manually. + +- secrets configuration (both for SOPS and git-agecrypt semi-secrets) +- kanidm user management +- tailscale auth key +- jellyfin configuration via web-ui + +## Semi-Secrets + +`semi-secret-vars.nix` is using [git-agecrypt](https://github.com/vlaci/git-agecrypt) +and following a pattern I discovered here: + - https://github.com/nyawox/arcanum/blob/4629dfba1bc6d4dd2f4cf45724df81289230b61a/var/README.md + - https://github.com/vlaci/git-agecrypt + +Essentially there are some details I won't want exposed in the repository, but +I do want them available to all my nix modules. The main one being the domain. + +While it's not really a secret in the way a password is, consider this effort a +mitigation against ddos attacks and automated requests and login attempts. diff --git a/hosts/lithium/configuration.nix b/hosts/lithium/configuration.nix new file mode 100644 index 0000000..5023ef1 --- /dev/null +++ b/hosts/lithium/configuration.nix @@ -0,0 +1,13 @@ +{ config, pkgs, ... }: +{ + sops.defaultSopsFile = ./secrets/common.yaml; + networking.hostName = "lithium"; + networking.domain = config.vars.domain; + environment.systemPackages = with pkgs; [ + zfs + ]; + services.openssh.enable = true; + programs.mosh.enable = true; + system.stateVersion = "25.05"; +} + diff --git a/hosts/lithium/default.nix b/hosts/lithium/default.nix new file mode 100644 index 0000000..4a02b76 --- /dev/null +++ b/hosts/lithium/default.nix @@ -0,0 +1,16 @@ +{ inputs, ... }: +{ + imports = [ + ../../modules/nixos/base.nix + inputs.sops-nix.nixosModules.sops + ./hardware.nix + ./configuration.nix + ./semi-secret-vars.nix + ./services/caddy.nix + ./services/tailscale.nix + ./services/kanidm.nix + ./services/jellyfin.nix + ./services/uptime-kuma.nix + ./services/file-shares.nix + ]; +} diff --git a/hosts/lithium/hardware.nix b/hosts/lithium/hardware.nix new file mode 100644 index 0000000..e31edbc --- /dev/null +++ b/hosts/lithium/hardware.nix @@ -0,0 +1,63 @@ +# Do not modify this file! It was generated by ‘nixos-generate-config’ +# and may be overwritten by future invocations. Please make changes +# to /etc/nixos/configuration.nix instead. +{ config, lib, modulesPath, ... }: + +{ + imports = + [ (modulesPath + "/installer/scan/not-detected.nix") + ]; + + boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "mpt3sas" "nvme" "usbhid" "usb_storage" "sd_mod" "sr_mod" ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-intel" ]; + boot.extraModulePackages = [ ]; + + fileSystems."/" = + { device = "/dev/disk/by-uuid/12861f9f-ca29-444d-9af1-330699a79a78"; + fsType = "btrfs"; + options = [ "subvol=root" ]; + }; + + fileSystems."/boot" = + { device = "/dev/disk/by-uuid/2B64-2D7E"; + fsType = "vfat"; + options = [ "fmask=0077" "dmask=0077" ]; + }; + + fileSystems."/home" = + { device = "/dev/disk/by-uuid/12861f9f-ca29-444d-9af1-330699a79a78"; + fsType = "btrfs"; + options = [ "subvol=home" ]; + }; + + fileSystems."/nix" = + { device = "/dev/disk/by-uuid/12861f9f-ca29-444d-9af1-330699a79a78"; + fsType = "btrfs"; + options = [ "subvol=nix" ]; + }; + + swapDevices = + [ { device = "/dev/disk/by-uuid/df457998-5ffc-422b-a3c9-6e5734c88a41"; } + { device = "/dev/disk/by-uuid/f970449e-2eee-450b-b19f-6361f4fb3c16"; } + ]; + + # Required despite not booting from zfs, in order to make zfs.ko available to modprobe. + # https://openzfs.github.io/openzfs-docs/Getting%20Started/NixOS/index.html#installation + boot.supportedFilesystems = [ "zfs" ]; + boot.zfs.forceImportRoot = false; + networking.hostId = "97fd89d8"; + boot.zfs.extraPools = [ "tank" ]; + services.zfs.autoScrub.enable = true; + + # Enables DHCP on each ethernet and wireless interface. In case of scripted networking + # (the default) this is the recommended approach. When using systemd-networkd it's + # still possible to use this option, but it's recommended to use it in conjunction + # with explicit per-interface declarations with `networking.interfaces..useDHCP`. + networking.useDHCP = lib.mkDefault true; + # networking.interfaces.eno1.useDHCP = lib.mkDefault true; + # networking.interfaces.enp6s0.useDHCP = lib.mkDefault true; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; +} diff --git a/hosts/lithium/secrets/caddy.env b/hosts/lithium/secrets/caddy.env new file mode 100644 index 0000000..ea06e03 --- /dev/null +++ b/hosts/lithium/secrets/caddy.env @@ -0,0 +1,9 @@ +CLOUDFLARE_API_TOKEN=ENC[AES256_GCM,data:fNkufR8Zzubh1+sJWfEFwfB/YckqeNdxbYKZCZ0qWGDOjanfzjUzKQ==,iv:6FEzPk4OUxqywIz3ChxCvs9RsBK7ypkrLiY2VFCwWzA=,tag:BBvcCXr0BRevU13SfDOLeg==,type:str] +sops_age__list_0__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBCSldSdUdMYk9uQzZuS0tr\nTStEZ3owSE5xdndRVjYrdUN5SVc3cmZiM1VJCjhBa1ExL3hBWEZOWjVxU1ZMcUJR\nUTJJN0lyU2xCNUFzeUdtdDZOaWFMVEEKLS0tIEI2emxCbUxVYVl4a20ySFhab0dC\nZUxkNXhpTXRhZ1pPU0sybnRPakhuOGsKJ3wUExxovoJSAOz+A6lNsCquGgTconmP\ng3XSNwxPhqtKUdVrw4CvDFH2AuU0RCKSbOzO2SNe72pyuEhtME/01g==\n-----END AGE ENCRYPTED FILE-----\n +sops_age__list_0__map_recipient=age1mv8xtvkuuw3hphq5ytaekz7p8a4kht79uajyhy534uy9e5472fhqj5zpxu +sops_age__list_1__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBNSFlMZmdsOUlldnF5dENn\nRVBkMThsMEg5dHQ5UFhhK0RtbVJuWU5ZMURrCjNFTWo4K0w5SUpGMVd2c3NQODhR\nQlMrc3BYY0Z6VldvNVdiQ2ZFZ3V6OGcKLS0tIC8vcWNuYXp3NmF3MmdNRVgvbUdu\nZGxwdU12M1lTa1VLMGx2bVRheVc3c1EKFGvr6wx2CyDyT3QxkpFnw8p10AQz+FwT\n+vCmueVLskc9UZavETzbif2LIWewuPtNjqGNGZwrWFMUZcy8kyVXUQ==\n-----END AGE ENCRYPTED FILE-----\n +sops_age__list_1__map_recipient=age148yre4vaxp6lm59rft24te46szawqyguf8znkrtpq7ud8tpteauqxkwyjl +sops_lastmodified=2025-06-07T02:03:12Z +sops_mac=ENC[AES256_GCM,data:Uw17J9ZYOhNENlfGnFUsjvVm/9IT+hrDDt8UvIIWSMhT54gXIejEDv1IWtNho6JlSN0Wrnl5hEeDFUkHFJ4Ae9EW1FwSpGH2dy6iZ6OUMQMI2hgE28J9wk8c5kSKirhXEPvmCaTBIypQnL0NcKHCuaovKnt1PRRwLrqANLoo9iw=,iv:87f5cCHjqPby2VRhLBJThgCvkGp6sajxYnS/Oyb3hdo=,tag:Gn6a20W1WHVr/Zyvyyk5TQ==,type:str] +sops_unencrypted_suffix=_unencrypted +sops_version=3.10.2 diff --git a/hosts/lithium/secrets/common.yaml b/hosts/lithium/secrets/common.yaml new file mode 100644 index 0000000..341bc36 --- /dev/null +++ b/hosts/lithium/secrets/common.yaml @@ -0,0 +1,27 @@ +kanidm: + admin-password: ENC[AES256_GCM,data:wNE9qWAjfp8tf29sn1Q6GYrbw8g=,iv:uzg971jGIVyEkEbcOm2W8dy4wVgWiL+4Ph/f/bnieI0=,tag:/yY1okvnJLYGw2OLBd2Zdg==,type:str] + idm-admin-password: ENC[AES256_GCM,data:jIWaXUgHjhp0bP/DrF1m+plzcvE=,iv:nNpIkg9FTbCncih1/pAk4o7teuk7Gf/nPXyrnpFx4no=,tag:WhhsjtEdyS3Zw4F7uF9APg==,type:str] +sops: + age: + - recipient: age1mv8xtvkuuw3hphq5ytaekz7p8a4kht79uajyhy534uy9e5472fhqj5zpxu + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB5cFlReGMxV1R3QW1Vd1RU + WTgzNm5tbGhld3RMTGpMU1M2eVdoU0hmZGtNCmZLSTNOMk9IMDh6K2svRmRveGw3 + dlFUZ2lzTDBJWnBSTEhVTmVDOHVTdW8KLS0tIDhLWVZWSVJYM2x4YTlZWWZZZVNh + T2drb0p6TjZrZldpU0VUd0xmcVJUSk0KMjX3vr/74/HU7fmulefUHiNzwX8LcAes + ob3fabhMk9lmbuQk21rpoWbz3PNTfCQH63q+h7gLJTCCW2ISTvh/KQ== + -----END AGE ENCRYPTED FILE----- + - recipient: age148yre4vaxp6lm59rft24te46szawqyguf8znkrtpq7ud8tpteauqxkwyjl + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBpK3VvNUhHNmJUQVZlREJl + c1oyZWx4ekNEM0VqL3NKanNmSmZtQXNpcWdVCmpWcklLVnhWUisvcGZzV1NHN3p4 + bW4wL09wL01XaHpveGdmbU4rbEp5NmsKLS0tIElhQVRmS05xUmJIZlI0S1dyWGhV + OGtKdHVwbWY2akJTQkF4YzlnNWQzNU0K81PyJ1tOvwOohNu9iUkS8vE7UXFRnJab + 8OLHtzX7FrkIH8rO2D5vEL9gPmxUtNKc9Ad3sndQls/yfg4wJAYedA== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2025-06-07T02:02:46Z" + mac: ENC[AES256_GCM,data:7mWOon8Hs2oU40l1dx4tVE8yXgcKoxfUAzY8zbtTzXqCOhzTzhY4OZAZiu4RiUSIOm7dMdQbH9zSx0j+5e2k9QflfJDDM3rWfunTa7L8Bm8k9b/WjS0Fnb7OV0InO6tLxQwkTamMcc7ORrKxwHB5PwuXD+efeWNXveHo5GYgF+M=,iv:seh2Pzt+AmmxyD5hwh3VkLQTDMq0Gh9mV6J3QrtxcmM=,tag:jpXn2fht8wArB2KD/ZmbyA==,type:str] + unencrypted_suffix: _unencrypted + version: 3.10.2 diff --git a/hosts/lithium/semi-secret-vars.nix b/hosts/lithium/semi-secret-vars.nix new file mode 100644 index 0000000..1598e07 --- /dev/null +++ b/hosts/lithium/semi-secret-vars.nix @@ -0,0 +1,8 @@ +age-encryption.org/v1 +-> ssh-ed25519 rhvgyQ 8V5ehsrqPR8s2joIfdpZRYDQpwH5BXI1GgQ/Qcb/Wg4 +ZKRZkXT0uPbXzuXLsteW31GsKzZy1deUl1GdWeQB+4U +-> "f&DjVar +Lhe9DbPHOqqKQ9HDhJB2xbIkrsxFGm39Yzr1J+ZbJnWYx5FCdGCCIexmv3GJy94t + +--- qKkjS2aEWavCLldEwi4MUTlDoQuIu9tSRr5yoeZVQhs +bZ~lU"Up v$?;:Zu҅^ŊQ/MBɉf]Ξ)4PY-߅ܻ ؟l&e2OUqXMDS7E&usU#d#؂W9^k.07hs4CRIwU aʯMwiUrikb}yV>r[8s \ No newline at end of file diff --git a/hosts/lithium/services/caddy.nix b/hosts/lithium/services/caddy.nix new file mode 100644 index 0000000..86a39cf --- /dev/null +++ b/hosts/lithium/services/caddy.nix @@ -0,0 +1,41 @@ +{ config, pkgs, ... }: +{ + sops.secrets.caddy_env = { + sopsFile = ../secrets/caddy.env; + format = "dotenv"; + mode = "0440"; + owner = config.services.caddy.user; + group = config.services.caddy.group; + restartUnits = [ "caddy.service" ]; + }; + services.caddy = { + enable = true; + package = pkgs.caddy.withPlugins { + # NOTE: Occasionally specify @latest and update the new versions, and the result hash. + plugins = [ + "github.com/mholt/caddy-dynamicdns@v0.0.0-20250430031602-b846b9e8fb83" + "github.com/caddy-dns/cloudflare@v0.2.1" + ]; + + # NOTE: Built on 6/4/2025 + hash = "sha256-swskhAr7yFJX+qy0FR54nqJarTOojwhV2Mbk7+fyS0I="; + }; + # NOTE: Use Staging CA while testing, check `systemctl status caddy` + # to see if everything is working. + # acmeCA = "https://acme-staging-v02.api.letsencrypt.org/directory"; + + # TODO: Add Metrics with Prometheus & Grafana + environmentFile = config.sops.secrets.caddy_env.path; + globalConfig = '' + # acme_dns cloudflare {env.CLOUDFLARE_API_TOKEN} + dynamic_dns { + provider cloudflare {env.CLOUDFLARE_API_TOKEN} + domains { + ${config.networking.domain} @ + } + dynamic_domains + } + ''; + }; + networking.firewall.allowedTCPPorts = [ 80 443 ]; +} diff --git a/hosts/lithium/services/file-shares.nix b/hosts/lithium/services/file-shares.nix new file mode 100644 index 0000000..7bf60b2 --- /dev/null +++ b/hosts/lithium/services/file-shares.nix @@ -0,0 +1,78 @@ +{ ... }: +{ + # NOTE: We do need to guarantee this group exists. + # and manually provision users with `sudo smbpasswd -a $username` + users.groups.samba = {}; + services.samba = { + enable = true; + openFirewall = true; + + nmbd.enable = false; # NOTE: Disable NetBIOS responses. + # usershares.enable = true; # NOTE: Members of group "samba" can create usershares. + + # NOTE: Refer to https://www.samba.org/samba/docs/current/man-html/smb.conf.5.html + # to configure this service. + settings = { + global = { + workgroup = "WORKGROUP"; + "hosts allow" = "192.168.50."; + "hosts deny" = "ALL"; + "guest account" = "nobody"; + "map to guest" = "bad user"; + + "log file" = "/var/log/samba/%m.log"; + "max log size" = 1000; + + "create mask" = "0660"; + "directory mask" = "2770"; + }; + + # NOTE: usershares enables users to create their own shares. This creates + # a share per-user. + homes = { + browseable = "no"; + writable = "yes"; + "read only" = "no"; + "guest ok" = "no"; + "valid users" = "%S"; + path = "/tank/shares/personal/%S"; + }; + + staging = { + comment = "Temp Upload Area"; + path = "/tank/shares/staging"; + browseable = "yes"; + writable = "yes"; + "guest ok" = "yes"; + "force user" = "nobody"; + "force group" = "nogroup"; + "create mask" = "0666"; + "directory mask" = "0777"; + }; + + backups = { + comment = "Device Backups"; + path = "/tank/shares/backups"; + browseable = "no"; + writable = "yes"; + "valid users" = "@samba"; + "guest ok" = "no"; + }; + + ## TODO: Time Machine Configuration + # http://wiki.nixos.org/wiki/Samba#Apple_Time_Machine + }; + }; + + # NOTE: This is used to advertise shares to Windows hosts. + services.samba-wsdd = { + enable = true; + openFirewall = true; + }; + #services.avahi = { + #enable = true; + #openFirewall = true; + #publish.enable = true; + #publish.userServices = true; + #}; +} diff --git a/hosts/lithium/services/jellyfin.nix b/hosts/lithium/services/jellyfin.nix new file mode 100644 index 0000000..7e6e7dc --- /dev/null +++ b/hosts/lithium/services/jellyfin.nix @@ -0,0 +1,20 @@ +{ config, pkgs, ... }: +let + svcDomain = "tv.${config.networking.domain}"; +in +{ + services.caddy.virtualHosts."${svcDomain}".extraConfig = '' + reverse_proxy :8096 + ''; + services.jellyfin = { + enable = true; + # NOTE: Keeping this open for now, for internal network use. + # ports 8096 for http and 8920 for https + openFirewall = true; + }; + environment.systemPackages = [ + pkgs.jellyfin + pkgs.jellyfin-web + pkgs.jellyfin-ffmpeg + ]; +} diff --git a/hosts/lithium/services/kanidm.nix b/hosts/lithium/services/kanidm.nix new file mode 100644 index 0000000..066652f --- /dev/null +++ b/hosts/lithium/services/kanidm.nix @@ -0,0 +1,126 @@ +{ config, pkgs, lib, ... }: +let + svcDomain = "id.${config.networking.domain}"; + caddyCertsRoot = "${config.services.caddy.dataDir}/.local/share/caddy/certificates"; + caddyCertsDir = "${caddyCertsRoot}/acme-v02.api.letsencrypt.org-directory"; + certsDir = "/var/lib/kanidm/certs"; +in +{ + # Example of yoinking certs from caddy: + # https://github.com/marcusramberg/nix-config/blob/e558914dd3705150511c5ef76278fc50bb4604f3/nixos/kanidm.nix#L3 + + # TODO: If possible, consider specifying the cert location here instead of the following kludge. + services.caddy.virtualHosts."${svcDomain}".extraConfig = '' + reverse_proxy :8443 { + transport http { + tls_server_name ${svcDomain} + } + } + ''; + + # NOTE: Attempted kludge due to caddy generating (and therefore owning the certs) + systemd.tmpfiles.rules = [ + "d ${certsDir} 0750 kanidm caddy -" + "C ${certsDir}/cert.pem - kanidm - - ${caddyCertsDir}/${svcDomain}/${svcDomain}.crt" + "C ${certsDir}/key.key - kanidm - - ${caddyCertsDir}/${svcDomain}/${svcDomain}.key" + ]; + systemd.services.kanidm = { + after = [ "systemd-tmpfiles-setup.service" ]; + requires = [ "caddy.service" "systemd-tmpfiles-setup.service" ]; + }; + users.users.kanidm.extraGroups = [ + "caddy" + ]; + + sops.secrets = { + "kanidm/admin-password" = { + group = "kanidm"; + mode = "440"; + }; + "kanidm/idm-admin-password" = { + group = "kanidm"; + mode = "440"; + }; + }; + + services.kanidm = { + package = pkgs.kanidmWithSecretProvisioning; + enableServer = true; + serverSettings = { + # NOTE: Required to start the server: https://kanidm.github.io/kanidm/stable/server_configuration.html + # domain, origin, tls_chain, tls_key + domain = svcDomain; + origin = "https://${svcDomain}"; + tls_chain = "${certsDir}/cert.pem"; + tls_key = "${certsDir}/key.key"; + + # NOTE: Optional Settings + ldapbindaddress = "127.0.0.1:3636"; # For Jellyfin LDAP integration. + + # trust_x_forwarded_for = true; + }; + + enableClient = true; + clientSettings.uri = config.services.kanidm.serverSettings.origin; + + # NOTE: POSIX accounts bound to LDAP assume 'anonymous' permissions. + # https://kanidm.github.io/kanidm/stable/integrations/pam_and_nsswitch.html + enablePam = true; + unixSettings = { + pam_allowed_login_groups = [ + "unix.admins" + ]; + home_attr = "uuid"; + home_alias = "name"; + }; + + # NOTE: There are manual steps required as root to allow a user to set + # their own credentials, or to confiugre an account as posix. As-is this + # module doesn't support provisioning a complete user /w credentials. + # Adding an account to `idm_high_privilege` prevents an account from being + # tampered with by any other admin accounts. + # https://kanidm.github.io/kanidm/stable/accounts/authentication_and_credentials.html#onboarding-a-new-person--resetting-credentials + provision = { + enable = true; + autoRemove = false; + adminPasswordFile = config.sops.secrets."kanidm/admin-password".path; + idmAdminPasswordFile = config.sops.secrets."kanidm/idm-admin-password".path; + + # NOTE: Basically all this can do is pair up a uuid with a collection of + # groups, and you still need to manually issue a reset token so that the + # user can create a Passekey and/or Password /w MFA. + persons = { + # https://kanidm.github.io/kanidm/stable/accounts/authentication_and_credentials.html#resetting-person-account-credentials + zenware = { + displayName = "zenware"; + groups = [ + "unix.admins" + "git.users" + "git.admins" + "tv.users" + ]; + }; + }; + groups = { + "unix.admins" = {}; + "git.users" = {}; + "git.admins" = {}; + "tv.users" = {}; + "tv.admins" = {}; + }; + }; + }; + + # NOTE: Allow Kanidm auth over SSH + services.openssh.settings = { + UsePAM = true; + PubkeyAuthentication = true; + PasswordAuthentication = true; + AuthorizedKeysCommand = "${ + lib.getExe' config.services.kanidm.package + "kanidm_ssh_authorizedkeys" + } %u"; + AuthorizedKeysCommandUser = "nobody"; + }; + +} diff --git a/hosts/lithium/services/tailscale.nix b/hosts/lithium/services/tailscale.nix new file mode 100644 index 0000000..4fee47f --- /dev/null +++ b/hosts/lithium/services/tailscale.nix @@ -0,0 +1,22 @@ +{ config, pkgs, ... }: +#let + #hostName = config.networking.hostName; + #tailnetName = "tail79151.ts.net"; + #svcDomain = "${hostName}.${tailnetName}"; +#in +{ + # NOTE: This does require a manual step of creating a tailscale account if + # you don't already have one, and generating an Auth Key: + # https://login.tailscale.com/admin/machines/new-linux + # After enabling this and generating an install script copy the authkey and + # run: `sudo tailscale up --auth-key=KEY` + + # NOTE: Use Caddy to create and manage SSL Certs for Tailscale + #services.caddy.virtualHosts."${svcDomain}".extraConfig = '' + #reverse_proxy : + #''; + services.tailscale = { + enable = true; + #permitCertUid = "caddy"; # Allow caddy to edit certs + }; +} diff --git a/hosts/lithium/services/uptime-kuma.nix b/hosts/lithium/services/uptime-kuma.nix new file mode 100644 index 0000000..c9669eb --- /dev/null +++ b/hosts/lithium/services/uptime-kuma.nix @@ -0,0 +1,18 @@ +{ config, pkgs, ... }: +let + svcDomain = "status.${config.networking.domain}"; + svcPort = config.services.uptime-kuma.settings.PORT; +in +{ + services.caddy.virtualHosts."${svcDomain}".extraConfig = '' + reverse_proxy :${svcPort} + ''; + # NOTE: Currently requires some web-interface configuration + # User must set up an admin account, monitors, and status pages manually. + services.uptime-kuma = { + enable = true; + # NOTE: NixOS Attributes here resolve into these ENV vars: + # https://github.com/louislam/uptime-kuma/wiki/Environment-Variables + # settings = {}; + }; +} diff --git a/hosts/neon/configuration.nix b/hosts/neon/configuration.nix new file mode 100644 index 0000000..8dbe56d --- /dev/null +++ b/hosts/neon/configuration.nix @@ -0,0 +1,7 @@ +{ ... }: +{ + networking.hostName = "neon"; + networking.networkmanager.enable = true; + services.openssh.enable = true; + system.stateVersion = "23.05"; +} diff --git a/hosts/neon/default.nix b/hosts/neon/default.nix new file mode 100644 index 0000000..6d3d3e0 --- /dev/null +++ b/hosts/neon/default.nix @@ -0,0 +1,14 @@ +{ inputs, ... }: +{ + imports = [ + ../../modules/nixos/base.nix + ../../modules/nixos/audio.nix + ../../modules/nixos/desktop.nix + # https://github.com/NixOS/nixos-hardware/blob/master/README.md#using-nix-flakes-support + inputs.nixos-hardware.nixosModules.gpd-pocket-3 + # override from nixos-hardware + ({config, lib, ...}: { services.xserver.videoDrivers = lib.mkForce [ "modesetting" ]; }) + ./hardware-configuration.nix + ./configuration.nix + ]; +} diff --git a/hardware-configuration.nix b/hosts/neon/hardware-configuration.nix similarity index 97% rename from hardware-configuration.nix rename to hosts/neon/hardware-configuration.nix index 72f143e..3edb1c2 100644 --- a/hardware-configuration.nix +++ b/hosts/neon/hardware-configuration.nix @@ -1,7 +1,7 @@ # Do not modify this file! It was generated by ‘nixos-generate-config’ # and may be overwritten by future invocations. Please make changes # to /etc/nixos/configuration.nix instead. -{ config, lib, pkgs, modulesPath, ... }: +{ config, lib, modulesPath, ... }: { imports = diff --git a/hosts/titanium/configuration.nix b/hosts/titanium/configuration.nix new file mode 100644 index 0000000..ea4519f --- /dev/null +++ b/hosts/titanium/configuration.nix @@ -0,0 +1,20 @@ +{ config, lib, pkgs, ... }: +{ + powerManagement.enable = false; + networking.hostName = "titanium"; + networking.networkmanager.enable = true; + environment.systemPackages = with pkgs; [ + sbctl # Secure-Boot + helix nil # nice for editing '.nix' + discord + signal-desktop + obs-studio + ]; + # Hardware Specific programs... + #programs.ryzen-monitor-ng.enable = true; + #programs.rog-control-center.enable = true; + services.openssh.enable = true; + services.tailscale.enable = true; + networking.firewall.trustedInterfaces = [ "tailscale0" ]; + system.stateVersion = "25.05"; +} diff --git a/hosts/titanium/default.nix b/hosts/titanium/default.nix new file mode 100644 index 0000000..23d9076 --- /dev/null +++ b/hosts/titanium/default.nix @@ -0,0 +1,17 @@ +{ inputs, ... }: +{ + nixpkgs.config.allowUnfree = true; + imports = [ + ../../modules/nixos/base.nix + ../../modules/nixos/audio.nix + ../../modules/nixos/desktop.nix + ../../modules/nixos/gaming.nix + inputs.nixos-hardware.nixosModules.asus-rog-strix-x570e + #./hardware.nix + ./configuration.nix + ./nvidia.nix + ./secure-boot.nix + inputs.disko.nixosModules.disko + ./disko.nix + ]; +} diff --git a/hosts/titanium/disko.nix b/hosts/titanium/disko.nix new file mode 100644 index 0000000..f4da2dd --- /dev/null +++ b/hosts/titanium/disko.nix @@ -0,0 +1,82 @@ +{ ... }: +{ + # Based on: + # https://github.com/nix-community/disko/blob/master/example/luks-btrfs-subvolumes.nix + # + # Run with: + # `sudo nix --experimental-features "nix-command flakes" run github:nix-community/disko/latest -- --mode destroy,format,mount /tmp/disk-config.nix` + disko.devices = { + disk = { + main = { + type = "disk"; + device = "/dev/disk/by-path/pci-0000:08:00.0-ata-2"; + content = { + type = "gpt"; + partitions = { + ESP = { + size = "512M"; + type = "EF00"; + content = { + type = "filesystem"; + format = "vfat"; + mountpoint = "/boot"; + mountOptions = [ "umask=0077" ]; + }; + }; + luks = { + size = "100%"; # Full Disk Encryption + content = { + type = "luks"; + name = "crypted"; + # disable settings.keyFile if you want to use interactive password entry + # passwordFile = "/tmp/secret.key"; # Interactive + extraOpenArgs = [ + "--allow-discards" + "--perf-no_read_workqueue" + "--perf-no_write_workqueue" + ]; + settings = { + allowDiscards = true; + crypttabExtraOpts = [ "fido2-device=auto" "token-timeout=10" ]; + #keyFile = "/tmp/secret.key"; + }; + #additionalKeyFiles = [ "/tmp/additionalSecret.key" ]; + content = { + type = "btrfs"; + extraArgs = [ "-L" "nixos" "-f" ]; # What? + subvolumes = { + "/root" = { + mountpoint = "/"; + mountOptions = [ + "compress=zstd" + "noatime" + ]; + }; + "/home" = { + mountpoint = "/home"; + mountOptions = [ + "compress=zstd" + "noatime" + ]; + }; + "/nix" = { + mountpoint = "/nix"; + mountOptions = [ + "compress=zstd" + "noatime" + ]; + }; + "/swap" = { + mountpoint = "/.swapvol"; + swap.swapfile.size = "32G"; + }; + }; + }; + }; + }; + }; + }; + }; + }; + }; +} diff --git a/hosts/titanium/hardware.nix b/hosts/titanium/hardware.nix new file mode 100644 index 0000000..a2a8362 --- /dev/null +++ b/hosts/titanium/hardware.nix @@ -0,0 +1,61 @@ +# Do not modify this file! It was generated by ‘nixos-generate-config’ +# and may be overwritten by future invocations. Please make changes +# to /etc/nixos/configuration.nix instead. +{ config, lib, pkgs, modulesPath, ... }: + +{ + imports = + [ (modulesPath + "/installer/scan/not-detected.nix") + ]; + + boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "usbhid" "usb_storage" "sd_mod" ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-amd" ]; + boot.extraModulePackages = [ ]; + + fileSystems."/" = + { device = "/dev/disk/by-uuid/0b3de117-c34f-4cc6-81db-5b84ea46cd51"; + fsType = "btrfs"; + options = [ "subvol=root" ]; + }; + + boot.initrd.luks.devices."crypted".device = "/dev/disk/by-uuid/0ccc4028-c27e-4259-ade9-a2b2081722cb"; + + fileSystems."/.swapvol" = + { device = "/dev/disk/by-uuid/0b3de117-c34f-4cc6-81db-5b84ea46cd51"; + fsType = "btrfs"; + options = [ "subvol=swap" ]; + }; + + fileSystems."/boot" = + { device = "/dev/disk/by-uuid/219D-4579"; + fsType = "vfat"; + options = [ "fmask=0077" "dmask=0077" ]; + }; + + fileSystems."/home" = + { device = "/dev/disk/by-uuid/0b3de117-c34f-4cc6-81db-5b84ea46cd51"; + fsType = "btrfs"; + options = [ "subvol=home" ]; + }; + + fileSystems."/nix" = + { device = "/dev/disk/by-uuid/0b3de117-c34f-4cc6-81db-5b84ea46cd51"; + fsType = "btrfs"; + options = [ "subvol=nix" ]; + }; + + swapDevices = [ ]; + + # Enables DHCP on each ethernet and wireless interface. In case of scripted networking + # (the default) this is the recommended approach. When using systemd-networkd it's + # still possible to use this option, but it's recommended to use it in conjunction + # with explicit per-interface declarations with `networking.interfaces..useDHCP`. + networking.useDHCP = lib.mkDefault true; + # networking.interfaces.enp4s0.useDHCP = lib.mkDefault true; + # networking.interfaces.enp5s0.useDHCP = lib.mkDefault true; + # networking.interfaces.wlp3s0.useDHCP = lib.mkDefault true; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; +} diff --git a/hosts/titanium/nvidia.nix b/hosts/titanium/nvidia.nix new file mode 100644 index 0000000..66b3fa2 --- /dev/null +++ b/hosts/titanium/nvidia.nix @@ -0,0 +1,24 @@ +{ config, ... }: { + # GPU Things + # NOTE: The following command can be helpful when diagnosing GPU issues: + # `nix shell nixpkgs#vulkan-tools -c vulkaninfo --summary` + hardware.graphics = { + enable = true; + enable32Bit = true; + }; + services.xserver.videoDrivers = [ "nvidia" ]; + # NOTE: This acceptLicense thing was necessary for nvidia packages to begin + # working, and it seems undocumented in the usual places. + # I found it on a forum thread, and then inside the nixpkgs repo. + # https://discourse.nixos.org/t/nvidia-settings-and-nvidia-offload-not-found/37187/23 + # https://github.com/NixOS/nixpkgs/blob/master/pkgs/os-specific/linux/nvidia-x11/generic.nix#L65 + nixpkgs.config.nvidia.acceptLicense = true; + hardware.nvidia = { + package = config.boot.kernelPackages.nvidiaPackages.latest; + modesetting.enable = true; + open = true; + nvidiaSettings = true; + powerManagement.enable = false; + powerManagement.finegrained = false; + }; +} diff --git a/hosts/titanium/secure-boot.nix b/hosts/titanium/secure-boot.nix new file mode 100644 index 0000000..82f4d98 --- /dev/null +++ b/hosts/titanium/secure-boot.nix @@ -0,0 +1,7 @@ +{ pkgs, lib, inputs, ... }: { + imports = with inputs; [ lanzaboote.nixosModules.lanzaboote ]; + environment.systemPackages = [ pkgs.sbctl ]; + boot.loader.systemd-boot.enable = lib.mkForce false; + boot.lanzaboote.enable = true; + boot.lanzaboote.pkiBundle = "/var/lib/sbctl"; +} diff --git a/lib/default.nix b/lib/default.nix new file mode 100644 index 0000000..3926ed9 --- /dev/null +++ b/lib/default.nix @@ -0,0 +1,47 @@ +{ nixpkgs, home-manager, inputs, ... }: +{ + # It's not really that I care about whether a system is a desktop system or + # a server system, but moreso that I care about whether a system is headless or not. + # I also care about things like if it's darwin, or wsl. + mkSystem = { + hostname, + system ? "x86_64-linux", + users ? [], + extraModules ? [] + }: + let + hostModule = import ../hosts/${hostname} { inherit inputs; }; + userModules = map (name: + import ../users/${name} { + pkgs = nixpkgs.legacyPackages.${system}; + lib = nixpkgs.lib; + } + ) users; + + homeUserNames = builtins.filter (name: + builtins.pathExists ../users/${name}/home.nix + ) users; + + homeUsers = nixpkgs.lib.listToAttrs (map (name: { + name = name; + value = import ../users/${name}/home.nix { + username = name; + pkgs = nixpkgs.legacyPackages.${system}; + lib = nixpkgs.lib; + }; + }) homeUserNames); + in + nixpkgs.lib.nixosSystem { + inherit system; + modules = [ hostModule ] + ++ userModules + ++ extraModules + ++ (if homeUserNames != [] then [ + home-manager.nixosModules.home-manager + { + home-manager.backupFileExtension = "hm-bak"; + home-manager.users = homeUsers; + } + ] else []); + }; +} diff --git a/modules/nixos/audio.nix b/modules/nixos/audio.nix new file mode 100644 index 0000000..a897bb4 --- /dev/null +++ b/modules/nixos/audio.nix @@ -0,0 +1,15 @@ +{ pkgs, ... }: +{ + # For real-time audio/production consider: https://github.com/musnix/musnix + security.rtkit.enable = true; + services.pipewire = { + enable = true; + alsa.enable = true; + alsa.support32Bit = true; + pulse.enable = true; + }; + + environment.systemPackages = with pkgs; [ + pavucontrol + ]; +} diff --git a/modules/nixos/base.nix b/modules/nixos/base.nix new file mode 100644 index 0000000..036a5c9 --- /dev/null +++ b/modules/nixos/base.nix @@ -0,0 +1,110 @@ +{ config, pkgs, lib, ... }: +{ + nixpkgs.config.allowUnfree = true; + nix.settings = { + experimental-features = [ "nix-command" "flakes" ]; + }; + + # Default to systemd-boot + boot.loader.systemd-boot.enable = true; + boot.loader.efi.canTouchEfiVariables = true; + + # https://datatracker.ietf.org/doc/html/rfc8375 + networking.domain = "home.arpa"; + + time.timeZone = "America/Chicago"; + + i18n.defaultLocale = "en_US.UTF-8"; + i18n.extraLocaleSettings = { + LC_ADDRESS = "en_US.UTF-8"; + LC_IDENTIFICATION = "en_US.UTF-8"; + LC_MEASUREMENT = "en_US.UTF-8"; + LC_MONETARY = "en_US.UTF-8"; + LC_NAME = "en_US.UTF-8"; + LC_NUMERIC = "en_US.UTF-8"; + LC_PAPER = "en_US.UTF-8"; + LC_TELEPHONE = "en_US.UTF-8"; + LC_TIME = "en_US.UTF-8"; + }; + + console.font = null; # Kernel will automatically choose a font. + console.keyMap = "us"; + # 4-bit ANSI -> Catpuccin Mocha Colors: https://catppuccin.com/palette/ + console.colors = [ + "11111b" # black -> crust + "f38ba8" # red -> red + "a6e3a1" # green -> green + "fab387" # yellow -> peach + "89b4fa" # blue -> blue + "cba6f7" # magenta -> mauve + "74c7ec" # cyan -> sapphire + "6c7086" # white -> overlay 0 + "313244" # bright black (gray) -> surface 0 + "eba0ac" # bright red -> maroon + "94e2d5" # bright green -> teal + "f9e2af" # bright yellow -> yellow + "b4befe" # bright blue -> lavender + "f5c2e7" # bright magenta -> pink + "89dceb" # bright cyan -> sky + "cdd6f4" # bright white -> text + ]; + + networking.firewall.enable = true; + + # Installed on every NixOS Host. + environment.systemPackages = with pkgs; [ + wget curl + ripgrep + ]; + programs = { + less = { + enable = true; + # https://ascending.wordpress.com/2011/02/11/unix-tip-make-less-more-friendly/ + # https://www.topbug.net/blog/2016/09/27/make-gnu-less-more-powerful/ + envVariables = { + LESS = lib.concatStrings [ + "--quit-if-one-screen " + "--ignore-case " + "--long-prompt " + "--raw-control-chars " # raw ANSI colors + "--hilite-unread " # first unread line after forward screen + "--tabs=4 " + "--no-init " # Don't use termcap init/deinit strings. + ]; + # Render colors + # TODO: Figure out how to represent those termcap sequences properly. + #LESS_TERMCAP_mb=$'\E[1;31m' # begin bold + #LESS_TERMCAP_md=$'\E[1;36m' # begin blink + #LESS_TERMCAP_me=$'\E[0m' # reset bold/blink + #LESS_TERMCAP_so=$'\E[01;44;33m' # begin reverse video + #LESS_TERMCAP_se=$'\E[0m' # reset reverse video + #LESS_TERMCAP_us=$'\E[1;32m' # begin underline + #LESS_TERMCAP_ue=$'\E[0m' # reset underline + }; + }; + + git.enable = true; + htop.enable = true; + command-not-found.enable = false; + bat.enable = true; + bandwhich.enable = true; + + nano.enable = false; + neovim = { + enable = true; + defaultEditor = true; + + viAlias = true; + vimAlias = true; + + withRuby = true; + withPython3 = true; + withNodeJs = true; + + #configure = {}; + }; + }; + + # Services running on all machines + services.avahi.enable = true; # zeroconf/mDNS(.local) +} diff --git a/modules/nixos/desktop.nix b/modules/nixos/desktop.nix new file mode 100644 index 0000000..7251a84 --- /dev/null +++ b/modules/nixos/desktop.nix @@ -0,0 +1,40 @@ +{ pkgs, ... }: +{ + environment.systemPackages = with pkgs; [ + yubikey-personalization + xdg-desktop-portal-gtk + xdg-desktop-portal-hyprland + xwayland + rofi-wayland + waybar + hyprpaper + kitty # hyprland default term + swww # wallpaper + ]; + services.xserver.enable = true; + services.xserver.xkb.layout = "us"; + + services.displayManager.gdm.enable = true; + services.desktopManager.gnome.enable = true; + + services.printing.enable = true; + + programs.hyprland = { + enable = true; + withUWSM = true; + xwayland.enable = true; + }; + programs.hyprlock.enable = true; + # Hint electron apps to use wayland + environment.sessionVariables = { + NIXOS_OZONE_WL = "1"; + }; + # screen sharing /w hyp + services.dbus.enable = true; + + fonts.packages = with pkgs; [ + nerd-fonts.fira-code + nerd-fonts.iosevka + atkinson-hyperlegible + ]; +} diff --git a/modules/nixos/gaming.nix b/modules/nixos/gaming.nix new file mode 100644 index 0000000..68b2946 --- /dev/null +++ b/modules/nixos/gaming.nix @@ -0,0 +1,17 @@ +{ config, lib, pkgs, ... }: +{ + environment.systemPackages = with pkgs; [ + mangohud + protonup-qt + lutris + bottles + heroic + ]; + programs.steam = { + enable = true; + remotePlay.openFirewall = true; + localNetworkGameTransfers.openFirewall = true; + protontricks.enable = true; + gamescopeSession.enable = true; + }; +} diff --git a/users/breakglass/default.nix b/users/breakglass/default.nix new file mode 100644 index 0000000..9318130 --- /dev/null +++ b/users/breakglass/default.nix @@ -0,0 +1,17 @@ +{ pkgs, lib, ... }: +{ + users.users.breakglass = { + home = + if pkgs.stdenv.isLinux then + lib.mkDefault "/home/breakglass" + else if pkgs.stdenv.isDarwin then + lib.mkDefault "/Users/breakglass" + else + abort "Unsupported OS"; + } // lib.optionalAttrs pkgs.stdenv.isLinux { + isNormalUser = true; + extraGroups = [ "wheel" ]; + # NOTE: Generated with `mkpasswd` + hashedPassword = "$y$j9T$U7phasQYqMhxY8WXoiHL51$IHHDTreR4uZrvAC1Xusjy2M0yXkU.vLy3z6zBjZCFX."; + }; +} diff --git a/users/jml/default.nix b/users/jml/default.nix new file mode 100644 index 0000000..592e6ba --- /dev/null +++ b/users/jml/default.nix @@ -0,0 +1,18 @@ +{ pkgs, lib, ... }: +{ + programs.fish.enable = true; + users.users.jml = { + shell = pkgs.fish; + home = + if pkgs.stdenv.isLinux then + lib.mkDefault "/home/jml" + else if pkgs.stdenv.isDarwin then + lib.mkDefault "/Users/jml" + else + abort "Unsupported OS"; + } // lib.optionalAttrs pkgs.stdenv.isLinux { + isNormalUser = true; + extraGroups = [ "networkmanager" "wheel" "samba" ]; + initialHashedPassword = "$y$j9T$R9y36VAOEudqmyVVgyYLD1$xQktVMaRP9qiARiJ6KATvyH6VAL1IKSJoPAo7k4YNZ."; + }; +} diff --git a/users/jml/home.nix b/users/jml/home.nix new file mode 100644 index 0000000..e6170c6 --- /dev/null +++ b/users/jml/home.nix @@ -0,0 +1,166 @@ +{ username, pkgs, lib, ... }: +{ + nixpkgs.config.allowUnfree = true; + # The following line is needed if I start using hyprland Home Manager Module + #wayland.windowManager.sway.systemd.enable = false; + # NOTE: This file contains options that resolve under home-manager.users. + home = { + inherit username; + stateVersion = "25.05"; + sessionVariables = { + EDITOR = "hx"; + }; + + homeDirectory = + if pkgs.stdenv.isLinux then + lib.mkDefault "/home/${username}" + else if pkgs.stdenv.isDarwin then + lib.mkDefault "/Users/${username}" + else + abort "Unsupported OS"; + }; + home.packages = with pkgs; [ ] + # linux only + # TODO: Add a test for linux + desktop environment + ++ (lib.optionals pkgs.stdenv.isLinux [ + cfspeedtest + helix + nil + ]) + # linux + desktop manager + #++ (lib.optionals (pkgs.stdenv.isLinux && osConfig.services.desktopManager.enabled != null) + #[ + # firefox + #]) + # darwin only + ++ (lib.optionals pkgs.stdenv.isDarwin [ + cfspeedtest + ripgrep + ]); + + programs = { + fish.enable = true; + home-manager.enable = true; + bat.enable = true; + fzf.enable = true; + jq.enable = true; + btop.enable = true; + zellij.enable = true; + + # Matrix Chat Apps + element-desktop.enable = true; + nheko.settings = true; + + # Additions from Windows + obsidian.enable = true; + obs-studio.enable = true; + keepassxc.enable = true; + wezterm.enable = true; + ghostty.enable = true; + gpg.enable = true; + # onedrive.enable = true; + # thunderbird.enable = true; + # vdirsyncer.enable = true; + nushell.enable = true; + helix.enable = true; + zoxide.enable = true; + fd.enable = true; + }; + + programs.starship = { + enable = true; + settings = { + add_newline = false; + line_break.disabled = true; + aws.disabled = true; + gcloud.disabled = true; + }; + }; + + programs.firefox = { + enable = true; + policies = { + DontCheckDefaultBrowser = true; + DisableTelemetry = true; + DisableFirefoxStudies = true; + DisablePocket = true; + DisableFirefoxScreenshots = true; + + UserMessaging = { + UrlbarInterventions = false; + SkipOnboarding = true; + }; + FirefoxSuggest = { + WebSuggestions = false; + SponsoredSuggestions = false; + ImproveSuggest = false; + }; + EnableTrackingProtection = { + Value = true; + Cryptomining = true; + Fingerprinting = true; + }; + + Homepage.StartPage = "previous-session"; + FirefoxHome = { + Search = true; + TopSites = false; + SponsoredTopSites = false; + Highlights = false; + Pocket = false; + SponsoredPocket = false; + Snippets = false; + }; + + Handlers.schemes.element = { + action = "useSystemDefault"; + ask = false; + }; + + Preferences = { + "browser.urlbar.suggest.searches" = true; + "browser.tabs.tabMinWidth" = 75; + + "browser.aboutConfig.showWarning" = false; + "browser.warnOnQuitShortcut" = false; + + "browser.tabs.loadInBackground" = true; + "browser.in-content.dark-mode" = true; + }; + }; + profiles = { + default = { + id = 0; + name = "default"; + isDefault = true; + settings = { + "widget.disable-workspace-management" = true; + }; + search = { + force = true; + default = "ddg"; # DuckDuckGo + }; + }; + }; + }; + + programs.git = { + enable = true; + userName = "Jay Looney"; + userEmail = "jay.m.looney@gmail.com"; + aliases = { + ol = "log --oneline"; + }; + ignores = [ "*~" "*.swp" ]; + extraConfig = { + push.default = "simple"; + credential.helper = "cache --timeout=7200"; + init.defaultBranch = "main"; + log.decorate = "full"; + log.date = "iso"; + merge.conflictStyle = "diff3"; + }; + }; + + # services.podman.enable = true; +}