diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fd099b8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,74 @@ +### Linux gitignore ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# Metadata left by Dolphin file manager, which comes with KDE Plasma +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +# Log files created by default by the nohup command +nohup.out + +### MacOS gitignore ### +# General +.DS_Store +__MACOSX/ +.AppleDouble +.LSOverride +Icon[] + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Nix gitignore ### +# Ignore build outputs from performing a nix-build or `nix build` command +result +result-* + +# Ignore automatically generated direnv output +.direnv + +### Vim gitignore ### +# Swap +[._]*.s[a-v][a-z] +# comment out the next line if you don't need vector files +!*.svg +[._]*.sw[a-p] +[._]s[a-rt-v][a-z] +[._]ss[a-gi-z] +[._]sw[a-p] + +# Session +Session.vim +Sessionx.vim + +# Temporary +.netrwhist +*~ +# Auto-generated tag files +tags +# Persistent undo +[._]*.un~ diff --git a/README.md b/README.md new file mode 100644 index 0000000..d650f9b --- /dev/null +++ b/README.md @@ -0,0 +1,157 @@ +# nix-dotfiles + +System and home configurations for my machines. + +| Machine | Role | OS | Arch | System config tool | Home config tool | +| -------- | --------------- | ------ | ------- | --------------------------- | ---------------- | +| anderson | server | linux | x86_64 | dpkg/Docker (not this repo) | home-manager | +| bosephus | server | linux | x86_64 | NixOS | home-manager | +| mcentire | server | linux | x86_64 | NixOS | home-manager | +| corianne | MacBook | darwin | aarch64 | nix-darwin | home-manager | +| odyssey | workstation | linux | x86_64 | Ansible | home-manager | + +## Quickstart + +### Home dotfiles + +> ![WARNING] +> Fedora systems will set this up automagically via Ansible. Follow the +[Fedora quickstart] instructions. + +Ensure Nix is installed, with the `nix` command and flakes enabled. I try to use +the [Determinate Nix installer] (with upstream Nix) to install Nix with these +options turned on by default. + +```bash +curl -fsSL https://install.determinate.systems/nix | sh -s -- install +``` + +Once Nix is installed, clone the repository to `~/.config/home-manager` and +initiate home-manager. + +```bash +git clone https://code.millironx.com/millironx/nix-dotfiles.git ~/.config/home-manager +nix run home-manager -- switch --flake ~/.config/home-manager#$USER@$(hostname -s) +``` + +In the case that the host has not been assigned a configuration within this repo +yet, pick a hostname with the same system OS, arch, and role as the target +system to get temporary dotfiles up and running. + +```bash +nix run home-manager -- switch --flake ~/.config/home-manager#millironx@anderson +``` + +Once an SSH (with or without GPG) key has been setup and added to the authorized +keys of the git server, switch the upstream to track an authorized (i.e. +read/write) version of the repo. + +```bash +cd ~/.config/home-manager +git remote set-url origin git@code.millironx.com:millironx/nix-dotfiles.git +cd - +``` + +### NixOS + +Switching to a flake-based config requires running as root. All of the following +commands are assumed to be running as root. + +Ensure that the `nix` command and flakes are enabled. + +```bash +sed -i '/^}/i nix.settings.experimental-features = [ "nix-command" "flakes" ];' /etc/nixos/configuration.nix +nixos-rebuild switch +``` + +> ![NOTE] +> To allow secret decryption in the system, the *machine*-specific SSH key must +> be added to the publicKeys attribute of all applicable secrets, and the +> `hardware-configuration.nix` file must be added to git. Copying arbitrary +> strings like SSH keys or disk UUIDs between systems can be painful, so it might +> be worth setting up the [home dotfiles] immediately after enabling flakes, +> then running these steps on the same machine to avoid typos. Alternatively, I +> might someday be smart enough to create an installer CD that automagically +> sets this up for me. + +Get the machine-specific public SSH key. + +```bash +cat /etc/ssh/ssh_host_ed25519_key.pub +``` + +On a separate machine, add the machine's SSH key to `./secrets.nix` and assign +it to any secrets it would need. + +```nix +let + bosephus-host = + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIxTfeg+GZsfmG8TuEV1xW1gXknAIKzZ3UjZ3guRY+EW root@nixos"; + bosephus-millironx = + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKaDPqRJHoqgY2pseh/mnhjaGWXprHk2s5I52LhHpHcF millironx@bosephus"; + odyssey-millironx = + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIN9Aj7BtQp1Roa0tgopDrUo7g2am5WJ43lO1d1fDUz45 millironx@odyssey"; + + system-administrators = [ + bosephus-millironx + odyssey-millironx + ]; +in { + "secrets/network-information.age".publicKeys = system-administrators + ++ [ bosephus-host ]; +} +``` + +Rekey the secrets, and push the updated secrets to the upstream repo. + +```bash +nix run github:ryantm/agenix -- --rekey +git add secrets.nix secrets/* +git commit -m "added $NEW_HOST to secrets" +git push +``` + +Copy the target machine's `hardware-configuration.nix` file to this repo's +`./systems/linux/hardware-configuration/$NEW_HOST.nix`, and be sure to update +the configuration to import its own `hardware-configuration`. + +```bash +cp /etc/nixos/hardware-configuration.nix ./systems/linux/hardware-configuration/$NEW_HOST.nix +``` + +```nix +{ config, pkgs, ... }: { + imports = [ + ./hardware-configuration/bosephus.nix + ]; +} +``` + +Commit and push the hardware configuration to the upstream repo. + +```bash +git add systems/linux/* +git commit -m "added $NEW_HOST hardware configuration" +git push +``` + +Now switch to the flake by pulling and switching in one step. + + +```bash +nixos-rebuild switch --flake git+https://code.millironx.com/millironx/nix-dotfiles#$(hostname -s) +``` + +### Fedora + +Fedora systems are managed using Ansible. + +TODO + +## Home settings + +TODO + +[determinate nix installer]: https://github.com/DeterminateSystems/nix-installer +[fedora quickstart]: #fedora +[home dotfiles]: #home-dotfiles diff --git a/bin/secret-translator.jl b/bin/secret-translator.jl new file mode 100644 index 0000000..c5dee39 --- /dev/null +++ b/bin/secret-translator.jl @@ -0,0 +1,32 @@ +#!/usr/bin/env julia +using TOML: TOML + +@show ENV["PATH"] + +function remove_podman_secrets() + run(`podman secret rm --all`) + return nothing +end #function + +function create_podman_secret(name, secret) + run( + addenv( + `podman secret create --env=true --replace $name SECRET`, + Dict("SECRET" => secret), + ), + ) + return nothing +end #function + +function parse_toml_secrets(file) + foreach(f -> create_podman_secret(f[1], f[2]), TOML.parsefile(file)) + return nothing +end #function + +function main() + remove_podman_secrets() + foreach(parse_toml_secrets, ARGS) + return 0 +end #function + +main() diff --git a/dotfiles/dolphinrc b/dotfiles/dolphinrc deleted file mode 100644 index 2f11ee4..0000000 --- a/dotfiles/dolphinrc +++ /dev/null @@ -1,24 +0,0 @@ -MenuBar=Disabled - -[DetailsMode] -PreviewSize=16 - -[General] -BrowseThroughArchives=true -EditableUrl=true -GlobalViewProps=false -ShowFullPath=true -ShowStatusBar=FullWidth -ShowZoomSlider=true -Version=202 - - -[KFileDialog Settings] -Places Icons Auto-resize=false -Places Icons Static Size=22 - -[MainWindow] -MenuBar=Disabled - -[PreviewSettings] -Plugins=audiothumbnail,avif,blenderthumbnail,comicbookthumbnail,cursorthumbnail,djvuthumbnail,ebookthumbnail,exrthumbnail,directorythumbnail,fontthumbnail,imagethumbnail,jpegthumbnail,jxl,kraorathumbnail,windowsexethumbnail,windowsimagethumbnail,mobithumbnail,opendocumentthumbnail,gsthumbnail,rawthumbnail,svgthumbnail,gdk-pixbuf-thumbnailer,ffmpegthumbs,gsf-office diff --git a/dotfiles/konsolerc b/dotfiles/konsolerc deleted file mode 100644 index 7f417e8..0000000 --- a/dotfiles/konsolerc +++ /dev/null @@ -1,9 +0,0 @@ -[Desktop Entry] -DefaultProfile=My Default.profile - -[MainWindow] -StatusBar=Disabled -ToolBarsMovable=Disabled - -[UiSettings] -ColorScheme=Default diff --git a/flake.lock b/flake.lock index a1cd1d3..6141529 100644 --- a/flake.lock +++ b/flake.lock @@ -14,11 +14,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1754433428, - "narHash": "sha256-NA/FT2hVhKDftbHSwVnoRTFhes62+7dxZbxj5Gxvghs=", + "lastModified": 1762618334, + "narHash": "sha256-wyT7Pl6tMFbFrs8Lk/TlEs81N6L+VSybPfiIgzU8lbQ=", "owner": "ryantm", "repo": "agenix", - "rev": "9edb1787864c4f59ae5074ad498b6272b3ec308d", + "rev": "fcdea223397448d35d9b31f798479227e80183f6", "type": "github" }, "original": { @@ -27,27 +27,6 @@ "type": "github" } }, - "crowdsec": { - "inputs": { - "flake-utils": "flake-utils", - "nixpkgs": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1752497357, - "narHash": "sha256-9epXn1+T6U4Kfyw8B9zMzbERxDB3VfaPXhVebtai6CE=", - "ref": "refs/heads/main", - "rev": "84db7dcea77f7f477d79e69e35fb0bb560232667", - "revCount": 42, - "type": "git", - "url": "https://codeberg.org/kampka/nix-flake-crowdsec.git" - }, - "original": { - "type": "git", - "url": "https://codeberg.org/kampka/nix-flake-crowdsec.git" - } - }, "flake-parts": { "inputs": { "nixpkgs-lib": [ @@ -69,23 +48,6 @@ "type": "github" } }, - "flake-utils": { - "inputs": { - "systems": "systems_2" - }, - "locked": { - "lastModified": 1731533236, - "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", - "type": "github" - }, - "original": { - "id": "flake-utils", - "type": "indirect" - } - }, "home-manager": { "inputs": { "nixpkgs": [ @@ -93,16 +55,16 @@ ] }, "locked": { - "lastModified": 1756679287, - "narHash": "sha256-Xd1vOeY9ccDf5VtVK12yM0FS6qqvfUop8UQlxEB+gTQ=", + "lastModified": 1764613336, + "narHash": "sha256-L979az28t/+SXvYw9qhOno5HLlDwkZOpz6LzCLnjmRM=", "owner": "nix-community", "repo": "home-manager", - "rev": "07fc025fe10487dd80f2ec694f1cd790e752d0e8", + "rev": "f3902b5d8767985680875ad86d028371100faeb3", "type": "github" }, "original": { "owner": "nix-community", - "ref": "release-25.05", + "ref": "release-25.11", "repo": "home-manager", "type": "github" } @@ -114,59 +76,117 @@ ] }, "locked": { - "lastModified": 1757432263, - "narHash": "sha256-qHn+/0+IOz5cG68BZUwL9BV3EO/e9eNKCjH3+N7wMdI=", + "lastModified": 1764161084, + "narHash": "sha256-HN84sByg9FhJnojkGGDSrcjcbeioFWoNXfuyYfJ1kBE=", "owner": "LnL7", "repo": "nix-darwin", - "rev": "1fef4404de4d1596aa5ab2bd68078370e1b9dcdb", + "rev": "e95de00a471d07435e0527ff4db092c84998698e", "type": "github" }, "original": { "owner": "LnL7", - "ref": "nix-darwin-25.05", + "ref": "nix-darwin-25.11", "repo": "nix-darwin", "type": "github" } }, + "nix-rosetta-builder": { + "inputs": { + "nixos-generators": "nixos-generators", + "nixpkgs": [ + "nixpkgs-darwin" + ] + }, + "locked": { + "lastModified": 1756177999, + "narHash": "sha256-aSbB7/jrt7ujiJ55f2uGhOo+usGxVSkqbAMVgg2jDls=", + "owner": "cpick", + "repo": "nix-rosetta-builder", + "rev": "ebb7162a975074fb570a2c3ac02bc543ff2e9df4", + "type": "github" + }, + "original": { + "owner": "cpick", + "repo": "nix-rosetta-builder", + "type": "github" + } + }, + "nixlib": { + "locked": { + "lastModified": 1736643958, + "narHash": "sha256-tmpqTSWVRJVhpvfSN9KXBvKEXplrwKnSZNAoNPf/S/s=", + "owner": "nix-community", + "repo": "nixpkgs.lib", + "rev": "1418bc28a52126761c02dd3d89b2d8ca0f521181", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nixpkgs.lib", + "type": "github" + } + }, + "nixos-generators": { + "inputs": { + "nixlib": "nixlib", + "nixpkgs": [ + "nix-rosetta-builder", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1737057290, + "narHash": "sha256-3Pe0yKlCc7EOeq1X/aJVDH0CtNL+tIBm49vpepwL1MQ=", + "owner": "nix-community", + "repo": "nixos-generators", + "rev": "d002ce9b6e7eb467cd1c6bb9aef9c35d191b5453", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nixos-generators", + "type": "github" + } + }, "nixpkgs": { "locked": { - "lastModified": 1757545623, - "narHash": "sha256-mCxPABZ6jRjUQx3bPP4vjA68ETbPLNz9V2pk9tO7pRQ=", + "lastModified": 1764522689, + "narHash": "sha256-SqUuBFjhl/kpDiVaKLQBoD8TLD+/cTUzzgVFoaHrkqY=", "owner": "nixos", "repo": "nixpkgs", - "rev": "8cd5ce828d5d1d16feff37340171a98fc3bf6526", + "rev": "8bb5646e0bed5dbd3ab08c7a7cc15b75ab4e1d0f", "type": "github" }, "original": { "owner": "nixos", - "ref": "nixos-25.05", + "ref": "nixos-25.11", "repo": "nixpkgs", "type": "github" } }, "nixpkgs-darwin": { "locked": { - "lastModified": 1757590060, - "narHash": "sha256-EWwwdKLMZALkgHFyKW7rmyhxECO74+N+ZO5xTDnY/5c=", + "lastModified": 1764572236, + "narHash": "sha256-hLp6T/vKdrBQolpbN3EhJOKTXZYxJZPzpnoZz+fEGlE=", "owner": "nixos", "repo": "nixpkgs", - "rev": "0ef228213045d2cdb5a169a95d63ded38670b293", + "rev": "b0924ea1889b366de6bb0018a9db70b2c43a15f8", "type": "github" }, "original": { "owner": "nixos", - "ref": "nixpkgs-25.05-darwin", + "ref": "nixpkgs-25.11-darwin", "repo": "nixpkgs", "type": "github" } }, "nixpkgs-unstable": { "locked": { - "lastModified": 1757034884, - "narHash": "sha256-PgLSZDBEWUHpfTRfFyklmiiLBE1i1aGCtz4eRA3POao=", + "lastModified": 1764642553, + "narHash": "sha256-mvbFFzVBhVK1FjyPHZGMAKpNiqkr7k++xIwy+p/NQvA=", "owner": "nixos", "repo": "nixpkgs", - "rev": "ca77296380960cd497a765102eeb1356eb80fed0", + "rev": "f720de59066162ee879adcc8c79e15c51fe6bfb4", "type": "github" }, "original": { @@ -184,11 +204,11 @@ ] }, "locked": { - "lastModified": 1757647720, - "narHash": "sha256-qf/utP3d1qBDl5R4yWUCt7E7CHTkw2NY8BEsS7lJ0dc=", + "lastModified": 1764683664, + "narHash": "sha256-Mr5HKf/bjAJ8H7/H0qJSk2BEV/OILkDIFKrGK0dUVUk=", "owner": "nix-community", "repo": "NUR", - "rev": "ef767aa25f9f917fe25d3848051f0e54ae42349f", + "rev": "b8b40e258cf4c959b06b7322648c87674633629b", "type": "github" }, "original": { @@ -207,11 +227,11 @@ ] }, "locked": { - "lastModified": 1756632588, - "narHash": "sha256-ydam6eggXf3ZwRutyCABwSbMAlX+5lW6w1SVZQ+kfSo=", + "lastModified": 1763909441, + "narHash": "sha256-56LwV51TX/FhgX+5LCG6akQ5KrOWuKgcJa+eUsRMxsc=", "owner": "nix-community", "repo": "plasma-manager", - "rev": "d47428e5390d6a5a8f764808a4db15929347cd77", + "rev": "b24ed4b272256dfc1cc2291f89a9821d5f9e14b4", "type": "github" }, "original": { @@ -222,11 +242,11 @@ }, "quadlet-nix": { "locked": { - "lastModified": 1754008153, - "narHash": "sha256-MYT1mDtSkiVg343agxgBFsnuNU3xS8vRy399JXX1Vw0=", + "lastModified": 1763141753, + "narHash": "sha256-XAHkOkLEWbRQZ6t/SowwOukrUfIneNQOC/UEQlTaPBU=", "owner": "SEIAROTg", "repo": "quadlet-nix", - "rev": "1b2d27d460d8c7e4da5ba44ede463b427160b5c4", + "rev": "211b5c626cf9ea91403b510e2ac5ca03a7194566", "type": "github" }, "original": { @@ -238,9 +258,9 @@ "root": { "inputs": { "agenix": "agenix", - "crowdsec": "crowdsec", "home-manager": "home-manager", "nix-darwin": "nix-darwin", + "nix-rosetta-builder": "nix-rosetta-builder", "nixpkgs": "nixpkgs", "nixpkgs-darwin": "nixpkgs-darwin", "nixpkgs-unstable": "nixpkgs-unstable", @@ -258,11 +278,11 @@ }, "locked": { "dir": "pkgs/firefox-addons", - "lastModified": 1757591399, - "narHash": "sha256-OlvNzfsqDok0y5PDY+2dK5T53GsxAdm1YGdYHjxAiHM=", + "lastModified": 1764648280, + "narHash": "sha256-xniOnxIx/qhm+maO4mb9BZ7FytcUhNeTm1Y/QBjNf8o=", "owner": "rycee", "repo": "nur-expressions", - "rev": "b7d4f61ce9db44ba82859e15f6e1c175959948e3", + "rev": "119826bd51ad1a8012e0585f3a073571a35a812e", "type": "gitlab" }, "original": { @@ -286,21 +306,6 @@ "repo": "default", "type": "github" } - }, - "systems_2": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } } }, "root": "root", diff --git a/flake.nix b/flake.nix index a5b90d9..3b4f7b7 100644 --- a/flake.nix +++ b/flake.nix @@ -3,8 +3,8 @@ inputs = { # Specify the source of Home Manager and Nixpkgs. - nixpkgs.url = "github:nixos/nixpkgs/nixos-25.05"; - nixpkgs-darwin.url = "github:nixos/nixpkgs/nixpkgs-25.05-darwin"; + nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11"; + nixpkgs-darwin.url = "github:nixos/nixpkgs/nixpkgs-25.11-darwin"; nixpkgs-unstable.url = "github:nixos/nixpkgs/nixpkgs-unstable"; # Inputs for both darwin and linux systems @@ -17,7 +17,7 @@ }; }; home-manager = { - url = "github:nix-community/home-manager/release-25.05"; + url = "github:nix-community/home-manager/release-25.11"; inputs.nixpkgs.follows = "nixpkgs"; }; nur = { @@ -30,10 +30,6 @@ }; # Linux-specific inputs - crowdsec = { - url = "git+https://codeberg.org/kampka/nix-flake-crowdsec.git"; - inputs.nixpkgs.follows = "nixpkgs"; - }; plasma-manager = { url = "github:nix-community/plasma-manager"; inputs = { @@ -45,23 +41,28 @@ # Darwin-specific inputs nix-darwin = { - url = "github:LnL7/nix-darwin/nix-darwin-25.05"; + url = "github:LnL7/nix-darwin/nix-darwin-25.11"; + inputs.nixpkgs.follows = "nixpkgs-darwin"; + }; + nix-rosetta-builder = { + url = "github:cpick/nix-rosetta-builder"; inputs.nixpkgs.follows = "nixpkgs-darwin"; }; }; outputs = { self, nix-darwin, nixpkgs, nixpkgs-darwin, nixpkgs-unstable - , home-manager, agenix, rycee-nurpkgs, nur, crowdsec, plasma-manager - , quadlet-nix, ... }: + , home-manager, agenix, rycee-nurpkgs, nur, plasma-manager, quadlet-nix + , nix-rosetta-builder, ... }: let mkHomeConfiguration = { hostname, arch ? "x86_64", os ? "linux" , desktop ? false, extraModules ? [ ] }: let system = "${arch}-${os}"; - pkgs = import nixpkgs { + syspkg = if os == "darwin" then nixpkgs-darwin else nixpkgs; + pkgs = import syspkg { inherit system; config.allowUnfree = true; - overlays = [ nur.overlays.default ]; + overlays = [ nur.overlays.default agenix.overlays.default ]; }; pkgs-unstable = import nixpkgs-unstable { inherit system; @@ -80,7 +81,7 @@ ] ++ (if desktop then [ ./homes/desktop.nix ] else [ ]) ++ (if (desktop && os == "linux") then [ ./homes/linux-desktop.nix - plasma-manager.homeManagerModules.plasma-manager + plasma-manager.homeModules.plasma-manager ] else [ ]) ++ extraModules; extraSpecialArgs = { @@ -101,16 +102,12 @@ "millironx@anderson" = mkHomeConfiguration { hostname = "anderson"; }; + "millironx@mcentire" = mkHomeConfiguration { hostname = "mcentire"; }; + "millironx@bosephus" = mkHomeConfiguration { hostname = "bosephus"; }; "tchristensen@beocat" = mkHomeConfiguration { hostname = "beocat"; }; - "millironx@harmony" = mkHomeConfiguration { - hostname = "harmony"; - arch = "aarch64"; - desktop = true; - }; - "millironx@odyssey" = mkHomeConfiguration { hostname = "odyssey"; desktop = true; @@ -126,8 +123,11 @@ }; agenix = agenix; }; - modules = - [ ./systems/darwin/corianne.nix agenix.darwinModules.default ]; + modules = [ + ./systems/darwin/corianne.nix + agenix.darwinModules.default + nix-rosetta-builder.darwinModules.default + ]; }; nixosConfigurations = { @@ -142,13 +142,14 @@ "mcentire" = nixpkgs.lib.nixosSystem { system = "x86_64-linux"; + specialArgs = { + home-manager-quadlet-nix = quadlet-nix.homeManagerModules.quadlet; + }; modules = [ ./systems/linux/mcentire.nix agenix.nixosModules.default + home-manager.nixosModules.home-manager quadlet-nix.nixosModules.quadlet - crowdsec.nixosModules.crowdsec - crowdsec.nixosModules.crowdsec-firewall-bouncer - { nixpkgs.overlays = [ crowdsec.overlays.default ]; } ]; }; }; diff --git a/homes/common.nix b/homes/common.nix index 2b452dd..78d70a0 100644 --- a/homes/common.nix +++ b/homes/common.nix @@ -1,13 +1,4 @@ -{ config, lib, pkgs, pkgs-unstable, ... }: -let - runic_version = "1.5.0"; - runic = pkgs.fetchFromGitHub { - owner = "fredrikekre"; - repo = "Runic.jl"; - rev = "v${runic_version}"; - hash = "sha256-y+kiBA94vUMHH0fEEBg7+c9PEgzjGqh6nCuSRnawhQI="; - }; -in { +{ config, lib, pkgs, pkgs-unstable, custom-pkgs, ... }: { imports = [ ./../programs/shells.nix ./../programs/bat.nix @@ -20,17 +11,8 @@ in { ]; home = { stateVersion = "23.11"; - file = { - ".local/bin/runic" = { - source = runic + "/bin/runic"; - executable = true; - }; - ".local/bin/git-runic" = { - source = runic + "/bin/git-runic"; - executable = true; - }; - }; packages = with pkgs; [ + agenix btop cowsay figlet @@ -44,7 +26,7 @@ in { jq julia-bin lynx - micromamba + mamba-cpp most nextflow p7zip @@ -52,6 +34,8 @@ in { pipx python3 zulu17 + custom-pkgs.jlfmt + custom-pkgs.runic ]; sessionVariables = { PAGER = "most"; @@ -83,24 +67,9 @@ in { "tailscale set --exit-node=$(tailscale exit-node suggest | awk '{print $4}' | head -n1)"; # tsed - TailScale Exit node Disconnect tsed = "tailscale set --exit-node="; + micromamba = "mamba"; }; sessionPath = [ "$HOME/.local/bin" ]; - activation = { - recordHmGitHash = lib.hm.dag.entryAfter [ "writeBoundary" ] '' - cd "$HOME/.config/home-manager" || exit 1 - if [ -z "$(${pkgs.git}/bin/git status --porcelain --untracked-files=no)" ]; then - run echo "$(${pkgs.git}/bin/git rev-parse HEAD)" | tee $HOME/.cache/hm-git-hash - else - run echo '*' | tee $HOME/.cache/hm-git-hash - fi - ''; - installRunic = lib.hm.dag.entryAfter [ "writeBoundary" ] '' - run ${pkgs.julia-bin}/bin/julia --project=@runic --startup-file=no -e 'using Pkg; Pkg.add(name="Runic", version="${runic_version}")' - ''; - installJuliaFormatter = lib.hm.dag.entryAfter [ "writeBoundary" ] '' - run ${pkgs.julia-bin}/bin/julia --project=@JuliaFormatter --startup-file=no -e 'using Pkg; Pkg.add(name="JuliaFormatter", version="2.1.6")' - ''; - }; }; programs = { home-manager.enable = true; diff --git a/homes/darwin.nix b/homes/darwin.nix index d3c2982..9cabb63 100644 --- a/homes/darwin.nix +++ b/homes/darwin.nix @@ -7,7 +7,7 @@ ]; home = { packages = with pkgs; [ - asitop + macpm pinentry_mac (pkgs.writeShellScriptBin "uq" '' xattr -rdv com.apple.quarantine "/Applications/$1.app" @@ -34,22 +34,6 @@ launchd = { enable = true; agents = { - ollama = { - enable = true; - config = { - Label = "local.home-manager.ollama"; - ProgramArguments = [ "${pkgs.ollama}/bin/ollama" "serve" ]; - RunAtLoad = true; - KeepAlive = true; - StandardOutPath = - "${config.home.homeDirectory}/Library/Logs/ollama.log"; - StandardErrorPath = - "${config.home.homeDirectory}/Library/Logs/ollama-error.log"; - EnvironmentVariables = { - PATH = "${lib.makeBinPath [ pkgs.ollama ]}:$PATH"; - }; - }; - }; freetube-sync = { enable = true; diff --git a/homes/desktop.nix b/homes/desktop.nix index 2a0790c..255db78 100644 --- a/homes/desktop.nix +++ b/homes/desktop.nix @@ -2,7 +2,6 @@ imports = [ ./../programs/firefox.nix - ./../programs/ghostty.nix ./../programs/zed.nix ./../services/gpg-agent.nix ]; @@ -23,7 +22,7 @@ nil nixd nixfmt-classic - ollama + nixos-rebuild quarto roboto-slab shellcheck diff --git a/homes/harmony.nix b/homes/harmony.nix deleted file mode 100644 index 2e48431..0000000 --- a/homes/harmony.nix +++ /dev/null @@ -1,95 +0,0 @@ -{ config, lib, pkgs, pkgs-unstable, ... }: { - # harmony is an Asahi Fedora box - # I don't use NixOS, so there are some programs that don't interact well with - # the base system (or won't even install) when installed from Nix. - # There is no uniform way to trigger dnf package installs from Nix, so I'm - # just going to list my packages here. I hope to create a custom script that - # mimics the ideas of a Brewfile someday - # TODO: Create a Brewfile equivalent for dnf - - # dnf repos: - # https://github.com/terrapkg/packages?tab=readme-ov-file - # https://pkgs.tailscale.com/stable/fedora/tailscale.repo - # https://packagecloud.io/filips/FirefoxPWA - - # copr repos: - # iucar/rstudio - - # dnf packages: - # apptainer - # chromium - # firefoxpwa - The nix version installs an "immutable" runtime, which simply launches extra browser windows on non-NixOS - # inkscape - # kate - # kdiff3 - # krita - # lutris - # musescore - # nextcloud-client - # nextcloud-client-dolphin - # obs-studio - # podman-compose - # podman-docker - # qownnotes - # qt - # rssguard - # rstudio-desktop - # steam - # supertuxkart - # tailscale - # thunderbird - # vlc - # vorta - The vorta package is aarch64 compatible, but you cannot see any icons, and it cannot access local ssh keys, so we have to use the dnf package instead - # yakuake - # zed - # zsh - # R - # https://downloads.sourceforge.net/project/mscorefonts2/rpms/msttcore-fonts-installer-2.6-1.noarch.rpm - home = { - username = "millironx"; - homeDirectory = "/home/millironx"; - # Signal desktop is not available in any other package repository for aarch64 linux - # Similarly, Bitwarden is non-functional in all other forms using a 16k page size - packages = with pkgs; [ - trayscale - veracrypt - pkgs-unstable.signal-desktop - pkgs.bitwarden-desktop - ]; - }; - programs = { - git = { - signing = { - key = "0x37A3041D1C8C4524!"; - signByDefault = true; - }; - }; - }; - services = { - gpg-agent = { sshKeys = [ "207D13371E19752A67AA2686C16354D9963821DB" ]; }; - }; - xdg = { - configFile = { - "nextflow.config".text = '' - params { - config_profile_description = 'harmony Asahi Linux local profile' - config_profile_contact = 'Thomas A. Christensen II <25492070+MillironX@users.noreply.github.com>' - config_profile_url = null - - max_memory = 12.GB - max_cpus = 12 - max_time = 7.d - } - - apptainer { - enabled = true - autoMounts = true - } - - process { - executor = 'local' - } - ''; - }; - }; -} diff --git a/homes/linux-desktop.nix b/homes/linux-desktop.nix index 6d78716..2516d48 100644 --- a/homes/linux-desktop.nix +++ b/homes/linux-desktop.nix @@ -113,20 +113,12 @@ in { configFile = { "plasma-workspace/env/ZED_WINDOW_DECORATIONS.sh".text = "export ZED_WINDOW_DECORATIONS=server"; - "dolphinrc".source = - mkOutOfStoreSymlink "${home-manager-repo}/dotfiles/dolphinrc"; - "konsolerc".source = - mkOutOfStoreSymlink "${home-manager-repo}/dotfiles/konsolerc"; "onedrive/config".text = '' force_session_upload = "true" delay_inotify_processing = "true" ''; - "yakuakerc".source = - mkOutOfStoreSymlink "${home-manager-repo}/dotfiles/yakuakerc"; }; dataFile = { - "konsole/My Default.profile".source = - mkOutOfStoreSymlink "${home-manager-repo}/dotfiles/MyDefault.profile"; "kio/servicemenus/kate.desktop".source = ./../dotfiles/kate.desktop; "kio/servicemenus/vlc.desktop".source = ./../dotfiles/vlc.desktop; "kio/servicemenus/word-to-pdf.desktop".source = diff --git a/homes/mcentire.nix b/homes/mcentire.nix new file mode 100644 index 0000000..82adbbc --- /dev/null +++ b/homes/mcentire.nix @@ -0,0 +1,8 @@ +{ ... }: { + home = { + username = "millironx"; + homeDirectory = "/home/millironx"; + }; + programs = { }; + services = { }; +} diff --git a/inventory.yaml b/inventory.yaml index ba53698..63f1f3c 100644 --- a/inventory.yaml +++ b/inventory.yaml @@ -3,20 +3,13 @@ ungrouped: hosts: localhost: ansible_connection: local - harmony: - ansible_connection: local odyssey: ansible_connection: local -asahi: - hosts: - harmony: - amd64: hosts: odyssey: fedora: hosts: - harmony: odyssey: diff --git a/modules/podman-secrets.nix b/modules/podman-secrets.nix new file mode 100644 index 0000000..fb99285 --- /dev/null +++ b/modules/podman-secrets.nix @@ -0,0 +1,167 @@ +# Warning to my future self: This module was "vibe coded" by Claude Sonnet 4.5. +# I originally had a hand-written module in here that did the secrets +# translation as the root user. I knew that I would want to support multiple +# secrets files and multiple users, and thought I should be able to create an +# arbitrary number of them, similar to how you can have an arbitrary number +# of `programs.firefox.profiles`. Unfortunately, even after looking at lots of +# example modules, I could not figure out the syntax (Nix has a serious lack +# of minimum working examples), so I broke down and asked Claude to rewrite it +# for me. Based on everything that I read, this seems to be exactly what I asked +# for. +# +# Here is the prompt I used to get here: +# [@Per service isolation on NixOS with Traefik](zed:///agent/thread/94cb8a22-ff0c-4772-a1a1-018b0107f334?name=Per+service+isolation+on+NixOS+with+Traefik) +# +# [@podman-secrets.nix](file:///home/millironx/.config/home-manager/modules/podman-secrets.nix) +# +# I originally wrote this module assuming that I would use Podman as root for +# all containers. I would like to fix the module to have as many secrets files +# processed as needed with services setup for each secret that is run as the +# appropriate user. Ideally, the syntax for working with secrets to be +# translated would be +# +# ```nix +# { config, ... }: { +# age.secrets = { +# "caddy.toml" = { +# file = ./../secrets/caddy.toml.age; +# owner = "caddy"; +# group = "caddy"; +# }; +# +# "authentik.toml" = { +# file = ./../secrets/authentik.toml.age; +# owner = "authentik"; +# group = "authentik"; +# }; +# +# "freshrss.toml" = { +# file = ./../secrets/freshrss.toml.age; +# owner = "freshrss"; +# group = "freshrss"; +# }; +# }; +# +# millironx.podman-secrets = with config; { +# caddy = { +# user = "caddy"; +# secrets-files = [ +# ./../not-really-secret.toml +# age.secrets."caddy.toml".path +# ]; +# }; +# authentik = { +# user = "authentik"; +# secrets-files = [ +# age.secrets."authentik.toml".path +# ]; +# }; +# freshrss = { +# user = "freshrss"; +# secrets-files = [ +# age.secrets."freshrss.toml".path +# ]; +# }; +# }; +# } +# ``` +# +# Can you help me rewrite the module to accomplish this? +# +{ config, lib, pkgs, ... }: +with lib; +let + cfg = config.millironx.podman-secrets; + + secret-translator = pkgs.writeScriptBin "secret-translator" + (builtins.readFile ./../bin/secret-translator.jl); + + # Submodule type for each service's secrets configuration + serviceSecretsType = types.submodule ({ config, name, ... }: { + options = { + user = mkOption { + type = types.str; + description = "User account to run the secrets translation service as"; + example = "caddy"; + }; + + secrets-files = mkOption { + type = types.listOf (types.either types.path types.string); + description = + "List of TOML files containing secrets to translate to Podman secrets"; + example = literalExpression '' + [ + "/run/agenix/caddy.toml" + ./../not-really-secret.toml + ] + ''; + default = [ ]; + }; + + ref = mkOption { + type = types.str; + description = + "Reference to the systemd service name for declaring dependencies"; + readOnly = true; + default = "podman-secrets-${name}.service"; + example = "podman-secrets-caddy.service"; + }; + }; + }); + + # Generate a systemd user service for each configured service + mkSecretsService = name: serviceCfg: + nameValuePair "podman-secrets-${name}" { + description = "Podman secrets converter service for ${name}"; + wantedBy = [ "default.target" ]; + + unitConfig.ConditionUser = "${serviceCfg.user}"; + + serviceConfig = { + Type = "oneshot"; + ProtectProc = "invisible"; + ExecStart = "${secret-translator}/bin/secret-translator ${ + lib.concatStringsSep " " serviceCfg.secrets-files + }"; + Environment = "PATH=/run/wrappers/bin:${ + lib.makeBinPath (with pkgs; [ shadow podman julia-lts-bin ]) + }"; + }; + }; + + # Filter out services with no secrets-files + enabledServices = + lib.filterAttrs (name: serviceCfg: serviceCfg.secrets-files != [ ]) cfg; + +in { + options.millironx.podman-secrets = mkOption { + type = types.attrsOf serviceSecretsType; + description = '' + Per-service Podman secrets configuration. + Each attribute creates a separate systemd user service that translates TOML secrets + files into Podman secrets. + ''; + example = literalExpression '' + { + caddy = { + user = "caddy"; + secrets-files = [ + ./../not-really-secret.toml + config.age.secrets."caddy.toml".path + ]; + }; + authentik = { + user = "authentik"; + secrets-files = [ + config.age.secrets."authentik.toml".path + ]; + }; + } + ''; + default = { }; + }; + + config = mkIf (enabledServices != { }) { + systemd.user.services = lib.mapAttrs' mkSecretsService enabledServices; + }; +} diff --git a/pkgs/default.nix b/pkgs/default.nix index 2e0c11e..8178242 100644 --- a/pkgs/default.nix +++ b/pkgs/default.nix @@ -1,6 +1,7 @@ { pkgs, ... }: - -{ - ark = pkgs.callPackage ./ark.nix { }; - sc4pac = pkgs.callPackage ./sc4pac.nix { }; +with pkgs; { + ark = callPackage ./ark.nix { }; + jlfmt = callPackage ./jlfmt.nix { }; + runic = callPackage ./runic.nix { }; + sc4pac = callPackage ./sc4pac.nix { }; } diff --git a/pkgs/jlfmt.nix b/pkgs/jlfmt.nix new file mode 100644 index 0000000..21916a4 --- /dev/null +++ b/pkgs/jlfmt.nix @@ -0,0 +1,12 @@ +{ pkgs, ... }: +with pkgs; +let + juliaWithPkgs = julia-bin.withPackages.override { setDefaultDepot = false; } + [ "JuliaFormatter" ]; + depotWithPkgs = runCommand "getDepot" { } '' + ${juliaWithPkgs}/bin/julia -e 'println(first(DEPOT_PATH))' | tee $out + ''; +in writeShellScriptBin "jlfmt" '' + export JULIA_DEPOT_PATH=$(< ${depotWithPkgs}) + exec ${juliaWithPkgs}/bin/julia --startup-file=no -e 'using JuliaFormatter; print(format_text(String(read(stdin))));' -- "$@" +'' diff --git a/pkgs/runic.nix b/pkgs/runic.nix new file mode 100644 index 0000000..b91b550 --- /dev/null +++ b/pkgs/runic.nix @@ -0,0 +1,12 @@ +{ pkgs, ... }: +with pkgs; +let + juliaWithRunic = + julia-bin.withPackages.override { setDefaultDepot = false; } [ "Runic" ]; + depotWithRunic = runCommand "getRunicDepot" { } '' + ${juliaWithRunic}/bin/julia -e 'println(first(DEPOT_PATH))' | tee $out + ''; +in writeShellScriptBin "runic" '' + export JULIA_DEPOT_PATH=$(< ${depotWithRunic}) + exec ${juliaWithRunic}/bin/julia --startup-file=no -e 'using Runic; exit(Runic.main(ARGS))' -- "$@" +'' diff --git a/playbooks/config.yaml b/playbooks/config.yaml index 4aa2dd4..156dc63 100644 --- a/playbooks/config.yaml +++ b/playbooks/config.yaml @@ -11,7 +11,7 @@ mode: "755" - name: Create Firefox DNS policy ansible.builtin.template: - src: "{{ playbook_dir }}/../templates/policies.json" + src: "policies.json" dest: /etc/firefox/policies/policies.json mode: "644" diff --git a/playbooks/nix.yaml b/playbooks/nix.yaml index 672725f..50d9952 100644 --- a/playbooks/nix.yaml +++ b/playbooks/nix.yaml @@ -45,7 +45,8 @@ register: home_manager_exists - name: Init home-manager ansible.builtin.shell: | - /nix/var/nix/profiles/default/bin/nix run home-manager -- switch --flake git+https://code.millironx.com/millironx/nix-dotfiles#{{ ansible_user_id }}@{{ ansible_hostname }} + /nix/var/nix/profiles/default/bin/nix run home-manager -- switch \ + --flake git+https://code.millironx.com/millironx/nix-dotfiles#{{ ansible_user_id }}@{{ ansible_hostname }} when: not home_manager_exists.stat.exists register: home_manager_init changed_when: home_manager_init.rc == 0 diff --git a/playbooks/packages.yaml b/playbooks/packages.yaml index 612acde..f13c01b 100644 --- a/playbooks/packages.yaml +++ b/playbooks/packages.yaml @@ -1,78 +1,33 @@ --- -# These are repos and packages that are useless or unavailable on Asahi Linux, -# or have completely separate install procedures. -- name: Configure amd64-specific dnf packages - hosts: amd64 - become: true - tasks: - - name: Install x86-specific dnf packages - ansible.builtin.dnf: - name: - - libdvdcss - - mkvtoolnix - - mpv - - protontricks - - x264 - - x264-libs - state: present - - name: Install VeraCrypt - ansible.builtin.dnf: - name: https://launchpad.net/veracrypt/trunk/1.26.20/+download/veracrypt-1.26.20-Fedora-40-x86_64.rpm - state: present - disable_gpg_check: true - -- name: Configure amd64-specific Flatpaks - hosts: amd64 - become: false - tasks: - - name: Install x86-specific Flatpaks - community.general.flatpak: - name: - - com.bitwarden.desktop - - com.slack.Slack - - dev.deedles.Trayscale - - org.signal.Signal - state: latest - method: user - remote: flathub - -- name: Configure Asahi Linux-specific dnf packages - hosts: asahi - become: true - tasks: - - name: Install aarch64-specific dnf packages - ansible.builtin.dnf: - name: - - veracrypt - -- name: Configure common (all arch) dnf packages +- name: Configure dnf packages hosts: fedora become: true tasks: - - name: Install common (all arch) dnf packages + - name: Install dnf packages ansible.builtin.dnf: name: - chromium - - firefoxpwa - fontconfig-devel - freetype-devel - fribidi-devel - - ghostty - inkscape - jq - kate - kdenlive - kdiff3 - krita + - libdvdcss - libjpeg-devel - libpng-devel - libtiff-devel - libwebp-devel - - musescore + - mkvtoolnix + - mpv - nextcloud-client - nextcloud-client-dolphin - obs-studio - onedrive + - protontricks - qownnotes - qt - rssguard @@ -82,6 +37,8 @@ - thunderbird - vlc - vorta + - x264 + - x264-libs - yakuake - zed - zsh @@ -92,6 +49,11 @@ name: https://downloads.sourceforge.net/project/mscorefonts2/rpms/msttcore-fonts-installer-2.6-1.noarch.rpm state: present disable_gpg_check: true + - name: Install VeraCrypt + ansible.builtin.dnf: + name: https://launchpad.net/veracrypt/trunk/1.26.20/+download/veracrypt-1.26.20-Fedora-40-x86_64.rpm + state: present + disable_gpg_check: true - name: Install rig (R installation manager) ansible.builtin.dnf: name: https://github.com/r-lib/rig/releases/download/latest/r-rig-latest-1.{{ ansible_architecture }}.rpm @@ -109,21 +71,24 @@ name: "*" state: latest # noqa: package-latest -- name: Configure common (all arch) Flatpaks +- name: Configure Flatpaks hosts: fedora become: false tasks: - - name: Install common (all arch) Flatpaks + - name: Install Flatpaks community.general.flatpak: name: + - com.bitwarden.desktop - com.github.tchx84.Flatseal - com.logseq.Logseq + - com.slack.Slack + - dev.deedles.Trayscale - io.freetubeapp.FreeTube - io.github.alainm23.planify - io.github.dweymouth.supersonic - io.openrct2.OpenRCT2 + - org.signal.Signal - org.zulip.Zulip - - net.ankiweb.Anki state: latest method: user remote: flathub diff --git a/playbooks/repos.yaml b/playbooks/repos.yaml index b771518..476dfdb 100644 --- a/playbooks/repos.yaml +++ b/playbooks/repos.yaml @@ -1,6 +1,6 @@ --- -- name: Configure amd64-specific package repositories - hosts: amd64 +- name: Configure dnf package repositories + hosts: fedora become: true tasks: - name: Install RPM Fusion free repository @@ -20,31 +20,6 @@ - name: Install Zotero COPR repository community.general.copr: name: "mozes/zotero7" - -# Asahi Linux comes with its own strange version of RPMFusion installed, so -# RPMFusion is installed only on amd64 systems. In addition, VeraCrypt and -# Zotero *are* available via COPR, but from different repos than their amd64 -# counterparts. -# Also, Asahi has its own version string, so we have to manually specify the -# chroot for COPR repos added via Ansible. This is handled automatically when -# using `dnf copr enable ...`, but not via Ansible. -- name: Configure Asahi Linux-specific package repositories - hosts: asahi - become: true - tasks: - - name: Install Zotero COPR repository - community.general.copr: - name: "isaksamsten/Zotero" - chroot: "fedora-{{ ansible_distribution_major_version }}-aarch64" - - name: Install VeraCrypt COPR repository - community.general.copr: - name: "architektapx/veracrypt" - chroot: "fedora-{{ ansible_distribution_major_version }}-aarch64" - -- name: Configure common (all arch) package repositories - hosts: fedora - become: true - tasks: - name: Install Tailscale repo ansible.builtin.yum_repository: name: tailscale-stable @@ -53,14 +28,6 @@ enabled: true gpgcheck: true gpgkey: https://pkgs.tailscale.com/stable/fedora/repo.gpg - - name: Install FirefoxPWA repository - ansible.builtin.yum_repository: - name: firefoxpwa - description: FirefoxPWA repository - baseurl: https://packagecloud.io/filips/FirefoxPWA/fedora/$releasever/$basearch - gpgcheck: true - gpgkey: https://packagecloud.io/filips/FirefoxPWA/gpgkey - enabled: true # Note that I still have to specify the chroot for COPR repos b/c of Asahi - name: Install RStudio copr repository community.general.copr: @@ -110,7 +77,7 @@ register: terra_priority changed_when: terra_priority.rc != 0 -- name: Configure Flathub remote +- name: Configure Flatpack remotes hosts: fedora become: false tasks: diff --git a/templates/policies.json b/playbooks/templates/policies.json similarity index 100% rename from templates/policies.json rename to playbooks/templates/policies.json diff --git a/programs/firefox.nix b/programs/firefox.nix index 520a4ef..ed6705f 100644 --- a/programs/firefox.nix +++ b/programs/firefox.nix @@ -1,4 +1,4 @@ -{ firefox-addons, buildFirefoxXpiAddon, lib, ... }: { +{ pkgs, firefox-addons, buildFirefoxXpiAddon, lib, ... }: { programs.firefox = { enable = true; package = @@ -31,49 +31,49 @@ }; }; containersForce = true; - extensions.packages = with firefox-addons; [ - bitwarden - multi-account-containers - floccus - libredirect - old-reddit-redirect - plasma-integration - pwas-for-firefox - ublock-origin - user-agent-string-switcher - web-archives - zotero-connector - (buildFirefoxXpiAddon rec { - pname = "always_in_container"; - version = "1.0.7"; - addonId = "{a1e9543e-5f73-4763-b376-04e53fd12cbd}"; - url = - "https://addons.mozilla.org/firefox/downloads/file/4032840/${pname}-${version}.xpi"; - sha256 = "sha256-bLxjL2P6Sd06q98MSHYRTNigtcjGwn/C2r4ANWCqKrw="; - meta = with lib; { - homepage = "https://github.com/tiansh/always-in-container"; - description = - "Chose a container every time you try to open a page out of a container"; - license = licenses.mpl20; - platforms = platforms.all; - }; - }) - (buildFirefoxXpiAddon rec { - pname = "open_with"; - version = "7.2.6"; - addonId = "openwith@darktrojan.net"; - url = - "https://addons.mozilla.org/firefox/downloads/file/3831723/${pname}-${version}.xpi"; - sha256 = "sha256-f9eGhLxg4UyVn4o5e4DRkraLWzj11SGto/GOwsJa9kg="; - meta = with lib; { - homepage = "https://darktrojan.github.io/openwith/"; - description = - "Quickly test out your web pages in Chrome, Edge, Safari, or Opera. Open With opens the current page in your other browsers with just two clicks."; - license = licenses.mpl20; - platforms = platforms.all; - }; - }) - ]; + extensions.packages = with firefox-addons; + [ + bitwarden + multi-account-containers + libredirect + old-reddit-redirect + ublock-origin + user-agent-string-switcher + zotero-connector + (buildFirefoxXpiAddon rec { + pname = "always_in_container"; + version = "1.0.7"; + addonId = "{a1e9543e-5f73-4763-b376-04e53fd12cbd}"; + url = + "https://addons.mozilla.org/firefox/downloads/file/4032840/${pname}-${version}.xpi"; + sha256 = "sha256-bLxjL2P6Sd06q98MSHYRTNigtcjGwn/C2r4ANWCqKrw="; + meta = with lib; { + homepage = "https://github.com/tiansh/always-in-container"; + description = + "Chose a container every time you try to open a page out of a container"; + license = licenses.mpl20; + platforms = platforms.all; + }; + }) + (buildFirefoxXpiAddon rec { + pname = "open_with"; + version = "7.2.6"; + addonId = "openwith@darktrojan.net"; + url = + "https://addons.mozilla.org/firefox/downloads/file/3831723/${pname}-${version}.xpi"; + sha256 = "sha256-f9eGhLxg4UyVn4o5e4DRkraLWzj11SGto/GOwsJa9kg="; + meta = with lib; { + homepage = "https://darktrojan.github.io/openwith/"; + description = + "Quickly test out your web pages in Chrome, Edge, Safari, or Opera. Open With opens the current page in your other browsers with just two clicks."; + license = licenses.mpl20; + platforms = platforms.all; + }; + }) + ] ++ (if pkgs.stdenv.hostPlatform.isDarwin then + [ ] + else + [ plasma-integration ]); search = { default = "Kagi"; privateDefault = "Milliron X Search"; @@ -246,7 +246,6 @@ "floccus_handmadeideas_org-browser-action" "7esoorv3_alefvanoon_anonaddy_me-browser-action" "plasma-browser-integration_kde_org-browser-action" - "firefoxpwa_filips_si-browser-action" "_d07ccf11-c0cd-4938-a265-2a4d6ad01189_-browser-action" # Web Archives "openwith_darktrojan_net-browser-action" "zotero_chnm_gmu_edu-browser-action" @@ -282,7 +281,6 @@ "floccus_handmadeideas_org-browser-action" "7esoorv3_alefvanoon_anonaddy_me-browser-action" "plasma-browser-integration_kde_org-browser-action" - "firefoxpwa_filips_si-browser-action" "ublock0_raymondhill_net-browser-action" "_d07ccf11-c0cd-4938-a265-2a4d6ad01189_-browser-action" "zotero_chnm_gmu_edu-browser-action" diff --git a/programs/ghostty.nix b/programs/ghostty.nix deleted file mode 100644 index bd91415..0000000 --- a/programs/ghostty.nix +++ /dev/null @@ -1,19 +0,0 @@ -{ pkgs, ... }: { - programs.ghostty = - let modifierKey = if pkgs.stdenv.isDarwin then "cmd" else "ctrl"; - in { - enable = true; - package = null; - enableBashIntegration = true; - enableZshIntegration = true; - settings = { - quick-terminal-position = "top"; - quick-terminal-screen = "main"; - quick-terminal-autohide = true; - quick-terminal-size = "50%,50%"; - keybind = "global:${modifierKey}+backquote=toggle_quick_terminal"; - macos-hidden = "always"; - linux-cgroup = "always"; - }; - }; -} diff --git a/programs/git.nix b/programs/git.nix index b4d790d..244e916 100644 --- a/programs/git.nix +++ b/programs/git.nix @@ -1,9 +1,11 @@ { ... }: { programs.git = { enable = true; - userName = "Thomas A. Christensen II"; - userEmail = "25492070+MillironX@users.noreply.github.com"; - extraConfig = { + settings = { + user = { + name = "Thomas A. Christensen II"; + email = "25492070+MillironX@users.noreply.github.com"; + }; core = { editor = "nvim"; }; credential = { helper = "store"; }; color = { ui = "auto"; }; @@ -40,6 +42,11 @@ }; merge = { conflictstyle = "zdiff3"; }; pull = { rebase = true; }; + "url \"ssh://git@github.com/\"".insteadOf = "https://github.com/"; + "url \"ssh://git@gitlab.com/\"".insteadOf = "https://gitlab.com/"; + "url \"ssh://git@codeberg.com/\"".insteadOf = "https://codeberg.com/"; + "url \"ssh://git@code.millironx.com/\"".insteadOf = + "https://code.millironx.com/"; }; }; } diff --git a/programs/konsole.nix b/programs/konsole.nix new file mode 100644 index 0000000..66043de --- /dev/null +++ b/programs/konsole.nix @@ -0,0 +1,23 @@ +{ ... }: { + programs.konsole = { + enable = true; + defaultProfile = "millironx"; + profiles.millironx = { + colorScheme = "Breeze"; + font = { + name = "MesloLGS NF"; + size = 10; + }; + extraConfig = { + "Cursor Options".CursorShape = 1; + General.RemoteTabTitleFormat = "[SSH] %H"; + "Interaction Options" = { + CopyTextAsHTML = false; + MouseWheelZoomEnabled = false; + UnderlineFilesEnabled = true; + }; + "Terminal Features".BlinkingCursorEnabled = true; + }; + }; + }; +} diff --git a/programs/plasma.nix b/programs/plasma.nix index ea0b6e7..2fdd769 100644 --- a/programs/plasma.nix +++ b/programs/plasma.nix @@ -1,7 +1,177 @@ { config, ... }: { + + imports = [ ./konsole.nix ./yakuake.nix ]; + programs.plasma = { enable = true; + overrideConfig = true; + shortcuts = { yakuake.toggle-window-state = "Ctrl+`"; }; + configFile = { + dolphinrc = { + DetailsMode.PreviewSize = 16; + General = { + BrowseThroughArchives = true; + EditableUrl = true; + GlobalViewProps = false; + ShowFullPath = true; + ShowStatusBar = "FullWidth"; + ShowZoomSlider = true; + }; + "KFileDialog Settings" = { + "Places Icons Auto-resize" = false; + "Places Icons Static Size" = 22; + }; + PreviewSettings.Plugins = + "audiothumbnail,avif,blenderthumbnail,comicbookthumbnail,cursorthumbnail,djvuthumbnail,ebookthumbnail,exrthumbnail,directorythumbnail,fontthumbnail,imagethumbnail,jpegthumbnail,jxl,kraorathumbnail,windowsexethumbnail,windowsimagethumbnail,mobithumbnail,opendocumentthumbnail,gsthumbnail,rawthumbnail,svgthumbnail,gdk-pixbuf-thumbnailer,ffmpegthumbs,gsf-office"; + }; + }; + input.mice = [{ + enable = true; + name = "Logitech M705"; + vendorId = "046d"; + productId = "406d"; + naturalScroll = true; + }]; + kwin = { + cornerBarrier = false; + titlebarButtons = { + left = [ "close" "minimize" "maximize" ]; + right = [ "help" ]; + }; + virtualDesktops = { + number = 2; + rows = 1; + }; + }; + panels = [ + ### Screen 0 panels ### + ##### Top: Fedora menu | App switcher | Menu bar | CPU monitor | Memory monitor | Network monitor | System tray | clock ##### + { + location = "top"; + floating = true; + height = 27; + lengthMode = "fill"; + opacity = "adaptive"; + hiding = "normalpanel"; + screen = 0; + widgets = [ + { kickoff = { icon = "fedora-logo-icon"; }; } + "org.kde.plasma.marginsseparator" + "org.kde.plasma.windowlist" + "org.kde.plasma.appmenu" + "org.kde.plasma.panelspacer" + { + name = "org.kde.plasma.systemmonitor.cpu"; + config = { + Appearance.chartFace = "org.kde.ksysguard.barchart"; + Sensors.highPrioritySensorIds = "[${ + builtins.concatStringsSep "," (builtins.genList + (i: ''"cpu/cpu${builtins.toString i}/usage"'') 12) + }]"; + }; + } + { + name = "org.kde.plasma.systemmonitor.memory"; + config.Appearance.chartFace = "org.kde.ksysguard.horizontalbars"; + } + { + name = "org.kde.plasma.systemmonitor.net"; + config = { + Appearance.chartFace = "org.kde.ksysguard.horizontalbars"; + Sensors.highPrioritySensorIds = + ''[ "network/all/download","network/all/upload" ]''; + # These are the values needed to make the network indicator + # actually useful, but it appears that plasma-manager doesn't + # support nesting this deep yet. Disable for now. + # "org.kde.ksysguard.horizontalbars".General = { + # rangeAuto = false; + # rangeFromMultiplier = 1048576; + # rangeFromUnit = 202; + # rangeToMultiplier = 1048576; + # rangeToUnit = 202; + # }; + }; + } + { systemTray = { }; } + { digitalClock = { }; } + ]; + } + ##### Bottom: Virtual desktop pager | Full-name taskbar w/ pins | Downloads folder | Trash folder + { + location = "bottom"; + floating = true; + height = 44; + lengthMode = "fill"; + opacity = "adaptive"; + hiding = "normalpanel"; + screen = 0; + widgets = [ + "org.kde.plasma.pager" + { + iconTasks = { + iconsOnly = false; + behavior.showTasks.onlyInCurrentScreen = true; + launchers = [ + "applications:systemsettings.desktop" + "applications:org.kde.discover.desktop" + "preferred://filemanager" + "preferred://browser" + "applications:net.thunderbird.Thunderbird.desktop" + "applications:io.github.alainm23.planify.desktop" + "applications:dev.zed.Zed.desktop" + "applications:com.logseq.Logseq.desktop" + "applications:net.lutris.Lutris.desktop" + ]; + }; + } + "org.kde.plasma.panelspacer" + "org.kde.plasma.marginsseparator" + { + name = "org.kde.plasma.folder"; + config = { + General.url = "file://${config.home.homeDirectory}/Downloads"; + }; + } + "org.kde.plasma.trash" + ]; + } + + ### Screen 1 panels ### + ##### Top: App switcher | Menu bar ##### + { + location = "top"; + floating = true; + height = 27; + lengthMode = "fill"; + opacity = "adaptive"; + hiding = "normalpanel"; + screen = 1; + widgets = [ "org.kde.plasma.windowlist" "org.kde.plasma.appmenu" ]; + } + ##### Bottom: Virtual desktop pager | Full-name taskbar w/o pins ##### + { + location = "bottom"; + floating = true; + height = 44; + lengthMode = "fill"; + opacity = "adaptive"; + hiding = "normalpanel"; + screen = 1; + widgets = [ + "org.kde.plasma.pager" + { + iconTasks = { + iconsOnly = false; + behavior.showTasks.onlyInCurrentScreen = true; + launchers = [ ]; + }; + } + ]; + } + ]; + powerdevil.AC.autoSuspend.action = "nothing"; workspace = { + lookAndFeel = "org.kde.breezedark.desktop"; wallpaperFillMode = "preserveAspectCrop"; wallpaperSlideShow = { interval = 86400; diff --git a/programs/shells.nix b/programs/shells.nix index 93b2d9d..edb0f18 100644 --- a/programs/shells.nix +++ b/programs/shells.nix @@ -1,7 +1,7 @@ { pkgs, ... }: let conda_init = shell: '' - eval "$(${pkgs.micromamba}/bin/micromamba shell hook --shell ${shell})" + eval "$(${pkgs.mamba-cpp}/bin/mamba shell hook --shell ${shell})" ''; nd_bash_function = '' @@ -10,11 +10,53 @@ let } ''; + sourceCodeDirectory = + if pkgs.stdenv.hostPlatform.isDarwin then "~/Developer" else "~/src"; + clone_bash_function = '' + unalias gc + function gc() { + REPO_ID="$(echo "''${1}" | \ + awk '{ + sub(/^git@/, ""); + sub(/^https:\/\//, ""); + sub (/:/, "/"); + sub(/\.git$/, ""); + print + }')" + git clone "https://''${REPO_ID}.git" ${sourceCodeDirectory}/''${REPO_ID} + cd ${sourceCodeDirectory}/''${REPO_ID} + } + + ''; + repo_init_function = '' + function rinit() { + REPO_ID="$(echo "''${1}" | \ + awk '{ + sub(/^git@/, ""); + sub(/^https:\/\//, ""); + sub (/:/, "/"); + sub(/\.git$/, ""); + print + }')" + + mkdir -p ${sourceCodeDirectory}/''${REPO_ID} + cd ${sourceCodeDirectory}/''${REPO_ID} + + git init + + git remote add origin git@''${REPO_ID/\//:}.git + } + + ''; + + shell_functions = shell: + (conda_init shell) + nd_bash_function + clone_bash_function + + repo_init_function; in { programs = { bash = { enable = true; - initExtra = conda_init "bash" + nd_bash_function + '' + initExtra = shell_functions "bash" + '' export PS1="[\[\e[32m\]\u\[\e[m\]@\[\e[33m\]\h\[\e[m\] \[\e[34m\]\W\[\e[m\]] \\$ " ''; }; @@ -37,7 +79,7 @@ in { "zsh-users/zsh-completions" ]; }; - initContent = conda_init "zsh" + nd_bash_function; + initContent = shell_functions "zsh"; }; }; } diff --git a/programs/taskbar.nix b/programs/taskbar.nix index 757d42d..cff7787 100644 --- a/programs/taskbar.nix +++ b/programs/taskbar.nix @@ -25,6 +25,10 @@ bundleIdentifier = "org.mozilla.thunderbird"; action = "launchOrActivateApp"; } + { + bundleIdentifier = "com.microsoft.Outlook"; + action = "launchOrActivateApp"; + } { bundleIdentifier = "dev.zed.Zed"; action = "launchOrActivateApp"; @@ -34,11 +38,13 @@ action = "launchOrActivateApp"; } { + # Instinct dashboard bundleIdentifier = "com.apple.Safari.WebApp.2F51A6D0-087A-438F-92D3-A73FE09CB4CC"; action = "launchOrActivateApp"; } { + # Carestream bundleIdentifier = "com.apple.Safari.WebApp.5EC6478E-03A6-4147-8A4D-6EF3DE3F06D3"; action = "launchOrActivateApp"; diff --git a/programs/yakuake.nix b/programs/yakuake.nix new file mode 100644 index 0000000..f8b86a7 --- /dev/null +++ b/programs/yakuake.nix @@ -0,0 +1,19 @@ +# Note: this file uses the lower-level `programs.plasma.configFile` syntax +# since plasma-manager does not yet support a high-level module for Yakuake +{ ... }: { + programs.plasma.configFile.yakuakerc = { + "Desktop Entry".DefaultProfile = "millironx.profile"; + Shortcuts = { + next-session = "Shift+Right; Ctrl+Shift+Tab"; + previous-terminal = "none"; + }; + Window = { + DynamicTabTitles = true; + Height = 60; + Screen = 1; + ShowSystrayIcon = false; + Width = 60; + }; + Dialogs.FirstRun = false; + }; +} diff --git a/programs/zed.nix b/programs/zed.nix index 19beb45..6985d69 100644 --- a/programs/zed.nix +++ b/programs/zed.nix @@ -1,4 +1,4 @@ -{ ... }: { +{ pkgs, ... }: { programs.zed-editor = { enable = true; extensions = [ @@ -23,27 +23,27 @@ use_modifier_to_send = true; default_model = { provider = "zed.dev"; - model = "claude-3-7-sonnet"; + model = "claude-sonnet-4-5"; }; default_profile = "minimal"; }; buffer_font_family = "FiraCode Nerd Font"; buffer_font_size = 11; features = { edit_prediction_provider = "zed"; }; + git_hosting_providers = [ + { + provider = "forgejo"; + base_url = "https://code.millironx.com"; + name = "Milliron X Code"; + } + { + provider = "forgejo"; + base_url = "https://codeberg.org"; + name = "Codeberg"; + } + ]; languages = { - Julia = { - formatter = { - external = { - command = "julia"; - arguments = [ - "--project=@JuliaFormatter" - "--startup-file=no" - "-e" - "using JuliaFormatter; print(format_text(String(read(stdin))));" - ]; - }; - }; - }; + Julia = { formatter = { external = { command = "jlfmt"; }; }; }; LaTeX = { formatter = { external = { @@ -52,13 +52,30 @@ }; }; }; + Nix = { + formatter.external.command = "${pkgs.nixfmt-classic}/bin/nixfmt"; + }; }; lsp = { - nil = { - initialization_options.formatting.command = [ "nixfmt" ]; - settings.nix.flake.autoArchive = true; + nil = { settings.nix.flake.autoArchive = true; }; + texlab = { + settings = { + build = { + onSave = false; + forwardSearchAfter = true; + }; + forwardSearch = if pkgs.stdenv.hostPlatform.isDarwin then { + executable = + "/Applications/Skim.app/Contents/SharedSupport/displayline"; + args = [ "-r" "-g" "%l" "%p" "%f" ]; + } else { + executable = "/usr/bin/okular"; + args = [ "--unique" "file:%p#src:%l%f" ]; + }; + }; }; tinymist = { + initialization_options = { preview.background.enabled = true; }; settings = { exportPdf = "onSave"; outputPath = "$root/$name"; @@ -81,5 +98,28 @@ ui_font_size = 16; wrap_guides = [ 80 92 120 ]; }; + userTasks = [ + { + label = "latexmk (project)"; + command = "latexmk"; + args = [ "-synctex=1" "-pdf" "-recorder" ]; + cwd = "$ZED_DIRNAME"; + tags = [ "latex-build" ]; + } + { + label = "Open Typst preview"; + command = "${ + if pkgs.stdenv.hostPlatform.isDarwin then "open" else "xdg-open" + } http://127.0.0.1:23635/"; + use_new_terminal = true; + allow_concurrent_runs = false; + reveal = "never"; + reveal_target = "dock"; + hide = "always"; + shell = "system"; + show_summary = true; + show_command = true; + } + ]; }; } diff --git a/secrets.nix b/secrets.nix index 3b3ede4..ed1352e 100644 --- a/secrets.nix +++ b/secrets.nix @@ -6,25 +6,36 @@ let "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIxTfeg+GZsfmG8TuEV1xW1gXknAIKzZ3UjZ3guRY+EW root@nixos"; bosephus-millironx = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKaDPqRJHoqgY2pseh/mnhjaGWXprHk2s5I52LhHpHcF millironx@bosephus"; - odyssey-millironx = - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIN9Aj7BtQp1Roa0tgopDrUo7g2am5WJ43lO1d1fDUz45 millironx@odyssey"; + corianne-host = + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHKKkucebeb1GcerOZAAs5GQsgTS8kXw5W41b9Fy9+hp root@corianne.local"; corianne-millironx = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOgL2lO9RJBdQYANoxGyWXcNKi5/NZkRHHo/rNqaYMc/ millironx@corianne"; - harmony-millironx = - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFBYxsCkw+ObDzIvU8z/rSlYcQx0JIt1bCVxKcDxeNNZ millironx@harmony"; - + mcentire-host = + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINT51tQgsKzTIQc9WSQj01h/gPRvAD3k9jRhXppY7xmd root@nixos"; + mcentire-millironx = + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOdC6eNx2nBi3PWK/n4GJMbVf+NlQJv13aUqxse/h1kL millironx@mcentire"; + odyssey-millironx = + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKM5Q2zl3b91j+foqcVeQT+wb5DFEp+MbgotTTaKqZZi millironx@odyssey"; system-administrators = [ anderson-millironx bosephus-millironx - odyssey-millironx corianne-millironx - harmony-millironx + mcentire-millironx + odyssey-millironx ]; in { + "secrets/ansible-vault-password.age".publicKeys = system-administrators; + "secrets/authentik.toml.age".publicKeys = system-administrators + ++ [ mcentire-host ]; + "secrets/borgmatic-passphrase.age".publicKeys = system-administrators + ++ [ mcentire-host ]; + "secrets/borgmatic-ssh-config.age".publicKeys = system-administrators + ++ [ mcentire-host ]; + "secrets/darwin-policies-json.age".publicKeys = system-administrators + ++ [ corianne-host ]; + "secrets/freshrss.toml.age".publicKeys = system-administrators + ++ [ mcentire-host ]; "secrets/network-information.age".publicKeys = system-administrators ++ [ bosephus-host ]; - "secrets/pihole.age".publicKeys = system-administrators ++ [ bosephus-host ]; - "secrets/ansible-vault-password.age".publicKeys = system-administrators; - "secrets/darwin-policies-json.age".publicKeys = system-administrators; } diff --git a/secrets/ansible-vault-password.age b/secrets/ansible-vault-password.age index 99a96bc..6c1379f 100644 --- a/secrets/ansible-vault-password.age +++ b/secrets/ansible-vault-password.age @@ -1,13 +1,13 @@ age-encryption.org/v1 --> ssh-ed25519 il3lzQ Ni2CHjeijXHfF62cUqVTm8MAOn6rRg8UrhqN6xvdkyk -DsT0Ysx88FlCLeRzoOGctX7KqatX9/UCr5WdtdLJAf4 --> ssh-ed25519 1g/xww jRn91F29sISMyi41anAlzVCzt1t1DnUqxtryqkTQPlM -cysgZLQR0YhiJYXBl59DjKbm+N8FnjA46wkQtnAzBFA --> ssh-ed25519 +kBihw t6wlSnDKGgSzGhNJnryXVbDR40DATaV3fHovtI/u7zo -zOyCZtzfLKeer9K6SMpfTxn6El4HB7gQFQqLOxIYB5U --> ssh-ed25519 dbKeHw cn+8WTwis58bYm2pfEra6LeLvzEZ8GhZrOEeN+kkhCM -fnlUAj8JtG8+r7Cj8xYUgF+JM6Pwqawn4sGI1LOeN78 --> ssh-ed25519 Svnssw zmDBR8TdRZ9NzNhwPYRN6c8naTxAkULyUZpKgk7Gshk -0XCwpegEIlGXhnzLLUtmciKQiYiZRgnSOSvCcYeXXk8 ---- D/lZ36n5sVste2NWfdOx8/klPh0CTmMjVQN74KIqDRY -]%C}NO"v#˱t_Q;^*!+<+dB/K` \ No newline at end of file +-> ssh-ed25519 il3lzQ SoqTRahd/xUVe/pDmQI3jT5X0lTxOwhy8hct20fwil4 +UuYpQa5GpDALKQEMbSLnH2rp3kgPL+4zJZbJirkB5Q0 +-> ssh-ed25519 1g/xww gYtrdwJlp1pxcZ7l+RvawRScFOh/ami3yJobbKPyLiM +oQ8dwIUIakV8WVvuknn87BYcmEBBT+UI3BxImd71/jE +-> ssh-ed25519 dbKeHw KVdpRmBWRYd5NC3UK3e8Em3XefnVxxc9KfXzbksvTC0 +fwyK7ORnCadX8Czs0WKW3ZDa1jeu7Wjba7Vm9nKqCBs +-> ssh-ed25519 3qPtug nEHt7lMjUrcehOGjtznFd+u60OdfG1dPJr/+aFP4OXY +DRtfbYQZJhMIfj1yHgpTdSU15z0Ld5/2wl0ATPN0W6w +-> ssh-ed25519 FRQvIA a4zYtpYFVGSum3wD850lM/wEcokckt4mlDRsrb6QrHY +QF75fGiiKZc1oHSOdy+QvPnQ1hhaLRebaE7i0keZxHw +--- 8Y5Q9SlGWEzp9jeVcoHFrv3PR+eDyhOPFXIFRxZ1Koo +JDlv\lu$0 {riP7ϥP锓kr(PHW]r ّr \ No newline at end of file diff --git a/secrets/authentik.toml.age b/secrets/authentik.toml.age new file mode 100644 index 0000000..d3c91cc Binary files /dev/null and b/secrets/authentik.toml.age differ diff --git a/secrets/borgmatic-passphrase.age b/secrets/borgmatic-passphrase.age new file mode 100644 index 0000000..c3969d2 Binary files /dev/null and b/secrets/borgmatic-passphrase.age differ diff --git a/secrets/borgmatic-ssh-config.age b/secrets/borgmatic-ssh-config.age new file mode 100644 index 0000000..63dd59f --- /dev/null +++ b/secrets/borgmatic-ssh-config.age @@ -0,0 +1,16 @@ +age-encryption.org/v1 +-> ssh-ed25519 il3lzQ 6815+wa1JjdVUu8U8vaRah8kVsjR3Fsot99HKWl4SSA +hPYVZuN1yTKQYFIQvIRdmr400aijXleA22Bxh1nXKdc +-> ssh-ed25519 1g/xww BPxSszlrjPTVJC+YOvo1N6ICUUdL7WKZX3DeambLjBY +JkJQFPGtxDtqeGj8Z1YtVq/pm6hgYKwzMW9MbtkvOwY +-> ssh-ed25519 dbKeHw PU5cZrKCkAvUjI/opkboDQKSWJ8osAOQiI62f9lbfRY +ef1z6ZcBASkhC4fdcN+RRE7q28rB/DN8CVdoNVkC81I +-> ssh-ed25519 3qPtug RWCMZiLQlS+bOOIBRGmoECOfqEI2uoaWCq/4ZuI86B4 +VOaLaH1VYnGoQzjb8seU6wVviB94W3qCdYW9Wf4PGIc +-> ssh-ed25519 FRQvIA LCDwNMNVtTcI3GHPR2mcgtR16yAYYwLplI2nKUwNxDQ +vwXBIDlK7FBOsKt48Zv8mp0WFA/TKH2UJxtUxZoxzMI +-> ssh-ed25519 +C0WRg a/R13LfNMjVjJxt2TtTHaqZOhsYJLY+HUGMHEDnkZWo +T2EfeE/a4W4bFvauynfh+4aJL1jhCpO8iBG2aTmKtyM +--- o7tyupfhbxh3dwSrBLsYX0V61va0USlqxheNsdBjVuQ +A q,:_aɪ)j12 #t,G* +XT(U5t6B皩80 *i׆^ߨ@aB@̃+oFwPci27YSXd g̲J \ No newline at end of file diff --git a/secrets/darwin-policies-json.age b/secrets/darwin-policies-json.age index 9fa2d2b..3f900dd 100644 Binary files a/secrets/darwin-policies-json.age and b/secrets/darwin-policies-json.age differ diff --git a/secrets/freshrss.toml.age b/secrets/freshrss.toml.age new file mode 100644 index 0000000..096f601 Binary files /dev/null and b/secrets/freshrss.toml.age differ diff --git a/secrets/network-information.age b/secrets/network-information.age index 0de69b7..2d42ae1 100644 Binary files a/secrets/network-information.age and b/secrets/network-information.age differ diff --git a/secrets/pihole.age b/secrets/pihole.age deleted file mode 100644 index babead9..0000000 --- a/secrets/pihole.age +++ /dev/null @@ -1,15 +0,0 @@ -age-encryption.org/v1 --> ssh-ed25519 il3lzQ Q+/uqZhUWs5pb5T1ocD+/qTSo4DJbd/W1exruQ34zAE -8HFRvEblGVrkoVaqAl/Af6wrDn6A+3unZIMBipEkwgA --> ssh-ed25519 1g/xww PqXxTvLaF6ZlcVov81VrVH130jFh2iGmHPRtBYV4ME4 -1VBknQzaNZyoz2wvgKX+IZGaOEnJ1xGvxPYxq10ar/U --> ssh-ed25519 +kBihw QXNxY9OQeIM98OqmHoa/S2kMZqSX+ndgxGyCJpHJ+gg -b3DmfUswyPQ09sp57v3QMNEF/Ka3w9Qj2s1kGUSinmQ --> ssh-ed25519 dbKeHw 5GzjKgjUX5e6Net7voWBykC17zRcdSFDFbDsSwp5FAU -GwTEg3YR9HdcQHPg+XjP2Lg1BpcWA4VunbZSBdxVaYU --> ssh-ed25519 Svnssw imRjD5CJu/jOac3t/APHbYBnsyJVQdebR6K52A6GdwM -n+Q9kEEkYRBuEzWlSwbjJNsjF8uKloeUEWYxHa29B4U --> ssh-ed25519 jb0ALQ 4qbGIofHcyhJVfL24peGqqzg0tFdxbWBHJFenwehIAI -Ta3ye4quyHvvE+2CGZwYvQMwWfdrLIdqADLvJYhllPY ---- 3hbht7PYqFafVmcQWQwv3q2gUXM8HXajtmAaMnrh59s - +X984R2,􈱨(#42#*, b禽H_z }߅x7A!))vʞ¨W-eX-G<@~Ek?kGlQK4c&*J9V_0 µz+ɰ¯C ўQGXkg* -jl31JMOE[=S \ No newline at end of file diff --git a/secrets_file.enc b/secrets_file.enc index 20df784..33fcb27 100644 --- a/secrets_file.enc +++ b/secrets_file.enc @@ -1,10 +1,8 @@ $ANSIBLE_VAULT;1.1;AES256 -65366137313461383534313965646333656565353061336361363661613033393264353661346337 -3838653162383134393463323631613439373663396363380a633339396236363962313333343465 -31623961393532666136616438633734366261353866383264323730383432326635626637343739 -3235313062623637380a386235316437396534353261383832643165316565386263396664363962 -62393364333335373631356161373263313930343565626433383539373030363662353630633933 -63336333613965653635313637336437653139616564313861336332323739653865383531356233 -31373530343766343131346663656566363038643230343462336332323135323337353539303763 -33366638393064323431323636346161343936643062323861313766613264336465326132333631 -33306666383561653965303539313366653030663330393363363565333439383133 +33613635643765623937663135313833396162343134383466343966333964386364356134663264 +3137633339396462633431316634623834646437646162360a626564313831373761636161656232 +35316566336232666336646231356665366633303530623961666465366163306166623336656364 +3835353035333031620a633332376237336530343134623832363534383761616564616138363766 +30306361383462353361636161636335313461313835663362393839623735313738316465656537 +66396635323432376530346532353238346139376261366237343763373535623364633731323830 +333730373965613131336166626230333263 diff --git a/secrets_harmony.enc b/secrets_harmony.enc deleted file mode 100644 index e91177d..0000000 --- a/secrets_harmony.enc +++ /dev/null @@ -1,6 +0,0 @@ -$ANSIBLE_VAULT;1.1;AES256 -38383539613238613864336630316433666436623334313334393762396536663530336264306661 -3338616565316138616666343862366638643134343931320a633366363539326461346636373738 -66393138653463663536313065623332383166386332303564323939336630333163623637386434 -6538393966633731660a616437356233643234363562366433663437383439326161353330356331 -63346432663036353332303266343361346266396437396131376531303265356233 diff --git a/secrets_odyssey.enc b/secrets_odyssey.enc index fba7129..a7a96da 100644 --- a/secrets_odyssey.enc +++ b/secrets_odyssey.enc @@ -1,6 +1,9 @@ $ANSIBLE_VAULT;1.1;AES256 -30343638643335363463653231623566623961613534323261393639623865633964653634333562 -3838613035393661656362383736313561366466396439390a383162366362643364636335613664 -39646137666437353762363764373562393736626530333336626261366232383063633732623238 -6531633638366335640a363461383535646663316533386137323966326237373836363561323462 -66646635383137333834363165666365366235333734646364616637383363666239 +61363033383536303833366237323662663236313163663033306138383162383062643830616466 +6531636430613462646161343939343363663533373737340a613433363666353432383463356439 +33656266633131336565613433653062656563656637656464346232656238646339303961373265 +6639643637303433380a393163366331373964353261383662656664643031626432366231346332 +34303964346137616233343930333331306363326332383465653163386539306430303965316437 +30343333373565623431653436653832356366343937653136346535316166383262623730343831 +62376532346237323465653261316339353034323633623632313630666531373839633665333637 +34356162356565396564 diff --git a/services/authentik.nix b/services/authentik.nix new file mode 100644 index 0000000..f507dac --- /dev/null +++ b/services/authentik.nix @@ -0,0 +1,205 @@ +{ config, pkgs, home-manager-quadlet-nix, ... }: +let + user = "authentik"; + state-directory = "/var/lib/authentik"; + port = "9000"; + +in { + # Secrets are translated in the system NixOS scope, but performed by the user, + # via a user systemd unit, so are available in the user's Podman secrets + age.secrets = { + "authentik.toml" = { + file = ./../secrets/authentik.toml.age; + owner = "${user}"; + }; + }; + + millironx.podman-secrets.authentik = { + user = "${user}"; + secrets-files = [ config.age.secrets."authentik.toml".path ]; + }; + + # Podman, unlike Docker apparently, does not automatically create mount points + # within folders, so every mounted folder needs to be specified here. + systemd.tmpfiles.rules = [ + "d ${state-directory} 1775 ${user} ${user} -" + "d ${state-directory}/database 1775 ${user} ${user} -" + "d ${state-directory}/media 1775 ${user} ${user} -" + "d ${state-directory}/certs 1775 ${user} ${user} -" + "d ${state-directory}/custom-templates 1775 ${user} ${user} -" + ]; + + services.caddy.virtualHosts."auth.millironx.com".extraConfig = '' + reverse_proxy http://127.0.0.1:${port} + ''; + + services.borgmatic.configurations."${config.networking.hostName}" = { + source_directories = [ + "${state-directory}/media" + "${state-directory}/certs" + "${state-directory}/custom-templates" + ]; + + postgresql_databases = [{ + name = "authentik"; + psql_command = + "/run/wrappers/bin/sudo -iu ${user} ${pkgs.podman}/bin/podman exec authentik-db psql --username=${user}"; + pg_dump_command = + "/run/wrappers/bin/sudo -iu ${user} ${pkgs.podman}/bin/podman exec authentik-db pg_dump --username=${user}"; + pg_restore_command = + "/run/wrappers/bin/sudo -iu ${user} ${pkgs.podman}/bin/podman exec authentik-db pg_restore --username=${user}"; + }]; + }; + + # Create a dedicated user for this service + users.users."${user}" = { + # Group is technically not mandatory, but NixOS complains that an unset + # group is "unsafe." The group has to be declared below + group = "${user}"; + + # System users don't have a shell. For security purposes, that *would* be + # superior, but that means that, e.g. borgmatic can't `sudo` into the + # account to access Podman commands + isNormalUser = true; + + # A home directory is mandatory in order to allow systemd user units to be + # created and run + home = "${state-directory}"; + createHome = true; + + # Settings for running containers while a login shell is not active + linger = true; + autoSubUidGidRange = true; + }; + users.groups."${user}" = { }; + + home-manager.users."${user}" = { config, osConfig, ... }: { + imports = [ home-manager-quadlet-nix ]; + + home.stateVersion = "25.05"; + + virtualisation.quadlet = { + containers = { + authentik-db = { + autoStart = true; + containerConfig = { + image = "docker.io/library/postgres:16"; + environments = { + POSTGRES_DB = "${user}"; + POSTGRES_USER = "${user}"; + }; + secrets = [ + # POSTGRES_PASSWORD is used by the container to setup the + # database, PGPASSWORD is used by pg_dump to authenticate for + # backup purposes + "AUTHENTIK_POSTGRESQL__PASSWORD,type=env,target=POSTGRES_PASSWORD" + "AUTHENTIK_POSTGRESQL__PASSWORD,type=env,target=PGPASSWORD" + ]; + + # Double dollarsigns are required for use of *container* environment + # variables, Nix escaping creates the weird $\${} syntax + healthCmd = "pg_isready -d $\${POSTGRES_DB} -U $\${POSTGRES_USER}"; + healthInterval = "30s"; + healthRetries = 5; + healthStartPeriod = "20s"; + + # Volumes have to be bound with the :U label to allow for username + # remapping in rootless containers. :Z/:z is not needed b/c NixOS + # does not support SELinux + volumes = + [ "${state-directory}/database:/var/lib/postgresql/data:U" ]; + + # A network is actually required for these containers to talk to one + # another. I suppose the more idiomatic way would be for these + # related containers to be in a "pod," but I'm still struggling + # learning the idiosyncrasies of quadlet/rootless/podman, so we'll + # stick with this for now + networks = + [ config.virtualisation.quadlet.networks.authentik-net.ref ]; + + }; + + # Allowing this container to start before the secrets are translated + # will lead to errors. Use osConfig b/c secrets are declared in the + # system NixOS scope, even though it is a user process. + unitConfig.Requires = + [ osConfig.millironx.podman-secrets.authentik.ref ]; + unitConfig.After = + [ osConfig.millironx.podman-secrets.authentik.ref ]; + }; + + authentik-worker = { + autoStart = true; + containerConfig = { + image = "ghcr.io/goauthentik/server:2025.10.2"; + environments = { + AUTHENTIK_POSTGRESQL__HOST = "authentik-db"; + AUTHENTIK_POSTGRESQL__NAME = "${user}"; + AUTHENTIK_POSTGRESQL__USER = "${user}"; + }; + exec = "worker"; + secrets = [ + "AUTHENTIK_POSTGRESQL__PASSWORD,type=env" + "AUTHENTIK_SECRET_KEY,type=env" + ]; + volumes = [ + "${state-directory}/media:/media:U" + "${state-directory}/custom-templates:/templates:U" + "${state-directory}/certs:/certs:U" + ]; + networks = + [ config.virtualisation.quadlet.networks.authentik-net.ref ]; + }; + unitConfig.Requires = + [ config.virtualisation.quadlet.containers.authentik-db.ref ]; + unitConfig.After = + [ config.virtualisation.quadlet.containers.authentik-db.ref ]; + }; + + authentik = { + autoStart = true; + containerConfig = { + image = "ghcr.io/goauthentik/server:2025.10.2"; + environments = { + AUTHENTIK_POSTGRESQL__HOST = "authentik-db"; + AUTHENTIK_POSTGRESQL__NAME = "${user}"; + AUTHENTIK_POSTGRESQL__USER = "${user}"; + }; + exec = "server"; + secrets = [ + "AUTHENTIK_POSTGRESQL__PASSWORD,type=env" + "AUTHENTIK_SECRET_KEY,type=env" + "AUTHENTIK_EMAIL__HOST,type=env" + "AUTHENTIK_EMAIL__PORT,type=env" + "AUTHENTIK_EMAIL__USERNAME,type=env" + "AUTHENTIK_EMAIL__PASSWORD,type=env" + "AUTHENTIK_EMAIL__USE_SSL,type=env" + "AUTHENTIK_EMAIL__FROM,type=env" + ]; + + # Change from Traefik: publish ports to localhost only via 127.0.0.1 + # and then reverse proxy to that port. Authentik does not appear to + # have a way to configure the port, so we will use the default of + # 9000 for non-secured traffic. + publishPorts = [ "127.0.0.1:${port}:${port}" ]; + volumes = [ + "${state-directory}/media:/media:U" + "${state-directory}/custom-templates:/templates:U" + ]; + networks = + [ config.virtualisation.quadlet.networks.authentik-net.ref ]; + }; + unitConfig.Requires = + [ config.virtualisation.quadlet.containers.authentik-db.ref ]; + unitConfig.After = + [ config.virtualisation.quadlet.containers.authentik-db.ref ]; + }; + }; + + networks.authentik-net = { }; + + # One of the main advantages of using Quadlet + autoUpdate.enable = true; + }; + }; +} diff --git a/services/borgmatic.nix b/services/borgmatic.nix new file mode 100644 index 0000000..c028df2 --- /dev/null +++ b/services/borgmatic.nix @@ -0,0 +1,35 @@ +{ pkgs, config, ... }: { + + # We don't want to expose the location where borg backups are going, so we + # will setup an encrypted ssh config that references the host/username + # combo as simply 'borgserver' + age.secrets = { + borgmatic-ssh-config = { file = ./../secrets/borgmatic-ssh-config.age; }; + borgmatic-passphrase = { file = ./../secrets/borgmatic-passphrase.age; }; + }; + + services.borgmatic = { + enable = true; + + # This is the bare-bones way to get Borgmatic up and running. Other services + # are expected to declare their stateful directories by adding to + # `services.borgmatic.configurations."${config.networking.hostName}".source_directories` + # and to add their databases to + # `services.borgmatic.configurations."${config.networking.hostName}".[mariadb|postgresql|etc]_databases` + + configurations."${config.networking.hostName}" = { + source_directories = [ "/home" "/root" ]; + repositories = [{ + label = "${config.networking.hostName}-default"; + path = "ssh://borgserver/./repo"; + }]; + ssh_command = + "${pkgs.openssh}/bin/ssh -F ${config.age.secrets.borgmatic-ssh-config.path}"; + encryption_passcommand = + "${pkgs.coreutils}/bin/cat ${config.age.secrets.borgmatic-passphrase.path}"; + keep_daily = 7; + keep_weekly = 4; + keep_monthly = 6; + }; + }; +} diff --git a/services/crowdsec.nix b/services/crowdsec.nix index f7e2e5a..5c3e279 100644 --- a/services/crowdsec.nix +++ b/services/crowdsec.nix @@ -1,90 +1,49 @@ -{ pkgs, config, ... }: -let - crowdsec-url = "127.0.0.1:2763"; - firewall-bouncer-name = "fw-bouncer"; - # Although this key can be reproduced by anyone who actually cares to, the - # Crowdsec API will not be exposed to the outside world, so keeping this key - # super secret really isn't that important to me. Still make it look random - # so that hungry botnets can't just slurp up the password in plaintext. - firewall-bouncer-key = builtins.hashString "sha256" - "${config.networking.hostName}-crowdsec-bouncer-salt"; - toMultiYAML = items: - pkgs.lib.concatMapStrings (item: - '' - - --- - '' + (pkgs.lib.generators.toYAML { } item) + "\n") items; -in { +{ pkgs, config, ... }: { services = { crowdsec = { enable = true; - settings = { - api.server = { listen_uri = crowdsec-url; }; - allowLocalJournalAccess = true; - crowdsec_service.acquisition_path = pkgs.writeText "acquisitions.yaml" - (toMultiYAML [ - { - source = "journalctl"; - journalctl_filter = [ "_SYSTEMD_UNIT=sshd.service" ]; - labels.type = "syslog"; - } - { - filenames = [ "/var/log/auth.log" ]; - labels.type = "syslog"; - } - { - filenames = [ "/var/log/syslog" "/var/log/kern.log" ]; - labels.type = "syslog"; - } - ]); + localConfig = { + acquisitions = [ + { + source = "journalctl"; + journalctl_filter = [ "_SYSTEMD_UNIT=sshd.service" ]; + labels.type = "syslog"; + } + { + filenames = [ "/var/log/auth.log" ]; + labels.type = "syslog"; + } + { + filenames = [ "/var/log/syslog" "/var/log/kern.log" ]; + labels.type = "syslog"; + } + ]; }; + hub = { + collections = [ + "crowdsecurity/base-http-scenarios" + "crowdsecurity/http-cve" + "crowdsecurity/http-dos" + "crowdsecurity/iptables" + "crowdsecurity/linux" + "crowdsecurity/sshd" + "crowdsecurity/whitelist-good-actors" + ]; + }; + settings = { + general = { api.server.enable = true; }; + # See https://github.com/NixOS/nixpkgs/issues/445342 + lapi.credentialsFile = "/var/lib/crowdsec/lapi-credentials.yaml"; + }; + autoUpdateService = true; }; + crowdsec-firewall-bouncer = { enable = true; - settings = { - api_url = firewall-bouncer-name; - api_key = firewall-bouncer-key; - }; + registerBouncer.enable = true; }; }; - systemd.services.crowdsec.serviceConfig = { - ExecStartPre = let - bouncer-script = pkgs.writeScriptBin "register-bouncer" '' - #!${pkgs.runtimeShell} - set -eu - set -o pipefail - - if ! cscli bouncers list | grep -q "${firewall-bouncer-name}"; then - cscli bouncers add "${firewall-bouncer-name}" --key "${firewall-bouncer-key}" - fi - ''; - collection-check = collection: '' - - if ! cscli collections list | grep -q "${collection}"; then - cscli collections install "${collection}" - fi - - ''; - collections = [ - "crowdsecurity/base-http-scenarios" - "crowdsecurity/http-cve" - "crowdsecurity/http-dos" - "crowdsecurity/iptables" - "crowdsecurity/linux" - "crowdsecurity/sshd" - "crowdsecurity/whitelist-good-actors" - ]; - collection-script = pkgs.writeScriptBin "install-collections" '' - #!${pkgs.runtimeShell} - set -eu - set -o pipefail - - ${pkgs.lib.concatMapStrings collection-check collections} - ''; - in [ - "${bouncer-script}/bin/register-bouncer" - "${collection-script}/bin/install-collections" - ]; - }; + systemd.tmpfiles.rules = let cfg = config.services.crowdsec; + in [ "d /var/lib/crowdsec 0755 ${cfg.user} ${cfg.group}" ]; } diff --git a/services/freshrss.nix b/services/freshrss.nix new file mode 100644 index 0000000..2f142ff --- /dev/null +++ b/services/freshrss.nix @@ -0,0 +1,146 @@ +{ config, pkgs, home-manager-quadlet-nix, ... }: + +let + user = "freshrss"; + port = "37374"; + stateDirectory = "/var/lib/freshrss"; + serviceContainer = "freshrss"; + stateSubDir = subDir: "${stateDirectory}/${subDir}"; + createTmpfilesRule = subDir: "d ${stateSubDir subDir} 1755 ${user} ${user}"; + + dbDirectories = [ "database" ]; + serviceDirectories = [ "data" "extensions" ]; +in { + age.secrets = { + "freshrss.toml" = { + file = ./../secrets/freshrss.toml.age; + owner = "${user}"; + }; + }; + + millironx.podman-secrets.freshrss = { + user = "${user}"; + secrets-files = [ config.age.secrets."freshrss.toml".path ]; + }; + + services.caddy.virtualHosts."feeds.millironx.com".extraConfig = '' + reverse_proxy http://127.0.0.1:${port} { + header_up X-Forwarded-Port 443 + } + ''; + + systemd.tmpfiles.rules = builtins.map createTmpfilesRule + ([ stateDirectory ] ++ dbDirectories ++ serviceDirectories); + + services.borgmatic.configurations."${config.networking.hostName}" = { + source_directories = builtins.map stateSubDir dbDirectories; + + postgresql_databases = [{ + name = serviceContainer; + psql_command = + "/run/wrappers/bin/sudo -iu ${user} ${pkgs.podman}/bin/podman exec ${serviceContainer}-db psql --username=${user}"; + pg_dump_command = + "/run/wrappers/bin/sudo -iu ${user} ${pkgs.podman}/bin/podman exec ${serviceContainer}-db pg_dump --username=${user}"; + pg_restore_command = + "/run/wrappers/bin/sudo -iu ${user} ${pkgs.podman}/bin/podman exec ${serviceContainer}-db pg_restore --username=${user}"; + }]; + }; + + users.users."${user}" = { + group = "${user}"; + isNormalUser = true; + home = "${stateDirectory}"; + createHome = true; + linger = true; + autoSubUidGidRange = true; + }; + users.groups."${user}" = { }; + + home-manager.users."${user}" = { config, osConfig, ... }: { + imports = [ home-manager-quadlet-nix ]; + + home.stateVersion = "25.05"; + + virtualisation.quadlet = let + inherit (config.virtualisation.quadlet) containers; + inherit (config.virtualisation.quadlet) networks; + secrets = osConfig.millironx.podman-secrets.freshrss; + + in { + containers = { + "${serviceContainer}-db" = { + autoStart = true; + containerConfig = { + image = "docker.io/library/postgres:16"; + environments = { + POSTGRES_DB = "${user}"; + POSTGRES_USER = "${user}"; + }; + secrets = [ + "POSTGRES_PASSWORD,type=env" + "POSTGRES_PASSWORD,type=env,target=PGPASSWORD" + ]; + healthCmd = "pg_isready -d $\${POSTGRES_DB} -U $\${POSTGRES_USER}"; + healthInterval = "30s"; + healthRetries = 5; + healthStartPeriod = "20s"; + volumes = + [ "${stateDirectory}/database:/var/lib/postgresql/data:U" ]; + networks = [ networks."${serviceContainer}".ref ]; + }; + unitConfig.Requires = [ secrets.ref ]; + unitConfig.After = [ secrets.ref ]; + }; + + "${serviceContainer}" = { + autoStart = true; + containerConfig = { + image = "docker.io/freshrss/freshrss:1"; + # Required to allow the container to talk to the host ports, in + # other words, to resolve Authentik correctly + addHosts = [ "auth.millironx.com:host-gateway" ]; + environments = { + TZ = osConfig.time.timeZone; + CRON_MIN = "2,32"; + LISTEN = "0.0.0.0:${port}"; + TRUSTED_PROXY = "172.16.0.1/12 192.168.0.1/16"; + OIDC_ENABLED = "1"; + OIDC_PROVIDER_METADATA_URL = + "https://auth.millironx.com/application/o/freshrss/.well-known/openid-configuration"; + OIDC_REMOTE_USER_CLAIM = "preferred_username"; + OIDC_SCOPES = "openid email profile"; + OIDC_X_FORWARDED_HEADERS = + "X-Forwarded-Host X-Forwarded-Port X-Forwarded-Proto"; + }; + secrets = [ + "OIDC_CLIENT_ID,type=env" + "OIDC_CLIENT_SECRET,type=env" + "OIDC_CLIENT_CRYPTO_KEY,type=env" + ]; + healthCmd = "cli/health.php"; + healthTimeout = "10s"; + healthStartPeriod = "60s"; + healthStartupInterval = "11s"; + healthInterval = "75s"; + healthRetries = 3; + networks = [ networks."${serviceContainer}".ref ]; + publishPorts = [ "127.0.0.1:${port}:${port}" ]; + volumes = [ + "${stateDirectory}/data:/var/www/FreshRSS/data:U" + "${stateDirectory}/extensions:/var/www/FreshRSS/extensions:U" + ]; + }; + unitConfig.Requires = + [ secrets.ref containers."${serviceContainer}-db".ref ]; + unitConfig.After = + [ secrets.ref containers."${serviceContainer}-db".ref ]; + }; + }; + + networks."${serviceContainer}" = { }; + + autoUpdate.enable = true; + autoEscape = true; + }; + }; +} diff --git a/services/nixos-update.nix b/services/nixos-update.nix deleted file mode 100644 index f0678ec..0000000 --- a/services/nixos-update.nix +++ /dev/null @@ -1,50 +0,0 @@ -# This file is designed to be used in the imports -{ pkgs, config, ... }: - -{ - environment.systemPackages = [ - (pkgs.writeScriptBin "update-nixos" '' - #!${pkgs.bash}/bin/bash - echo "Requesting system update..." - ${pkgs.systemd}/bin/systemctl start nixos-update.service - '') - ]; - - # Service to update NixOS configuration from git repo - systemd.services."nixos-update" = { - description = "Update NixOS configuration from git repository"; - path = with pkgs; [ coreutils ]; - script = '' - # Rebuild the system directly from the remote flake - ${pkgs.nixos-rebuild}/bin/nixos-rebuild switch --flake git+https://code.millironx.com/millironx/nix-dotfiles.git#${config.networking.hostName} - ''; - serviceConfig = { - Type = "oneshot"; - User = "root"; - }; - }; - - # Timer to run the update service daily at 3am - systemd.timers."nixos-update" = { - wantedBy = [ "timers.target" ]; - description = "Run NixOS update daily at 3am"; - timerConfig = { - OnCalendar = "3:00"; - Persistent = true; - Unit = "nixos-update.service"; - }; - }; - - # Polkit rule to allow non-root users to trigger the update - security.polkit.extraConfig = '' - polkit.addRule(function(action, subject) { - if (action.id == "org.freedesktop.systemd1.manage-units" && - action.lookup("unit") == "nixos-update.service" && - action.lookup("verb") == "start" && - subject.isInGroup("wheel")) { - return polkit.Result.YES; - } - }); - ''; - -} diff --git a/services/pihole.nix b/services/pihole.nix deleted file mode 100644 index ed4cd19..0000000 --- a/services/pihole.nix +++ /dev/null @@ -1,27 +0,0 @@ -{ config, ... }: - -{ - age.secrets = { - pihole-credentials = { - file = ./../secrets/pihole.age; - owner = "root"; - group = "root"; - }; - }; - virtualisation = { - quadlet = { - containers = { - pihole = { - containerConfig = { - image = "docker.io/pihole/pihole:2025.06.2"; - publishPorts = - [ "53:53/tcp" "53:53/udp" "80:80/tcp" "443:443/tcp" ]; - environmentFiles = [ config.age.secrets.pihole-credentials.path ]; - networks = [ "bridge" ]; - dns = [ "127.0.0.1" "194.242.2.9" "9.9.9.9" ]; - }; - }; - }; - }; - }; -} diff --git a/services/samba.nix b/services/samba.nix index e6fa607..b248d33 100644 --- a/services/samba.nix +++ b/services/samba.nix @@ -2,7 +2,6 @@ services.samba = { enable = true; package = pkgs.sambaFull; - securityType = "user"; openFirewall = true; settings = { global = { diff --git a/systems/darwin/corianne.nix b/systems/darwin/corianne.nix index db34258..c83a9a1 100644 --- a/systems/darwin/corianne.nix +++ b/systems/darwin/corianne.nix @@ -15,6 +15,8 @@ in { rig-install ]; + age.secrets.firefox-policy.file = ./../../secrets/darwin-policies-json.age; + # Use a custom configuration.nix location. # $ darwin-rebuild switch -I darwin-config=$HOME/.config/nixpkgs/darwin/configuration.nix environment.darwinConfig = "$HOME/.config/home-manager/configuration.nix"; @@ -24,9 +26,27 @@ in { }; # Auto upgrade nix package and the daemon service. - nix.enable = true; - #services.nix-daemon.tempDir = "/nix/tmp"; - nix.package = pkgs.nix; + nix = { + enable = true; + gc = { + automatic = true; + interval = { Weekday = 1; }; + options = '' + --delete-older-than 14d + ''; + }; + # Needed for rosetta-builder, see + # + # + linux-builder = { + enable = true; + ephemeral = true; + }; + extraOptions = '' + extra-platforms = x86_64-darwin + ''; + }; + nix-rosetta-builder.onDemand = true; # Create /etc/zshrc that loads the nix-darwin environment. programs.zsh.enable = true; # default shell on catalina @@ -71,11 +91,12 @@ in { in [ (sysApp "Firefox") (sysApp "Thunderbird") - (sysApp "Logseq") + (sysApp "Microsoft Outlook") (sysApp "Zed") + (sysApp "Logseq") (sysApp "Steam") - (chromeApp "Instinct Dashboard") - (chromeApp "Carestream") + (localApp "Instinct Dashboard") + (localApp "Carestream") ]; show-process-indicators = true; show-recents = false; @@ -130,6 +151,11 @@ in { --user=${config.system.primaryUser} \ --set-home \ _rig-install ${r-version} + + echo "Applying custom defaults..." + /usr/bin/defaults import \ + /Library/Preferences/org.mozilla.firefox \ + ${config.age.secrets.firefox-policy.path} ''; nix.settings.experimental-features = [ "nix-command" "flakes" ]; @@ -177,23 +203,18 @@ in { "docker" "docker-buildx" "docker-credential-helper" - "firefoxpwa" "mpv" ]; casks = [ "alt-tab" - "anki" "db-browser-for-sqlite" "dolphin" "firefox" "freetube" - "ghostty" "inkscape" "iterm2" - "logi-options+" "logseq" "macfuse" - "musescore" "nextcloud" "openrct2" "qownnotes" @@ -201,6 +222,7 @@ in { "rig" "rstudio" "signal" + "skim" "slack" "stats" "steam" @@ -210,6 +232,7 @@ in { "ungoogled-chromium" "veracrypt" "vlc" + "vienna" "vorta" "zed" "zotero" diff --git a/systems/linux/bosephus.nix b/systems/linux/bosephus.nix index 3a0a271..6c8d181 100644 --- a/systems/linux/bosephus.nix +++ b/systems/linux/bosephus.nix @@ -8,9 +8,7 @@ imports = [ ./hardware-configuration/bosephus.nix ./hardware-configuration/bosephus-external-drives.nix - ./../../services/nixos-update.nix ./../../services/samba.nix - ./../../services/pihole.nix ]; # Bootloader. @@ -18,8 +16,8 @@ boot.loader.efi.canTouchEfiVariables = true; # Ignore lid - so I can close without having the system go into sleep mode - services.logind.lidSwitch = "ignore"; - services.logind.lidSwitchDocked = "ignore"; + services.logind.settings.Login.HandleLidSwitch = "ignore"; + services.logind.settings.Login.HandleLidSwitchDocked = "ignore"; # Secrets age.secrets = { diff --git a/systems/linux/mcentire.nix b/systems/linux/mcentire.nix index 07135d1..fa0c08b 100644 --- a/systems/linux/mcentire.nix +++ b/systems/linux/mcentire.nix @@ -3,8 +3,11 @@ { imports = [ # Include the results of the hardware scan. ./hardware-configuration/mcentire.nix - ./../../services/nixos-update.nix + ./../../modules/podman-secrets.nix + ./../../services/borgmatic.nix ./../../services/crowdsec.nix + ./../../services/authentik.nix + ./../../services/freshrss.nix ]; # Use the GRUB 2 boot loader. @@ -15,6 +18,7 @@ useDHCP = false; interfaces.eth0.useDHCP = true; hostName = "mcentire"; # Define your hostname. + firewall.allowedTCPPorts = [ 80 443 ]; }; # Set your time zone. @@ -40,7 +44,7 @@ millironx = { isNormalUser = true; description = "Thomas A. Christensen II"; - extraGroups = [ "wheel" ]; + extraGroups = [ "adm" "wheel" ]; }; }; @@ -54,8 +58,15 @@ services = { openssh.enable = true; tailscale.enable = true; + caddy.enable = true; + + # Do not "enable" database services, but include the package configuration + # so that borgmatic does not freak out about unset variables + postgresql.package = pkgs.postgresql_17; }; + virtualisation.quadlet.enable = true; + system.stateVersion = "25.05"; # Did you read the comment? nix = { extraOptions = "experimental-features = nix-command flakes"; }; }