commit ebc1be52179ed50844ed9d3ac1a03611e2c1089b Author: gwg313 Date: Wed Apr 15 18:26:05 2026 -0400 initial commit diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..88f5c12 --- /dev/null +++ b/flake.lock @@ -0,0 +1,792 @@ +{ + "nodes": { + "base16": { + "inputs": { + "fromYaml": "fromYaml" + }, + "locked": { + "lastModified": 1755819240, + "narHash": "sha256-qcMhnL7aGAuFuutH4rq9fvAhCpJWVHLcHVZLtPctPlo=", + "owner": "SenchoPens", + "repo": "base16.nix", + "rev": "75ed5e5e3fce37df22e49125181fa37899c3ccd6", + "type": "github" + }, + "original": { + "owner": "SenchoPens", + "repo": "base16.nix", + "type": "github" + } + }, + "base16-fish": { + "flake": false, + "locked": { + "lastModified": 1765809053, + "narHash": "sha256-XCUQLoLfBJ8saWms2HCIj4NEN+xNsWBlU1NrEPcQG4s=", + "owner": "tomyun", + "repo": "base16-fish", + "rev": "86cbea4dca62e08fb7fd83a70e96472f92574782", + "type": "github" + }, + "original": { + "owner": "tomyun", + "repo": "base16-fish", + "rev": "86cbea4dca62e08fb7fd83a70e96472f92574782", + "type": "github" + } + }, + "base16-helix": { + "flake": false, + "locked": { + "lastModified": 1760703920, + "narHash": "sha256-m82fGUYns4uHd+ZTdoLX2vlHikzwzdu2s2rYM2bNwzw=", + "owner": "tinted-theming", + "repo": "base16-helix", + "rev": "d646af9b7d14bff08824538164af99d0c521b185", + "type": "github" + }, + "original": { + "owner": "tinted-theming", + "repo": "base16-helix", + "type": "github" + } + }, + "base16-vim": { + "flake": false, + "locked": { + "lastModified": 1732806396, + "narHash": "sha256-e0bpPySdJf0F68Ndanwm+KWHgQiZ0s7liLhvJSWDNsA=", + "owner": "tinted-theming", + "repo": "base16-vim", + "rev": "577fe8125d74ff456cf942c733a85d769afe58b7", + "type": "github" + }, + "original": { + "owner": "tinted-theming", + "repo": "base16-vim", + "rev": "577fe8125d74ff456cf942c733a85d769afe58b7", + "type": "github" + } + }, + "firefox-gnome-theme": { + "flake": false, + "locked": { + "lastModified": 1764873433, + "narHash": "sha256-1XPewtGMi+9wN9Ispoluxunw/RwozuTRVuuQOmxzt+A=", + "owner": "rafaelmardojai", + "repo": "firefox-gnome-theme", + "rev": "f7ffd917ac0d253dbd6a3bf3da06888f57c69f92", + "type": "github" + }, + "original": { + "owner": "rafaelmardojai", + "repo": "firefox-gnome-theme", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1751685974, + "narHash": "sha256-NKw96t+BgHIYzHUjkTK95FqYRVKB8DHpVhefWSz/kTw=", + "ref": "refs/heads/main", + "rev": "549f2762aebeff29a2e5ece7a7dc0f955281a1d1", + "revCount": 92, + "type": "git", + "url": "https://git.lix.systems/lix-project/flake-compat.git" + }, + "original": { + "type": "git", + "url": "https://git.lix.systems/lix-project/flake-compat.git" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1772408722, + "narHash": "sha256-rHuJtdcOjK7rAHpHphUb1iCvgkU3GpfvicLMwwnfMT0=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "f20dc5d9b8027381c474144ecabc9034d6a839a3", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-parts_2": { + "inputs": { + "nixpkgs-lib": [ + "nvf", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1769996383, + "narHash": "sha256-AnYjnFWgS49RlqX7LrC4uA+sCCDBj0Ry/WOJ5XWAsa0=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "57928607ea566b5db3ad13af0e57e921e6b12381", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-parts_3": { + "inputs": { + "nixpkgs-lib": [ + "stylix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1767609335, + "narHash": "sha256-feveD98mQpptwrAEggBQKJTYbvwwglSbOv53uCfH9PY=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "250481aafeb741edfe23d29195671c19b36b6dca", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "fromYaml": { + "flake": false, + "locked": { + "lastModified": 1731966426, + "narHash": "sha256-lq95WydhbUTWig/JpqiB7oViTcHFP8Lv41IGtayokA8=", + "owner": "SenchoPens", + "repo": "fromYaml", + "rev": "106af9e2f715e2d828df706c386a685698f3223b", + "type": "github" + }, + "original": { + "owner": "SenchoPens", + "repo": "fromYaml", + "type": "github" + } + }, + "gnome-shell": { + "flake": false, + "locked": { + "host": "gitlab.gnome.org", + "lastModified": 1767737596, + "narHash": "sha256-eFujfIUQDgWnSJBablOuG+32hCai192yRdrNHTv0a+s=", + "owner": "GNOME", + "repo": "gnome-shell", + "rev": "ef02db02bf0ff342734d525b5767814770d85b49", + "type": "gitlab" + }, + "original": { + "host": "gitlab.gnome.org", + "owner": "GNOME", + "ref": "gnome-49", + "repo": "gnome-shell", + "type": "gitlab" + } + }, + "home-manager": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1774715571, + "narHash": "sha256-MMjHif46sc/iLF9JqxBhFaueSPRcjPeP9AiujERH6N0=", + "owner": "nix-community", + "repo": "home-manager", + "rev": "557f5e38ce94ef0f02f05de7ae65057d4b2a89a6", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "home-manager", + "type": "github" + } + }, + "import-tree": { + "locked": { + "lastModified": 1773693634, + "narHash": "sha256-BtZ2dtkBdSUnFPPFc+n0kcMbgaTxzFNPv2iaO326Ffg=", + "owner": "vic", + "repo": "import-tree", + "rev": "c41e7d58045f9057880b0d85e1152d6a4430dbf1", + "type": "github" + }, + "original": { + "owner": "vic", + "repo": "import-tree", + "type": "github" + } + }, + "mnw": { + "locked": { + "lastModified": 1770419553, + "narHash": "sha256-b1XqsH7AtVf2dXmq2iyRr2NC1yG7skY7Z6N2MpWHlK4=", + "owner": "Gerg-L", + "repo": "mnw", + "rev": "2aaffa8030d0b262176146adbb6b0e6374ce2957", + "type": "github" + }, + "original": { + "owner": "Gerg-L", + "repo": "mnw", + "type": "github" + } + }, + "ndg": { + "inputs": { + "nixpkgs": [ + "nvf", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1768214250, + "narHash": "sha256-hnBZDQWUxJV3KbtvyGW5BKLO/fAwydrxm5WHCWMQTbw=", + "owner": "feel-co", + "repo": "ndg", + "rev": "a6bd3c1ce2668d096e4fdaaa03ad7f03ba1fbca8", + "type": "github" + }, + "original": { + "owner": "feel-co", + "ref": "refs/tags/v2.6.0", + "repo": "ndg", + "type": "github" + } + }, + "niri": { + "inputs": { + "niri-stable": "niri-stable", + "niri-unstable": "niri-unstable", + "nixpkgs": [ + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable", + "xwayland-satellite-stable": "xwayland-satellite-stable", + "xwayland-satellite-unstable": "xwayland-satellite-unstable" + }, + "locked": { + "lastModified": 1774620721, + "narHash": "sha256-QucawBaJ6Rl5JWAXAbpouXM1MsvlzwCsPGl3zOwLtzw=", + "owner": "sodiboo", + "repo": "niri-flake", + "rev": "fb83d584532282f585cd02f3aa513e98b843e7e7", + "type": "github" + }, + "original": { + "owner": "sodiboo", + "repo": "niri-flake", + "type": "github" + } + }, + "niri-stable": { + "flake": false, + "locked": { + "lastModified": 1756556321, + "narHash": "sha256-RLD89dfjN0RVO86C/Mot0T7aduCygPGaYbog566F0Qo=", + "owner": "YaLTeR", + "repo": "niri", + "rev": "01be0e65f4eb91a9cd624ac0b76aaeab765c7294", + "type": "github" + }, + "original": { + "owner": "YaLTeR", + "ref": "v25.08", + "repo": "niri", + "type": "github" + } + }, + "niri-unstable": { + "flake": false, + "locked": { + "lastModified": 1774616418, + "narHash": "sha256-z+dLkAS4bqytIlOI4h2MnjBJrSP4d1Awx0n+IV5YA3Y=", + "owner": "YaLTeR", + "repo": "niri", + "rev": "8f48f56fe19918b5cfa02e5d68a47ebaf7bf3dee", + "type": "github" + }, + "original": { + "owner": "YaLTeR", + "repo": "niri", + "type": "github" + } + }, + "nix-index-database": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1775970782, + "narHash": "sha256-7jt9Vpm48Yy5yAWigYpde+HxtYEpEuyzIQJF4VYehhk=", + "owner": "nix-community", + "repo": "nix-index-database", + "rev": "bedba5989b04614fc598af9633033b95a937933f", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nix-index-database", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1774386573, + "narHash": "sha256-4hAV26quOxdC6iyG7kYaZcM3VOskcPUrdCQd/nx8obc=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "46db2e09e1d3f113a13c0d7b81e2f221c63b8ce9", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "lastModified": 1772328832, + "narHash": "sha256-e+/T/pmEkLP6BHhYjx6GmwP5ivonQQn0bJdH9YrRB+Q=", + "owner": "nix-community", + "repo": "nixpkgs.lib", + "rev": "c185c7a5e5dd8f9add5b2f8ebeff00888b070742", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nixpkgs.lib", + "type": "github" + } + }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1774388614, + "narHash": "sha256-tFwzTI0DdDzovdE9+Ras6CUss0yn8P9XV4Ja6RjA+nU=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "1073dad219cb244572b74da2b20c7fe39cb3fa9e", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-25.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1774610258, + "narHash": "sha256-HaThtroVD9wRdx7KQk0B75JmFcXlMUoEdDFNOMOlsOs=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "832efc09b4caf6b4569fbf9dc01bec3082a00611", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "noctalia": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "noctalia-qs": "noctalia-qs" + }, + "locked": { + "lastModified": 1774807148, + "narHash": "sha256-/2YNEHj/OVdp4iXMRNxLlfkABb6mBo0VmWwymInpwkI=", + "owner": "noctalia-dev", + "repo": "noctalia-shell", + "rev": "e41c78e2facbbf4b03f34ea56eea922e546d77c8", + "type": "github" + }, + "original": { + "owner": "noctalia-dev", + "repo": "noctalia-shell", + "type": "github" + } + }, + "noctalia-qs": { + "inputs": { + "nixpkgs": [ + "noctalia", + "nixpkgs" + ], + "systems": "systems", + "treefmt-nix": "treefmt-nix" + }, + "locked": { + "lastModified": 1774734782, + "narHash": "sha256-rq/8sJPI8wD4P3CXSyvW/dPuAa+qXGdqzAKM3eunZ+4=", + "owner": "noctalia-dev", + "repo": "noctalia-qs", + "rev": "8e216ba101d761b8a71f359246941d50e22bad3f", + "type": "github" + }, + "original": { + "owner": "noctalia-dev", + "repo": "noctalia-qs", + "type": "github" + } + }, + "nur": { + "inputs": { + "flake-parts": [ + "stylix", + "flake-parts" + ], + "nixpkgs": [ + "stylix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1767810917, + "narHash": "sha256-ZKqhk772+v/bujjhla9VABwcvz+hB2IaRyeLT6CFnT0=", + "owner": "nix-community", + "repo": "NUR", + "rev": "dead29c804adc928d3a69dfe7f9f12d0eec1f1a4", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "NUR", + "type": "github" + } + }, + "nvf": { + "inputs": { + "flake-compat": "flake-compat", + "flake-parts": "flake-parts_2", + "mnw": "mnw", + "ndg": "ndg", + "nixpkgs": [ + "nixpkgs" + ], + "systems": "systems_2" + }, + "locked": { + "lastModified": 1774736237, + "narHash": "sha256-uQ+Was7QP9Bupr0XZyZXOAD32Ox8z2mJnevT2FmDwS8=", + "owner": "notashelf", + "repo": "nvf", + "rev": "a0636d5c977743851c91d3c2e74bfac90be48835", + "type": "github" + }, + "original": { + "owner": "notashelf", + "repo": "nvf", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-parts": "flake-parts", + "home-manager": "home-manager", + "import-tree": "import-tree", + "niri": "niri", + "nix-index-database": "nix-index-database", + "nixpkgs": "nixpkgs", + "noctalia": "noctalia", + "nvf": "nvf", + "secrets": "secrets", + "sops-nix": "sops-nix", + "stylix": "stylix", + "wrapper-modules": "wrapper-modules" + } + }, + "secrets": { + "locked": { + "lastModified": 1752235224, + "narHash": "sha256-R+K+AhNgwcFxG+lwHOTyOWd0yFBb66ZJQM1BIIYaLbo=", + "ref": "refs/heads/main", + "rev": "cc5d82658e9667f79569b19f09729edf6cf12286", + "revCount": 5, + "type": "git", + "url": "ssh://git@github.com/gwg313/nixos-secrets.git" + }, + "original": { + "type": "git", + "url": "ssh://git@github.com/gwg313/nixos-secrets.git" + } + }, + "sops-nix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1774303811, + "narHash": "sha256-fhG4JAcLgjKwt+XHbjs8brpWnyKUfU4LikLm3s0Q/ic=", + "owner": "Mic92", + "repo": "sops-nix", + "rev": "614e256310e0a4f8a9ccae3fa80c11844fba7042", + "type": "github" + }, + "original": { + "owner": "Mic92", + "repo": "sops-nix", + "type": "github" + } + }, + "stylix": { + "inputs": { + "base16": "base16", + "base16-fish": "base16-fish", + "base16-helix": "base16-helix", + "base16-vim": "base16-vim", + "firefox-gnome-theme": "firefox-gnome-theme", + "flake-parts": "flake-parts_3", + "gnome-shell": "gnome-shell", + "nixpkgs": [ + "nixpkgs" + ], + "nur": "nur", + "systems": "systems_3", + "tinted-foot": "tinted-foot", + "tinted-kitty": "tinted-kitty", + "tinted-schemes": "tinted-schemes", + "tinted-tmux": "tinted-tmux", + "tinted-zed": "tinted-zed" + }, + "locked": { + "lastModified": 1774124764, + "narHash": "sha256-Poz9WTjiRlqZIf197CrMMJfTifZhrZpbHFv0eU1Nhtg=", + "owner": "danth", + "repo": "stylix", + "rev": "e31c79f571c5595a155f84b9d77ce53a84745494", + "type": "github" + }, + "original": { + "owner": "danth", + "repo": "stylix", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1689347949, + "narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=", + "owner": "nix-systems", + "repo": "default-linux", + "rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default-linux", + "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" + } + }, + "systems_3": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "tinted-foot": { + "flake": false, + "locked": { + "lastModified": 1726913040, + "narHash": "sha256-+eDZPkw7efMNUf3/Pv0EmsidqdwNJ1TaOum6k7lngDQ=", + "owner": "tinted-theming", + "repo": "tinted-foot", + "rev": "fd1b924b6c45c3e4465e8a849e67ea82933fcbe4", + "type": "github" + }, + "original": { + "owner": "tinted-theming", + "repo": "tinted-foot", + "rev": "fd1b924b6c45c3e4465e8a849e67ea82933fcbe4", + "type": "github" + } + }, + "tinted-kitty": { + "flake": false, + "locked": { + "lastModified": 1735730497, + "narHash": "sha256-4KtB+FiUzIeK/4aHCKce3V9HwRvYaxX+F1edUrfgzb8=", + "owner": "tinted-theming", + "repo": "tinted-kitty", + "rev": "de6f888497f2c6b2279361bfc790f164bfd0f3fa", + "type": "github" + }, + "original": { + "owner": "tinted-theming", + "repo": "tinted-kitty", + "type": "github" + } + }, + "tinted-schemes": { + "flake": false, + "locked": { + "lastModified": 1767710407, + "narHash": "sha256-+W1EB79Jl0/gm4JqmO0Nuc5C7hRdp4vfsV/VdzI+des=", + "owner": "tinted-theming", + "repo": "schemes", + "rev": "2800e2b8ac90f678d7e4acebe4fa253f602e05b2", + "type": "github" + }, + "original": { + "owner": "tinted-theming", + "repo": "schemes", + "type": "github" + } + }, + "tinted-tmux": { + "flake": false, + "locked": { + "lastModified": 1767489635, + "narHash": "sha256-e6nnFnWXKBCJjCv4QG4bbcouJ6y3yeT70V9MofL32lU=", + "owner": "tinted-theming", + "repo": "tinted-tmux", + "rev": "3c32729ccae99be44fe8a125d20be06f8d7d8184", + "type": "github" + }, + "original": { + "owner": "tinted-theming", + "repo": "tinted-tmux", + "type": "github" + } + }, + "tinted-zed": { + "flake": false, + "locked": { + "lastModified": 1767488740, + "narHash": "sha256-wVOj0qyil8m+ouSsVZcNjl5ZR+1GdOOAooAatQXHbuU=", + "owner": "tinted-theming", + "repo": "base16-zed", + "rev": "11abb0b282ad3786a2aae088d3a01c60916f2e40", + "type": "github" + }, + "original": { + "owner": "tinted-theming", + "repo": "base16-zed", + "type": "github" + } + }, + "treefmt-nix": { + "inputs": { + "nixpkgs": [ + "noctalia", + "noctalia-qs", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1772660329, + "narHash": "sha256-IjU1FxYqm+VDe5qIOxoW+pISBlGvVApRjiw/Y/ttJzY=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "3710e0e1218041bbad640352a0440114b1e10428", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "treefmt-nix", + "type": "github" + } + }, + "wrapper-modules": { + "inputs": { + "nixpkgs": "nixpkgs_2" + }, + "locked": { + "lastModified": 1775505758, + "narHash": "sha256-akjVahwblAas9ea/pCopiQcmzAkNpRmPxh4j1nHPNV4=", + "owner": "BirdeeHub", + "repo": "nix-wrapper-modules", + "rev": "483f1b2e52c919a4eb495b441b2245289406ed6b", + "type": "github" + }, + "original": { + "owner": "BirdeeHub", + "repo": "nix-wrapper-modules", + "type": "github" + } + }, + "xwayland-satellite-stable": { + "flake": false, + "locked": { + "lastModified": 1755491097, + "narHash": "sha256-m+9tUfsmBeF2Gn4HWa6vSITZ4Gz1eA1F5Kh62B0N4oE=", + "owner": "Supreeeme", + "repo": "xwayland-satellite", + "rev": "388d291e82ffbc73be18169d39470f340707edaa", + "type": "github" + }, + "original": { + "owner": "Supreeeme", + "ref": "v0.7", + "repo": "xwayland-satellite", + "type": "github" + } + }, + "xwayland-satellite-unstable": { + "flake": false, + "locked": { + "lastModified": 1773622265, + "narHash": "sha256-wToKwH7IgWdGLMSIWksEDs4eumR6UbbsuPQ42r0oTXQ=", + "owner": "Supreeeme", + "repo": "xwayland-satellite", + "rev": "a879e5e0896a326adc79c474bf457b8b99011027", + "type": "github" + }, + "original": { + "owner": "Supreeeme", + "repo": "xwayland-satellite", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..92bc71b --- /dev/null +++ b/flake.nix @@ -0,0 +1,53 @@ +{ + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + flake-parts.url = "github:hercules-ci/flake-parts"; + import-tree.url = "github:vic/import-tree"; + wrapper-modules.url = "github:BirdeeHub/nix-wrapper-modules"; + + home-manager.url = "github:nix-community/home-manager"; + home-manager.inputs.nixpkgs.follows = "nixpkgs"; + + sops-nix.url = "github:Mic92/sops-nix"; + sops-nix.inputs.nixpkgs.follows = "nixpkgs"; + + stylix.url = "github:danth/stylix"; + stylix.inputs.nixpkgs.follows = "nixpkgs"; + + nvf = { + url = "github:notashelf/nvf"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + niri.url = "github:sodiboo/niri-flake"; + niri.inputs.nixpkgs.follows = "nixpkgs"; + noctalia = { + url = "github:noctalia-dev/noctalia-shell"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + nix-index-database = { + url = "github:nix-community/nix-index-database"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + secrets.url = "git+ssh://git@github.com/gwg313/nixos-secrets.git"; + }; + + outputs = + inputs: + inputs.flake-parts.lib.mkFlake { inherit inputs; } { + systems = [ + "x86_64-linux" + "aarch64-linux" + "x86_64-darwin" + "aarch64-darwin" + ]; + imports = [ + (inputs.import-tree ./modules/top) + (inputs.import-tree ./modules/hosts) + (inputs.import-tree ./modules/users) + (inputs.import-tree ./modules/aspects) + (inputs.import-tree ./modules/features) + ]; + }; +} diff --git a/modules/aspects/roles.nix b/modules/aspects/roles.nix new file mode 100644 index 0000000..f6a5a3d --- /dev/null +++ b/modules/aspects/roles.nix @@ -0,0 +1,106 @@ +{ + lib, + ... +}: +{ + options.dendritic.roles = lib.mkOption { + type = lib.types.attrsOf ( + lib.types.submodule ( + { ... }: + { + options = { + roles = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + }; + + features = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + }; + }; + } + ) + ); + default = { }; + }; + + config.dendritic.roles = { + core-user.features = [ + "shell" + "nh" + "system-nix" + ]; + + security-baseline.features = [ + "security-kernel-hardened" + "system-services" + "security-systemd" + "security-sysctl" + "security-firewall" + "security-auditd" + "security-malware-clamav" + "ntp-chrony" + "security-usbguard" + ]; + + desktop-wayland.features = [ + "browser-brave" + "wm-niri" + "terminal-ghostty" + "audio" + "bluetooth" + "gnupg-agent" + "wayland" + "proton" + "earlyoom" + "dolphin" + ]; + + laptop.features = [ + "hardware-laptop" + "security-systemd-bluetooth" + ]; + + server.roles = [ + "security-baseline" + "security-sysctl-strict" + ]; + + server.features = [ + "security-ssh-hardening" + "security-firewall-nftables" + "ops-server-base" + ]; + + developer.features = [ + "developer-cli" + "editor-neovim" + "containers-podman" + "ops-k8s" + "virtualization-libvirt" + "security-sudo-rs" + "security-ssh-client" + "dev-devenv" + ]; + + secrets-managed.features = [ + "secrets-system-sops" + "secrets-home-sops" + ]; + + theme-stylix.features = [ + "theme-system-stylix" + "theme-home-stylix" + ]; + + workstation.roles = [ + "core-user" + "security-baseline" + "desktop-wayland" + "developer" + "secrets-managed" + "theme-stylix" + ]; + }; +} diff --git a/modules/features/containers-podman.nix b/modules/features/containers-podman.nix new file mode 100644 index 0000000..f07d0b7 --- /dev/null +++ b/modules/features/containers-podman.nix @@ -0,0 +1,34 @@ +{ ... }: +{ + config.dendritic.features.containers-podman = { + nixosModules = [ + ( + { pkgs, ... }: + { + virtualisation = { + podman = { + enable = true; + + # Create a `docker` alias for podman, to use it as a drop-in replacement + dockerCompat = true; + + # Required for containers under podman-compose to be able to talk to each other. + defaultNetwork.settings.dns_enabled = true; + }; + }; + + environment.systemPackages = with pkgs; [ + buildah # Tool for building OCI (Open Container Initiative) and Docker container images. + distrobox # Lightweight utility for running Linux distributions in containers. + dive # A tool for exploring a Docker image, allowing inspection of layer contents. + grype # A vulnerability scanner for container images and filesystems. + hadolint # Dockerfile linter to analyze and enforce best practices in containerization. + podman-compose # Podman plugin for managing multi-container applications. + podman-tui # Text-based user interface (TUI) for Podman, facilitating container management. + syft # Open-source tool for scanning and analyzing container images for software composition and vulnerabilities. + ]; + } + ) + ]; + }; +} diff --git a/modules/features/desktop/audio.nix b/modules/features/desktop/audio.nix new file mode 100644 index 0000000..1dff16a --- /dev/null +++ b/modules/features/desktop/audio.nix @@ -0,0 +1,45 @@ +{ ... }: +{ + config.dendritic.features.audio = { + nixosModules = [ + ( + { pkgs, ... }: + { + # Enable sound with pipewire. + services.pulseaudio.enable = false; + # hardware.alsa.enablePersistence = true; + 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; + extraConfig.pipewire.adjust-sample-rate = { + "context.properties" = { + "default.clock.rate" = 192000; + #"defautlt.allowed-rates" = [ 192000 48000 44100 ]; + "defautlt.allowed-rates" = [ 192000 ]; + #"default.clock.quantum" = 32; + #"default.clock.min-quantum" = 32; + #"default.clock.max-quantum" = 32; + }; + }; + }; + + environment.systemPackages = with pkgs; [ + pipewire + wireplumber + easyeffects + pavucontrol + ]; + } + ) + ]; + }; +} diff --git a/modules/features/desktop/bluetooth.nix b/modules/features/desktop/bluetooth.nix new file mode 100644 index 0000000..d18b5b1 --- /dev/null +++ b/modules/features/desktop/bluetooth.nix @@ -0,0 +1,22 @@ +{ ... }: +{ + config.dendritic.features.bluetooth = { + nixosModules = [ + ( + { lib, ... }: + { + + services.blueman.enable = true; + hardware.bluetooth = { + enable = lib.mkForce true; + powerOnBoot = true; + }; + + services.upower = { + enable = true; + }; + } + ) + ]; + }; +} diff --git a/modules/features/desktop/dolphin.nix b/modules/features/desktop/dolphin.nix new file mode 100644 index 0000000..b45cae2 --- /dev/null +++ b/modules/features/desktop/dolphin.nix @@ -0,0 +1,148 @@ +{ ... }: +{ + config.dendritic.features.dolphin.homeModules = [ + ( + { pkgs, config, ... }: + let + user = config.dendritic.current.primaryUser; + + placesXbel = '' + + + + + + Documents + + + + false + + + + + + Downloads + + + + false + + + + + + Repositories + + + + false + + + + + ''; + in + { + home.packages = with pkgs; [ + kdePackages.dolphin + kdePackages.kio + kdePackages.kio-extras + kdePackages.kio-fuse + kdePackages.ark + kdePackages.ffmpegthumbs + kdePackages.kdegraphics-thumbnailers + # whitesur-icon-theme + ]; + + # Helps some non-KDE apps pick Dolphin for directory browsing. + xdg.mimeApps = { + enable = true; + defaultApplications = { + "inode/directory" = [ "org.kde.dolphin.desktop" ]; + }; + }; + + # Dolphin / KDE bookmarks ("Places" in the sidebar). + home.file.".local/share/user-places.xbel".text = placesXbel; + + # Optional: keep GTK bookmarks too, for apps that still use them. + gtk.gtk3.bookmarks = [ + "file:///home/${user}/Documents Documents" + "file:///home/${user}/Downloads Downloads" + "file:///home/${user}/repos Repositories" + ]; + + # Basic KDE config so Dolphin behaves sanely outside Plasma. + xdg.configFile."kdeglobals".text = '' + [Icons] + Theme=WhiteSur + + [KDE] + SingleClick=false + ''; + + # Dolphin settings. + xdg.configFile."dolphinrc".text = '' + [General] + BrowseThroughArchives=true + ShowFullPath=false + ShowSpaceInfo=true + ShowZoomSlider=true + + [KFileDialog Settings] + Places Icons Auto-resize=false + Places Icons Static Size=22 + + [MainWindow] + MenuBar=Disabled + ToolBarsMovable=Disabled + + [PreviewSettings] + Plugins=appimagethumbnail,audiothumbnail,blenderthumbnail,comicbookthumbnail,directorythumbnail,ebookthumbnail,exrthumbnail,fontthumbnail,gvsthumbnail,htmlthumbnail,imagethumbnail,jpegthumbnail,opendocumentthumbnail,postscriptthumbnail,rawthumbnail,svgthumbnail,windowsexethumbnail + + [UiSettings] + ShowStatusBar=false + ShowZoomSlider=true + ''; + + # File dialog / view settings. + xdg.configFile."kiorc".text = '' + [Confirmations] + ConfirmDelete=true + ConfirmEmptyTrash=true + + [Executable scripts] + behaviourOnLaunch=alwaysAsk + ''; + + # Optional: make Ark the preferred archive app in many cases. + xdg.desktopEntries.ark = { + name = "Ark"; + exec = "ark %U"; + terminal = false; + categories = [ + "Qt" + "KDE" + "Utility" + "Archiving" + ]; + mimeType = [ + "application/zip" + "application/x-tar" + "application/x-compressed-tar" + "application/x-bzip-compressed-tar" + "application/x-xz-compressed-tar" + "application/x-7z-compressed" + "application/x-rar" + ]; + }; + + home.sessionVariables = { + XDG_ICON_DIR = "${pkgs.whitesur-icon-theme}/share/icons/WhiteSur"; + }; + } + ) + ]; +} diff --git a/modules/features/desktop/earlyoom.nix b/modules/features/desktop/earlyoom.nix new file mode 100644 index 0000000..1bb654b --- /dev/null +++ b/modules/features/desktop/earlyoom.nix @@ -0,0 +1,29 @@ +{ ... }: +{ + config.dendritic.features.earlyoom = { + nixosModules = [ + ( + { ... }: + { + services.earlyoom = { + enable = true; + + freeMemThreshold = 15; + freeSwapThreshold = 10; + + extraArgs = [ + "-g" + "--sort-by-rss" + + "--avoid" + "^(niri|ghostty|systemd|qemu-system.*)" + + "--prefer" + "^(brave|electron|chromium|firefox|libreoffice|gimp|podman|conmon)" + ]; + }; + } + ) + ]; + }; +} diff --git a/modules/features/desktop/gnupg-agent.nix b/modules/features/desktop/gnupg-agent.nix new file mode 100644 index 0000000..4a95f76 --- /dev/null +++ b/modules/features/desktop/gnupg-agent.nix @@ -0,0 +1,21 @@ +{ ... }: +{ + config.dendritic.features.gnupg-agent = { + nixosModules = [ + ( + { pkgs, ... }: + { + programs.gnupg.agent = { + enable = true; + enableSSHSupport = true; + pinentryPackage = pkgs.pinentry-qt; + }; + + environment.sessionVariables = { + SSH_AUTH_SOCK = "/run/user/1000/gnupg/S.gpg-agent.ssh"; + }; + } + ) + ]; + }; +} diff --git a/modules/features/desktop/niri.nix b/modules/features/desktop/niri.nix new file mode 100644 index 0000000..06c9b89 --- /dev/null +++ b/modules/features/desktop/niri.nix @@ -0,0 +1,90 @@ +{ ... }: +{ + config.dendritic.features.wm-niri = { + homeModules = [ + ( + { pkgs, inputs, ... }: + { + imports = [ + inputs.niri.homeModules.niri + ../../home/programs/niri/settings.nix + ../../home/programs/niri/keybinds.nix + ../../home/programs/niri/rules.nix + ../../home/programs/niri/autostart.nix + ../../home/programs/niri/noctaliashell.nix + ]; + + services.gnome-keyring.enable = true; + } + ) + + ]; + nixosModules = [ + ( + { pkgs, lib, ... }: + { + imports = [ + ../../nixos/gui/xdg.nix + ]; + services.greetd = + let + niri-config = pkgs.writeText "niri-config" '' + hotkey-overlay { + skip-at-startup + } + environment { + GTK_USE_PORTAL "0" + GDK_DEBUG "no-portals" + } + + // other settings + + spawn-at-startup "sh" "-c" "${pkgs.greetd.regreet}/bin/regreet; pkill -f niri" + ''; + in + { + enable = true; + settings = { + default_session = { + command = "niri -c ${niri-config}"; + user = "greeter"; + }; + }; + }; + programs.regreet.enable = true; + # services.displayManager.enable = false; + # services.displayManager = { + # sddm = { + # package = pkgs.kdePackages.sddm; + # enable = true; + # wayland.enable = true; + # }; + # }; + # services.displayManager.gdm = { + # enable = false; + # wayland = true; + # }; + services.displayManager.enable = lib.mkForce false; + services.xserver = { + enable = true; + + xkb = { + variant = ""; + layout = "us"; + }; + }; + + environment = { + variables = { + TERMINAL = "ghostty"; + EDITOR = "nvim"; + VISUAL = "nvim"; + PAGER = "moar"; + PASSWORD_STORE_DIR = "$HOME/.local/share/password-store"; + }; + }; + } + ) + ]; + }; +} diff --git a/modules/features/desktop/theme-stylix.nix b/modules/features/desktop/theme-stylix.nix new file mode 100644 index 0000000..87e07a8 --- /dev/null +++ b/modules/features/desktop/theme-stylix.nix @@ -0,0 +1,36 @@ +{ ... }: +{ + config.dendritic.features = { + theme-home-stylix = { + includeInEmbeddedHomeManager = false; + homeModules = [ + ({ inputs, pkgs, ... }: { + imports = [ + inputs.stylix.homeModules.stylix + ]; + + stylix.enable = true; + stylix.base16Scheme = "${pkgs.base16-schemes}/share/themes/atelier-sulphurpool-light.yaml"; + }) + ]; + }; + + theme-system-stylix.nixosModules = [ + ({ inputs, pkgs, ... }: { + imports = [ + inputs.stylix.nixosModules.stylix + ]; + + stylix = { + enable = true; + base16Scheme = "${pkgs.base16-schemes}/share/themes/atelier-sulphurpool-light.yaml"; + fonts = { + monospace = { + name = "JetBrainsMono Nerd Font"; + }; + }; + }; + }) + ]; + }; +} diff --git a/modules/features/desktop/wayland.nix b/modules/features/desktop/wayland.nix new file mode 100644 index 0000000..31b8e79 --- /dev/null +++ b/modules/features/desktop/wayland.nix @@ -0,0 +1,36 @@ +{ ... }: +{ + config.dendritic.features.wayland = { + nixosModules = [ + ( + { pkgs, ... }: + { + environment.systemPackages = with pkgs; [ + wlr-randr + wl-clipboard + ]; + + environment.sessionVariables = { + POLKIT_AUTH_AGENT = "${pkgs.polkit_gnome}/libexec/polkit-gnome-authentication-agent-1"; + GSETTINGS_SCHEMA_DIR = "${pkgs.gsettings-desktop-schemas}/share/gsettings-schemas/${pkgs.gsettings-desktop-schemas.name}/glib-2.0/schemas"; + WLR_NO_HARDWARE_CURSORS = "1"; + NIXOS_OZONE_WL = "1"; + MOZ_ENABLE_WAYLAND = "1"; + SDL_VIDEODRIVER = "wayland"; + _JAVA_AWT_WM_NONREPARENTING = "1"; + CLUTTER_BACKEND = "wayland"; + # WLR_RENDERER = "vulkan"; + GTK_USE_PORTAL = "1"; + #NIXOS_XDG_OPEN_USE_PORTAL = "1"; # Sets the desktop portal to use flatpak + WLR_NO_HARDWARE_CURSOR = "1"; + GDK_BACKEND = "wayland"; + QT_QPA_PLATFORM = "wayland;xcb"; + QT_AUTO_SCREEN_SCALE_FACTOR = "1"; + QT_WAYLAND_DISABLE_WINDOWDECORATION = "1"; + QT_QPA_PLATFORMTHEME = "qt5ct"; + }; + } + ) + ]; + }; +} diff --git a/modules/features/developer-cli.nix b/modules/features/developer-cli.nix new file mode 100644 index 0000000..2738294 --- /dev/null +++ b/modules/features/developer-cli.nix @@ -0,0 +1,15 @@ +{ ... }: +{ + config.dendritic.features.developer-cli.nixosModules = [ + ( + { pkgs, inputs, ... }: + { + imports = [ inputs.nix-index-database.nixosModules.default ]; + environment.systemPackages = with pkgs; [ git ]; + + programs.command-not-found.enable = false; + programs.nix-index-database.comma.enable = true; + } + ) + ]; +} diff --git a/modules/features/editor-neovim-package.nix b/modules/features/editor-neovim-package.nix new file mode 100644 index 0000000..169db28 --- /dev/null +++ b/modules/features/editor-neovim-package.nix @@ -0,0 +1,8 @@ +{ ... }: +{ + config.dendritic.features.editor-neovim.homeModules = [ + ({ pkgs, ... }: { + home.packages = with pkgs; [ neovim ]; + }) + ]; +} diff --git a/modules/features/hardware-laptop.nix b/modules/features/hardware-laptop.nix new file mode 100644 index 0000000..cb85d4c --- /dev/null +++ b/modules/features/hardware-laptop.nix @@ -0,0 +1,18 @@ +{ ... }: +{ + config.dendritic.features.hardware-laptop = { + + nixosModules = [ + ( + { config, ... }: + { + networking.networkmanager.enable = true; + powerManagement.enable = true; + + services.tlp.enable = true; + + } + ) + ]; + }; +} diff --git a/modules/features/nvim.nix b/modules/features/nvim.nix new file mode 100644 index 0000000..3a63c58 --- /dev/null +++ b/modules/features/nvim.nix @@ -0,0 +1,95 @@ +{ inputs, ... }: +{ + config.dendritic.features.editor-neovim.homeModules = [ + ({ pkgs, lib, ... }: { + imports = [ + inputs.nvf.homeManagerModules.default + ../home/programs/neovim/options.nix + ../home/programs/neovim/languages.nix + ../home/programs/neovim/picker.nix + ../home/programs/neovim/snacks.nix + ../home/programs/neovim/keymaps.nix + ../home/programs/neovim/utils.nix + ../home/programs/neovim/mini.nix + ]; + + programs.nvf = { + enable = true; + + settings.vim = { + startPlugins = with pkgs; [ + vimPlugins.grug-far-nvim + vimPlugins.image-nvim + vimPlugins.vim-hoogle + vimPlugins.telescope_hoogle + vimPlugins.zk-nvim + vimPlugins.cyberdream-nvim + vimPlugins.octo-nvim + ]; + + luaConfigRC.colorscheme = '' + vim.cmd.colorscheme("cyberdream-light") + ''; + + luaConfigRC.zk_nvim = '' + local ok, zk = pcall(require, "zk") + if not ok then return end + + zk.setup({ + picker = "select", + lsp = { + config = { + name = "zk", + cmd = { "zk", "lsp" }, + filetypes = { "markdown" }, + }, + auto_attach = { enabled = true }, + }, + }) + ''; + }; + + settings.vim.augroups = [ + { name = "SwiftSetup"; } + ]; + + settings.vim.autocmds = [ + { + event = [ "FileType" ]; + pattern = [ "swift" ]; + group = "SwiftSetup"; + desc = "Start sourcekit-lsp for Swift files"; + callback = lib.generators.mkLuaInline '' + function() + local lspconfig = require("lspconfig") + for _, client in pairs(vim.lsp.get_active_clients()) do + if client.name == "sourcekit" then return end + end + lspconfig.sourcekit.setup({ + cmd = { "sourcekit-lsp" }, + filetypes = { "swift", "swiftpm" }, + root_dir = lspconfig.util.root_pattern("Package.swift", ".git"), + }) + end + ''; + } + + { + event = [ "BufWritePost" ]; + pattern = [ "*.swift" ]; + group = "SwiftSetup"; + desc = "Format Swift files on save"; + callback = lib.generators.mkLuaInline '' + function() + if vim.fn.executable("swift-format") == 1 then + vim.cmd("silent! noautocmd !swift-format -i %") + vim.cmd("checktime") + end + end + ''; + } + ]; + }; + }) + ]; +} diff --git a/modules/features/ops/ops-k8s.nix b/modules/features/ops/ops-k8s.nix new file mode 100644 index 0000000..9d45ec9 --- /dev/null +++ b/modules/features/ops/ops-k8s.nix @@ -0,0 +1,23 @@ +{ ... }: +{ + config.dendritic.features.ops-k8s.homeModules = [ ]; + + config.dendritic.features.ops-k8s.nixosModules = [ + ( + { pkgs, ... }: + { + environment.systemPackages = with pkgs; [ + argocd # Declarative, GitOps continuous delivery tool for Kubernetes. + k3d # Lightweight utility to run Kubernetes clusters using Docker. + k9s # Kubernetes CLI to visually navigate and manage resources in clusters. + kind # Kubernetes IN Docker: Tool for running local Kubernetes clusters using Docker container nodes. + kubectl # Kubernetes command-line tool for interacting with clusters. + kubectx # Switch between Kubernetes contexts and namespaces with ease. + kubernetes-helm # Package manager for Kubernetes applications, simplifying deployment and management. + minikube # Local Kubernetes cluster for easy testing and development. + stern # Multi-container log tailing and streaming for Kubernetes. + ]; + } + ) + ]; +} diff --git a/modules/features/ops/ops-nh.nix b/modules/features/ops/ops-nh.nix new file mode 100644 index 0000000..eefdf3a --- /dev/null +++ b/modules/features/ops/ops-nh.nix @@ -0,0 +1,59 @@ +{ ... }: +{ + config.dendritic.features.nh = { + homeModules = [ + ( + { config, ... }: + let + NH_NO_CHECKS = "1"; + + configFor = flakePath: { + enable = true; + + clean = { + enable = true; + extraArgs = "--keep 3 --keep-since 8d"; + }; + + flake = flakePath; + }; + in + { + home.sessionVariables = { + inherit NH_NO_CHECKS; + }; + + programs.nh = configFor "${config.home.homeDirectory}/nix-config"; + } + ) + ]; + + nixosModules = [ + ( + { config, ... }: + let + NH_NO_CHECKS = "1"; + user = config.dendritic.current.primaryUser; + + configFor = flakePath: { + enable = true; + + clean = { + enable = true; + extraArgs = "--keep 3 --keep-since 8d"; + }; + + flake = flakePath; + }; + in + { + environment.variables = { + inherit NH_NO_CHECKS; + }; + + programs.nh = configFor "/home/${user}/nix-config"; + } + ) + ]; + }; +} diff --git a/modules/features/programs/browsers/browser-brave.nix b/modules/features/programs/browsers/browser-brave.nix new file mode 100644 index 0000000..32a5e1a --- /dev/null +++ b/modules/features/programs/browsers/browser-brave.nix @@ -0,0 +1,130 @@ +{ ... }: +{ + config.dendritic.features.browser-brave.homeModules = [ + ( + { pkgs, ... }: + { + programs.brave = { + enable = true; + commandLineArgs = [ + # Wayland Native + "--enable-features=UseOzonePlatform" + "--ozone-platform=wayland" + + # Hardware Acceleration (NVIDIA optimized) + "--enable-accelerated-video-decode" + "--enable-gpu-rasterization" + "--enable-zero-copy" + "--ignore-gpu-blocklist" + + # Performance + "--enable-features=VaapiVideoDecoder" + "--enable-features=VaapiVideoEncoder" + "--enable-features=CanvasOopRasterization" + "--disable-features=UseChromeOSDirectVideoDecoder" + + # Privacy & Security + "--disable-features=MediaRouter" # Disable Chromecast + "--disable-features=OptimizationHints" # No Google suggestions + "--disable-features=AutofillSavePaymentMethods" + "--disable-background-networking" # No telemetry + "--disable-sync" # Manual sync control + + # Wayland-specific fixes + "--disable-features=WaylandWpColorManagerV1" # Color management fix + + # UI/UX + "--force-dark-mode" # Match Stylix theme + "--enable-features=WebUIDarkMode" + "--no-default-browser-check" + ]; + extensions = [ + # let + # ids = [ + "cjpalhdlnbpafiamejdnhcphjbkeiagm" # ublock origin + "dbepggeogbaibhgnhhndojpepiihcmeb" # vimium + "eimadpbcbfnmbkopoojfekhnkhdbieeh" # dark reader + "pkehgijcmpdhfbdbbnkijodmdjhbjlgp" # privacy badger + "ghmbeldphafepmbegfdlkpapadhbakde" # proton pass + "mmjbdbjnoablegbkcklggeknkfcjkjia" # custom new tab page + ]; + # in + # map (id: { inherit id; }) ids; + }; + + xdg.configFile."BraveSoftware/Brave-Browser/Policies/managed/policy.json".text = builtins.toJSON { + BraveShieldsAdControl = 2; + BraveShieldsTrackersBlocked = 1; + BraveShieldsHttpsEverywhere = 1; + BraveRewardsDisabled = 1; + BraveWalletDisabled = 1; + BraveVPNDisabled = 1; + BraveAIChatEnabled = 0; + PasswordManagerEnabled = 0; + BravePlaylistEnabled = 0; + BraveWebDiscoveryEnabled = 0; + BraveStatsPingEnabled = 0; + DnsOverHttpsMode = "automatic"; + BraveDarkMode = 1; + }; + + home.sessionVariables = { + DEFAULT_BROWSER = "${pkgs.brave}/bin/brave"; + BROWSER = "${pkgs.brave}/bin/brave"; + }; + + xdg.desktopEntries = { + brave-incognito = { + name = "Brave (Private window)"; + genericName = "Navigateur Web"; + exec = "brave --incognito"; + icon = "brave-browser"; + terminal = false; + categories = [ + "Network" + "WebBrowser" + ]; + mimeType = [ + "text/html" + "text/xml" + ]; + }; + brave-tor = { + name = "Brave (Private window w/Tor)"; + genericName = "Navigateur Web"; + exec = "brave --tor"; + icon = "brave-browser"; + terminal = false; + categories = [ + "Network" + "WebBrowser" + ]; + }; + }; + + # ================================================================= + # BRAVE SETTINGS (via brave://flags) + # ================================================================= + + # These need to be set manually in brave://flags on first launch: + # - Enable Tab Groups (UI) + # - Enable Parallel Downloading + # - Enable Reader Mode + # - GPU Rasterization: Enabled + # - Override software rendering list: Enabled + # + # Privacy settings (brave://settings/privacy): + # - Block trackers & ads: Aggressive + # - Block all fingerprinting + # - Upgrade connections to HTTPS + # - Block scripts: Off (breaks sites, use uBlock instead) + # - Block cookies: Only 3rd party + # + # Appearance (brave://settings/appearance): + # - Show home button: Off + # - Show bookmarks bar: Only on new tab + # - Use wide address bar: On + } + ) + ]; +} diff --git a/modules/features/programs/cli/cli-zoxide.nix b/modules/features/programs/cli/cli-zoxide.nix new file mode 100644 index 0000000..4b8bbde --- /dev/null +++ b/modules/features/programs/cli/cli-zoxide.nix @@ -0,0 +1,23 @@ +{ ... }: +{ + config.dendritic.features.cli-zoxide = { + homeModules = [ + ( + { ... }: + { + programs.zoxide = { + enable = true; + enableZshIntegration = true; + }; + + programs.zsh = { + shellAliases = { + cd = "z"; + }; + }; + } + ) + ]; + + }; +} diff --git a/modules/features/programs/cli/cli-zsh.nix b/modules/features/programs/cli/cli-zsh.nix new file mode 100644 index 0000000..4297b28 --- /dev/null +++ b/modules/features/programs/cli/cli-zsh.nix @@ -0,0 +1,150 @@ +{ ... }: +{ + config.dendritic.features.cli-zsh = { + homeModules = [ + ( + { config, pkgs, ... }: + { + programs.zsh = { + enable = true; + enableCompletion = true; + autosuggestion.enable = true; + syntaxHighlighting.enable = true; + syntaxHighlighting.highlighters = [ + "main" + "brackets" + "pattern" + "regexp" + "root" + "line" + ]; + historySubstringSearch.enable = true; + shellAliases = { + update = "sudo nixos-rebuild switch"; + clean = "nix-collect-garbage -d"; + repair = "nix-store --repair --verify --check-contents"; + reload = "source ~/.zshrc"; + + "." = "cd ../"; + ".." = "cd ../../"; + "..." = "cd ../../../"; + "...." = "cd ../../../../"; + + ps = "procs"; + grep = "rg"; + cat = "bat --theme=base16 --color=always --paging=never --tabs=2 --wrap=never --plain"; + vim = "nvim"; + + # Default flags + rm = "rm -i"; + chmod = "chmod -R"; + cp = "cp -R -i -v"; + mv = "mv -i -v"; + mkdir = "mkdir -p -v"; + df = "df -h"; + du = "du -h -s"; + dd = "dd status=progress bs=4M conv=fdatasync "; + wgetpaste = "wgetpaste -Xx"; + sudo = "sudo "; # Makes sudo work with es + ssh = "TERM=xterm ssh"; # Fixes some issues with ssh on some terminals + wget = "wget -c"; + ping = "ping -c 5"; + ftp = "ftp -p"; + + # Misc alieses I use often + + ports = "ss -tulanp"; + rmd = "rm -rf"; + mine = "sudo chown -R $(whoami):users"; + benchmark = "hyperfine --warmup 3 "; + c = "clear"; + listen = "lsof -P -i -n"; + octal = "stat -c '%a %n'"; + f = "$(pay-respects zsh)"; + }; + + history = { + size = 10000; + path = "${config.xdg.dataHome}/zsh/history"; + }; + initContent = '' + eval "$(pay-respects zsh --alias)" + + # search history based on what's typed in the prompt + autoload -U history-search-end + zle -N history-beginning-search-backward-end history-search-end + zle -N history-beginning-search-forward-end history-search-end + bindkey "^[OA" history-beginning-search-backward-end + bindkey "^[OB" history-beginning-search-forward-end + + + # General completion behavior + zstyle ':completion:*' completer _extensions _complete _approximate + # Use cache + zstyle ':completion:*' use-cache on + zstyle ':completion:*' cache-path "$XDG_CACHE_HOME/zsh/.zcompcache" + + + # Complete the alias + zstyle ':completion:*' complete true + # Autocomplete options + zstyle ':completion:*' complete-options true + + + # Completion matching control + zstyle ':completion:*' matcher-list 'm:{a-zA-Z}={A-Za-z}' 'r:|[._-]=* r:|=*' 'l:|=* r:|=*' + zstyle ':completion:*' keep-prefix true + + + # Group matches and describe + zstyle ':completion:*' menu select + zstyle ':completion:*' list-grouped false + zstyle ':completion:*' list-separator ''' + zstyle ':completion:*' group-name ''' + zstyle ':completion:*' verbose yes + zstyle ':completion:*:matches' group 'yes' + zstyle ':completion:*:warnings' format '%F{red}%B-- No match for: %d --%b%f' + zstyle ':completion:*:messages' format '%d' + zstyle ':completion:*:corrections' format '%B%d (errors: %e)%b' + zstyle ':completion:*:descriptions' format '[%d]' + + + # Colors + zstyle ':completion:*' list-colors ''${(s.:.)LS_COLORS} + + + # case insensitive tab completion + zstyle ':completion:*:*:cd:*' tag-order local-directories directory-stack path-directories + zstyle ':completion:*:*:cd:*:directory-stack' menu yes select + zstyle ':completion:*:-tilde-:*' group-order 'named-directories' 'path-directories' 'users' 'expand' + zstyle ':completion:*:*:-command-:*:*' group-order aliases builtins functions commands + zstyle ':completion:*' special-dirs true + zstyle ':completion:*' squeeze-slashes true + + + # Sort + zstyle ':completion:*' sort false + zstyle ":completion:*:git-checkout:*" sort false + zstyle ':completion:*' file-sort modification + zstyle ':completion:*:eza' sort false + zstyle ':completion:complete:*:options' sort false + zstyle ':completion:files' sort false + + autoload -U compinit && compinit + export CARAPACE_BRIDGES='zsh,fish,bash,inshellisense' # optional + zstyle ':completion:*' format $'\e[2;37mCompleting %d\e[m' + source <(carapace _carapace) + zstyle ':completion:*:git:*' group-order 'main commands' 'alias commands' 'external commands' + ''; + }; + + home.packages = with pkgs; [ + fastfetch + pay-respects + ]; + } + ) + ]; + + }; +} diff --git a/modules/features/programs/cli/dev-devenv.nix b/modules/features/programs/cli/dev-devenv.nix new file mode 100644 index 0000000..8207ab9 --- /dev/null +++ b/modules/features/programs/cli/dev-devenv.nix @@ -0,0 +1,20 @@ +{ ... }: +{ + config.dendritic.features.dev-devenv = { + homeModules = [ + ( + { pkgs, ... }: + { + home.packages = with pkgs; [ + cachix + devenv + ]; + + programs.direnv.enable = true; + programs.direnv.nix-direnv.enable = true; + } + ) + ]; + + }; +} diff --git a/modules/features/programs/cli/filemanager-yazi.nix b/modules/features/programs/cli/filemanager-yazi.nix new file mode 100644 index 0000000..784bde6 --- /dev/null +++ b/modules/features/programs/cli/filemanager-yazi.nix @@ -0,0 +1,136 @@ +{ ... }: +{ + config.dendritic.features.filemanager-yazi = { + homeModules = [ + ( + { pkgs, lib, ... }: + { + programs.yazi = { + enable = true; + enableZshIntegration = true; + shellWrapperName = "y"; + plugins = { + starship = pkgs.yaziPlugins.starship; + full-border = pkgs.yaziPlugins.full-border; + chmod = pkgs.yaziPlugins.chmod; + compress = pkgs.yaziPlugins.compress; + smart-paste = pkgs.yaziPlugins.smart-paste; + smart-enter = pkgs.yaziPlugins.smart-enter; + smart-filter = pkgs.yaziPlugins.smart-filter; + }; + settings = { + yazi = { + ratio = [ + 1 + 4 + 3 + ]; + sort_by = "natural"; + sort_sensitive = true; + sort_reverse = false; + sort_dir_first = true; + linemode = "none"; + show_hidden = true; + show_symlink = true; + }; + + preview = { + image_filter = "lanczos3"; + image_quality = 90; + tab_size = 1; + max_width = 600; + max_height = 900; + cache_dir = ""; + ueberzug_scale = 1; + ueberzug_offset = [ + 0 + 0 + 0 + 0 + ]; + }; + + tasks = { + micro_workers = 5; + macro_workers = 10; + bizarre_retry = 5; + }; + }; + initLua = '' + require("starship"):setup() + require("full-border"):setup() + require("smart-enter"):setup { + open_multi = true, + } + ''; + keymap = { + mgr.prepend_keymap = [ + { + on = [ + "c" + "a" + "a" + ]; + run = "plugin compress"; + desc = "Archive selected files"; + } + { + on = [ + "c" + "a" + "p" + ]; + run = "plugin compress -p"; + desc = "Archive selected files (password)"; + } + { + on = [ + "c" + "a" + "h" + ]; + run = "plugin compress -ph"; + desc = "Archive selected files (password+header)"; + } + { + on = [ + "c" + "a" + "l" + ]; + run = "plugin compress -l"; + desc = "Archive selected files (compression level)"; + } + { + on = [ + "c" + "a" + "u" + ]; + run = "plugin compress -phl"; + desc = "Archive selected files (password+header+level)"; + } + { + on = "p"; + run = "plugin smart-paste"; + desc = "Paste into the hovered directory or CWD"; + } + { + on = "l"; + run = "plugin smart-enter"; + desc = "Enter the child directory, or open the file"; + } + { + on = "F"; + run = "plugin smart-filter"; + desc = "Smart filter"; + } + ]; + }; + }; + } + ) + ]; + + }; +} diff --git a/modules/features/programs/cli/git-lazygit.nix b/modules/features/programs/cli/git-lazygit.nix new file mode 100644 index 0000000..9a1cf0c --- /dev/null +++ b/modules/features/programs/cli/git-lazygit.nix @@ -0,0 +1,46 @@ +{ ... }: +{ + config.dendritic.features.git-lazygit = { + homeModules = [ + ( + { config, lib, ... }: + let + accent = "#${config.lib.stylix.colors.base0D}"; + muted = "#${config.lib.stylix.colors.base03}"; + in + { + programs.lazygit = { + enable = true; + settings = lib.mkForce { + + disableStartupPopups = true; + notARepository = "skip"; + promptToReturnFromSubprocess = false; + update.method = "never"; + + git = { + commit.signOff = true; + overrideGpg = true; + }; + gui = { + theme = { + activeBorderColor = [ + accent + "bold" + ]; + inactiveBorderColor = [ muted ]; + }; + showListFooter = false; + showRandomTip = false; + showCommandLog = false; + showBottomLine = false; + nerdFontsVersion = "3"; + }; + }; + }; + } + ) + ]; + + }; +} diff --git a/modules/features/programs/cli/terminal-ghostty.nix b/modules/features/programs/cli/terminal-ghostty.nix new file mode 100644 index 0000000..164d314 --- /dev/null +++ b/modules/features/programs/cli/terminal-ghostty.nix @@ -0,0 +1,38 @@ +{ ... }: +{ + config.dendritic.features.terminal-ghostty = { + homeModules = [ + ( + { pkgs, lib, ... }: + { + programs.ghostty = { + enable = true; + enableZshIntegration = true; + installVimSyntax = true; + settings = { + window-padding-x = 10; + window-padding-y = 10; + auto-update = "off"; + working-directory = "home"; + window-inherit-working-directory = false; # avoid inheritance + keybinds = [ ]; + # background-opacity = 0.8; + background-opacity = 1; + confirm-close-surface = false; + font-family = lib.mkForce "Comic Code Ligatures"; + # font-family = lib.mkForce "Terminus"; + font-size = 14; + gtk-titlebar = false; + # theme = "Teerb"; + }; + }; + home.packages = with pkgs; [ + ueberzugpp + terminus_font_ttf + ]; + } + ) + ]; + + }; +} diff --git a/modules/features/programs/proton.nix b/modules/features/programs/proton.nix new file mode 100644 index 0000000..fdb1743 --- /dev/null +++ b/modules/features/programs/proton.nix @@ -0,0 +1,27 @@ +{ ... }: +{ + config.dendritic.features.proton.homeModules = [ + ( + { pkgs, ... }: + { + home.packages = with pkgs; [ + proton-vpn + proton-pass + proton-authenticator + ]; + + # Fix Proton Authenticator desktop entry + xdg.desktopEntries = { + "Proton Authenticator" = { + name = "Proton Authenticator"; + exec = "env WEBKIT_DISABLE_COMPOSITING_MODE=1 ${pkgs.proton-authenticator}/bin/proton-authenticator"; + icon = "proton-authenticator"; + type = "Application"; + categories = [ "Utility" ]; + terminal = false; + }; + }; + } + ) + ]; +} diff --git a/modules/features/secrets-sops.nix b/modules/features/secrets-sops.nix new file mode 100644 index 0000000..8850b21 --- /dev/null +++ b/modules/features/secrets-sops.nix @@ -0,0 +1,34 @@ +{ ... }: +{ + config.dendritic.features = { + secrets-home-sops.homeModules = [ + ({ inputs, config, ... }: { + imports = [ + inputs.sops-nix.homeModules.sops + ]; + + sops.defaultSopsFile = ../../secrets/users/${config.dendritic.current.primaryUser}.yaml; + sops.defaultSopsFormat = "yaml"; + sops.age.keyFile = "${config.home.homeDirectory}/.config/sops/age/keys.txt"; + + sops.secrets."user/test" = { + path = ".config/secrets/test"; + }; + }) + ]; + + secrets-system-sops.nixosModules = [ + ({ inputs, config, ... }: { + imports = [ + inputs.sops-nix.nixosModules.sops + ]; + + sops.defaultSopsFile = ../../secrets/hosts/${config.dendritic.current.hostName}.yaml; + sops.defaultSopsFormat = "yaml"; + sops.age.keyFile = "/home/${config.dendritic.current.primaryUser}/.config/sops/age/keys.txt"; + + sops.secrets."system/example" = { }; + }) + ]; + }; +} diff --git a/modules/features/security/security-auditd.nix b/modules/features/security/security-auditd.nix new file mode 100644 index 0000000..83cdfb7 --- /dev/null +++ b/modules/features/security/security-auditd.nix @@ -0,0 +1,22 @@ +{ ... }: +{ + config.dendritic.features.security-auditd = { + nixosModules = [ + ( + { ... }: + { + # start as early in the boot process as possible + boot.kernelParams = [ "audit=1" ]; + + security.auditd.enable = true; + security.audit.enable = true; + + security.audit.rules = [ + # Log all program executions on 64-bit architecture + "-a exit,always -F arch=b64 -S execve" + ]; + } + ) + ]; + }; +} diff --git a/modules/features/security/security-firewall-nftables.nix b/modules/features/security/security-firewall-nftables.nix new file mode 100644 index 0000000..981d753 --- /dev/null +++ b/modules/features/security/security-firewall-nftables.nix @@ -0,0 +1,17 @@ +{ ... }: +{ + config.dendritic.features.security-firewall = { + nixosModules = [ + ( + { ... }: + { + networking.nftables.enable = true; + + networking.firewall = { + enable = true; + }; + } + ) + ]; + }; +} diff --git a/modules/features/security/security-kernel-hardened.nix b/modules/features/security/security-kernel-hardened.nix new file mode 100644 index 0000000..d715a8b --- /dev/null +++ b/modules/features/security/security-kernel-hardened.nix @@ -0,0 +1,102 @@ +{ ... }: +{ + config.dendritic.features.security-kernel-hardened = { + nixosModules = [ + ( + { pkgs, ... }: + { + boot.kernelPackages = pkgs.linuxPackages_hardened; + + security = { + protectKernelImage = true; + lockKernelModules = false; # this breaks iptables, wireguard, and virtd + + # force-enable the Page Table Isolation (PTI) Linux kernel feature + forcePageTableIsolation = true; + + # User namespaces are required for sandboxing. + # this means you cannot set `"user.max_user_namespaces" = 0;` in sysctl + allowUserNamespaces = true; + + # Disable unprivileged user namespaces, unless containers are enabled + # unprivilegedUsernsClone = true; + allowSimultaneousMultithreading = true; + }; + + boot.kernelParams = [ + # make it harder to influence slab cache layout + "slab_nomerge" + # enables zeroing of memory during allocation and free time + # helps mitigate use-after-free vulnerabilaties + "init_on_alloc=1" + "init_on_free=1" + # randomizes page allocator freelist, improving security by + # making page allocations less predictable + "page_alloc.shuffel=1" + # enables Kernel Page Table Isolation, which mitigates Meltdown and + # prevents some KASLR bypasses + "pti=on" + # randomizes the kernel stack offset on each syscall + # making attacks that rely on a deterministic stack layout difficult + "randomize_kstack_offset=on" + # disables vsyscalls, they've been replaced with vDSO + "vsyscall=none" + # disables debugfs, which exposes sensitive info about the kernel + "debugfs=off" + # certain exploits cause an "oops", this makes the kernel panic if an "oops" occurs + "oops=panic" + # only alows kernel modules that have been signed with a valid key to be loaded + # making it harder to load malicious kernel modules + # can make VirtualBox or Nvidia drivers unusable + "module.sig_enforce=1" + # prevents user space code excalation + "lockdown=confidentiality" + # "rd.udev.log_level=3" + # "udev.log_priority=3" + ]; + boot.blacklistedKernelModules = [ + # Obscure networking protocols + "dccp" + "sctp" + "rds" + "tipc" + "n-hdlc" + "ax25" + "netrom" + "x25" + "rose" + "decnet" + "econet" + "af_802154" + "ipx" + "appletalk" + "psnap" + "p8023" + "p8022" + "can" + "atm" + # Various rare filesystems + "cramfs" + "freevxfs" + "jffs2" + "hfs" + "hfsplus" + "udf" + + # Not so rare filesystems + "squashfs" + "cifs" + "nfs" + "nfsv3" + "nfsv4" + "ksmbd" + "gfs2" + # vivid driver is only useful for testing purposes and has been the + # cause of privilege escalation vulnerabilities + "vivid" + ]; + } + ) + ]; + }; +} diff --git a/modules/features/security/security-malware-clamav.nix b/modules/features/security/security-malware-clamav.nix new file mode 100644 index 0000000..9aadba0 --- /dev/null +++ b/modules/features/security/security-malware-clamav.nix @@ -0,0 +1,33 @@ +{ ... }: +{ + config.dendritic.features.security-malware-clamav = { + nixosModules = [ + ( + { pkgs, ... }: + { + environment.systemPackages = with pkgs; [ + clamav + ]; + + services.clamav = { + daemon.enable = true; + updater.enable = true; + updater.frequency = 12; # Number of database checks per day + scanner = { + enable = true; + # 4:00 AM + interval = "*-*-* 04:00:00"; + scanDirectories = [ + "/home" + "/var/lib" + "/tmp" + "/etc" + "/var/tmp" + ]; + }; + }; + } + ) + ]; + }; +} diff --git a/modules/features/security/security-ssh-client.nix b/modules/features/security/security-ssh-client.nix new file mode 100644 index 0000000..1e15ba4 --- /dev/null +++ b/modules/features/security/security-ssh-client.nix @@ -0,0 +1,87 @@ +{ ... }: +{ + config.dendritic.features.security-ssh-client = { + + nixosModules = [ + ( + { ... }: + { + programs.ssh = { + # disable unnecessary forwardings + forwardX11 = false; + + # explicitly define cryptography algorithms to avoid the use of weak algorithms + # AES CTR modes have been removed to mitigate the Terrapin attack + # https://terrapin-attack.com/ + ciphers = [ + "aes256-gcm@openssh.com" + "aes128-gcm@openssh.com" + ]; + hostKeyAlgorithms = [ + "ssh-ed25519" + "ssh-ed25519-cert-v01@openssh.com" + "sk-ssh-ed25519@openssh.com" + "sk-ssh-ed25519-cert-v01@openssh.com" + "rsa-sha2-256" + "rsa-sha2-256-cert-v01@openssh.com" + "rsa-sha2-512" + "rsa-sha2-512-cert-v01@openssh.com" + ]; + macs = [ + "hmac-sha2-256-etm@openssh.com" + "hmac-sha2-512-etm@openssh.com" + "umac-128-etm@openssh.com" + ]; + kexAlgorithms = [ + "sntrup761x25519-sha512@openssh.com" + "curve25519-sha256" + "curve25519-sha256@libssh.org" + "diffie-hellman-group16-sha512" + "diffie-hellman-group18-sha512" + ]; + extraConfig = " + # disable unnecessary forwardings + ForwardAgent no + ForwardX11Trusted no + GatewayPorts no + Tunnel no + + # disable unnecessary authentication methods + ChallengeResponseAuthentication no + HostbasedAuthentication no + + # define authentication methods to be used + PasswordAuthentication yes + PubkeyAuthentication yes + PreferredAuthentications publickey,password + + # disable pre-connection compression as it could cause security issues + Compression no + + # in addition to checking a host's hostname, also check the host's IP address + # this provides extra safety against DNS spoofing attacks + CheckHostIP yes + + # ask the user if the user wants to accept the new host's host key + StrictHostKeyChecking ask + + # hash the entries in the known_hosts file to prevent disclosure + # of the file's content + HashKnownHosts yes + + # send a keepalive message to the server when the session has been idle for 60 seconds + # this prevents/detects connection timeouts + ServerAliveInterval 60 + + # increase the number of password retries + NumberOfPasswordPrompts 5 + + # display an ASCII art of the server's host key + VisualHostKey yes + "; + }; + } + ) + ]; + }; +} diff --git a/modules/features/security/security-ssh.nix b/modules/features/security/security-ssh.nix new file mode 100644 index 0000000..d302249 --- /dev/null +++ b/modules/features/security/security-ssh.nix @@ -0,0 +1,177 @@ +{ ... }: +{ + config.dendritic.features = { + + security-ssh-server-hardening.nixosModules = [ + ( + { config, ... }: + { + + services.fail2ban = { + enable = true; + maxretry = 5; + bantime = "1h"; + # ignoreIP = [ + # "172.16.0.0/12" + # "192.168.0.0/16" + # "2601:881:8100:8de0:31e6:ac52:b5be:462a" + # "matrix.org" + # "app.element.io" # don't ratelimit matrix users + # ]; + + bantime-increment = { + enable = true; # Enable increment of bantime after each violation + multipliers = "1 2 4 8 16 32 64 128 256"; + maxtime = "168h"; # Do not ban for more than 1 week + overalljails = true; # Calculate the bantime based on all the violations + }; + }; + # https://www.ssh-audit.com/hardening_guides.html + # https://github.com/jtesta/ssh-audit + services.openssh = { + enable = true; + settings = { + ########## Features ########## + + # disallow ssh-agent forwarding to prevent lateral movement + AllowAgentForwarding = false; + + # prevent TCP ports from being forwarded over SSH tunnels + # **please be aware that disabling TCP forwarding does not prevent port forwarding** + # any user with an interactive login shell can spin up his/her own instance of sshd + AllowTcpForwarding = true; + + # prevent StreamLocal (Unix-domain socket) forwarding + AllowStreamLocalForwarding = false; + + # disables all forwarding features + # overrides all other forwarding switches + DisableForwarding = false; + + # disallow remote hosts from connecting to forwarded ports + # i.e. forwarded ports are forced to bind to 127.0.0.1 instad of 0.0.0.0 + GatewayPorts = "no"; + + # prevent tun device forwarding + PermitTunnel = false; + + # suppress MOTD + PrintMotd = false; + + # disable X11 forwarding since it is not necessary + X11Forwarding = false; + + ########## Authentication ########## + + # AllowUsers = ["${user}"]; + + # Use keys only. Remove if you want to SSH using password (not recommended) + PasswordAuthentication = false; + HostbasedAuthentication = false; + + # enable pubkey authentication + PubkeyAuthentication = true; + + # Forbid root login through SSH. + PermitRootLogin = "no"; + + # nix enables pam by default + # UsePAM = false; + + # challenge-response authentication backend it not configured by default + # therefore, it is set to "no" by default to avoid the use of an unconfigured backend + ChallengeResponseAuthentication = false; + + # set maximum authentication retries to prevent brute force attacks + MaxAuthTries = 3; + + # disallow connecting using empty passwords + PermitEmptyPasswords = false; + + ########## Cryptography ########## + + # explicitly define cryptography algorithms to avoid the use of weak algorithms + # AES CTR modes have been removed to mitigate the Terrapin attack + # https://terrapin-attack.com/ + + Ciphers = [ + "aes256-gcm@openssh.com" + "aes128-gcm@openssh.com" + ]; + Macs = [ + "hmac-sha2-256-etm@openssh.com" + "hmac-sha2-512-etm@openssh.com" + "umac-128-etm@openssh.com" + ]; + KexAlgorithms = [ + "sntrup761x25519-sha512@openssh.com" + "curve25519-sha256" + "curve25519-sha256@libssh.org" + "diffie-hellman-group16-sha512" + "diffie-hellman-group18-sha512" + ]; + + # hostKeyAlgorithms = [ + # "rsa-sha2-512" + # "rsa-sha2-256" + # "ssh-ed25519" + # ]; + + ########## Connection Preferences ########## + + # enforce SSH server to only use SSH protocol version 2 + # SSHv1 contains security issues and should be avoided at all costs + # SSHv1 is disabled by default after OpenSSH 7.0, but this option is + # specified anyways to ensure this configuration file's compatibility + # with older versions of OpenSSH server + Protocol = 2; + + # number of client alive messages sent without client responding + ClientAliveCountMax = 2; + + # send a keepalive message to the client when the session has been idle for 300 seconds + # this prevents/detects connection timeouts + ClientAliveInterval = 300; + + # compression before encryption might cause security issues + Compression = false; + + # prevent SSH trust relationships from allowing lateral movements + IgnoreRhosts = true; + + # log verbosely for addtional information + LogLevel = "VERBOSE"; + + # allow a maximum of two multiplexed sessions over a single TCP connection + MaxSessions = 2; + + # let ClientAliveInterval handle keepalive + TCPKeepAlive = false; + + # disable reverse DNS lookups + # UseDNS = false; + }; + extraConfig = '' + ########## Features ########## + + # accept locale-related environment variables + AcceptEnv LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES + AcceptEnv LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT + AcceptEnv LC_IDENTIFICATION LC_ALL LANGUAGE + AcceptEnv XMODIFIERS + + ########## Connection Preferences ########## + # disable reverse DNS lookups + UseDNS no + + ########## Disable GSS ########## + + GSSAPIAuthentication no + ''; + }; + } + ) + ]; + + }; +} diff --git a/modules/features/security/security-sudo-rs.nix b/modules/features/security/security-sudo-rs.nix new file mode 100644 index 0000000..124818c --- /dev/null +++ b/modules/features/security/security-sudo-rs.nix @@ -0,0 +1,24 @@ +{ ... }: +{ + config.dendritic.features.security-sudo-rs = { + nixosModules = [ + ( + { config, ... }: + let + user = config.dendritic.current.primaryUser; + in + { + security = { + sudo.enable = false; + sudo-rs = { + enable = true; + execWheelOnly = true; + wheelNeedsPassword = true; + }; + }; + + } + ) + ]; + }; +} diff --git a/modules/features/security/security-usbguard.nix b/modules/features/security/security-usbguard.nix new file mode 100644 index 0000000..41d8a9f --- /dev/null +++ b/modules/features/security/security-usbguard.nix @@ -0,0 +1,50 @@ +{ ... }: +{ + config.dendritic.features.security-usbguard = { + nixosModules = [ + ( + { + config, + pkgs, + lib, + ... + }: + let + user = config.dendritic.current.primaryUser; + in + { + services.usbguard = { + enable = true; + IPCAllowedUsers = [ + "root" + user + ]; + + # presentDevicePolicy refers to how to treat USB devices + # that are already connected when the daemon starts + presentDevicePolicy = "allow"; + + rules = lib.mkBefore '' + # allow `only` devices with mass storage interfaces (USB Mass Storage) + allow with-interface equals { 08:*:* } + + # allow mice and keyboards + # allow with-interface equals { 03:*:* } + + # Reject devices with suspicious combination of interfaces + reject with-interface all-of { 08:*:* 03:00:* } + reject with-interface all-of { 08:*:* 03:01:* } + reject with-interface all-of { 08:*:* e0:*:* } + reject with-interface all-of { 08:*:* 02:*:* } + ''; + }; + + environment.systemPackages = with pkgs; [ + usbguard + usbguard-notifier + ]; + } + ) + ]; + }; +} diff --git a/modules/features/security/sysctl/security-sysctl-bpf.nix b/modules/features/security/sysctl/security-sysctl-bpf.nix new file mode 100644 index 0000000..1c2c5f0 --- /dev/null +++ b/modules/features/security/sysctl/security-sysctl-bpf.nix @@ -0,0 +1,18 @@ +# Breaks tracing and perf tools +{ ... }: +{ + config.dendritic.features.security-sysctl-bpf = { + nixosModules = [ + ( + { ... }: + { + boot.kernel.sysctl = { + "kernel.unprivileged_bpf_disabled" = 1; + # should be enabled along with bpf above + "net.core.bpf_jit_harden" = 2; + }; + } + ) + ]; + }; +} diff --git a/modules/features/security/sysctl/security-sysctl-debug.nix b/modules/features/security/sysctl/security-sysctl-debug.nix new file mode 100644 index 0000000..bb009e9 --- /dev/null +++ b/modules/features/security/sysctl/security-sysctl-debug.nix @@ -0,0 +1,17 @@ +# Restricts dmesg +{ ... }: +{ + config.dendritic.features.security-sysctl-debug = { + nixosModules = [ + ( + { ... }: + { + boot.kernel.sysctl = { + "kernel.kptr_restrict" = 2; + "kernel.dmesg_restrict" = 1; + }; + } + ) + ]; + }; +} diff --git a/modules/features/security/sysctl/security-sysctl-kernel.nix b/modules/features/security/sysctl/security-sysctl-kernel.nix new file mode 100644 index 0000000..a60576a --- /dev/null +++ b/modules/features/security/sysctl/security-sysctl-kernel.nix @@ -0,0 +1,29 @@ +{ ... }: +{ + config.dendritic.features.security-sysctl-kernel = { + nixosModules = [ + ( + { ... }: + { + boot.kernel.sysctl = { + "fs.suid_dumpable" = 0; + # prevent pointer leaks + # Note: certian container runtimes or browser sandboxes might rely on the following + # restrict loading TTY line disciplines to the CAP_SYS_MODULE + "dev.tty.ldisk_autoload" = 0; + # prevent exploit of use-after-free flaws + "vm.unprivileged_userfaultfd" = 0; + # kexec is used to boot another kernel during runtime and can be abused + "kernel.kexec_load_disabled" = 1; + # Kernel self-protection + # SysRq exposes a lot of potentially dangerous debugging functionality to unprivileged users + # 4 makes it so a user can only use the secure attention key. A value of 0 would disable completely + "kernel.sysrq" = 4; + # restrict all usage of performance events to the CAP_PERFMON capability + "kernel.perf_event_paranoid" = 3; + }; + } + ) + ]; + }; +} diff --git a/modules/features/security/sysctl/security-sysctl-network.nix b/modules/features/security/sysctl/security-sysctl-network.nix new file mode 100644 index 0000000..6ffe7ee --- /dev/null +++ b/modules/features/security/sysctl/security-sysctl-network.nix @@ -0,0 +1,54 @@ +{ ... }: +{ + config.dendritic.features.security-sysctl-network = { + nixosModules = [ + ( + { ... }: + { + boot.kernel.sysctl = { + # protect against SYN flood attacks (denial of service attack) + "net.ipv4.tcp_syncookies" = 1; + # protection against TIME-WAIT assassination + "net.ipv4.tcp_rfc1337" = 1; + # enable source validation of packets received (prevents IP spoofing) + "net.ipv4.conf.default.rp_filter" = 1; + "net.ipv4.conf.all.rp_filter" = 1; + + "net.ipv4.conf.all.accept_redirects" = 0; + "net.ipv4.conf.default.accept_redirects" = 0; + "net.ipv4.conf.all.secure_redirects" = 0; + "net.ipv4.conf.default.secure_redirects" = 0; + # Protect against IP spoofing + "net.ipv6.conf.all.accept_redirects" = 0; + "net.ipv6.conf.default.accept_redirects" = 0; + "net.ipv4.conf.all.send_redirects" = 0; + "net.ipv4.conf.default.send_redirects" = 0; + + # prevent man-in-the-middle attacks + "net.ipv4.icmp_echo_ignore_all" = 1; + + # ignore ICMP request, helps avoid Smurf attacks + "net.ipv4.conf.all.forwarding" = 0; + "net.ipv4.conf.default.accept_source_route" = 0; + "net.ipv4.conf.all.accept_source_route" = 0; + "net.ipv6.conf.all.accept_source_route" = 0; + "net.ipv6.conf.default.accept_source_route" = 0; + # Reverse path filtering causes the kernel to do source validation of + "net.ipv6.conf.all.forwarding" = 0; + "net.ipv6.conf.all.accept_ra" = 0; + "net.ipv6.conf.default.accept_ra" = 0; + + ## TCP hardening + # Prevent bogus ICMP errors from filling up logs. + "net.ipv4.icmp_ignore_bogus_error_responses" = 1; + + # Disable TCP SACK + "net.ipv4.tcp_sack" = 0; + "net.ipv4.tcp_dsack" = 0; + "net.ipv4.tcp_fack" = 0; + }; + } + ) + ]; + }; +} diff --git a/modules/features/security/sysctl/security-sysctl-ptrace.nix b/modules/features/security/sysctl/security-sysctl-ptrace.nix new file mode 100644 index 0000000..3825380 --- /dev/null +++ b/modules/features/security/sysctl/security-sysctl-ptrace.nix @@ -0,0 +1,16 @@ +# Breaks Debuggers +{ ... }: +{ + config.dendritic.features.security-sysctl-ptrace = { + nixosModules = [ + ( + { ... }: + { + boot.kernel.sysctl = { + "kernel.yama.ptrace_scope" = 2; + }; + } + ) + ]; + }; +} diff --git a/modules/features/security/sysctl/security-sysctl-strict.nix b/modules/features/security/sysctl/security-sysctl-strict.nix new file mode 100644 index 0000000..f87e95b --- /dev/null +++ b/modules/features/security/sysctl/security-sysctl-strict.nix @@ -0,0 +1,12 @@ +# Enable on server not dev machines +{ ... }: +{ + config.dendritic.features.security-sysctl-strict = { + features = [ + "security-sysctl-ptrace" + "security-sysctl-bpf" + "security-sysctl-debug" + "security-sysctl-userns" + ]; + }; +} diff --git a/modules/features/security/sysctl/security-sysctl-userns.nix b/modules/features/security/sysctl/security-sysctl-userns.nix new file mode 100644 index 0000000..0d75256 --- /dev/null +++ b/modules/features/security/sysctl/security-sysctl-userns.nix @@ -0,0 +1,16 @@ +# Can break containers and flatpaks +{ ... }: +{ + config.dendritic.features.security-sysctl-userns = { + nixosModules = [ + ( + { lib, ... }: + { + boot.kernel.sysctl = lib.mkForce { + "kernel.unprivileged_userns_clone" = 0; + }; + } + ) + ]; + }; +} diff --git a/modules/features/security/sysctl/security-sysctl-userspace.nix b/modules/features/security/sysctl/security-sysctl-userspace.nix new file mode 100644 index 0000000..99fc5df --- /dev/null +++ b/modules/features/security/sysctl/security-sysctl-userspace.nix @@ -0,0 +1,35 @@ +{ ... }: +{ + config.dendritic.features.security-sysctl-userspace = { + nixosModules = [ + ( + { ... }: + { + boot.kernel.sysctl = { + # 0 breaks browsers + "kernel.unprivileged_userns_clone" = 1; + + "vm.mmap_min_addr" = 65536; + "vm.swappiness" = 10; + + # ASLR memory protection (64-bit systems) + "vm.mmap_rnd_bits" = 32; + "vm.mmap_rnd_compat_bits" = 16; + + # only permit symlinks to be followed when outside of a world-writable sticky directory + "fs.protected_symlinks" = 1; + "fs.protected_hardlinks" = 1; + # Prevent creating files in potentially attacker-controlled environments + "fs.protected_fifos" = 2; + "fs.protected_regular" = 2; + + # Randomize memory + "kernel.randomize_va_space" = 2; + # Exec Shield (Stack protection) + "kernel.exec-shield" = 1; + }; + } + ) + ]; + }; +} diff --git a/modules/features/security/sysctl/security-sysctl.nix b/modules/features/security/sysctl/security-sysctl.nix new file mode 100644 index 0000000..81afc75 --- /dev/null +++ b/modules/features/security/sysctl/security-sysctl.nix @@ -0,0 +1,21 @@ +{ ... }: +{ + config.dendritic.features.security-sysctl = { + + features = [ + "security-sysctl-kernel" + "security-sysctl-network" + "security-sysctl-userspace" + ]; + nixosModules = [ + + ( + { pkgs, ... }: + { + + environment.systemPackages = [ pkgs.kernel-hardening-checker ]; + } + ) + ]; + }; +} diff --git a/modules/features/security/systemd/security-systemd-ask-password-console.nix b/modules/features/security/systemd/security-systemd-ask-password-console.nix new file mode 100644 index 0000000..f144edf --- /dev/null +++ b/modules/features/security/systemd/security-systemd-ask-password-console.nix @@ -0,0 +1,46 @@ +{ ... }: +{ + config.dendritic.features.security-systemd-ask-password-console = { + nixosModules = [ + ( + { ... }: + { + systemd.services.systemd-ask-password-console.serviceConfig = { + NoNewPrivileges = true; + ProtectSystem = "strict"; + ProtectHome = true; + ProtectClock = true; + ProtectHostname = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + ProtectProc = "invisible"; + PrivateTmp = true; + PrivateMounts = true; + PrivateNetwork = true; + PrivateDevices = true; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + RestrictAddressFamilies = [ + "~AF_INET6" + "~AF_INET" + "~AF_PACKET" + ]; + MemoryDenyWriteExecute = true; + DevicePolicy = "closed"; + LockPersonality = true; + SystemCallFilter = [ + "~@keyring" + "~@swap" + "~@clock" + "~@module" + "~@obsolete" + "~@cpu-emulation" + ]; + }; + } + ) + ]; + }; +} diff --git a/modules/features/security/systemd/security-systemd-ask-password-wall.nix b/modules/features/security/systemd/security-systemd-ask-password-wall.nix new file mode 100644 index 0000000..d46646e --- /dev/null +++ b/modules/features/security/systemd/security-systemd-ask-password-wall.nix @@ -0,0 +1,46 @@ +{ ... }: +{ + config.dendritic.features.security-systemd-ask-password-wall = { + nixosModules = [ + ( + { ... }: + { + systemd.services.systemd-ask-password-wall.serviceConfig = { + NoNewPrivileges = true; + ProtectSystem = "strict"; + ProtectHome = true; + ProtectClock = true; + ProtectHostname = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + ProtectProc = "invisible"; + PrivateTmp = true; + PrivateMounts = true; + PrivateNetwork = true; + PrivateDevices = true; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + RestrictAddressFamilies = [ + "~AF_INET6" + "~AF_INET" + "~AF_PACKET" + ]; + MemoryDenyWriteExecute = true; + DevicePolicy = "closed"; + LockPersonality = true; + SystemCallFilter = [ + "~@keyring" + "~@swap" + "~@clock" + "~@module" + "~@obsolete" + "~@cpu-emulation" + ]; + }; + } + ) + ]; + }; +} diff --git a/modules/features/security/systemd/security-systemd-auditd.nix b/modules/features/security/systemd/security-systemd-auditd.nix new file mode 100644 index 0000000..42ca59d --- /dev/null +++ b/modules/features/security/systemd/security-systemd-auditd.nix @@ -0,0 +1,51 @@ +{ ... }: +{ + config.dendritic.features.security-systemd-auditd = { + nixosModules = [ + ( + { ... }: + { + systemd.services.auditd.serviceConfig = { + NoNewPrivileges = true; + ProtectSystem = "full"; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectControlGroups = true; + ProtectProc = "invisible"; + ProtectClock = true; + PrivateTmp = true; + PrivateNetwork = true; + PrivateMounts = true; + PrivateDevices = true; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + RestrictAddressFamilies = [ + "~AF_INET6" + "~AF_INET" + "~AF_PACKET" + ]; + MemoryDenyWriteExecute = true; + LockPersonality = true; + SystemCallFilter = [ + "~@clock" + "~@module" + "~@mount" + "~@swap" + "~@obsolete" + "~@cpu-emulation" + ]; + SystemCallArchitectures = "native"; + CapabilityBoundingSet = [ + "~CAP_CHOWN" + "~CAP_FSETID" + "~CAP_SETFCAP" + ]; + }; + } + ) + ]; + }; +} diff --git a/modules/features/security/systemd/security-systemd-bluetooth.nix b/modules/features/security/systemd/security-systemd-bluetooth.nix new file mode 100644 index 0000000..f9b63cc --- /dev/null +++ b/modules/features/security/systemd/security-systemd-bluetooth.nix @@ -0,0 +1,28 @@ +{ ... }: +{ + config.dendritic.features.security-systemd-bluetooth = { + nixosModules = [ + ( + { lib, ... }: + { + systemd.services.bluetooth.serviceConfig = lib.mkForce { + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + ProtectHostname = true; + ProtectControlGroups = true; + ProtectProc = "invisible"; + SystemCallFilter = [ + "~@obsolete" + "~@cpu-emulation" + "~@swap" + "~@reboot" + "~@mount" + ]; + SystemCallArchitectures = "native"; + }; + } + ) + ]; + }; +} diff --git a/modules/features/security/systemd/security-systemd-dbus.nix b/modules/features/security/systemd/security-systemd-dbus.nix new file mode 100644 index 0000000..ae01387 --- /dev/null +++ b/modules/features/security/systemd/security-systemd-dbus.nix @@ -0,0 +1,50 @@ +{ ... }: +{ + config.dendritic.features.security-systemd-dbus = { + nixosModules = [ + ( + { ... }: + { + systemd.services.dbus.serviceConfig = { + NoNewPrivileges = true; + ProtectSystem = "stric"; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + PrivateMounts = true; + PrivateDevices = true; + PrivateTmp = true; + RestrictSUIDSGID = true; + RestrictRealtime = true; + RestrictAddressFamilies = [ + "AF_UNIX" + ]; + RestrictNamespaces = true; + SystemCallErrorNumber = "EPERM"; + SystemCallArchitectures = "native"; + SystemCallFilter = [ + "~@obsolete" + "~@resources" + "~@debug" + "~@mount" + "~@reboot" + "~@swap" + "~@cpu-emulation" + ]; + LockPersonality = true; + IPAddressDeny = [ + "0.0.0.0/0" + "::/0" + ]; + MemoryDenyWriteExecute = true; + DevicePolicy = "closed"; + UMask = 0077; + }; + } + ) + ]; + }; +} diff --git a/modules/features/security/systemd/security-systemd-getty.nix b/modules/features/security/systemd/security-systemd-getty.nix new file mode 100644 index 0000000..2d0ed25 --- /dev/null +++ b/modules/features/security/systemd/security-systemd-getty.nix @@ -0,0 +1,49 @@ +{ ... }: +{ + config.dendritic.features.security-systemd-getty = { + nixosModules = [ + ( + { ... }: + { + systemd.services."getty@".serviceConfig = { + NoNewPrivileges = true; + ProtectSystem = "stric"; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + ProtectClock = true; + PrivateMounts = true; + PrivateTmp = true; + RestrictSUIDSGID = true; + RestrictRealtime = true; + RestrictAddressFamilies = [ + "AF_UNIX" + "AF_NETLINK" + ]; + RestrictNamespaces = true; + SystemCallErrorNumber = "EPERM"; + SystemCallArchitectures = "native"; + SystemCallFilter = [ + "~@obsolete" + "~@debug" + "~@reboot" + "~@swap" + "~@clock" + "~@cpu-emulation" + ]; + LockPersonality = true; + IPAddressDeny = [ + "0.0.0.0/0" + "::/0" + ]; + MemoryDenyWriteExecute = true; + UMask = 0077; + }; + } + ) + ]; + }; +} diff --git a/modules/features/security/systemd/security-systemd-journald.nix b/modules/features/security/systemd/security-systemd-journald.nix new file mode 100644 index 0000000..864fc32 --- /dev/null +++ b/modules/features/security/systemd/security-systemd-journald.nix @@ -0,0 +1,18 @@ +{ ... }: +{ + config.dendritic.features.security-systemd-journald = { + nixosModules = [ + ( + { ... }: + { + systemd.services.systemd-journald.serviceConfig = { + NoNewPrivileges = true; + ProtectProc = "invisible"; + ProtectHostname = true; + PrivateMounts = true; + }; + } + ) + ]; + }; +} diff --git a/modules/features/security/systemd/security-systemd-machined.nix b/modules/features/security/systemd/security-systemd-machined.nix new file mode 100644 index 0000000..4ecf86a --- /dev/null +++ b/modules/features/security/systemd/security-systemd-machined.nix @@ -0,0 +1,33 @@ +{ ... }: +{ + config.dendritic.features.security-systemd-machined = { + nixosModules = [ + ( + { ... }: + { + systemd.services.systemd-machined.serviceConfig = { + NoNewPrivileges = true; + ProtectSystem = "strict"; + ProtectHome = true; + ProtectClock = true; + ProtectHostname = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + ProtectProc = "invisible"; + PrivateTmp = true; + PrivateMounts = true; + PrivateUsers = true; + PrivateNetwork = true; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + RestrictAddressFamilies = [ "AF_UNIX" ]; + MemoryDenyWriteExecute = true; + SystemCallArchitectures = "native"; + }; + } + ) + ]; + }; +} diff --git a/modules/features/security/systemd/security-systemd-ncsd.nix b/modules/features/security/systemd/security-systemd-ncsd.nix new file mode 100644 index 0000000..fec0253 --- /dev/null +++ b/modules/features/security/systemd/security-systemd-ncsd.nix @@ -0,0 +1,38 @@ +{ ... }: +{ + config.dendritic.features.security-systemd-ncsd = { + nixosModules = [ + ( + { ... }: + { + systemd.services.nscd.serviceConfig = { + ProtectClock = true; + ProtectHostname = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + ProtectControlGroups = true; + ProtectProc = "invisible"; + RestrictNamespaces = true; + RestrictRealtime = true; + MemoryDenyWriteExecute = true; + LockPersonality = true; + SystemCallFilter = [ + "~@mount" + "~@swap" + "~@clock" + "~@obsolete" + "~@cpu-emulation" + ]; + SystemCallArchitectures = "native"; + CapabilityBoundingSet = [ + "~CAP_CHOWN" + "~CAP_FSETID" + "~CAP_SETFCAP" + ]; + }; + } + ) + ]; + }; +} diff --git a/modules/features/security/systemd/security-systemd-networkmanager-dispatcher.nix b/modules/features/security/systemd/security-systemd-networkmanager-dispatcher.nix new file mode 100644 index 0000000..75a856e --- /dev/null +++ b/modules/features/security/systemd/security-systemd-networkmanager-dispatcher.nix @@ -0,0 +1,47 @@ +{ ... }: +{ + config.dendritic.features.security-systemd-networkmanager-dispatcher = { + nixosModules = [ + ( + { ... }: + { + systemd.services.NetworkManager-dispatcher.serviceConfig = { + NoNewPrivileges = true; + ProtectSystem = "strict"; + ProtectHome = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + ProtectControlGroups = true; + ProtectClock = true; + ProtectHostname = true; + ProtectProc = "invisible"; + PrivateTmp = true; + PrivateMounts = true; + RestrictRealtime = true; + RestrictAddressFamilies = [ + "AF_UNIX" + "AF_NETLINK" + "AF_INET" + "AF_INET6" + "AF_PACKET" + ]; + RestrictNamespaces = true; + RestrictSUIDSGID = true; + MemoryDenyWriteExecute = true; + SystemCallFilter = [ + "~@mount" + "~@module" + "~@swap" + "~@obsolete" + "~@cpu-emulation" + "ptrace" + ]; + SystemCallArchitectures = "native"; + LockPersonality = true; + CapabilityBoundingSet = "CAP_NET_ADMIN CAP_NET_RAW"; + }; + } + ) + ]; + }; +} diff --git a/modules/features/security/systemd/security-systemd-networkmanager.nix b/modules/features/security/systemd/security-systemd-networkmanager.nix new file mode 100644 index 0000000..2dc56af --- /dev/null +++ b/modules/features/security/systemd/security-systemd-networkmanager.nix @@ -0,0 +1,44 @@ +{ ... }: +{ + config.dendritic.features.security-systemd-networkmanager = { + nixosModules = [ + ( + { ... }: + { + systemd.services.NetworkManager.serviceConfig = { + NoNewPrivileges = true; + ProtectHome = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + ProtectControlGroups = true; + ProtectClock = true; + ProtectHostname = true; + ProtectProc = "invisible"; + PrivateTmp = true; + RestrictRealtime = true; + RestrictAddressFamilies = [ + "AF_UNIX" + "AF_NETLINK" + "AF_INET" + "AF_INET6" + "AF_PACKET" + ]; + RestrictNamespaces = true; + RestrictSUIDSGID = true; + MemoryDenyWriteExecute = true; + SystemCallFilter = [ + "~@mount" + "~@module" + "~@swap" + "~@obsolete" + "~@cpu-emulation" + "ptrace" + ]; + SystemCallArchitectures = "native"; + LockPersonality = true; + }; + } + ) + ]; + }; +} diff --git a/modules/features/security/systemd/security-systemd-nix-daemon.nix b/modules/features/security/systemd/security-systemd-nix-daemon.nix new file mode 100644 index 0000000..b1e1323 --- /dev/null +++ b/modules/features/security/systemd/security-systemd-nix-daemon.nix @@ -0,0 +1,68 @@ +{ ... }: +{ + config.dendritic.features.security-systemd-nix-daemon = { + nixosModules = [ + ( + { ... }: + { + systemd.services.nix-daemon.serviceConfig = { + NoNewPrivileges = true; + ProtectControlGroups = true; + ProtectKernelModules = true; + PrivateMounts = true; + PrivateTmp = true; + PrivateDevices = true; + RestrictSUIDSGID = true; + RestrictRealtime = true; + RestrictNamespaces = [ "~cgroup" ]; + RestrictAddressFamilies = [ + "AF_UNIX" + "AF_NETLINK" + "AF_INET6" + "AF_INET" + ]; + CapabilityBoundingSet = [ + "~CAP_SYS_CHROOT" + "~CAP_BPF" + "~CAP_AUDIT_WRITE" + "~CAP_AUDIT_CONTROL" + "~CAP_AUDIT_READ" + "~CAP_SYS_PTRACE" + "~CAP_SYS_NICE" + "~CAP_SYS_RESOURCE" + "~CAP_SYS_RAWIO" + "~CAP_SYS_TIME" + "~CAP_SYS_PACCT" + "~CAP_LINUX_IMMUTABLE" + "~CAP_IPC_LOCK" + "~CAP_WAKE_ALARM" + "~CAP_SYS_TTY_CONFIG" + "~CAP_SYS_BOOT" + "~CAP_LEASE" + "~CAP_BLOCK_SUSPEND" + "~CAP_MAC_ADMIN" + "~CAP_MAC_OVERRIDE" + ]; + SystemCallErrorNumber = "EPERM"; + SystemCallArchitectures = "native"; + SystemCallFilter = [ + "~@resources" + "~@module" + "~@obsolete" + "~@debug" + "~@reboot" + "~@swap" + "~@cpu-emulation" + "~@clock" + "~@raw-io" + ]; + LockPersonality = true; + MemoryDenyWriteExecute = true; + DevicePolicy = "closed"; + UMask = 0077; + }; + } + ) + ]; + }; +} diff --git a/modules/features/security/systemd/security-systemd-reload-systemd-vconsole-setup.nix b/modules/features/security/systemd/security-systemd-reload-systemd-vconsole-setup.nix new file mode 100644 index 0000000..dab73f3 --- /dev/null +++ b/modules/features/security/systemd/security-systemd-reload-systemd-vconsole-setup.nix @@ -0,0 +1,44 @@ +{ ... }: +{ + config.dendritic.features.security-systemd-reload-vconsole = { + nixosModules = [ + ( + { ... }: + { + systemd.services.reload-systemd-vconsole-setup.serviceConfig = { + NoNewPrivileges = true; + ProtectSystem = "strict"; + ProtectHome = true; + ProtectClock = true; + ProtectHostname = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + ProtectProc = "invisible"; + PrivateTmp = true; + PrivateMounts = true; + PrivateNetwork = true; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + RestrictAddressFamilies = [ + "~AF_INET6" + "~AF_INET" + "~AF_PACKET" + ]; + MemoryDenyWriteExecute = true; + DevicePolicy = "closed"; + LockPersonality = true; + SystemCallFilter = [ + "~@keyring" + "~@swap" + "~@obsolete" + "~@cpu-emulation" + ]; + SystemCallArchitectures = "native"; + }; + } + ) + ]; + }; +} diff --git a/modules/features/security/systemd/security-systemd-rescue.nix b/modules/features/security/systemd/security-systemd-rescue.nix new file mode 100644 index 0000000..3458fd6 --- /dev/null +++ b/modules/features/security/systemd/security-systemd-rescue.nix @@ -0,0 +1,48 @@ +{ ... }: +{ + config.dendritic.features.security-systemd-rescue = { + nixosModules = [ + ( + { ... }: + { + systemd.services.rescue.serviceConfig = { + NoNewPrivileges = true; + ProtectSystem = "full"; + ProtectClock = true; + ProtectHostname = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + ProtectControlGroups = true; + ProtectProc = "invisible"; + PrivateTmp = true; + PrivateNetwork = true; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + RestrictAddressFamilies = [ + "~AF_INET6" + "~AF_INET" + "~AF_PACKET" + ]; + MemoryDenyWriteExecute = true; + LockPersonality = true; + SystemCallFilter = [ + "~@swap" + "~@clock" + "~@obsolete" + "~@cpu-emulation" + "~@resources" + ]; + SystemCallArchitectures = "native"; + CapabilityBoundingSet = [ + "~CAP_CHOWN" + "~CAP_FSETID" + "~CAP_SETFCAP" + ]; + }; + } + ) + ]; + }; +} diff --git a/modules/features/security/systemd/security-systemd-rtkit.nix b/modules/features/security/systemd/security-systemd-rtkit.nix new file mode 100644 index 0000000..dca4a4a --- /dev/null +++ b/modules/features/security/systemd/security-systemd-rtkit.nix @@ -0,0 +1,44 @@ +{ ... }: +{ + config.dendritic.features.security-systemd-rtkit = { + nixosModules = [ + ( + { lib, ... }: + { + systemd.services.rtkit-daemon.serviceConfig = { + NoNewPrivileges = true; + ProtectSystem = "strict"; + ProtectHome = true; + ProtectClock = true; + ProtectHostname = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + ProtectControlGroups = lib.mkDefault true; + PrivateTmp = lib.mkDefault true; + PrivateMounts = true; + PrivateDevices = true; + RestrictNamespaces = true; + RestrictSUIDSGID = true; + RestrictAddressFamilies = [ + "~AF_INET6" + "~AF_INET" + "~AF_PACKET" + ]; + MemoryDenyWriteExecute = true; + DevicePolicy = "closed"; + LockPersonality = true; + SystemCallFilter = [ + "~@keyring" + "~@swap" + "~@clock" + "~@module" + "~@obsolete" + "~@cpu-emulation" + ]; + }; + } + ) + ]; + }; +} diff --git a/modules/features/security/systemd/security-systemd-sshd.nix b/modules/features/security/systemd/security-systemd-sshd.nix new file mode 100644 index 0000000..09442c3 --- /dev/null +++ b/modules/features/security/systemd/security-systemd-sshd.nix @@ -0,0 +1,42 @@ +{ ... }: +{ + config.dendritic.features.security-systemd-sshd = { + nixosModules = [ + ( + { ... }: + { + systemd.services.sshd.serviceConfig = { + NoNewPrivileges = true; + ProtectSystem = "strict"; + ProtectHome = "read-only"; + ProtectClock = true; + ProtectHostname = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + ProtectControlGroups = true; + ProtectProc = "invisible"; + PrivateTmp = true; + PrivateMounts = true; + PrivateDevices = true; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + MemoryDenyWriteExecute = true; + LockPersonality = true; + DevicePolicy = "closed"; + SystemCallFilter = [ + "~@keyring" + "~@swap" + "~@clock" + "~@module" + "~@obsolete" + "~@cpu-emulation" + ]; + SystemCallArchitectures = "native"; + }; + } + ) + ]; + }; +} diff --git a/modules/features/security/systemd/security-systemd-udevd.nix b/modules/features/security/systemd/security-systemd-udevd.nix new file mode 100644 index 0000000..0a139a6 --- /dev/null +++ b/modules/features/security/systemd/security-systemd-udevd.nix @@ -0,0 +1,23 @@ +{ ... }: +{ + config.dendritic.features.security-systemd-udevd = { + nixosModules = [ + ( + { ... }: + { + systemd.services.systemd-udevd.serviceConfig = { + NoNewPrivileges = true; + ProtectSystem = "strict"; + ProtectHome = true; + ProtectKernelLogs = true; + ProtectControlGroups = true; + ProtectClock = true; + ProtectProc = "invisible"; + RestrictNamespaces = true; + CapabilityBoundingSet = "~CAP_SYS_PTRACE ~CAP_SYS_PACCT"; + }; + } + ) + ]; + }; +} diff --git a/modules/features/security/systemd/security-systemd-user.nix b/modules/features/security/systemd/security-systemd-user.nix new file mode 100644 index 0000000..a2d79c8 --- /dev/null +++ b/modules/features/security/systemd/security-systemd-user.nix @@ -0,0 +1,41 @@ +{ ... }: +{ + config.dendritic.features.security-systemd-user = { + nixosModules = [ + ( + { ... }: + { + systemd.services."user@".serviceConfig = { + ProtectSystem = "strict"; + ProtectClock = true; + ProtectHostname = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + ProtectProc = "invisible"; + PrivateTmp = true; + PrivateNetwork = true; + MemoryDenyWriteExecute = true; + RestrictAddressFamilies = [ + "AF_UNIX" + "AF_NETLINK" + "AF_BLUETOOTH" + ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallFilter = [ + "~@keyring" + "~@swap" + "~@debug" + "~@module" + "~@obsolete" + "~@cpu-emulation" + ]; + SystemCallArchitectures = "native"; + }; + } + ) + ]; + }; +} diff --git a/modules/features/security/systemd/security-systemd.nix b/modules/features/security/systemd/security-systemd.nix new file mode 100644 index 0000000..fec6494 --- /dev/null +++ b/modules/features/security/systemd/security-systemd.nix @@ -0,0 +1,58 @@ +{ ... }: +{ + config.dendritic.features.security-systemd = { + features = [ + "security-systemd-ask-password-console" + "security-systemd-ask-password-wall" + "security-systemd-auditd" + # "security-systemd-dbus" using dbus-broker + "security-systemd-display-manager" + # "security-systemd-getty" + "security-systemd-journald" + "security-systemd-machined" + "security-systemd-ncsd" + "security-systemd-networkmanager" + "security-systemd-networkmanager-dispatcher" + "security-systemd-nix-daemon" + "security-systemd-reload-vconsole" + "security-systemd-rescue" + "security-systemd-rtkit" + "security-systemd-sshd" + "security-systemd-udevd" + #"security-systemd-user" + ]; + + nixosModules = [ + ( + { lib, ... }: + { + services = { + # mDNS/DNS-SD + avahi.enable = false; + # Geoclue (location services) + geoclue2.enable = false; + # udisks2.enable = false; + # accounts-daemon.enable = lib.mkDefault false; + }; + # Only needed for WWAN/3G/4G modems, otherwise it runs `mmcli` unnecessarily + networking.modemmanager.enable = false; + # Bluetooth has a long history of vulnerabilities + hardware.bluetooth.enable = false; + # Prefer manual upgrades on a hardened system + system.autoUpgrade.enable = false; + + systemd.coredump.enable = false; + # ➡️ Sets the kernel's resource limit (ulimit -c 0) + security.pam.loginLimits = [ + { + domain = "*"; # Applies to all users/sessions + type = "-"; # Set both soft and hard limits + item = "core"; # The soft/hard limit item + value = "0"; # Core dumps size is limited to 0 (effectively disabled) + } + ]; + } + ) + ]; + }; +} diff --git a/modules/features/security/systemd/services-systemd-display-manager.nix b/modules/features/security/systemd/services-systemd-display-manager.nix new file mode 100644 index 0000000..70403d9 --- /dev/null +++ b/modules/features/security/systemd/services-systemd-display-manager.nix @@ -0,0 +1,68 @@ +{ ... }: +{ + config.dendritic.features.security-systemd-display-manager = { + nixosModules = [ + ( + { lib, ... }: + { + systemd.services.display-manager.serviceConfig = { + ProtectSystem = "full"; + ProtectControlGroups = true; + ProtectClock = true; + ProtectKernelModules = true; + PrivateMounts = true; + PrivateIPC = true; + RestrictSUIDSGID = true; + RestrictRealtime = true; + RestrictNamespaces = [ + "~cgroup" + ]; + RestrictAddressFamilies = [ + "AF_UNIX" + "AF_NETLINK" + "AF_INET" + "AF_INET6" + ]; + SystemCallErrorNumber = "EPERM"; + SystemCallFilter = [ + "~@obsolete" + "~@cpu-emulation" + "~@clock" + "~@swap" + "~@module" + "~@reboot" + "~@raw-io" + "~@debug" + ]; + SystemCallArchitectures = "native"; + LockPersonality = true; + IPAddressDeny = [ + "0.0.0.0/0" + "::/0" + ]; + CapabilityBoundingSet = [ + "CAP_SYS_ADMIN" + "CAP_SETUID" + "CAP_SETGID" + "CAP_SETPCAP" + "CAP_KILL" + "CAP_SYS_TTY_CONFIG" + "CAP_DAC_OVERRIDE" + "CAP_DAC_READ_SEARCH" + "CAP_FOWNER" + "CAP_IPC_OWNER" + "CAP_FSETID" + "CAP_SETFCAP" + "CAP_CHOWN" + ]; + DeviceAllow = "/dev/tty7 rw"; + DevicePolicy = "closed"; + UMask = 0077; + LogLevelMax = "debug"; + KeyringMode = lib.mkForce "private"; + }; + } + ) + ]; + }; +} diff --git a/modules/features/shell.nix b/modules/features/shell.nix new file mode 100644 index 0000000..42b0498 --- /dev/null +++ b/modules/features/shell.nix @@ -0,0 +1,41 @@ +{ ... }: +{ + config.dendritic.features.shell = { + features = [ + "filemanager-yazi" + "cli-zoxide" + "git-lazygit" + "cli-zsh" + ]; + + homeModules = [ + ( + { pkgs, ... }: + { + programs.home-manager.enable = true; + programs.zsh.enable = true; + + home.packages = with pkgs; [ ripgrep ]; + } + ) + ../home/programs/starship.nix + ../home/programs/eza.nix + ../home/programs/bat.nix + ../home/programs/carapace.nix + ../home/programs/fzf.nix + ../home/programs/btop.nix + ../home/programs/atuin.nix + ../home/programs/broot.nix + ]; + + nixosModules = [ + ( + { config, pkgs, ... }: + { + programs.zsh.enable = true; + users.users.${config.dendritic.current.primaryUser}.shell = pkgs.zsh; + } + ) + ]; + }; +} diff --git a/modules/features/system/networking-networkmanager.nix b/modules/features/system/networking-networkmanager.nix new file mode 100644 index 0000000..b15aaa1 --- /dev/null +++ b/modules/features/system/networking-networkmanager.nix @@ -0,0 +1,16 @@ +{ ... }: +{ + config.dendritic.features.networkmanager = { + nixosModules = [ + ( + { ... }: + { + networking.networkmanager = { + enable = true; + wifi.scanRandMacAddress = true; + }; + } + ) + ]; + }; +} diff --git a/modules/features/system/ntp-chrony.nix b/modules/features/system/ntp-chrony.nix new file mode 100644 index 0000000..1fde91c --- /dev/null +++ b/modules/features/system/ntp-chrony.nix @@ -0,0 +1,45 @@ +{ ... }: +{ + config.dendritic.features.ntp-chrony = { + nixosModules = [ + ( + { ... }: + { + services.chrony = { + enable = true; + enableNTS = true; + servers = [ + "time.cloudflare.com iburst nts" + "ntppool1.time.nl iburst nts" + "nts.netnod.se iburst nts" + "ptbtime1.ptb.de iburst nts" + "time.dfm.dk iburst nts" + "time.cifelli.xyz iburst nts" + ]; + # havent worked out the kinks yet + # extraConfig = '' + # minsources 3 + # authselectmode require + + # # EF + # dscp 46 + + # driftfile /var/lib/chrony/drift + # dumpdir /var/lib/chrony + # ntsdumpdir /var/lib/chrony + + # leapseclist /usr/share/zoneinfo/leap-seconds.list + # makestep 1.0 3 + + # rtconutc + + # cmdport 0 + + # noclientlog + # ''; + }; + } + ) + ]; + }; +} diff --git a/modules/features/system/system-nix.nix b/modules/features/system/system-nix.nix new file mode 100644 index 0000000..4523914 --- /dev/null +++ b/modules/features/system/system-nix.nix @@ -0,0 +1,36 @@ +{ ... }: +{ + config.dendritic.features.system-nix = { + nixosModules = [ + ( + { config, ... }: + + let + user = config.dendritic.current.primaryUser; + in + { + nixpkgs.config.allowUnfree = true; + + nix.settings = { + experimental-features = [ + "nix-command" + "flakes" + ]; + + auto-optimise-store = true; + + download-buffer-size = 262144000; + trusted-users = [ user ]; + warn-dirty = false; + + accept-flake-config = true; + }; + + services.fstrim.enable = true; + + time.timeZone = "America/Toronto"; + } + ) + ]; + }; +} diff --git a/modules/features/system/system-services.nix b/modules/features/system/system-services.nix new file mode 100644 index 0000000..bd1c141 --- /dev/null +++ b/modules/features/system/system-services.nix @@ -0,0 +1,41 @@ +{ ... }: +{ + config.dendritic.features.system-services = { + nixosModules = [ + ( + { ... }: + { + users.groups.netdev = { }; + + services = { + dbus.implementation = "broker"; + + logrotate = { + enable = true; + settings = { + header = { + dateext = true; + }; + + "var/log/audit/audit.log" = { + frequency = "daily"; + rotate = 3; + size = "100k"; + }; + }; + }; + + journald = { + storage = "volatile"; # Store logs in memory + upload.enable = false; # Disable remote log upload (the default) + extraConfig = '' + SystemMaxUse=500M + SystemMaxFileSize=50M + ''; + }; + }; + } + ) + ]; + }; +} diff --git a/modules/features/virtualization-libvirt.nix b/modules/features/virtualization-libvirt.nix new file mode 100644 index 0000000..f58009b --- /dev/null +++ b/modules/features/virtualization-libvirt.nix @@ -0,0 +1,63 @@ +{ ... }: +{ + config.dendritic.features.virtualization-libvirt = { + nixosModules = [ + ( + { config, pkgs, ... }: + let + user = config.dendritic.current.primaryUser; + in + { + networking.firewall.trustedInterfaces = [ "virbr0" ]; + boot.kernelModules = [ "kvm-amd" ]; + + environment.systemPackages = with pkgs; [ + virt-manager + virtiofsd + vagrant + ]; + + users.users.${user} = { + extraGroups = [ + "libvirtd" + "qemu-libvirtd" + "kvm" + ]; + }; + + # Allow VM to run as non-root without ulimit + security.pam.loginLimits = [ + { + domain = "${user}"; + type = "soft"; + item = "memlock"; + value = "20000000"; + } + { + domain = "${user}"; + type = "hard"; + item = "memlock"; + value = "20000000"; + } + ]; + + virtualisation.libvirtd = { + enable = true; + # qemu.ovmf.enable = true; + # qemu.runAsRoot = false; + onBoot = "ignore"; + onShutdown = "shutdown"; + qemu = { + package = pkgs.qemu_kvm; + runAsRoot = true; + swtpm.enable = true; + }; + }; + + users.extraGroups.libvirtd.members = [ "${user}" ]; + + } + ) + ]; + }; +} diff --git a/modules/home/devenv.nix b/modules/home/devenv.nix new file mode 100644 index 0000000..d0f8e6f --- /dev/null +++ b/modules/home/devenv.nix @@ -0,0 +1,9 @@ +{pkgs, ...}: { + home.packages = with pkgs; [ + cachix + devenv + ]; + + programs.direnv.enable = true; + programs.direnv.nix-direnv.enable = true; +} diff --git a/modules/home/programs/atuin.nix b/modules/home/programs/atuin.nix new file mode 100644 index 0000000..0ade0ff --- /dev/null +++ b/modules/home/programs/atuin.nix @@ -0,0 +1,9 @@ +# ✨ Magical shell history +{...}: { + programs.atuin = { + enable = true; + flags = [ + "--disable-up-arrow" + ]; + }; +} diff --git a/modules/home/programs/bat.nix b/modules/home/programs/bat.nix new file mode 100644 index 0000000..f53b995 --- /dev/null +++ b/modules/home/programs/bat.nix @@ -0,0 +1,6 @@ +# A cat clone with syntax highlighting and Git integration. +{...}: { + programs.bat = { + enable = true; + }; +} diff --git a/modules/home/programs/brave.nix b/modules/home/programs/brave.nix new file mode 100644 index 0000000..35834e6 --- /dev/null +++ b/modules/home/programs/brave.nix @@ -0,0 +1,123 @@ +{ pkgs, ... }: +{ + programs.brave = { + enable = true; + commandLineArgs = [ + # Wayland Native + "--enable-features=UseOzonePlatform" + "--ozone-platform=wayland" + + # Hardware Acceleration (NVIDIA optimized) + "--enable-accelerated-video-decode" + "--enable-gpu-rasterization" + "--enable-zero-copy" + "--ignore-gpu-blocklist" + + # Performance + "--enable-features=VaapiVideoDecoder" + "--enable-features=VaapiVideoEncoder" + "--enable-features=CanvasOopRasterization" + "--disable-features=UseChromeOSDirectVideoDecoder" + + # Privacy & Security + "--disable-features=MediaRouter" # Disable Chromecast + "--disable-features=OptimizationHints" # No Google suggestions + "--disable-features=AutofillSavePaymentMethods" + "--disable-background-networking" # No telemetry + "--disable-sync" # Manual sync control + + # Wayland-specific fixes + "--disable-features=WaylandWpColorManagerV1" # Color management fix + + # UI/UX + "--force-dark-mode" # Match Stylix theme + "--enable-features=WebUIDarkMode" + "--no-default-browser-check" + ]; + extensions = [ + # let + # ids = [ + "cjpalhdlnbpafiamejdnhcphjbkeiagm" # ublock origin + "dbepggeogbaibhgnhhndojpepiihcmeb" # vimium + "eimadpbcbfnmbkopoojfekhnkhdbieeh" # dark reader + "pkehgijcmpdhfbdbbnkijodmdjhbjlgp" # privacy badger + "ghmbeldphafepmbegfdlkpapadhbakde" # proton pass + "mmjbdbjnoablegbkcklggeknkfcjkjia" # custom new tab page + ]; + # in + # map (id: { inherit id; }) ids; + }; + + xdg.configFile."BraveSoftware/Brave-Browser/Policies/managed/policy.json".text = builtins.toJSON { + BraveShieldsAdControl = 2; + BraveShieldsTrackersBlocked = 1; + BraveShieldsHttpsEverywhere = 1; + BraveRewardsDisabled = 1; + BraveWalletDisabled = 1; + BraveVPNDisabled = 1; + BraveAIChatEnabled = 0; + PasswordManagerEnabled = 0; + BravePlaylistEnabled = 0; + BraveWebDiscoveryEnabled = 0; + BraveStatsPingEnabled = 0; + DnsOverHttpsMode = "automatic"; + BraveDarkMode = 1; + }; + + home.sessionVariables = { + DEFAULT_BROWSER = "${pkgs.brave}/bin/brave"; + BROWSER = "${pkgs.brave}/bin/brave"; + }; + + xdg.desktopEntries = { + brave-incognito = { + name = "Brave (Private window)"; + genericName = "Navigateur Web"; + exec = "brave --incognito"; + icon = "brave-browser"; + terminal = false; + categories = [ + "Network" + "WebBrowser" + ]; + mimeType = [ + "text/html" + "text/xml" + ]; + }; + brave-tor = { + name = "Brave (Private window w/Tor)"; + genericName = "Navigateur Web"; + exec = "brave --tor"; + icon = "brave-browser"; + terminal = false; + categories = [ + "Network" + "WebBrowser" + ]; + }; + }; + + # ================================================================= + # BRAVE SETTINGS (via brave://flags) + # ================================================================= + + # These need to be set manually in brave://flags on first launch: + # - Enable Tab Groups (UI) + # - Enable Parallel Downloading + # - Enable Reader Mode + # - GPU Rasterization: Enabled + # - Override software rendering list: Enabled + # + # Privacy settings (brave://settings/privacy): + # - Block trackers & ads: Aggressive + # - Block all fingerprinting + # - Upgrade connections to HTTPS + # - Block scripts: Off (breaks sites, use uBlock instead) + # - Block cookies: Only 3rd party + # + # Appearance (brave://settings/appearance): + # - Show home button: Off + # - Show bookmarks bar: Only on new tab + # - Use wide address bar: On +} diff --git a/modules/home/programs/broot.nix b/modules/home/programs/broot.nix new file mode 100644 index 0000000..bc473a5 --- /dev/null +++ b/modules/home/programs/broot.nix @@ -0,0 +1,7 @@ +# An interactive treeview directory navigator +{...}: { + programs.broot = { + enable = true; + enableZshIntegration = true; + }; +} diff --git a/modules/home/programs/btop.nix b/modules/home/programs/btop.nix new file mode 100644 index 0000000..4857256 --- /dev/null +++ b/modules/home/programs/btop.nix @@ -0,0 +1,5 @@ +{...}: { + programs.btop = { + enable = true; + }; +} diff --git a/modules/home/programs/carapace.nix b/modules/home/programs/carapace.nix new file mode 100644 index 0000000..56828df --- /dev/null +++ b/modules/home/programs/carapace.nix @@ -0,0 +1,6 @@ +{ ... }: +{ + programs.carapace = { + enable = true; + }; +} diff --git a/modules/home/programs/eza.nix b/modules/home/programs/eza.nix new file mode 100644 index 0000000..5435300 --- /dev/null +++ b/modules/home/programs/eza.nix @@ -0,0 +1,15 @@ +# A modern replacement for 'ls'. +{...}: { + programs.eza = { + enable = true; + enableZshIntegration = true; + git = true; + icons = "auto"; + extraOptions = [ + "--group-directories-first" + "--no-quotes" + "--git-ignore" + "--icons=always" + ]; + }; +} diff --git a/modules/home/programs/fzf.nix b/modules/home/programs/fzf.nix new file mode 100644 index 0000000..9ad8965 --- /dev/null +++ b/modules/home/programs/fzf.nix @@ -0,0 +1,33 @@ +# Fzf is a general-purpose command-line fuzzy finder. +{ + config, + lib, + ... +}: let + accent = "#" + config.lib.stylix.colors.base0D; + foreground = "#" + config.lib.stylix.colors.base05; + muted = "#" + config.lib.stylix.colors.base03; +in { + programs.fzf = { + enable = true; + enableZshIntegration = true; + colors = lib.mkForce { + "fg+" = accent; + "bg+" = "-1"; + "fg" = foreground; + "bg" = "-1"; + "prompt" = muted; + "pointer" = accent; + }; + defaultOptions = [ + "--margin=1" + "--layout=reverse" + "--border=none" + "--info='hidden'" + "--header=''" + "--prompt='/ '" + "-i" + "--no-bold" + ]; + }; +} diff --git a/modules/home/programs/ghostty.nix b/modules/home/programs/ghostty.nix new file mode 100644 index 0000000..b5513c2 --- /dev/null +++ b/modules/home/programs/ghostty.nix @@ -0,0 +1,31 @@ +{ + pkgs, + lib, + ... +}: { + programs.ghostty = { + enable = true; + enableZshIntegration = true; + installVimSyntax = true; + settings = { + window-padding-x = 10; + window-padding-y = 10; + auto-update = "off"; + working-directory = "home"; + window-inherit-working-directory = false; # avoid inheritance + keybinds = []; + # background-opacity = 0.8; + background-opacity = 1; + confirm-close-surface = false; + font-family = lib.mkForce "Comic Code Ligatures"; + # font-family = lib.mkForce "Terminus"; + font-size = 14; + gtk-titlebar = false; + # theme = "Teerb"; + }; + }; + home.packages = with pkgs; [ + ueberzugpp + terminus_font_ttf + ]; +} diff --git a/modules/home/programs/k9s.nix b/modules/home/programs/k9s.nix new file mode 100644 index 0000000..85427ac --- /dev/null +++ b/modules/home/programs/k9s.nix @@ -0,0 +1,6 @@ +# A terminal-based Kubernetes CLI. +{...}: { + programs.k9s = { + enable = true; + }; +} diff --git a/modules/home/programs/neovim/keymaps.nix b/modules/home/programs/neovim/keymaps.nix new file mode 100644 index 0000000..7439054 --- /dev/null +++ b/modules/home/programs/neovim/keymaps.nix @@ -0,0 +1,256 @@ +{ + programs.nvf.settings.vim = { + globals.mapleader = " "; + binds = { + whichKey = { + enable = true; + # TODO: registers + register = { }; + }; + }; + keymaps = [ + # General Mappings + { + key = "s"; + mode = "n"; + silent = true; + action = "lua require('flash').jump()"; + desc = "Flash"; + } + { + key = "K"; + mode = "n"; + silent = true; + action = "lua vim.lsp.buf.hover()"; + desc = "LSP Hover"; + } + { + key = ""; + mode = "n"; + silent = true; + action = "bnext"; + desc = "Next Buffer"; + } + { + key = "bd"; + mode = "n"; + silent = true; + action = "bd"; + desc = "Close current buffer"; + } + + # Disable Arrow Keys in Normal Mode + # { + # key = ""; + # mode = "n"; + # silent = true; + # action = "k"; + # desc = "Disable Up Arrow"; + # } + # { + # key = ""; + # mode = "n"; + # silent = true; + # action = "j"; + # desc = "Disable Down Arrow"; + # } + # { + # key = ""; + # mode = "n"; + # silent = true; + # action = "h"; + # desc = "Disable Left Arrow"; + # } + # { + # key = ""; + # mode = "n"; + # silent = true; + # action = "l"; + # desc = "Disable Right Arrow"; + # } + + # UI + { + key = "uw"; + mode = "n"; + silent = true; + action = "set wrap!"; + desc = "Toggle word wrapping"; + } + { + key = "ul"; + mode = "n"; + silent = true; + action = "set linebreak!"; + desc = "Toggle linebreak"; + } + { + key = "us"; + mode = "n"; + silent = true; + action = "set spell!"; + desc = "Toggle spellLazyGitcheck"; + } + { + key = "uc"; + mode = "n"; + silent = true; + action = "set cursorline!"; + desc = "Toggle cursorline"; + } + { + key = "un"; + mode = "n"; + silent = true; + action = "set number!"; + desc = "Toggle line numbers"; + } + { + key = "ur"; + mode = "n"; + silent = true; + action = "set relativenumber!"; + desc = "Toggle relative line numbers"; + } + { + key = "ut"; + mode = "n"; + silent = true; + action = "set showtabline=2"; + desc = "Show tabline"; + } + { + key = "uT"; + mode = "n"; + silent = true; + action = "set showtabline=0"; + desc = "Hide tabline"; + } + + # Windows + { + key = "ws"; + mode = "n"; + silent = true; + action = "split"; + desc = "Split"; + } + { + key = "wv"; + mode = "n"; + silent = true; + action = "vsplit"; + desc = "VSplit"; + } + { + key = "wd"; + mode = "n"; + silent = true; + action = "close"; + desc = "Close"; + } + { + key = "ma"; + mode = "n"; + silent = true; + action = "close"; + desc = "Close"; + } + { + key = ""; + mode = "n"; + silent = true; + action = "lua require('smart-splits').move_cursor_left()"; + desc = "Move to left split"; + } + + { + key = ""; + mode = "n"; + silent = true; + action = "lua require('smart-splits').move_cursor_down()"; + desc = "Move to lower split"; + } + { + key = ""; + mode = "n"; + silent = true; + action = "lua require('smart-splits').move_cursor_up()"; + desc = "Move to upper split"; + } + { + key = ""; + mode = "n"; + silent = true; + action = "lua require('smart-splits').move_cursor_right()"; + desc = "Move to right split"; + } + + { + key = ""; + mode = "n"; + silent = true; + action = "lua require('smart-splits').resize_left()"; + desc = "Resize left"; + } + { + key = ""; + mode = "n"; + silent = true; + action = "lua require('smart-splits').resize_down()"; + desc = "Resize down"; + } + { + key = ""; + mode = "n"; + silent = true; + action = "lua require('smart-splits').resize_up()"; + desc = "Resize up"; + } + { + key = ""; + mode = "n"; + silent = true; + action = "lua require('smart-splits').resize_right()"; + desc = "Resize right"; + } + + { + key = "w="; + mode = "n"; + silent = true; + action = "="; + desc = "Equalize Splits"; + } + { + key = "we"; + mode = "n"; + silent = true; + action = "lua require('smart-splits').swap_buf_right()"; + desc = "Swap Buffer Right"; + } + { + key = "wh"; + mode = "n"; + silent = true; + action = "lua require('smart-splits').swap_buf_left()"; + desc = "Swap Buffer Left"; + } + + { + key = ""; + mode = "n"; + silent = true; + action = "zz"; + desc = "Jump back and center"; + } + { + key = ""; + mode = "n"; + silent = true; + action = "zz"; + desc = "Jump forward and center"; + } + ]; + }; +} diff --git a/modules/home/programs/neovim/languages.nix b/modules/home/programs/neovim/languages.nix new file mode 100644 index 0000000..3577553 --- /dev/null +++ b/modules/home/programs/neovim/languages.nix @@ -0,0 +1,132 @@ +{ + lib, + pkgs, + ... +}: +{ + programs.nvf.settings.vim = { + diagnostics = { + enable = true; + config = { + signs = { + text = { + "vim.diagnostic.severity.Error" = " "; + "vim.diagnostic.severity.Warn" = " "; + "vim.diagnostic.severity.Hint" = " "; + "vim.diagnostic.severity.Info" = " "; + }; + }; + underline = true; + update_in_insert = true; + virtual_text = { + format = + lib.generators.mkLuaInline + # lua + '' + function(diagnostic) + return string.format("%s", diagnostic.message) + --return string.format("%s (%s)", diagnostic.message, diagnostic.source) + end + ''; + }; + }; + nvim-lint = { + enable = true; + }; + }; + syntaxHighlighting = true; + treesitter = { + enable = true; + autotagHtml = true; + context.enable = true; + highlight = { + enable = true; + }; + }; + lsp = { + enable = true; + trouble.enable = true; + lspSignature.enable = true; + lspconfig.enable = true; + formatOnSave = true; + inlayHints.enable = true; + null-ls.enable = true; + servers.nixd.settings.nil.nix.autoArchive = true; + otter-nvim = { + enable = true; + setupOpts = { + buffers.set_filetype = true; + lsp = { + diagnostic_update_event = [ + "BufWritePost" + "InsertLeave" + ]; + }; + }; + }; + lspkind.enable = true; + lspsaga = { + enable = true; + setupOpts = { + ui = { + code_action = ""; + }; + lightbulb = { + sign = false; + virtual_text = true; + }; + breadcrumbs.enable = false; + }; + }; + }; + languages = { + enableDAP = true; + enableExtraDiagnostics = true; + enableFormat = true; + enableTreesitter = true; + + astro.enable = true; + python.enable = true; + java.enable = true; + clang.enable = true; + go.enable = true; + markdown = { + enable = true; + format.type = [ "prettierd" ]; + extensions = { + markview-nvim = { + enable = true; + }; + }; + extraDiagnostics.enable = true; + }; + ts.enable = true; + ts.extensions.ts-error-translator.enable = true; + css.enable = true; + svelte.enable = true; + ocaml.enable = true; + haskell.enable = true; + haskell.lsp.enable = true; + haskell.treesitter.enable = true; + haskell.dap.enable = false; + html.enable = true; + bash.enable = true; + nix.enable = true; + nix.format.type = [ "nixfmt" ]; + tailwind.enable = true; + typst = { + enable = true; + lsp.enable = true; + lsp.servers = [ "tinymist" ]; + format.enable = true; + treesitter.enable = true; + }; + rust.enable = true; + }; + formatter = { + conform-nvim = { + enable = true; + }; + }; + }; +} diff --git a/modules/home/programs/neovim/mini.nix b/modules/home/programs/neovim/mini.nix new file mode 100644 index 0000000..c7ff3d7 --- /dev/null +++ b/modules/home/programs/neovim/mini.nix @@ -0,0 +1,14 @@ +{ + programs.nvf.settings.vim.mini = { + starter.enable = true; + comment.enable = true; + # cursorword.enable = true; + icons.enable = true; + indentscope.enable = true; + notify.enable = true; + pairs.enable = true; + diff.enable = true; + git.enable = true; + # snippets.enable = true; + }; +} diff --git a/modules/home/programs/neovim/options.nix b/modules/home/programs/neovim/options.nix new file mode 100644 index 0000000..8776771 --- /dev/null +++ b/modules/home/programs/neovim/options.nix @@ -0,0 +1,23 @@ +{ + programs.nvf.settings.vim = { + viAlias = false; + vimAlias = true; + withNodeJs = true; + # syntaxHighlighting = true; + options = { + autoindent = true; + shiftwidth = 2; + signcolumn = "yes"; + tabstop = 2; + softtabstop = 2; + wrap = false; + undofile = true; + shada = "!,'100,<50,s10,h"; + }; + clipboard = { + enable = true; + registers = "unnamedplus"; + providers.wl-copy.enable = true; + }; + }; +} diff --git a/modules/home/programs/neovim/picker.nix b/modules/home/programs/neovim/picker.nix new file mode 100644 index 0000000..5b1a629 --- /dev/null +++ b/modules/home/programs/neovim/picker.nix @@ -0,0 +1,256 @@ +{ + programs.nvf.settings.vim = { + utility = { + oil-nvim.enable = true; + snacks-nvim = { + setupOpts = { + picker.enabled = true; + explorer.enabled = true; + }; + }; + }; + keymaps = [ + # Top Pickers & Explorer + { + key = " "; + mode = "n"; + silent = true; + action = "lua Snacks.picker.smart()"; + desc = "Smart Find Files"; + } + { + key = ","; + mode = "n"; + silent = true; + action = "lua Snacks.picker.buffers()"; + desc = "Buffers"; + } + { + key = "/"; + mode = "n"; + silent = true; + action = "lua Snacks.picker.grep()"; + desc = "Grep"; + } + { + key = ":"; + mode = "n"; + silent = true; + action = "lua Snacks.picker.command_history()"; + desc = "Command History"; + } + { + key = "e"; + mode = "n"; + silent = true; + action = "lua Snacks.explorer()"; + desc = "File Explorer"; + } + { + key = "-"; + mode = "n"; + silent = true; + action = "Oil"; + desc = "Oil"; + } + + # Find + { + key = "fb"; + mode = "n"; + silent = true; + action = "lua Snacks.picker.buffers()"; + desc = "Buffers"; + } + { + key = "fc"; + mode = "n"; + silent = true; + action = ''lua Snacks.picker.files({ cwd = vim.fn.stdpath("config") })''; + desc = "Find Config File"; + } + { + key = "ff"; + mode = "n"; + silent = true; + action = "lua Snacks.picker.files()"; + desc = "Find Files"; + } + { + key = "fg"; + mode = "n"; + silent = true; + action = "lua Snacks.picker.git_files()"; + desc = "Find Git Files"; + } + { + key = "fp"; + mode = "n"; + silent = true; + action = "lua Snacks.picker.projects()"; + desc = "Projects"; + } + { + key = "fr"; + mode = "n"; + silent = true; + action = "lua Snacks.picker.recent()"; + desc = "Recent"; + } + { + key = "fn"; + mode = "n"; + silent = true; + action = "lua Snacks.picker.notifications()"; + desc = "Notification History"; + } + { + key = "fe"; + mode = "n"; + silent = true; + action = "lua Snacks.picker.icons()"; + desc = "Emoji"; + } + + # Git + { + key = "gb"; + mode = "n"; + silent = true; + action = "lua Snacks.picker.git_branches()"; + desc = "Git Branches"; + } + { + key = "gL"; + mode = "n"; + silent = true; + action = "lua Snacks.picker.git_log()"; + desc = "Git Log Line"; + } + { + key = "gs"; + mode = "n"; + silent = true; + action = "lua Snacks.picker.git_status()"; + desc = "Git Status"; + } + { + key = "gS"; + mode = "n"; + silent = true; + action = "lua Snacks.picker.git_stash()"; + desc = "Git Stash"; + } + { + key = "gd"; + mode = "n"; + silent = true; + action = "lua Snacks.picker.git_diff()"; + desc = "Git Diff (Hunks)"; + } + { + key = "gf"; + mode = "n"; + silent = true; + action = "lua Snacks.picker.git_log_file()"; + desc = "Git Log File"; + } + + # Grep + { + key = "sb"; + mode = "n"; + silent = true; + action = "lua Snacks.picker.lines()"; + desc = "Buffer Lines"; + } + { + key = "st"; + mode = "n"; + silent = true; + action = "lua Snacks.picker.todo_comments()"; + desc = "Todos"; + } + { + key = "sB"; + mode = "n"; + silent = true; + action = "lua Snacks.picker.grep_buffers()"; + desc = "Grep Open Buffers"; + } + { + key = "sg"; + mode = "n"; + silent = true; + action = "lua Snacks.picker.grep()"; + desc = "Grep"; + } + { + key = "sw"; + mode = "n"; + silent = true; + action = "lua Snacks.picker.grep_word()"; + desc = "Visual selection or word"; + } + { + key = "sr"; + mode = "n"; + silent = true; + action = "nohlsearch"; + desc = "Reset search"; + } + + # LSP + { + key = "gd"; + mode = "n"; + silent = true; + action = "lua Snacks.picker.lsp_definitions()"; + desc = "Goto Definition"; + } + { + key = "gD"; + mode = "n"; + silent = true; + action = "lua Snacks.picker.lsp_declarations()"; + desc = "Goto Declaration"; + } + { + key = "gr"; + mode = "n"; + silent = true; + action = "lua Snacks.picker.lsp_references()"; + desc = "References"; + nowait = true; + } + { + key = "gI"; + mode = "n"; + silent = true; + action = "lua Snacks.picker.lsp_implementations()"; + desc = "Goto Implementation"; + } + { + key = "gy"; + mode = "n"; + silent = true; + action = "lua Snacks.picker.lsp_type_definitions()"; + desc = "Goto Type Definition"; + } + { + key = "ss"; + mode = "n"; + silent = true; + action = "lua Snacks.picker.lsp_symbols()"; + desc = "LSP Symbols"; + } + { + key = "sS"; + mode = "n"; + silent = true; + action = "lua Snacks.picker.lsp_workspace_symbols()"; + desc = "LSP Workspace Symbols"; + } + ]; + }; +} diff --git a/modules/home/programs/neovim/snacks.nix b/modules/home/programs/neovim/snacks.nix new file mode 100644 index 0000000..0624537 --- /dev/null +++ b/modules/home/programs/neovim/snacks.nix @@ -0,0 +1,19 @@ +{ + programs.nvf.settings.vim.utility.snacks-nvim = { + enable = true; + setupOpts = { + image = { + enabled = true; + doc = { + inline = false; + float = true; + }; + }; + quickfile.enabled = true; + statuscolumn.enabled = true; + zen.enabled = true; + bufdelete.enabled = true; + gitsigns.enabled = true; + }; + }; +} diff --git a/modules/home/programs/neovim/utils.nix b/modules/home/programs/neovim/utils.nix new file mode 100644 index 0000000..9d3319c --- /dev/null +++ b/modules/home/programs/neovim/utils.nix @@ -0,0 +1,149 @@ +{ + lib, + pkgs, + ... +}: +{ + programs.nvf.settings.vim = { + undoFile.enable = true; + # theme = lib.mkForce { + # enable = true; + # name = "catppuccin"; + # style = "latte"; + # transparent = false; + # }; + + navigation = { + harpoon.enable = true; + }; + utility = { + motion.flash-nvim.enable = true; + outline.aerial-nvim.enable = true; + diffview-nvim.enable = true; + surround.enable = true; + smart-splits.enable = true; + yanky-nvim.enable = true; + }; + tabline.nvimBufferline.enable = true; + notes.todo-comments.enable = true; + # assistant.copilot = { + # enable = true; + # cmp.enable = true; + # }; + statusline.lualine.enable = true; + autocomplete = { + nvim-cmp = { + enable = true; + sources = { + buffer = "[Buffer]"; + nvim-cmp = null; + path = "[Path]"; + }; + sourcePlugins = [ + pkgs.vimPlugins.cmp-cmdline + ]; + }; + }; + snippets.luasnip.enable = true; + snippets.luasnip.customSnippets.snipmate = { + all = [ + { + trigger = "if"; + body = "if $1 else $2"; + } + ]; + nix = [ + { + trigger = "mkOption"; + body = '' + mkOption { + type = $1; + default = $2; + description = $3; + example = $4; + } + ''; + } + ]; + haskell = [ + { + trigger = "zzis"; + body = '' + insert :: (Ord a) => a -> [a] -> [a] + insert x [] = [x] + insert x (y:ys) + | x <= y = x : y : ys + | otherwise = y : insert x ys + + insertionSort :: (Ord a) => [a] -> [a] + insertionSort [] = [] + insertionSort (x:xs) = insert x (insertionSort xs) + ''; + } + { + trigger = "zzso"; + body = '' + sortOn :: Ord b => (a -> b) -> [a] -> [a] + sortOn f = foldr insert [] + where + insert x [] = [x] + insert x (y:ys) + | f x <= f y = x : y : ys + | otherwise = y : insert x ys + ''; + } + ]; + }; + ui = { + noice.enable = true; + colorizer.enable = true; + }; + visuals = { + rainbow-delimiters.enable = true; + nvim-scrollbar = { + enable = false; + }; + }; + git = { + enable = true; + gitsigns.enable = true; + }; + terminal.toggleterm = { + enable = true; + lazygit = { + enable = true; + mappings.open = "gl"; + }; + }; + formatter.conform-nvim.enable = true; + binds.hardtime-nvim.enable = true; + utility.motion.precognition.enable = true; + binds.hardtime-nvim.setupOpts = { + max_count = 3; # Optional: limit of consecutive presses + disabled_keys = { + "" = false; + "" = false; + "" = false; + "" = false; + }; + restricted_keys = { + "" = [ + "n" + "x" + ]; + "" = [ + "n" + "x" + ]; + "" = [ + "n" + "x" + ]; + "" = [ + "n" + "x" + ]; + }; + }; + }; +} diff --git a/modules/home/programs/niri/applications.nix b/modules/home/programs/niri/applications.nix new file mode 100644 index 0000000..a179ecf --- /dev/null +++ b/modules/home/programs/niri/applications.nix @@ -0,0 +1,10 @@ +{pkgs}: { + browser = "${pkgs.firefox}/bin/firefox"; + terminal = "${pkgs.ghostty}/bin/ghostty"; + fileManager = "${pkgs.thunar}/bin/thunar"; + appLauncher = "${pkgs.walker}/bin/walker"; + + screenshotArea = "${pkgs.bash}/bin/bash -c '${pkgs.grim}/bin/grim -g \"\\\$(${pkgs.slurp}/bin/slurp)\" - | ${pkgs.wl-clipboard}/bin/wl-copy'"; + screenshotWindow = "${pkgs.bash}/bin/bash -c '${pkgs.grim}/bin/grim -g \"\\\$(${pkgs.slurp}/bin/slurp -w)\" - | ${pkgs.wl-clipboard}/bin/wl-copy'"; + screenshotOutput = "${pkgs.bash}/bin/bash -c '${pkgs.grim}/bin/grim - | ${pkgs.wl-clipboard}/bin/wl-copy'"; +} diff --git a/modules/home/programs/niri/autostart.nix b/modules/home/programs/niri/autostart.nix new file mode 100644 index 0000000..5138408 --- /dev/null +++ b/modules/home/programs/niri/autostart.nix @@ -0,0 +1,26 @@ +{ + lib, + pkgs, + ... +}: +{ + home.packages = with pkgs; [ + xwayland-satellite + xdg-desktop-portal-gtk + ]; + programs.niri.settings.spawn-at-startup = [ + { + command = [ + "systemctl" + "--user" + "start" + "hyprpolkitagent" + ]; + } + { command = [ "xwayland-satellite" ]; } + { command = [ "qs" ]; } + { command = [ "swww-daemon" ]; } + #{ command = ["${pkgs.swaybg}/bin/swaybg" "-o" "DP-1" "-i" "/home/lysec/nixos/assets/wallpapers/clouds.png" "-m" "fill"]; } + #{ command = ["sh" "-c" "swww-daemon & swww img /home/lysec/nixos/wallpapers/cloud.png"]; } + ]; +} diff --git a/modules/home/programs/niri/keybinds.nix b/modules/home/programs/niri/keybinds.nix new file mode 100644 index 0000000..dce4bc8 --- /dev/null +++ b/modules/home/programs/niri/keybinds.nix @@ -0,0 +1,106 @@ +{ + lib, + config, + pkgs, + ... +}: +let + apps = import ./applications.nix { inherit pkgs; }; +in +{ + programs.niri.settings.binds = + with config.lib.niri.actions; + let + pactl = "${pkgs.pulseaudio}/bin/pactl"; + + volume-up = spawn pactl [ + "set-sink-volume" + "@DEFAULT_SINK@" + "+5%" + ]; + volume-down = spawn pactl [ + "set-sink-volume" + "@DEFAULT_SINK@" + "-5%" + ]; + in + { + # Quickshell Keybinds Start + "super+space".action = spawn [ + "noctalia-shell" + "ipc" + "call" + "launcher" + "toggle" + ]; + + "super+shift+Slash".action = show-hotkey-overlay; + + "super+l".action = spawn [ + "noctalia-shell" + "ipc" + "call" + "lockScreen" + "lock" + ]; + + "xf86audioraisevolume".action = volume-up; + "xf86audiolowervolume".action = volume-down; + + "XF86MonBrightnessDown".action = spawn [ + "noctalia-shell" + "ipc" + "call" + "brightness" + "decrease" + ]; + "XF86MonBrightnessUp".action = spawn [ + "noctalia-shell" + "ipc" + "call" + "brightness" + "increase" + ]; + + "super+q".action = close-window; + # "super+b".action = spawn apps.browser; + "super+Return".action = spawn apps.terminal; + #"super+Control+Return".action = spawn apps.appLauncher; + "super+E".action = spawn apps.fileManager; + "super+o".action = toggle-overview; + + "super+f".action = maximize-column; + "super+Shift+f".action = fullscreen-window; + "super+t".action = toggle-window-floating; + "super+r".action = switch-preset-column-width; + "super+Shift+r".action = switch-preset-window-height; + + "super+b".action = + spawn-sh "${pkgs.grim}/bin/grim \"$HOME/Pictures/screenshots/desktop-$(${pkgs.busybox}/bin/date +%Y%m%d%H%M).png\""; + "super+Shift+b".action = + spawn-sh "${pkgs.grim}/bin/grim -g \"$(${pkgs.slurp}/bin/slurp -d)\" - | ${pkgs.wl-clipboard}/bin/wl-copy"; # Screenshot selection directly to clipboard + + "super+Left".action = focus-column-left; + "super+Right".action = focus-column-right; + "super+Down".action = focus-workspace-down; + "super+Up".action = focus-workspace-up; + + "super+Shift+Left".action = move-column-left; + "super+Shift+Right".action = move-column-right; + "super+Shift+Down".action = move-column-to-workspace-down; + "super+Shift+Up".action = move-column-to-workspace-up; + + "super+Control+Left".action = focus-monitor-left; + "super+Control+Right".action = focus-monitor-right; + "super+Control+Down".action = focus-monitor-down; + "super+Control+Up".action = focus-monitor-up; + + "super+BracketLeft".action = consume-or-expel-window-left; + "super+BracketRight".action = consume-or-expel-window-right; + "super+Comma".action = consume-window-into-column; + "super+Period".action = expel-window-from-column; + + "super+Home".action = focus-column-first; + "super+End".action = focus-column-last; + }; +} diff --git a/modules/home/programs/niri/noctaliashell.nix b/modules/home/programs/niri/noctaliashell.nix new file mode 100644 index 0000000..f418e89 --- /dev/null +++ b/modules/home/programs/niri/noctaliashell.nix @@ -0,0 +1,78 @@ +{ + pkgs, + inputs, + ... +}: +{ + imports = [ + inputs.noctalia.homeModules.default + ]; + + # configure options + programs.noctalia-shell = { + enable = true; + settings = { + # configure noctalia here + nightLight = { + enabled = true; + }; + dock = { + enabled = false; + }; + bar = { + density = "default"; + position = "top"; + floating = true; + showCapsule = false; + widgets = { + left = [ + { + id = "ControlCenter"; + useDistroLogo = true; + } + { + id = "Network"; + } + { + id = "Bluetooth"; + } + ]; + center = [ + { + hideUnoccupied = false; + id = "Workspace"; + labelMode = "none"; + } + ]; + right = [ + { + id = "Tray"; + } + { + alwaysShowPercentage = false; + id = "Battery"; + warningThreshold = 30; + } + { + formatHorizontal = "HH:mm"; + formatVertical = "HH mm"; + id = "Clock"; + useMonospacedFont = true; + usePrimaryColor = true; + } + ]; + }; + }; + colorSchemes.predefinedScheme = "Monochrome"; + general = { + avatarImage = "/home/gwg313/.face"; + radiusRatio = 0.2; + }; + location = { + monthBeforeDay = true; + name = "Ottawa, Canada"; + }; + }; + # this may also be a string or a path to a JSON file. + }; +} diff --git a/modules/home/programs/niri/rules.nix b/modules/home/programs/niri/rules.nix new file mode 100644 index 0000000..7f8b7dc --- /dev/null +++ b/modules/home/programs/niri/rules.nix @@ -0,0 +1,48 @@ +{ + config, + pkgs, + ... +}: +{ + programs.niri.settings = { + layer-rules = [ + { + matches = [ + { + namespace = "^quickshell-wallpaper$"; + } + ]; + #place-within-backdrop = true; + } + { + matches = [ + { + namespace = "^quickshell-overview$"; + } + ]; + place-within-backdrop = true; + } + { + matches = [ + { + namespace = "^swww-daemon$"; + } + ]; + place-within-backdrop = true; + } + ]; + + window-rules = [ + { + matches = [ { } ]; + geometry-corner-radius = { + top-left = 20.0; + top-right = 20.0; + bottom-left = 20.0; + bottom-right = 20.0; + }; + clip-to-geometry = true; + } + ]; + }; +} diff --git a/modules/home/programs/niri/scripts.nix b/modules/home/programs/niri/scripts.nix new file mode 100644 index 0000000..94a6b16 --- /dev/null +++ b/modules/home/programs/niri/scripts.nix @@ -0,0 +1,31 @@ +{ pkgs, ... }: +let + brightnessScript = pkgs.writeShellScriptBin "brightness" '' + BUS=10 + STEP=5 + MIN=0 + MAX=100 + OSD_FILE="/tmp/brightness_osd_level" + + current=$(ddcutil --bus=$BUS getvcp 10 | grep -oP "current value\\s*=\\s*\\K[0-9]+") + new=$current + + if [[ "$1" == "up" ]]; then + new=$((current + STEP)) + (( new > MAX )) && new=$MAX + elif [[ "$1" == "down" ]]; then + new=$((current - STEP)) + (( new < MIN )) && new=$MIN + else + exit 1 + fi + + ddcutil --bus=$BUS setvcp 10 "$new" + echo "$new" > "$OSD_FILE" + ''; +in +{ + home.packages = [ + brightnessScript + ]; +} diff --git a/modules/home/programs/niri/settings.nix b/modules/home/programs/niri/settings.nix new file mode 100644 index 0000000..b36bbea --- /dev/null +++ b/modules/home/programs/niri/settings.nix @@ -0,0 +1,155 @@ +{ + config, + pkgs, + ... +}: { + home.sessionVariables = { + XDG_ICON_DIR = "${pkgs.whitesur-icon-theme}/share/icons/WhiteSur"; + GSETTINGS_SCHEMA_DIR = "${pkgs.gsettings-desktop-schemas}/share/gsettings-schemas/${pkgs.gsettings-desktop-schemas.name}/glib-2.0/schemas"; + XCURSOR_THEME = "Bibata-Original-Ice"; + XCURSOR_SIZE = "20"; + }; + programs.niri = { + enable = true; + package = pkgs.niri; + settings = { + environment = { + # CLUTTER_BACKEND = "wayland"; + # DISPLAY = null; + # MOZ_ENABLE_WAYLAND = "1"; + # NIXOS_OZONE_WL = "1"; + # QT_QPA_PLATFORM = "wayland;xcb"; + # QT_WAYLAND_DISABLE_WINDOWDECORATION = "1"; + SDL_VIDEODRIVER = "wayland"; + WLR_RENDERER = "vulkan"; + WLR_NO_HARDWARE_CURSORS = "1"; + QT_QPA_PLATFORMTHEME = "qt6ct"; + GTK_IM_MODULE = "simple"; + # GDK_BACKEND = "wayland,x11"; + }; + spawn-at-startup = [ + { + command = [ + "noctalia-shell" + ]; + } + ]; + + prefer-no-csd = true; + + hotkey-overlay = { + skip-at-startup = true; + }; + switch-events = { + lid-close.action.spawn = [ + "noctalia-shell" + "ipc" + "call" + "lockScreen" + "lock" + ]; + }; + + layout = { + preset-column-widths = [ + {proportion = 1. / 3.;} + {proportion = 1. / 2.;} + {proportion = 2. / 3.;} + ]; + default-column-width = { + proportion = 0.5; + }; + + preset-window-heights = [ + {proportion = 1. / 3.;} + {proportion = 1. / 2.;} + {proportion = 2. / 3.;} + ]; + + background-color = "#00000000"; + + focus-ring = { + enable = true; + width = 3; + active = { + color = "#A8AEFF"; + }; + inactive = { + color = "#505050"; + }; + }; + + gaps = 6; + + struts = { + left = 20; + right = 20; + top = 20; + bottom = 20; + }; + }; + + input = { + touchpad = { + click-method = "button-areas"; + dwt = true; + dwtp = true; + natural-scroll = true; + scroll-method = "two-finger"; + tap = true; + tap-button-map = "left-right-middle"; + middle-emulation = true; + accel-profile = "adaptive"; + }; + focus-follows-mouse.enable = true; + focus-follows-mouse.max-scroll-amount = "0%"; + warp-mouse-to-focus.enable = false; + }; + + outputs = { + "eDP-1" = { + mode = { + width = 1920; + height = 1080; + }; + scale = 1.0; + position = { + x = 0; + y = 0; + }; + }; + + "Microstep MSI MP161 E2 PB7H314B00225" = { + mode = { + width = 1920; + height = 1080; + }; + scale = 1.0; + position = { + x = 0; + y = -1080; + }; + }; + }; + + cursor = { + size = 20; + theme = "Adwaita"; + }; + + environment = { + CLUTTER_BACKEND = "wayland"; + GDK_BACKEND = "wayland,x11"; + MOZ_ENABLE_WAYLAND = "1"; + NIXOS_OZONE_WL = "1"; + QT_QPA_PLATFORM = "wayland"; + QT_WAYLAND_DISABLE_WINDOWDECORATION = "1"; + ELECTRON_OZONE_PLATFORM_HINT = "auto"; + + XDG_SESSION_TYPE = "wayland"; + XDG_CURRENT_DESKTOP = "niri"; + DISPLAY = ":0"; + }; + }; + }; +} diff --git a/modules/home/programs/pass.nix b/modules/home/programs/pass.nix new file mode 100644 index 0000000..3d373ea --- /dev/null +++ b/modules/home/programs/pass.nix @@ -0,0 +1,12 @@ +# ✨ password manager +{pkgs, ...}: { + programs.password-store = { + enable = true; + package = pkgs.pass.withExtensions (exts: [exts.pass-otp]); + }; + + home.packages = with pkgs; [ + tessen + zbar + ]; +} diff --git a/modules/home/programs/starship.nix b/modules/home/programs/starship.nix new file mode 100644 index 0000000..3651512 --- /dev/null +++ b/modules/home/programs/starship.nix @@ -0,0 +1,54 @@ +# A customizable prompt for shells. +{ + config, + lib, + ... +}: let + accent = "#${config.lib.stylix.colors.base0D}"; + background-alt = "#${config.lib.stylix.colors.base01}"; +in { + programs.starship = { + enable = true; + enableZshIntegration = true; + settings = { + add_newline = true; + format = lib.concatStrings [ + "$hostname" + "$directory" + "$git_branch" + "$git_state" + "$git_status" + "$character" + ]; + directory = { + style = accent; + }; + + character = { + success_symbol = "[❯](${accent})"; + error_symbol = "[❯](red)"; + vimcmd_symbol = "[❮](cyan)"; + }; + + git_branch = { + symbol = "[](${background-alt}) "; + style = "fg:${accent} bg:${background-alt}"; + format = "on [$symbol$branch]($style)[](${background-alt}) "; + }; + + git_status = { + format = "[[(*$conflicted$untracked$modified$staged$renamed$deleted)](218)($ahead_behind$stashed)]($style)"; + style = "cyan"; + conflicted = ""; + renamed = ""; + deleted = ""; + stashed = "≡"; + }; + + git_state = { + format = "([$state( $progress_current/$progress_total)]($style)) "; + style = "bright-black"; + }; + }; + }; +} diff --git a/modules/home/programs/yazi.nix b/modules/home/programs/yazi.nix new file mode 100644 index 0000000..4361d5b --- /dev/null +++ b/modules/home/programs/yazi.nix @@ -0,0 +1,126 @@ +{ pkgs, ... }: +{ + programs.yazi = { + enable = true; + enableZshIntegration = true; + shellWrapperName = "y"; + plugins = { + starship = pkgs.yaziPlugins.starship; + full-border = pkgs.yaziPlugins.full-border; + chmod = pkgs.yaziPlugins.chmod; + compress = pkgs.yaziPlugins.compress; + smart-paste = pkgs.yaziPlugins.smart-paste; + smart-enter = pkgs.yaziPlugins.smart-enter; + smart-filter = pkgs.yaziPlugins.smart-filter; + }; + settings = { + yazi = { + ratio = [ + 1 + 4 + 3 + ]; + sort_by = "natural"; + sort_sensitive = true; + sort_reverse = false; + sort_dir_first = true; + linemode = "none"; + show_hidden = true; + show_symlink = true; + }; + + preview = { + image_filter = "lanczos3"; + image_quality = 90; + tab_size = 1; + max_width = 600; + max_height = 900; + cache_dir = ""; + ueberzug_scale = 1; + ueberzug_offset = [ + 0 + 0 + 0 + 0 + ]; + }; + + tasks = { + micro_workers = 5; + macro_workers = 10; + bizarre_retry = 5; + }; + }; + initLua = '' + require("starship"):setup() + require("full-border"):setup() + require("smart-enter"):setup { + open_multi = true, + } + ''; + keymap = { + mgr.prepend_keymap = [ + { + on = [ + "c" + "a" + "a" + ]; + run = "plugin compress"; + desc = "Archive selected files"; + } + { + on = [ + "c" + "a" + "p" + ]; + run = "plugin compress -p"; + desc = "Archive selected files (password)"; + } + { + on = [ + "c" + "a" + "h" + ]; + run = "plugin compress -ph"; + desc = "Archive selected files (password+header)"; + } + { + on = [ + "c" + "a" + "l" + ]; + run = "plugin compress -l"; + desc = "Archive selected files (compression level)"; + } + { + on = [ + "c" + "a" + "u" + ]; + run = "plugin compress -phl"; + desc = "Archive selected files (password+header+level)"; + } + { + on = "p"; + run = "plugin smart-paste"; + desc = "Paste into the hovered directory or CWD"; + } + { + on = "l"; + run = "plugin smart-enter"; + desc = "Enter the child directory, or open the file"; + } + { + on = "F"; + run = "plugin smart-filter"; + desc = "Smart filter"; + } + ]; + }; + }; +} diff --git a/modules/hosts/candlekeep.nix b/modules/hosts/candlekeep.nix new file mode 100644 index 0000000..48cf5f6 --- /dev/null +++ b/modules/hosts/candlekeep.nix @@ -0,0 +1,23 @@ +{ + config.dendritic.hosts.candlekeep = { + system = "x86_64-linux"; + type = "laptop"; + roles = [ + "workstation" + "laptop" + ]; + primaryUser = "gwg313"; + primaryUserExtraGroups = [ + "wheel" + "networkmanager" + "audio" + ]; + + nixosModules = [ + ../nixos/hardware/candlekeep.nix + ../nixos/hosts/candlekeep/ssh.nix + ../nixos/hosts/candlekeep/nfs.nix + ../nixos/hosts/candlekeep/networking.nix + ]; + }; +} diff --git a/modules/nixos/bluetooth.nix b/modules/nixos/bluetooth.nix new file mode 100644 index 0000000..c1b1f3a --- /dev/null +++ b/modules/nixos/bluetooth.nix @@ -0,0 +1,17 @@ +{ + lib, + config, + ... +}: { + config = lib.mkIf config.bluetooth.enable { + services.blueman.enable = true; + hardware.bluetooth = { + enable = true; + powerOnBoot = true; + }; + + services.upower = { + enable = true; + }; + }; +} diff --git a/modules/nixos/clamav.nix b/modules/nixos/clamav.nix new file mode 100644 index 0000000..4562585 --- /dev/null +++ b/modules/nixos/clamav.nix @@ -0,0 +1,13 @@ +{ pkgs, ... }: +{ + environment.systemPackages = with pkgs; [ + clamav + ]; + + services.clamav = { + daemon.enable = true; + updater.enable = true; + scanner.enable = true; + fangfrisch.enable = true; + }; +} diff --git a/modules/nixos/comma.nix b/modules/nixos/comma.nix new file mode 100644 index 0000000..481a279 --- /dev/null +++ b/modules/nixos/comma.nix @@ -0,0 +1,5 @@ +{_}: { + # Disable so comma can be installed + programs.command-not-found.enable = false; + programs.nix-index-database.comma.enable = true; +} diff --git a/modules/nixos/gnupg-agent.nix b/modules/nixos/gnupg-agent.nix new file mode 100644 index 0000000..14c4853 --- /dev/null +++ b/modules/nixos/gnupg-agent.nix @@ -0,0 +1,9 @@ +{ pkgs, ... }: +{ + programs.gnupg.agent = { + enable = true; + enableSSHSupport = true; + enableZshIntegration = true; + pinentryPackage = pkgs.pinentry-qt; + }; +} diff --git a/modules/nixos/gui/dbus.nix b/modules/nixos/gui/dbus.nix new file mode 100644 index 0000000..d433ad5 --- /dev/null +++ b/modules/nixos/gui/dbus.nix @@ -0,0 +1,14 @@ +{ + config, + pkgs, + ... +}: { + services.dbus = { + enable = true; + packages = [pkgs.dconf]; + }; + + programs.dconf = { + enable = true; + }; +} diff --git a/modules/nixos/gui/gnupg.nix b/modules/nixos/gui/gnupg.nix new file mode 100644 index 0000000..237da40 --- /dev/null +++ b/modules/nixos/gui/gnupg.nix @@ -0,0 +1,11 @@ +{pkgs, ...}: { + programs.gnupg.agent = { + enable = true; + enableSSHSupport = true; + pinentryPackage = pkgs.pinentry-qt; + }; + + environment.sessionVariables = { + SSH_AUTH_SOCK = "/run/user/1000/gnupg/S.gpg-agent.ssh"; + }; +} diff --git a/modules/nixos/gui/wayland.nix b/modules/nixos/gui/wayland.nix new file mode 100644 index 0000000..ee4a70a --- /dev/null +++ b/modules/nixos/gui/wayland.nix @@ -0,0 +1,26 @@ +{pkgs, ...}: { + environment.systemPackages = with pkgs; [ + wlr-randr + wl-clipboard + ]; + + environment.sessionVariables = { + POLKIT_AUTH_AGENT = "${pkgs.polkit_gnome}/libexec/polkit-gnome-authentication-agent-1"; + GSETTINGS_SCHEMA_DIR = "${pkgs.gsettings-desktop-schemas}/share/gsettings-schemas/${pkgs.gsettings-desktop-schemas.name}/glib-2.0/schemas"; + WLR_NO_HARDWARE_CURSORS = "1"; + NIXOS_OZONE_WL = "1"; + MOZ_ENABLE_WAYLAND = "1"; + SDL_VIDEODRIVER = "wayland"; + _JAVA_AWT_WM_NONREPARENTING = "1"; + CLUTTER_BACKEND = "wayland"; + # WLR_RENDERER = "vulkan"; + GTK_USE_PORTAL = "1"; + #NIXOS_XDG_OPEN_USE_PORTAL = "1"; # Sets the desktop portal to use flatpak + WLR_NO_HARDWARE_CURSOR = "1"; + GDK_BACKEND = "wayland"; + QT_QPA_PLATFORM = "wayland;xcb"; + QT_AUTO_SCREEN_SCALE_FACTOR = "1"; + QT_WAYLAND_DISABLE_WINDOWDECORATION = "1"; + QT_QPA_PLATFORMTHEME = "qt5ct"; + }; +} diff --git a/modules/nixos/gui/xdg.nix b/modules/nixos/gui/xdg.nix new file mode 100644 index 0000000..9efef88 --- /dev/null +++ b/modules/nixos/gui/xdg.nix @@ -0,0 +1,40 @@ +{ + config, + lib, + pkgs, + ... +}: +{ + # XDG Portals + xdg = { + autostart.enable = true; + portal = { + enable = true; + wlr.enable = true; + xdgOpenUsePortal = true; + extraPortals = [ + # pkgs.xdg-desktop-portal-hyprland + pkgs.xdg-desktop-portal-gtk + ]; + config = { + common = { + default = [ "gtk" ]; + }; + }; + }; + }; + environment.pathsToLink = [ + "/share/applications" + "/share/xdg-desktop-portal" + ]; + environment.systemPackages = with pkgs; [ + xdg-utils + xdg-desktop-portal + xdg-desktop-portal-gtk + wl-clipboard + ]; + + environment.sessionVariables = { + XDG_SESSION_TYPE = "wayland"; + }; +} diff --git a/modules/nixos/hardware/candlekeep.nix b/modules/nixos/hardware/candlekeep.nix new file mode 100644 index 0000000..ccdad03 --- /dev/null +++ b/modules/nixos/hardware/candlekeep.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, + pkgs, + modulesPath, + ... +}: { + imports = [ + (modulesPath + "/installer/scan/not-detected.nix") + # ../../common/nixos/nfs.nix + ]; + + boot.initrd.availableKernelModules = [ + "nvme" + "xhci_pci" + "usb_storage" + "sd_mod" + "rtsx_pci_sdmmc" + ]; + boot.initrd.kernelModules = []; + boot.kernelModules = ["kvm-amd"]; + boot.extraModulePackages = []; + + boot = { + loader = { + systemd-boot.enable = true; + efi.canTouchEfiVariables = true; + }; + initrd.luks.devices."luks-1dbfdeb6-8537-41b2-abf0-09373af3eeee".device = "/dev/disk/by-uuid/1dbfdeb6-8537-41b2-abf0-09373af3eeee"; + }; + fileSystems."/" = { + device = "/dev/disk/by-uuid/c79b630a-d130-42ed-8cdc-3f8545fe2993"; + fsType = "ext4"; + }; + + boot.initrd.luks.devices."luks-96e3b309-ca79-4b42-aca5-3f098b123758".device = "/dev/disk/by-uuid/96e3b309-ca79-4b42-aca5-3f098b123758"; + fileSystems."/boot" = { + device = "/dev/disk/by-uuid/1FBA-8B80"; + fsType = "vfat"; + options = [ + "fmask=0077" + "dmask=0077" + ]; + }; + swapDevices = [ + {device = "/dev/disk/by-uuid/17eec89e-2381-4a25-8935-63cbcc67d07c";} + ]; + + # 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.enp2s0f0.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/modules/nixos/hosts/candlekeep/git.nix b/modules/nixos/hosts/candlekeep/git.nix new file mode 100644 index 0000000..d298567 --- /dev/null +++ b/modules/nixos/hosts/candlekeep/git.nix @@ -0,0 +1,155 @@ +{ + pkgs, + ... +}: +{ + programs.git = { + enable = true; + + settings = { + user = { + name = "gwg313"; + email = "gwg313@pm.me"; + }; + + credential = { + helper = "!pass-git-helper $@"; + }; + user = { + signingkey = "60FF63B4826B7400"; + }; + commit = { + gpgsign = true; + verbose = "true"; + }; + diff = { + algorithm = "histogram"; + colorMoved = "plain"; + mnemonicPrefix = "true"; + renames = "true"; + compactionHeuristic = "true"; + tool = "nvimdiff"; + }; + "difftool \"nvimdiff\"" = { + cmd = "nvim -d \"$LOCAL\" \"$REMOTE\" -c \"wincmd w\" -c \"wincmd L\""; + }; + merge = { + tool = "nvimdiff4"; + prompt = "false"; + conflictstyle = "zdiff3"; + }; + "mergetool \"nvimdiff4\"" = { + cmd = "nvim -d $LOCAL $BASE $REMOTE $MERGED -c '$wincmd w' -c 'wincmd J'"; + }; + mergetool = { + keepBackup = false; + }; + init = { + defaultBranch = "main"; + }; + core = { + pager = "delta"; + editor = "nvim"; + }; + delta = { + features = "line-numbers decorations"; + navigate = "true"; + whitespace-error-style = "22 reverse"; + }; + interactive = { + diffFilter = "delta --color-only"; + }; + push = { + default = "simple"; + autoSetupRemote = "true"; + followTags = "true"; + }; + pull = { + rebase = "true"; + }; + rebase = { + autoSquash = "true"; + autoStash = "true"; + updateRefs = "true"; + }; + fetch = { + prune = "true"; + pruneTags = "true"; + all = "true"; + }; + tag = { + sort = "version:refname"; + }; + branch = { + sort = "-committerdate"; + }; + + help = { + autocorrect = "true"; + }; + rerere = { + enabled = "true"; + autoupdate = "true"; + }; + + color.ui = "1"; + }; + ignores = [ + "__pycache__" + ".direnv" + "npm-debug.log" + ".cache/" + ".DS_Store" + ".idea/" + "*.swp" + "*.elc" + "auto-save-list" + ".direnv/" + "node_modules" + "result" + "result-*" + ]; + }; + + programs.zsh = { + shellAliases = { + trackme = "git branch --set-upstream-to=origin/$(git symbolic-ref --short HEAD)"; + rebasemain = "git pull origin main --rebase"; + hist = ''log --pretty=format:"%Cgreen%h %Creset%cd %Cblue[%cn] %Creset%s%C(yellow)%d%C(reset)" --graph --date=relative --decorate --all''; + llog = ''log --graph --name-status --pretty=format:"%C(red)%h %C(reset)(%cd) %C(green)%an %Creset%s %C(yellow)%d%Creset" --date=relative''; + g = "lazygit"; + ga = "git add"; + gc = "git commit"; + gcu = "git add . && git commit -m 'Update'"; + gp = "git push"; + gpl = "git pull"; + gs = "git status"; + gd = "git diff"; + gco = "git checkout"; + gcb = "git checkout -b"; + gbr = "git branch"; + grs = "git reset HEAD~1"; + grh = "git reset --hard HEAD~1"; + + gaa = "git add ."; + gcm = "git commit -m"; + }; + }; + + home.packages = with pkgs; [ + cocogitto + delta + lazygit + gh + pre-commit + graphite-cli + tig + pass-git-helper + ]; + + xdg.configFile."pass-git-helper/git-pass-mapping.ini".text = '' + [git.gwg313.xyz*] + target=git/https/git.gwg313.xyz + line_username=1 + ''; +} diff --git a/modules/nixos/hosts/candlekeep/networking.nix b/modules/nixos/hosts/candlekeep/networking.nix new file mode 100644 index 0000000..ea09647 --- /dev/null +++ b/modules/nixos/hosts/candlekeep/networking.nix @@ -0,0 +1,82 @@ +{ config, ... }: +let + user = config.dendritic.current.primaryUser; + secretName = "wireless.env"; +in +{ + sops.secrets.${secretName} = { + sopsFile = ../../../../secrets/users/${user}.yaml; + }; + + networking.networkmanager.ensureProfiles = { + environmentFiles = [ + config.sops.secrets.${secretName}.path + ]; + + profiles = { + home-wifi = { + connection.id = "home-wifi"; + connection.type = "wifi"; + wifi.ssid = "$home_uuid"; + wifi-security = { + auth-alg = "open"; + key-mgmt = "wpa-psk"; + psk = "$home_psk"; + }; + connection.autoconnect-priority = 100; + }; + + eduroam = { + connection.id = "eduroam"; + connection.type = "wifi"; + wifi.ssid = "eduroam"; + wifi-security = { + key-mgmt = "wpa-eap"; + }; + "802-1x" = { + eap = "peap;"; + identity = "$eduroam_identity"; + password = "$school_password"; + phase2-auth = "mschapv2"; + }; + connection.autoconnect = true; + connection.autoconnect-priority = 80; + wifi.powersave = 2; + wifi.mode = "infrastructure"; + ipv4.method = "auto"; + # ipv4.dns = "8.8.8.8,8.8.4.4."; + # ipv6.dns = "2001:4860:4860::8888"; + # wifi.mac-address-randomization = "random"; + ipv4.dhcp-send-hostname = false; + # ipv4.dhcp-hostname = "NoName"; + # connection.metered = "yes"; + }; + + school = { + connection.id = "School"; + connection.type = "wifi"; + wifi.ssid = "$school_uuid"; + wifi-security = { + key-mgmt = "wpa-eap"; + }; + "802-1x" = { + eap = "peap;"; + identity = "$school_identity"; + password = "$school_password"; + phase2-auth = "mschapv2"; + }; + connection.autoconnect = true; + connection.autoconnect-priority = 90; + wifi.powersave = 2; + wifi.mode = "infrastructure"; + ipv4.method = "auto"; + # ipv4.dns = "8.8.8.8,8.8.4.4."; + # ipv6.dns = "2001:4860:4860::8888"; + # wifi.mac-address-randomization = "random"; + ipv4.dhcp-send-hostname = false; + # ipv4.dhcp-hostname = "NoName"; + # connection.metered = "yes"; + }; + }; + }; +} diff --git a/modules/nixos/hosts/candlekeep/nfs.nix b/modules/nixos/hosts/candlekeep/nfs.nix new file mode 100644 index 0000000..7efa4a2 --- /dev/null +++ b/modules/nixos/hosts/candlekeep/nfs.nix @@ -0,0 +1,53 @@ +{ + config, + lib, + inputs, + ... +}: +{ + fileSystems = { + "/books" = { + device = inputs.secrets.nfs.devices.books; + fsType = "nfs"; + options = [ + "x-systemd.automount" + "noauto" + "x-systemd.after=network-online.target" + "x-systemd.mount-timeout=90" + ]; + }; + + "/music" = { + device = inputs.secrets.nfs.devices.music; + fsType = "nfs"; + options = [ + "x-systemd.automount" + "noauto" + "x-systemd.after=network-online.target" + "x-systemd.mount-timeout=90" + ]; + }; + + "/personal" = { + device = inputs.secrets.nfs.devices.personal; + fsType = "nfs"; + options = [ + "x-systemd.automount" + "noauto" + "x-systemd.after=network-online.target" + "x-systemd.mount-timeout=90" + ]; + }; + + "/backups" = { + device = inputs.secrets.nfs.devices.backups; + fsType = "nfs"; + options = [ + "x-systemd.automount" + "noauto" + "x-systemd.after=network-online.target" + "x-systemd.mount-timeout=90" + ]; + }; + }; +} diff --git a/modules/nixos/hosts/candlekeep/ssh-hosts.nix b/modules/nixos/hosts/candlekeep/ssh-hosts.nix new file mode 100644 index 0000000..d2de21b --- /dev/null +++ b/modules/nixos/hosts/candlekeep/ssh-hosts.nix @@ -0,0 +1,106 @@ +{ ... }: +{ + programs.ssh = { + enable = true; + hashKnownHosts = true; + matchBlocks = { + "router" = { + hostname = "router.local.gwg313.xyz"; + user = "glen"; + identityFile = "/home/gwg313/.ssh/home/id_ed25519"; + }; + "github.com" = { + hostname = "github.com"; + identityFile = "/home/gwg313/.ssh/github/id_ed25519"; + }; + "candlekeep" = { + hostname = "candlekeep.local.gwg313.xyz"; + user = "gwg313"; + identityFile = "/home/gwg313/.ssh/home/id_ed25519"; + }; + "candlekeep.zt" = { + hostname = "candlekeep.zt"; # added to hosts in zerotier config + user = "gwg313"; + identityFile = "/home/gwg313/.ssh/home/id_ed25519"; + }; + "grymforge" = { + hostname = "grymforge.local.gwg313.xyz"; + user = "gwg313"; + identityFile = "/home/gwg313/.ssh/home/id_ed25519"; + }; + "grymforge.zt" = { + hostname = "grymforge.zt"; # added to hosts in zerotier config + user = "gwg313"; + identityFile = "/home/gwg313/.ssh/home/id_ed25519"; + extraOptions = { + "VisualHostKey" = "no"; + }; + }; + + "waypoint" = { + hostname = "waypoint.local.gwg313.xyz"; + user = "root"; + identityFile = "/home/gwg313/.ssh/colmena/id_ed25519"; + }; + + "seikan" = { + hostname = "147.182.147.32"; + user = "root"; + identityFile = "/home/gwg313/.ssh/digital_ocean/id_ed25519"; + }; + + "panopticon" = { + hostname = "10.1.10.9"; + user = "root"; + identityFile = "/home/gwg313/.ssh/colmena/id_ed25519"; + }; + + "vault-tec" = { + hostname = "10.1.10.13"; + user = "root"; + identityFile = "/home/gwg313/.ssh/colmena/id_ed25519"; + }; + + "qnx" = { + hostname = "10.1.40.32"; + user = "qnxuser"; + identitiesOnly = true; + identityFile = "/home/gwg313/.ssh/qnxuser"; + extraOptions = { + "Ciphers" = "aes256-ctr"; + }; + }; + + "qnxzt" = { + hostname = "10.1.40.32"; + user = "qnxuser"; + identitiesOnly = true; + identityFile = "/home/gwg313/.ssh/qnxuser"; + proxyJump = "grymforge.zt"; + extraOptions = { + "Ciphers" = "aes256-ctr"; + "VisualHostKey" = "no"; + }; + }; + + "qnxproj" = { + hostname = "10.1.10.66"; + user = "qnxuser"; + identitiesOnly = true; + identityFile = "/home/gwg313/.ssh/qnxuser"; + proxyJump = "bastion"; + extraOptions = { + "Ciphers" = "aes256-ctr"; + "VisualHostKey" = "no"; + }; + }; + + "bastion" = { + hostname = "10.1.10.9"; + user = "bastion"; + identitiesOnly = true; + identityFile = "/home/gwg313/.ssh/qnxuser"; + }; + }; + }; +} diff --git a/modules/nixos/hosts/candlekeep/ssh.nix b/modules/nixos/hosts/candlekeep/ssh.nix new file mode 100644 index 0000000..568667b --- /dev/null +++ b/modules/nixos/hosts/candlekeep/ssh.nix @@ -0,0 +1,19 @@ +{ config, ... }: + +let + user = config.dendritic.current.primaryUser; +in +{ + services.openssh = { + enable = true; + + settings = { + AllowUsers = [ user ]; + }; + }; + + users.users.${user}.openssh.authorizedKeys.keys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILq54YrM3BbhBs0oDLOrc1bkg6FCCmkV4E3pWLZp0ejN gwg313@pm.me" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPZK7127ict2+Urhi1cbD6EIU85mD4lkQ9/ihaif0jsX" # Phone + ]; +} diff --git a/modules/nixos/hosts/candlekeep/syncthing.nix b/modules/nixos/hosts/candlekeep/syncthing.nix new file mode 100644 index 0000000..93dac43 --- /dev/null +++ b/modules/nixos/hosts/candlekeep/syncthing.nix @@ -0,0 +1,78 @@ +{ + inputs, + ... +}: +{ + services.syncthing = { + enable = true; + dataDir = "/home/gwg313"; + openDefaultPorts = true; + configDir = "/home/gwg313/.config/syncthing"; + user = "gwg313"; + group = "users"; + guiAddress = "127.0.0.1:8384"; + + overrideDevices = true; + overrideFolders = true; + settings = { + gui = { + user = inputs.secrets.syncthing.gui_user; + password = inputs.secrets.syncthing.gui_password; + }; + devices = { + "grymforge" = { + id = inputs.secrets.syncthing.grymforge_id; + }; + }; + + folders = { + "repos" = { + path = "/home/gwg313/repos"; + devices = [ "grymforge" ]; + versioning = { + type = "staggered"; + params = { + cleanInterval = "3600"; + maxAge = "15768000"; + }; + }; + }; + "documents" = { + path = "/home/gwg313/Documents"; + devices = [ "grymforge" ]; + versioning = { + type = "staggered"; + params = { + cleanInterval = "3600"; + maxAge = "15768000"; + }; + }; + }; + + "password-store" = { + path = "/home/gwg313/.local/share/password-store"; + devices = [ "grymforge" ]; + versioning = { + type = "staggered"; + params = { + cleanInterval = "3600"; + maxAge = "15768000"; + }; + }; + }; + + "zettelkasten" = { + path = "/home/gwg313/zettelkasten/"; + devices = [ "grymforge" ]; + versioning = { + type = "staggered"; + params = { + cleanInterval = "3600"; + maxAge = "15768000"; + }; + }; + }; + }; + }; + }; +} diff --git a/modules/nixos/hosts/candlekeep/usbguard.nix b/modules/nixos/hosts/candlekeep/usbguard.nix new file mode 100644 index 0000000..025a730 --- /dev/null +++ b/modules/nixos/hosts/candlekeep/usbguard.nix @@ -0,0 +1,4 @@ +{ lib, ... }: +{ + services.usbguard.rules = lib.mkAfter ""; +} diff --git a/modules/nixos/locale.nix b/modules/nixos/locale.nix new file mode 100644 index 0000000..0628300 --- /dev/null +++ b/modules/nixos/locale.nix @@ -0,0 +1,19 @@ +{_}: { + # Set your time zone. + time.timeZone = "America/Toronto"; + + # 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"; + }; +} diff --git a/modules/nixos/logrotate.nix b/modules/nixos/logrotate.nix new file mode 100644 index 0000000..fdfc239 --- /dev/null +++ b/modules/nixos/logrotate.nix @@ -0,0 +1,15 @@ +{...}: { + services.logrotate = { + settings = { + header = { + dateext = true; + }; + + "var/log/audit/audit.log" = { + frequency = "daily"; + rotate = 3; + size = "100k"; + }; + }; + }; +} diff --git a/modules/nixos/nftables.nix b/modules/nixos/nftables.nix new file mode 100644 index 0000000..2195281 --- /dev/null +++ b/modules/nixos/nftables.nix @@ -0,0 +1,7 @@ +{ ... }: +{ + networking.nftables.enable = true; + networking.firewall = { + enable = true; + }; +} diff --git a/modules/nixos/pipewire.nix b/modules/nixos/pipewire.nix new file mode 100644 index 0000000..d5eb8d8 --- /dev/null +++ b/modules/nixos/pipewire.nix @@ -0,0 +1,34 @@ +{pkgs, ...}: { + # Enable sound with pipewire. + hardware.pulseaudio.enable = false; + # hardware.alsa.enablePersistence = true; + 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; + extraConfig.pipewire.adjust-sample-rate = { + "context.properties" = { + "default.clock.rate" = 192000; + #"defautlt.allowed-rates" = [ 192000 48000 44100 ]; + "defautlt.allowed-rates" = [192000]; + #"default.clock.quantum" = 32; + #"default.clock.min-quantum" = 32; + #"default.clock.max-quantum" = 32; + }; + }; + }; + + environment.systemPackages = with pkgs; [ + pipewire + wireplumber + easyeffects + ]; +} diff --git a/modules/nixos/ssh/default.nix b/modules/nixos/ssh/default.nix new file mode 100644 index 0000000..c6f352e --- /dev/null +++ b/modules/nixos/ssh/default.nix @@ -0,0 +1,12 @@ +{ lib, ... }: +{ + imports = [ + ./ssh.nix + ./ssh_client.nix + ./ssh_guard.nix + ]; + + ssh_client.enable = lib.mkDefault true; + ssh.enable = lib.mkDefault false; + ssh_guard.enable = lib.mkDefault false; +} diff --git a/modules/nixos/ssh/ssh.nix b/modules/nixos/ssh/ssh.nix new file mode 100644 index 0000000..9f5ca8e --- /dev/null +++ b/modules/nixos/ssh/ssh.nix @@ -0,0 +1,146 @@ +{ user, ... }: +{ + # https://www.ssh-audit.com/hardening_guides.html + # https://github.com/jtesta/ssh-audit + services.openssh = { + enable = true; + settings = { + ########## Features ########## + + # disallow ssh-agent forwarding to prevent lateral movement + AllowAgentForwarding = false; + + # prevent TCP ports from being forwarded over SSH tunnels + # **please be aware that disabling TCP forwarding does not prevent port forwarding** + # any user with an interactive login shell can spin up his/her own instance of sshd + AllowTcpForwarding = true; + + # prevent StreamLocal (Unix-domain socket) forwarding + AllowStreamLocalForwarding = false; + + # disables all forwarding features + # overrides all other forwarding switches + DisableForwarding = false; + + # disallow remote hosts from connecting to forwarded ports + # i.e. forwarded ports are forced to bind to 127.0.0.1 instad of 0.0.0.0 + GatewayPorts = "no"; + + # prevent tun device forwarding + PermitTunnel = false; + + # suppress MOTD + PrintMotd = false; + + # disable X11 forwarding since it is not necessary + X11Forwarding = false; + + ########## Authentication ########## + + # AllowUsers = ["${user}"]; + + # Use keys only. Remove if you want to SSH using password (not recommended) + PasswordAuthentication = false; + HostbasedAuthentication = false; + + # enable pubkey authentication + PubkeyAuthentication = true; + + # Forbid root login through SSH. + PermitRootLogin = "no"; + + # nix enables pam by default + # UsePAM = false; + + # challenge-response authentication backend it not configured by default + # therefore, it is set to "no" by default to avoid the use of an unconfigured backend + ChallengeResponseAuthentication = false; + + # set maximum authentication retries to prevent brute force attacks + MaxAuthTries = 3; + + # disallow connecting using empty passwords + PermitEmptyPasswords = false; + + ########## Cryptography ########## + + # explicitly define cryptography algorithms to avoid the use of weak algorithms + # AES CTR modes have been removed to mitigate the Terrapin attack + # https://terrapin-attack.com/ + + Ciphers = [ + "aes256-gcm@openssh.com" + "aes128-gcm@openssh.com" + ]; + Macs = [ + "hmac-sha2-256-etm@openssh.com" + "hmac-sha2-512-etm@openssh.com" + "umac-128-etm@openssh.com" + ]; + KexAlgorithms = [ + "sntrup761x25519-sha512@openssh.com" + "curve25519-sha256" + "curve25519-sha256@libssh.org" + "diffie-hellman-group16-sha512" + "diffie-hellman-group18-sha512" + ]; + + # hostKeyAlgorithms = [ + # "rsa-sha2-512" + # "rsa-sha2-256" + # "ssh-ed25519" + # ]; + + ########## Connection Preferences ########## + + # enforce SSH server to only use SSH protocol version 2 + # SSHv1 contains security issues and should be avoided at all costs + # SSHv1 is disabled by default after OpenSSH 7.0, but this option is + # specified anyways to ensure this configuration file's compatibility + # with older versions of OpenSSH server + Protocol = 2; + + # number of client alive messages sent without client responding + ClientAliveCountMax = 2; + + # send a keepalive message to the client when the session has been idle for 300 seconds + # this prevents/detects connection timeouts + ClientAliveInterval = 300; + + # compression before encryption might cause security issues + Compression = false; + + # prevent SSH trust relationships from allowing lateral movements + IgnoreRhosts = true; + + # log verbosely for addtional information + LogLevel = "VERBOSE"; + + # allow a maximum of two multiplexed sessions over a single TCP connection + MaxSessions = 2; + + # let ClientAliveInterval handle keepalive + TCPKeepAlive = false; + + # disable reverse DNS lookups + # UseDNS = false; + }; + extraConfig = '' + ########## Features ########## + + # accept locale-related environment variables + AcceptEnv LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES + AcceptEnv LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT + AcceptEnv LC_IDENTIFICATION LC_ALL LANGUAGE + AcceptEnv XMODIFIERS + + ########## Connection Preferences ########## + # disable reverse DNS lookups + UseDNS no + + ########## Disable GSS ########## + + GSSAPIAuthentication no + ''; + }; +} diff --git a/modules/nixos/ssh/ssh_client.nix b/modules/nixos/ssh/ssh_client.nix new file mode 100644 index 0000000..8baccf8 --- /dev/null +++ b/modules/nixos/ssh/ssh_client.nix @@ -0,0 +1,79 @@ +{ + ... +}: +{ + programs.ssh = { + # disable unnecessary forwardings + forwardX11 = false; + + # explicitly define cryptography algorithms to avoid the use of weak algorithms + # AES CTR modes have been removed to mitigate the Terrapin attack + # https://terrapin-attack.com/ + ciphers = [ + "aes256-gcm@openssh.com" + "aes128-gcm@openssh.com" + ]; + hostKeyAlgorithms = [ + "ssh-ed25519" + "ssh-ed25519-cert-v01@openssh.com" + "sk-ssh-ed25519@openssh.com" + "sk-ssh-ed25519-cert-v01@openssh.com" + "rsa-sha2-256" + "rsa-sha2-256-cert-v01@openssh.com" + "rsa-sha2-512" + "rsa-sha2-512-cert-v01@openssh.com" + ]; + macs = [ + "hmac-sha2-256-etm@openssh.com" + "hmac-sha2-512-etm@openssh.com" + "umac-128-etm@openssh.com" + ]; + kexAlgorithms = [ + "sntrup761x25519-sha512@openssh.com" + "curve25519-sha256" + "curve25519-sha256@libssh.org" + "diffie-hellman-group16-sha512" + "diffie-hellman-group18-sha512" + ]; + extraConfig = " + # disable unnecessary forwardings + ForwardAgent no + ForwardX11Trusted no + GatewayPorts no + Tunnel no + + # disable unnecessary authentication methods + ChallengeResponseAuthentication no + HostbasedAuthentication no + + # define authentication methods to be used + PasswordAuthentication yes + PubkeyAuthentication yes + PreferredAuthentications publickey,password + + # disable pre-connection compression as it could cause security issues + Compression no + + # in addition to checking a host's hostname, also check the host's IP address + # this provides extra safety against DNS spoofing attacks + CheckHostIP yes + + # ask the user if the user wants to accept the new host's host key + StrictHostKeyChecking ask + + # hash the entries in the known_hosts file to prevent disclosure + # of the file's content + HashKnownHosts yes + + # send a keepalive message to the server when the session has been idle for 60 seconds + # this prevents/detects connection timeouts + ServerAliveInterval 60 + + # increase the number of password retries + NumberOfPasswordPrompts 5 + + # display an ASCII art of the server's host key + VisualHostKey yes + "; + }; +} diff --git a/modules/nixos/ssh/ssh_guard.nix b/modules/nixos/ssh/ssh_guard.nix new file mode 100644 index 0000000..7c67345 --- /dev/null +++ b/modules/nixos/ssh/ssh_guard.nix @@ -0,0 +1,16 @@ +{ + config, + lib, + ... +}: { + services.sshguard = { + enable = true; + services = [ + "sshd" + ]; + blocktime = 120; + detection_time = 1800; + blacklist_threshold = 120; + blacklist_file = "/var/lib/sshguard/blacklist.db"; + }; +} diff --git a/modules/nixos/sysctl/file_system.nix b/modules/nixos/sysctl/file_system.nix new file mode 100644 index 0000000..2cc0586 --- /dev/null +++ b/modules/nixos/sysctl/file_system.nix @@ -0,0 +1,41 @@ +{ + config, + pkgs, + ... +}: { + boot.kernel.sysctl = { + # disallow core dumping by SUID/SGID programs + "fs.suid_dumpable" = 0; + + # protect the creation of hard links + # one of the following conditions must be fulfilled + # - the user can only link to files that he or she owns + # - the user must first have read and write access to a file, that he/she wants to link to + "fs.protected_hardlinks" = 1; + + # protect the creation of symbolic links + # one of the following conditions must be fulfilled + # - the process following the symbolic link is the owner of the symbolic link + # - the owner of the directory is also the owner of the symbolic link + "fs.protected_symlinks" = 1; + + # enable extended FIFO protection + "fs.protected_fifos" = 2; + + # similar to protected_fifos, but it avoids writes to an attacker-controlled regular file + "fs.protected_regular" = 2; + + # increase system file descriptor limit + # this value can be up to: + # - 2147483647 (0x7fffffff) on a 32-bit system + # - 9223372036854775807 (0x7fffffffffffffff) on a 64-bit system + # be aware that the Linux kernel documentation suggests that inode-max should be 3-4 times + # larger than this value + "fs.file-max" = 9223372036854775807; + + # increase the amount of files that can be watched + # each file watch handle takes 1080 bytes + # up to 540 MiB of memory will be consumed if all 524288 handles are used + "fs.inotify.max_user_watches" = 524288; + }; +} diff --git a/modules/nixos/sysctl/kernel.nix b/modules/nixos/sysctl/kernel.nix new file mode 100644 index 0000000..960c1d1 --- /dev/null +++ b/modules/nixos/sysctl/kernel.nix @@ -0,0 +1,70 @@ +{ + config, + pkgs, + ... +}: +{ + boot.kernel.sysctl = { + # enable ExecShield protection + # 2 enables ExecShield by default unless applications bits are set to disabled + # uncomment on systems without NX/XD protections + # check with: dmesg | grep --color '[NX|DX]*protection' + #kernel.exec-shield = 2 + + # enable ASLR + # turn on protection and randomize stack, vdso page and mmap + randomize brk base address + "kernel.randomize_va_space" = 2; + + # controls the System Request debugging functionality of the kernel + "kernel.sysrq" = 0; + + # controls whether core dumps will append the PID to the core filename + # useful for debugging multi-threaded applications + "kernel.core_uses_pid" = 1; + + # restrict access to kernel address + # kernel pointers printed using %pK will be replaced with 0’s regardless of privileges + "kernel.kptr_restrict" = 2; + + # Ptrace protection using Yama + # - 1: only a parent process can be debugged + # - 2: only admins can use ptrace (CAP_SYS_PTRACE capability required) + # - 3: disables ptrace completely, reboot is required to re-enable ptrace + # If you need ptrace to work, then avoid non-ancestor ptrace access to running processes and their credentials, and use value "1". + # # breaks debuggers + # "kernel.yama.ptrace_scope" = 3; + + # restrict kernel logs to root only + "kernel.dmesg_restrict" = 1; + + # restrict BPF JIT compiler to root only + "kernel.unprivileged_bpf_disabled" = 1; + + # disables kexec as it can be used to livepatch the running kernel + "kernel.kexec_load_disabled" = 1; + + # disable the loading of kernel modules + # this can be used to prevent runtime insertion of malicious modules + # could break the system if enabled within sysctl.conf + # consider setting this manually after system is up + # sudo sysctl -w kernel.modules_disabled=1 + #kernel.modules_disabled = 1 + + # allow for more PIDs + # this value can be up to: + # - 32768 (2^15) on a 32-bit system + # - 4194304 (2^22) on a 64-bit system + "kernel.pid_max" = 4194304; + + # reboot machine after kernel panic + #kernel.panic = 10 + + # restrict perf subsystem usage + "kernel.perf_event_paranoid" = 3; + "kernel.perf_cpu_time_max_percent" = 1; + "kernel.perf_event_max_sample_rate" = 1; + + # prevent unprivileged attackers from loading vulnerable line disciplines with the TIOCSETD ioctl + "dev.tty.ldisc_autoload" = 0; + }; +} diff --git a/modules/nixos/sysctl/network.nix b/modules/nixos/sysctl/network.nix new file mode 100644 index 0000000..1f13683 --- /dev/null +++ b/modules/nixos/sysctl/network.nix @@ -0,0 +1,8 @@ +{ + config, + pkgs, + ... +}: { + boot.kernel.sysctl = { + }; +} diff --git a/modules/nixos/sysctl/networking/default.nix b/modules/nixos/sysctl/networking/default.nix new file mode 100644 index 0000000..edb4ce2 --- /dev/null +++ b/modules/nixos/sysctl/networking/default.nix @@ -0,0 +1,7 @@ +{ + imports = [ + ./ipv4.nix + ./ipv6.nix + ./net.nix + ]; +} diff --git a/modules/nixos/sysctl/networking/ipv4.nix b/modules/nixos/sysctl/networking/ipv4.nix new file mode 100644 index 0000000..7f50248 --- /dev/null +++ b/modules/nixos/sysctl/networking/ipv4.nix @@ -0,0 +1,114 @@ +{ + config, + pkgs, + ... +}: { + boot.kernel.sysctl = { + # enable BBR congestion control + "net.ipv4.tcp_congestion_control" = "bbr"; + + # disallow IPv4 packet forwarding + "net.ipv4.ip_forward" = 0; + + # enable SYN cookies for SYN flooding protection + "net.ipv4.tcp_syncookies" = 1; + + # number of times SYNACKs for a passive TCP connection attempt will be retransmitted + "net.ipv4.tcp_synack_retries" = 5; + + # do not send redirects + "net.ipv4.conf.default.send_redirects" = 0; + "net.ipv4.conf.all.send_redirects" = 0; + + # do not accept packets with SRR option + "net.ipv4.conf.default.accept_source_route" = 0; + "net.ipv4.conf.all.accept_source_route" = 0; + + # enable reverse path source validation (BCP38) + # refer to RFC1812, RFC2827, and BCP38 (http://www.bcp38.info) + "net.ipv4.conf.default.rp_filter" = 1; + "net.ipv4.conf.all.rp_filter" = 1; + + # log packets with impossible addresses to kernel log + "net.ipv4.conf.default.log_martians" = 1; + "net.ipv4.conf.all.log_martians" = 1; + + # do not accept ICMP redirect messages + "net.ipv4.conf.default.accept_redirects" = 0; + "net.ipv4.conf.default.secure_redirects" = 0; + "net.ipv4.conf.all.accept_redirects" = 0; + "net.ipv4.conf.all.secure_redirects" = 0; + + # disable sending and receiving of shared media redirects + # this setting overwrites net.ipv4.conf.all.secure_redirects + # refer to RFC1620 + "net.ipv4.conf.default.shared_media" = 0; + "net.ipv4.conf.all.shared_media" = 0; + + # always use the best local address for announcing local IP via ARP + "net.ipv4.conf.default.arp_announce" = 2; + "net.ipv4.conf.all.arp_announce" = 2; + + # reply only if the target IP address is local address configured on the incoming interface + "net.ipv4.conf.default.arp_ignore" = 1; + "net.ipv4.conf.all.arp_ignore" = 1; + + # drop Gratuitous ARP frames to prevent ARP poisoning + # this can cause issues when ARP proxies are used in the network + "net.ipv4.conf.default.drop_gratuitous_arp" = 1; + "net.ipv4.conf.all.drop_gratuitous_arp" = 1; + + # ignore all ICMP echo requests + #net.ipv4.icmp_echo_ignore_all = 1 + + # ignore all ICMP echo and timestamp requests sent to broadcast/multicast + "net.ipv4.icmp_echo_ignore_broadcasts" = 1; + + # ignore bad ICMP errors + "net.ipv4.icmp_ignore_bogus_error_responses" = 1; + + # mitigate TIME-WAIT Assassination hazards in TCP + # refer to RFC1337 + "net.ipv4.tcp_rfc1337" = 1; + + # disable TCP window scaling + # this makes the host less susceptible to TCP RST DoS attacks + # could drastically reduce throughput if latency is high + #net.ipv4.tcp_window_scaling = 0 + + # increase system IP port limits + "net.ipv4.ip_local_port_range" = "1024 65535"; + + # TCP timestamps could provide protection against wrapped sequence numbers, + # but the host's uptime can be calculated precisely from its timestamps + # it is also possible to differentiate operating systems based on their use of timestamps + # - 0: disable TCP timestamps + # - 1: enable timestamps as defined in RFC1323 and use random offset for + # each connection rather than only using the current time + # - 2: enable timestamps without random offsets + "net.ipv4.tcp_timestamps" = 0; + + # enabling SACK can increase the throughput + # but SACK is commonly exploited and rarely used + "net.ipv4.tcp_sack" = 0; + "net.ipv4.tcp_dsack" = 0; + "net.ipv4.tcp_fack" = 0; + + # divide socket buffer evenly between TCP window size and application + "net.ipv4.tcp_adv_win_scale" = 1; + + # SSR could impact TCP's performance on a fixed-speed network (e.g., wired) + # but it could be helpful on a variable-speed network (e.g., LTE) + # uncomment this if you are on a fixed-speed network + "net.ipv4.tcp_slow_start_after_idle" = 0; + + # enabling MTU probing helps mitigating PMTU blackhole issues + # this may not be desirable on congested networks + "net.ipv4.tcp_mtu_probing" = 1; + "net.ipv4.tcp_base_mss" = 1024; + + # increase memory thresholds to prevent packet dropping + "net.ipv4.tcp_rmem" = "4096 87380 8388608"; + "net.ipv4.tcp_wmem" = "4096 87380 8388608"; + }; +} diff --git a/modules/nixos/sysctl/networking/ipv6.nix b/modules/nixos/sysctl/networking/ipv6.nix new file mode 100644 index 0000000..693f774 --- /dev/null +++ b/modules/nixos/sysctl/networking/ipv6.nix @@ -0,0 +1,56 @@ +{ + config, + pkgs, + ... +}: { + boot.kernel.sysctl = { + # disallow IPv6 packet forwarding + "net.ipv6.conf.default.forwarding" = 0; + "net.ipv6.conf.all.forwarding" = 0; + + # number of Router Solicitations to send until assuming no routers are present + "net.ipv6.conf.default.router_solicitations" = 0; + "net.ipv6.conf.all.router_solicitations" = 0; + + # do not accept Router Preference from RA + "net.ipv6.conf.default.accept_ra_rtr_pref" = 0; + "net.ipv6.conf.all.accept_ra_rtr_pref" = 0; + + # learn prefix information in router advertisement + "net.ipv6.conf.default.accept_ra_pinfo" = 0; + "net.ipv6.conf.all.accept_ra_pinfo" = 0; + + # setting controls whether the system will accept Hop Limit settings from a router advertisement + "net.ipv6.conf.default.accept_ra_defrtr" = 0; + "net.ipv6.conf.all.accept_ra_defrtr" = 0; + + # router advertisements can cause the system to assign a global unicast address to an interface + "net.ipv6.conf.default.autoconf" = 0; + "net.ipv6.conf.all.autoconf" = 0; + + # number of neighbor solicitations to send out per address + "net.ipv6.conf.default.dad_transmits" = 0; + "net.ipv6.conf.all.dad_transmits" = 0; + + # number of global unicast IPv6 addresses can be assigned to each interface + "net.ipv6.conf.default.max_addresses" = 1; + "net.ipv6.conf.all.max_addresses" = 1; + + # enable IPv6 Privacy Extensions (RFC30;41) and prefer the temporary address + #"net.ipv6.conf.default.use_tempaddr" = 2; # Nix sets by default + "net.ipv6.conf.all.use_tempaddr" = 2; + + # ignore IPv6 ICMP redirect messages + "net.ipv6.conf.default.accept_redirects" = 0; + "net.ipv6.conf.all.accept_redirects" = 0; + + # do not accept packets with SRR option + "net.ipv6.conf.default.accept_source_route" = 0; + "net.ipv6.conf.all.accept_source_route" = 0; + + # ignore all ICMPv6 echo requests + #net.ipv6.icmp.echo_ignore_all = 1 + #net.ipv6.icmp.echo_ignore_anycast = 1 + #net.ipv6.icmp.echo_ignore_multicast = 1 + }; +} diff --git a/modules/nixos/sysctl/networking/net.nix b/modules/nixos/sysctl/networking/net.nix new file mode 100644 index 0000000..b7ce139 --- /dev/null +++ b/modules/nixos/sysctl/networking/net.nix @@ -0,0 +1,17 @@ +_: { + boot.kernel.sysctl = { + # increase the maximum length of processor input queues + "net.core.netdev_max_backlog" = 250000; + + # enable BPF JIT hardening for all users + # this trades off performance, but can mitigate JIT spraying + "net.core.bpf_jit_harden" = 2; + + # increase TCP max buffer size setable using setsockopt() + "net.core.rmem_max" = 8388608; + "net.core.wmem_max" = 8388608; + "net.core.rmem_default" = 8388608; + "net.core.wmem_default" = 8388608; + #net.core.optmem_max = 40960 + }; +} diff --git a/modules/nixos/sysctl/virtualization.nix b/modules/nixos/sysctl/virtualization.nix new file mode 100644 index 0000000..073ca9b --- /dev/null +++ b/modules/nixos/sysctl/virtualization.nix @@ -0,0 +1,18 @@ +{ + config, + pkgs, + ... +}: { + boot.kernel.sysctl = { + # do not allow mmap in lower addresses + "vm.mmap_min_addr" = 65536; + + # improve mmap ASLR effectiveness + "vm.mmap_rnd_bits" = 32; + "vm.mmap_rnd_compat_bits" = 16; + + # prevent unprivileged users from accessing userfaultfd + # restricts syscall to the privileged users or the CAP_SYS_PTRACE capability + "vm.unprivileged_userfaultfd" = 0; + }; +} diff --git a/modules/top/classes/home-manager.nix b/modules/top/classes/home-manager.nix new file mode 100644 index 0000000..006c31a --- /dev/null +++ b/modules/top/classes/home-manager.nix @@ -0,0 +1,112 @@ +{ + lib, + inputs, + config, + ... +}: +let + inherit (lib) + concatMapAttrs + mapAttrs' + nameValuePair + flatten + unique + ; + + hmLib = inputs.home-manager.lib; + allRoles = config.dendritic.roles; + allFeatures = config.dendritic.features; + + resolveRoles = + let + go = + seen: roleName: + if builtins.elem roleName seen then + throw "Circular dendritic role reference detected: ${ + builtins.concatStringsSep " -> " (seen ++ [ roleName ]) + }" + else + let + role = allRoles.${roleName} or null; + in + if role == null then + throw "Unknown dendritic role: ${roleName}" + else + [ roleName ] ++ flatten (map (go (seen ++ [ roleName ])) role.roles); + in + roleNames: unique (flatten (map (go [ ]) roleNames)); + + resolveFeatures = + let + go = + seen: featureName: + if builtins.elem featureName seen then + throw "Circular dendritic feature reference detected: ${ + builtins.concatStringsSep " -> " (seen ++ [ featureName ]) + }" + else + let + feature = allFeatures.${featureName} or null; + in + if feature == null then + throw "Unknown dendritic feature: ${featureName}" + else + [ featureName ] ++ flatten (map (go (seen ++ [ featureName ])) feature.features); + in + featureNames: unique (flatten (map (go [ ]) featureNames)); + + mkHomeConfigurations = concatMapAttrs ( + username: user: + mapAttrs' ( + hostName: home: + let + resolvedRoleNames = resolveRoles home.roles; + roleFeatureNames = flatten (map (roleName: allRoles.${roleName}.features) resolvedRoleNames); + resolvedFeatureNames = resolveFeatures roleFeatureNames; + + featureHomeModules = flatten ( + map (featureName: allFeatures.${featureName}.homeModules) resolvedFeatureNames + ); + in + nameValuePair "${username}@${hostName}" ( + hmLib.homeManagerConfiguration { + pkgs = import inputs.nixpkgs { + system = home.system; + }; + + modules = [ + ../inventory/current.nix + ../inventory/features.nix + ../inventory/hosts.nix + ../inventory/users.nix + ../../aspects/roles.nix + + { + dendritic.current = { + inherit hostName; + primaryUser = user.username; + }; + } + ] + ++ user.homeModules + ++ featureHomeModules + ++ home.extraModules + ++ [ + { + home.username = user.username; + home.homeDirectory = "/home/${user.username}"; + home.stateVersion = home.stateVersion; + } + ]; + + extraSpecialArgs = { + inherit inputs; + }; + } + ) + ) user.homes + ) config.dendritic.users; +in +{ + config.flake.homeConfigurations = mkHomeConfigurations; +} diff --git a/modules/top/classes/nixos.nix b/modules/top/classes/nixos.nix new file mode 100644 index 0000000..55d7115 --- /dev/null +++ b/modules/top/classes/nixos.nix @@ -0,0 +1,162 @@ +{ + lib, + inputs, + config, + ... +}: +let + inherit (lib) + mapAttrs + flatten + unique + optionals + optional + ; + + allRoles = config.dendritic.roles; + allFeatures = config.dendritic.features; + + resolveRoles = + let + go = + seen: roleName: + if builtins.elem roleName seen then + throw "Circular dendritic role reference detected: ${ + builtins.concatStringsSep " -> " (seen ++ [ roleName ]) + }" + else + let + role = allRoles.${roleName} or null; + in + if role == null then + throw "Unknown dendritic role: ${roleName}" + else + [ roleName ] ++ flatten (map (go (seen ++ [ roleName ])) role.roles); + in + roleNames: unique (flatten (map (go [ ]) roleNames)); + + resolveFeatures = + let + go = + seen: featureName: + if builtins.elem featureName seen then + throw "Circular dendritic feature reference detected: ${ + builtins.concatStringsSep " -> " (seen ++ [ featureName ]) + }" + else + let + feature = allFeatures.${featureName} or null; + in + if feature == null then + throw "Unknown dendritic feature: ${featureName}" + else + [ featureName ] ++ flatten (map (go (seen ++ [ featureName ])) feature.features); + in + featureNames: unique (flatten (map (go [ ]) featureNames)); + + mkHost = + hostName: host: + let + username = if host.primaryUser == null then null else host.primaryUser; + + user = if username == null then null else config.dendritic.users.${username}; + + userHome = if user == null then null else user.homes.${hostName} or null; + + resolvedRoleNames = resolveRoles host.roles; + roleFeatureNames = flatten (map (roleName: allRoles.${roleName}.features) resolvedRoleNames); + resolvedFeatureNames = resolveFeatures roleFeatureNames; + + featureNixosModules = flatten ( + map (featureName: allFeatures.${featureName}.nixosModules) resolvedFeatureNames + ); + + featureHomeModules = + if userHome == null then + [ ] + else + let + resolvedHomeRoleNames = resolveRoles userHome.roles; + homeRoleFeatureNames = flatten ( + map (roleName: allRoles.${roleName}.features) resolvedHomeRoleNames + ); + resolvedHomeFeatureNames = resolveFeatures homeRoleFeatureNames; + embeddedHomeFeatureNames = builtins.filter ( + featureName: allFeatures.${featureName}.includeInEmbeddedHomeManager + ) resolvedHomeFeatureNames; + in + flatten (map (featureName: allFeatures.${featureName}.homeModules) embeddedHomeFeatureNames); + in + inputs.nixpkgs.lib.nixosSystem { + system = host.system; + + specialArgs = { + inherit inputs; + }; + + modules = [ + ../inventory/current.nix + ../inventory/features.nix + ../inventory/hosts.nix + ../inventory/users.nix + ../../aspects/roles.nix + + { + dendritic.current = { + inherit hostName; + primaryUser = username; + }; + + networking.hostName = hostName; + } + ] + ++ host.nixosModules + ++ featureNixosModules + ++ optional (username != null) { + users.users.${username} = { + isNormalUser = true; + extraGroups = host.primaryUserExtraGroups; + }; + } + ++ optionals (user != null) user.nixosModules + ++ optional (userHome != null) inputs.home-manager.nixosModules.home-manager + ++ optional (userHome != null) { + home-manager.useGlobalPkgs = true; + home-manager.useUserPackages = true; + + home-manager.extraSpecialArgs = { + inherit inputs; + }; + + home-manager.sharedModules = [ + ../inventory/current.nix + ../inventory/features.nix + ../inventory/hosts.nix + ../inventory/users.nix + ../../aspects/roles.nix + ]; + + home-manager.users.${username}.imports = [ + { + dendritic.current = { + inherit hostName; + primaryUser = username; + }; + } + ] + ++ user.homeModules + ++ featureHomeModules + ++ userHome.extraModules + ++ [ + { + home.username = user.username; + home.homeDirectory = "/home/${user.username}"; + home.stateVersion = userHome.stateVersion; + } + ]; + }; + }; +in +{ + config.flake.nixosConfigurations = mapAttrs mkHost config.dendritic.hosts; +} diff --git a/modules/top/inventory/current.nix b/modules/top/inventory/current.nix new file mode 100644 index 0000000..bafbbaa --- /dev/null +++ b/modules/top/inventory/current.nix @@ -0,0 +1,12 @@ +{ lib, ... }: +{ + options.dendritic.current = { + hostName = lib.mkOption { + type = lib.types.str; + }; + + primaryUser = lib.mkOption { + type = lib.types.str; + }; + }; +} diff --git a/modules/top/inventory/features.nix b/modules/top/inventory/features.nix new file mode 100644 index 0000000..c3d531b --- /dev/null +++ b/modules/top/inventory/features.nix @@ -0,0 +1,36 @@ +{ lib, ... }: +{ + options.dendritic.features = lib.mkOption { + type = lib.types.attrsOf ( + lib.types.submodule ({ name, ... }: { + options = { + name = lib.mkOption { + type = lib.types.str; + default = name; + }; + + features = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + }; + + nixosModules = lib.mkOption { + type = lib.types.listOf lib.types.deferredModule; + default = [ ]; + }; + + homeModules = lib.mkOption { + type = lib.types.listOf lib.types.deferredModule; + default = [ ]; + }; + + includeInEmbeddedHomeManager = lib.mkOption { + type = lib.types.bool; + default = true; + }; + }; + }) + ); + default = { }; + }; +} diff --git a/modules/top/inventory/hosts.nix b/modules/top/inventory/hosts.nix new file mode 100644 index 0000000..5f5a63b --- /dev/null +++ b/modules/top/inventory/hosts.nix @@ -0,0 +1,43 @@ +{lib, ...}: { + options.dendritic.hosts = lib.mkOption { + type = lib.types.attrsOf ( + lib.types.submodule ({name, ...}: { + options = { + name = lib.mkOption { + type = lib.types.str; + default = name; + }; + + system = lib.mkOption { + type = lib.types.str; + }; + + type = lib.mkOption { + type = lib.types.enum ["laptop" "desktop" "server"]; + }; + + roles = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = []; + }; + + primaryUser = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + }; + + primaryUserExtraGroups = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = ["wheel" "networkmanager"]; + }; + + nixosModules = lib.mkOption { + type = lib.types.listOf lib.types.deferredModule; + default = []; + }; + }; + }) + ); + default = {}; + }; +} diff --git a/modules/top/inventory/users.nix b/modules/top/inventory/users.nix new file mode 100644 index 0000000..5689820 --- /dev/null +++ b/modules/top/inventory/users.nix @@ -0,0 +1,63 @@ +{lib, ...}: { + options.dendritic.users = lib.mkOption { + type = lib.types.attrsOf ( + lib.types.submodule ({name, ...}: { + options = { + name = lib.mkOption { + type = lib.types.str; + default = name; + }; + + username = lib.mkOption { + type = lib.types.str; + default = name; + }; + + nixosModules = lib.mkOption { + type = lib.types.listOf lib.types.deferredModule; + default = []; + }; + + homeModules = lib.mkOption { + type = lib.types.listOf lib.types.deferredModule; + default = []; + }; + + homes = lib.mkOption { + type = lib.types.attrsOf ( + lib.types.submodule ({name, ...}: { + options = { + host = lib.mkOption { + type = lib.types.str; + default = name; + }; + + system = lib.mkOption { + type = lib.types.str; + }; + + stateVersion = lib.mkOption { + type = lib.types.str; + default = "25.05"; + }; + + roles = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = []; + }; + + extraModules = lib.mkOption { + type = lib.types.listOf lib.types.deferredModule; + default = []; + }; + }; + }) + ); + default = {}; + }; + }; + }) + ); + default = {}; + }; +} diff --git a/modules/users/gwg313.nix b/modules/users/gwg313.nix new file mode 100644 index 0000000..1f626f6 --- /dev/null +++ b/modules/users/gwg313.nix @@ -0,0 +1,21 @@ +{ + config.dendritic.users.gwg313 = { + username = "gwg313"; + + nixosModules = [ + ../nixos/hosts/candlekeep/syncthing.nix + ]; + + homeModules = [ + ../nixos/hosts/candlekeep/git.nix + ../nixos/hosts/candlekeep/ssh-hosts.nix + ]; + + homes.candlekeep = { + system = "x86_64-linux"; + stateVersion = "25.11"; + roles = [ "workstation" ]; + extraModules = [ ]; + }; + }; +} diff --git a/nc.zip b/nc.zip new file mode 100644 index 0000000..652c94e Binary files /dev/null and b/nc.zip differ diff --git a/secrets/.sops.yaml b/secrets/.sops.yaml new file mode 100644 index 0000000..8376684 --- /dev/null +++ b/secrets/.sops.yaml @@ -0,0 +1,4 @@ +creation_rules: + - path_regex: .*\.yaml$ + age: age1k3hs0gyzrmsdyqh9lpret46q3xaayxxntruzc4euy6h3slqn4u6q36h7rg + diff --git a/secrets/hosts/candlekeep.yaml b/secrets/hosts/candlekeep.yaml new file mode 100644 index 0000000..d01e47a --- /dev/null +++ b/secrets/hosts/candlekeep.yaml @@ -0,0 +1,17 @@ +system: + example: ENC[AES256_GCM,data:HGduc8uq6YhzDBM=,iv:IDdNjIjWAhTEzHiGrsuSpHSjidpeFnGdzkiBCjBv5H4=,tag:pm2IGSy3siDAhn2E7lMUhA==,type:str] +sops: + age: + - recipient: age1k3hs0gyzrmsdyqh9lpret46q3xaayxxntruzc4euy6h3slqn4u6q36h7rg + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA0R1VYMmEvdWk5UzBQLzds + Q2JzSC9zZFJVWldHbTlkdTIrSXNFLzZqdjBFClJqZmxTSCtjeGwxNjA3VFVscEtP + NEhOcU9la2MzNWNEK1NwU0dNTHlPNFUKLS0tIHVWVlRkRHlwb21IemRFS3FTT1kz + U21XTEVjNWgzVHVYQ2dDQmIrV21EdGMKw14LaWlK9WbBXxnNvKfNgg44K9/Y7p5H + +3QeahQRu8OYn/tFyaMiRxIzLWOhBhtdqAH8k2GN2X5TxzGA1vxGXg== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2026-04-14T00:22:17Z" + mac: ENC[AES256_GCM,data:bPSDTqcfnnUcj80y+9qUfWkX9NcBWdQETMC3qyZYB3FWrJryepWn4bMUEb5IBfwcZXiKWmvyOTXjFYEkx4F4YGZA0qz3usuq6EjeZDSFrpf9Itr9wdc400mc7cf5YFtNOkf+BuE3nsYQDj1KViBKReEgMjZe9bHjvmi1f+utvjM=,iv:DcvjgVAUTily7Xm1+3NCA7/P3+qE05WlQkkqKggm27g=,tag:47TIh6ybU4Cu8QybMxuz/g==,type:str] + unencrypted_suffix: _unencrypted + version: 3.12.2 diff --git a/secrets/shared/common.yaml b/secrets/shared/common.yaml new file mode 100644 index 0000000..27f7c3b --- /dev/null +++ b/secrets/shared/common.yaml @@ -0,0 +1,19 @@ +test: + hello: ENC[AES256_GCM,data:OE7OJN0=,iv:cyja5KnVeqf8Me5yUvpmqo39Nb9tdiqF8qA/+u2oMnk=,tag:kDkam2Y4YmtLjy0yBQU3Zg==,type:str] +user: + test: ENC[AES256_GCM,data:j8I1OFmPAMLrkCZ68cpMLWDLZPfqshA=,iv:S3FvhSwhtXLUr37i0+GKhjjZk1Yv9xg7PpA1uMQHOBM=,tag:eYdylOy2NBr3CVpLG6P9NQ==,type:str] +sops: + age: + - recipient: age1k3hs0gyzrmsdyqh9lpret46q3xaayxxntruzc4euy6h3slqn4u6q36h7rg + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBFYW5Ma0I2MU1YTFhhWElW + Y0JhNnRqZUpiZkZrQUlFYU1QR1o0TFNDMGxvCjM3TEQyVlhra3VnU040NTkrU0Uz + SWc4UVlxUDNqeG9ValowdlpQZ0dJaTQKLS0tIEJhWUx3WWEvYUYzTUlKU1UyVW5w + U3VVVmxqUFVhQytCclJsVHJ3Y1VWUHcKooy3sWL5twR46DL6BGNvgLlMUNSqEjqQ + 6/C1vEt8LswwHzAFB3v14mepzwh6vXGuutNmmdLW6Am2jlC1XmMO4A== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2026-03-28T19:30:01Z" + mac: ENC[AES256_GCM,data:Z6PZxr3Pbsq6ayjILM5xaofXIDsdEsgwgedb/xylmfS6iXEtb4fSXQyisF0XUvsOn+dVrPfDHOrqrXcn7bDbrFfQFiLQRYURVOX35f06Bm12YVsGdN0smzhXNXgEOe0wBcxOF8j02XOHZ1NvD9iZresL/T1U/Y5QkM5KcSJRiW8=,iv:vbQOAXWZec1eNkECnINm/Tqz3OGR8M8XhLZApGisF7I=,tag:mho7pJ8n1LlCMuHybDzrNw==,type:str] + unencrypted_suffix: _unencrypted + version: 3.11.0 diff --git a/secrets/users/gwg313.yaml b/secrets/users/gwg313.yaml new file mode 100644 index 0000000..2542d04 --- /dev/null +++ b/secrets/users/gwg313.yaml @@ -0,0 +1,18 @@ +user: + test: ENC[AES256_GCM,data:2jULskFjSU3ifFdluLtHk3VwWdgQSQ==,iv:2PX6eRzufWTECuI6NuSNI0vPeL1ushUyIFAytVElPq4=,tag:azR2Xibosy5jliEXx2MnBQ==,type:str] +wireless.env: ENC[AES256_GCM,data:o1bjN4RMO23NTwezfaU5aRDA5HwbQKfa8sUZWRw2mJdwEF9UvQ+RFHRbPLW7srljyTw3Wk3MLG6/Q/Kq8S3inNBLy3dZ82DMKnMDir2RBsov5cLjLEvhzvMtDQkx40CKB6+3qee9ofAaZTzEMlzolk4/9OhbseA8NIQdBsEJYS7NqgLTO12Wxjm2Y9gNfJblGlhtsECwsd4vYOSYu8XIpe/d7IV7y8jkuuHzmsvSBZod10wjl1FasL3wOarkA591N8oiva/1,iv:JVkHQ6I+6+QiuIYFTtj0mAL+kS+74PNDGevmhPOhkds=,tag:WwCmdWngSYsAwHnhODU5Qg==,type:str] +sops: + age: + - recipient: age1k3hs0gyzrmsdyqh9lpret46q3xaayxxntruzc4euy6h3slqn4u6q36h7rg + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA0aEZnSUV4a3Zyc2hjNmJS + dzVXR0ErOWJMcWhkU09oakt4ZlBpdGgrOEZBCmQ1c3NVczczRmwzVzFabCtFODRi + TDNxUEN3ejFPdHBFVXRvQlFuWkpma3cKLS0tIGgwTFNyTmUyZjlWWTdpcldIYWlJ + eDdDdTVYeko4SndGWTBkTWlYVlRyL3cKqvwC+v3uYRmT43ncQVWkihz3kU0QY5O0 + fq265FkM97qySeakPYghx9yctL5tMeQlzLo543SUduaTJuj8PYzf4A== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2026-04-13T23:03:20Z" + mac: ENC[AES256_GCM,data:S1jAUcxo8Q2YwNCq/QcSO8g6qfHLx1F3QHjrcFfqXHVZn3yzau6tK7a2L5fBZaJBx5rW3WP+IHHUbmhQt1bof6XF8ZzKoTj7tb3Rpxb2i7T65pEjCyJdernoXpejpUVQ2ZADW1UaJ8t3HpV6KmbmneI9DNfoR+FGTazv85eRZTY=,iv:kc5r6pjpZJsM3dA1UgAXOOM15e66FlyunUEz9iXZE98=,tag:wv24Q6Dbi+Cd7qtTQPSrtA==,type:str] + unencrypted_suffix: _unencrypted + version: 3.12.2