Personal Nix setup
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'ramune'

+1894 -867
+58 -28
flake.lock
··· 27 27 "type": "github" 28 28 } 29 29 }, 30 + "apple-silicon": { 31 + "inputs": { 32 + "flake-compat": "flake-compat", 33 + "nixpkgs": [ 34 + "nixpkgs" 35 + ], 36 + "rust-overlay": "rust-overlay" 37 + }, 38 + "locked": { 39 + "lastModified": 1725418254, 40 + "narHash": "sha256-2zPzPP9Eu5NxgJxTVcuCCX5xh7CWy7rYaLHfaAZS6H8=", 41 + "owner": "tpwrules", 42 + "repo": "nixos-apple-silicon", 43 + "rev": "c5f944f49a052232015bb3c03524b69e3fdd2aa4", 44 + "type": "github" 45 + }, 46 + "original": { 47 + "owner": "tpwrules", 48 + "repo": "nixos-apple-silicon", 49 + "type": "github" 50 + } 51 + }, 30 52 "autocomplete-nvim": { 31 53 "flake": false, 32 54 "locked": { ··· 102 124 } 103 125 }, 104 126 "flake-compat": { 127 + "locked": { 128 + "lastModified": 1688025799, 129 + "narHash": "sha256-ktpB4dRtnksm9F5WawoIkEneh1nrEvuxb5lJFt1iOyw=", 130 + "owner": "nix-community", 131 + "repo": "flake-compat", 132 + "rev": "8bf105319d44f6b9f0d764efa4fdef9f1cc9ba1c", 133 + "type": "github" 134 + }, 135 + "original": { 136 + "owner": "nix-community", 137 + "repo": "flake-compat", 138 + "type": "github" 139 + } 140 + }, 141 + "flake-compat_2": { 105 142 "flake": false, 106 143 "locked": { 107 144 "lastModified": 1696426674, ··· 292 329 "lanzaboote": { 293 330 "inputs": { 294 331 "crane": "crane", 295 - "flake-compat": "flake-compat", 332 + "flake-compat": "flake-compat_2", 296 333 "flake-parts": "flake-parts", 297 334 "nixpkgs": [ 298 335 "nixpkgs" 299 336 ], 300 337 "pre-commit-hooks-nix": "pre-commit-hooks-nix", 301 - "rust-overlay": "rust-overlay" 338 + "rust-overlay": "rust-overlay_2" 302 339 }, 303 340 "locked": { 304 341 "lastModified": 1725379389, ··· 716 753 "root": { 717 754 "inputs": { 718 755 "agenix": "agenix", 756 + "apple-silicon": "apple-silicon", 719 757 "darwin": "darwin", 720 758 "flake-utils": "flake-utils", 721 759 "home-manager": "home-manager", ··· 724 762 "nixos-hardware": "nixos-hardware", 725 763 "nixpkgs": "nixpkgs", 726 764 "nvim-plugins": "nvim-plugins", 727 - "verdaccio": "verdaccio", 728 765 "wezterm": "wezterm" 729 766 } 730 767 }, 731 768 "rust-overlay": { 769 + "flake": false, 770 + "locked": { 771 + "lastModified": 1686795910, 772 + "narHash": "sha256-jDa40qRZ0GRQtP9EMZdf+uCbvzuLnJglTUI2JoHfWDc=", 773 + "owner": "oxalica", 774 + "repo": "rust-overlay", 775 + "rev": "5c2b97c0a9bc5217fc3dfb1555aae0fb756d99f9", 776 + "type": "github" 777 + }, 778 + "original": { 779 + "owner": "oxalica", 780 + "repo": "rust-overlay", 781 + "type": "github" 782 + } 783 + }, 784 + "rust-overlay_2": { 732 785 "inputs": { 733 786 "nixpkgs": [ 734 787 "lanzaboote", ··· 749 802 "type": "github" 750 803 } 751 804 }, 752 - "rust-overlay_2": { 805 + "rust-overlay_3": { 753 806 "inputs": { 754 807 "nixpkgs": [ 755 808 "wezterm", ··· 849 902 "type": "github" 850 903 } 851 904 }, 852 - "verdaccio": { 853 - "inputs": { 854 - "flake-utils": [ 855 - "flake-utils" 856 - ], 857 - "nixpkgs": [ 858 - "nixpkgs" 859 - ] 860 - }, 861 - "locked": { 862 - "lastModified": 1716358108, 863 - "narHash": "sha256-z+3PZpjNoaMgoQzcAhxAKU7WQRnkG2MJAQqZQyp+60U=", 864 - "owner": "kitten", 865 - "repo": "verdaccio.nix", 866 - "rev": "950859707eb77b0d774d3b9185089b9779ef3801", 867 - "type": "github" 868 - }, 869 - "original": { 870 - "owner": "kitten", 871 - "repo": "verdaccio.nix", 872 - "type": "github" 873 - } 874 - }, 875 905 "vim-fugitive": { 876 906 "flake": false, 877 907 "locked": { ··· 931 961 "nixpkgs": [ 932 962 "nixpkgs" 933 963 ], 934 - "rust-overlay": "rust-overlay_2", 964 + "rust-overlay": "rust-overlay_3", 935 965 "zlib": "zlib" 936 966 }, 937 967 "locked": {
+16 -11
flake.nix
··· 7 7 nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; 8 8 nixos-hardware.url = "github:NixOS/nixos-hardware/master"; 9 9 10 + apple-silicon = { 11 + url = "github:tpwrules/nixos-apple-silicon"; 12 + inputs = { 13 + nixpkgs.follows = "nixpkgs"; 14 + }; 15 + }; 16 + 10 17 lanzaboote = { 11 18 url = "github:nix-community/lanzaboote"; 12 19 inputs = { ··· 43 50 }; 44 51 }; 45 52 46 - verdaccio = { 47 - url = "github:kitten/verdaccio.nix"; 48 - inputs = { 49 - nixpkgs.follows = "nixpkgs"; 50 - flake-utils.follows = "flake-utils"; 51 - }; 52 - }; 53 - 54 53 wezterm = { 55 54 url = "github:wez/wezterm/main?dir=nix"; 56 55 inputs = { ··· 68 67 }; 69 68 }; 70 69 71 - outputs = { nixos-hardware, ...} @ inputs: let 70 + outputs = { nixos-hardware, apple-silicon, ...} @ inputs: let 72 71 inherit (import ./lib/system.nix inputs) mkSystem; 73 72 overlays = [ 74 73 inputs.nvim-plugins.overlays.default 75 74 (self: super: { 76 - inherit (inputs.verdaccio.packages.${self.system}) 77 - verdaccio; 78 75 inherit (inputs.language-servers.packages.${self.system}) 79 76 typescript-language-server 80 77 vscode-langservers-extracted; ··· 104 101 inherit overlays; 105 102 system = "x86_64-linux"; 106 103 hostname = "cola"; 104 + }; 105 + 106 + nixosConfigurations."ramune" = mkSystem { 107 + overlays = overlays ++ [ 108 + apple-silicon.overlays.apple-silicon-overlay 109 + ]; 110 + system = "aarch64-linux"; 111 + hostname = "ramune"; 107 112 }; 108 113 }; 109 114 }
+320
lib/ipv4.nix
··· 1 + /* See: https://github.com/djacu/nixpkgs/blob/adb22cf/lib/network.nix */ 2 + { lib, ... }: 3 + 4 + let 5 + /* 6 + Converts an IP address from a list of ints to a string. 7 + 8 + Type: prettyIp :: [ Int ] -> String 9 + 10 + Examples: 11 + prettyIp [ 192 168 70 9 ] 12 + => "192.168.70.9" 13 + */ 14 + prettyIp = addr: 15 + lib.concatStringsSep "." (builtins.map builtins.toString addr); 16 + 17 + /* 18 + Given a bit mask, return the associated subnet mask. 19 + 20 + Type: bitMaskToSubnetMask :: Int -> [ Int ] 21 + 22 + Examples: 23 + bitMaskToSubnetMask 15 24 + => [ 255 254 0 0 ] 25 + bitMaskToSubnetMask 24 26 + => [ 255 255 255 0 ] 27 + */ 28 + bitMaskToSubnetMask = bitMask: let 29 + numOctets = 4; 30 + octetBits = 8; 31 + octetMin = 0; 32 + octetMax = 255; 33 + # How many initial parts of the mask are full (=255) 34 + fullParts = bitMask / octetBits; 35 + in 36 + lib.genList ( 37 + idx: 38 + # Fill up initial full parts 39 + if idx < fullParts 40 + then octetMax 41 + # If we're above the first non-full part, fill with 0 42 + else if fullParts < idx 43 + then octetMin 44 + # First non-full part generation 45 + else _genPartialMask (lib.mod bitMask octetBits) 46 + ) 47 + numOctets; 48 + 49 + /* 50 + Generate a the partial portion of a subnet mask. 51 + 52 + Type: _genPartialMask :: Int -> Int 53 + 54 + Examples: 55 + _genPartialMask 0 56 + => 0 57 + _genPartialMask 1 58 + => 128 59 + _genPartialMask 2 60 + => 192 61 + _genPartialMask 3 62 + => 224 63 + _genPartialMask 4 64 + => 240 65 + _genPartialMask 5 66 + => 248 67 + _genPartialMask 6 68 + => 252 69 + _genPartialMask 7 70 + => 254 71 + */ 72 + _genPartialMask = n: 73 + if n == 0 74 + then 0 75 + else _genPartialMask (n - 1) / 2 + 128; 76 + 77 + /* 78 + Given a subnet mask, return the associated bit mask. 79 + 80 + Type: subnetMaskToBitMask :: [ Int ] -> Int 81 + 82 + Examples: 83 + subnetMaskToBitMask [ 255 254 0 0 ] 84 + => 15 85 + subnetMaskToBitMask [ 255 255 255 0 ] 86 + => 24 87 + */ 88 + subnetMaskToBitMask = subnetMask: let 89 + partialBits = octet: 90 + if octet == 0 91 + then 0 92 + else (lib.mod octet 2) + partialBits (octet / 2); 93 + in 94 + builtins.foldl' 95 + (x: y: x + y) 96 + 0 97 + (builtins.map partialBits subnetMask); 98 + 99 + /* 100 + Given a CIDR, return the IP Address. 101 + 102 + Type: cidrToIpAddress :: String -> [ Int ] 103 + 104 + Examples: 105 + cidrToIpAddress "192.168.70.9/15" 106 + => [ 192 168 70 9 ] 107 + */ 108 + cidrToIpAddress = cidr: let 109 + splitParts = lib.splitString "/" cidr; 110 + addr = lib.elemAt splitParts 0; 111 + parsed = 112 + builtins.map 113 + lib.toInt 114 + (builtins.match "([0-9]+)\\.([0-9]+)\\.([0-9]+)\\.([0-9]+)" addr); 115 + checkBounds = octet: 116 + (octet >= 0) && (octet <= 255); 117 + in 118 + if (builtins.all checkBounds parsed) 119 + then parsed 120 + else builtins.throw "IP ${prettyIp addr} has out of bounds octet(s)"; 121 + 122 + /* 123 + Given a CIDR, return the bitmask. 124 + 125 + Type: cidrToBitMask :: String -> Int 126 + 127 + Examples: 128 + cidrToBitMask "192.168.70.9/15" 129 + => 15 130 + */ 131 + cidrToBitMask = cidr: let 132 + splitParts = lib.splitString "/" cidr; 133 + mask = lib.toInt (lib.elemAt splitParts 1); 134 + checkBounds = mask: 135 + (mask >= 0) && (mask <= 32); 136 + in 137 + if (checkBounds mask) 138 + then mask 139 + else builtins.throw "Bitmask ${builtins.toString mask} is invalid."; 140 + 141 + /* 142 + Given a CIDR, return the associated subnet mask. 143 + 144 + Type: cidrToSubnetMask :: String -> [ Int ] 145 + 146 + Examples: 147 + cidrToSubnetMask "192.168.70.9/15" 148 + => [ 255 254 0 0 ] 149 + */ 150 + cidrToSubnetMask = cidr: 151 + bitMaskToSubnetMask (cidrToBitMask cidr); 152 + 153 + /* 154 + Given a CIDR, return the associated network ID. 155 + 156 + Type: cidrToNetworkId :: String -> [ Int ] 157 + 158 + Examples: 159 + cidrToNetworkId "192.168.70.9/15" 160 + => [ 192 168 0 0 ] 161 + */ 162 + cidrToNetworkId = cidr: let 163 + ip = cidrToIpAddress cidr; 164 + subnetMask = cidrToSubnetMask cidr; 165 + in 166 + lib.zipListsWith lib.bitAnd ip subnetMask; 167 + 168 + /* 169 + Given a CIDR, return the associated first usable IP address. 170 + 171 + Type: cidrToFirstUsableIp :: String -> [ Int ] 172 + 173 + Examples: 174 + cidrToFirstUsableIp "192.168.70.9/15" 175 + => [ 192 168 0 1 ] 176 + */ 177 + cidrToFirstUsableIp = cidr: let 178 + networkId = cidrToNetworkId cidr; 179 + in 180 + incrementIp networkId 1; 181 + 182 + /* 183 + Given a CIDR, return the associated broadcast address. 184 + 185 + Type: cidrToBroadcastAddress :: String -> [ Int ] 186 + 187 + Examples: 188 + cidrToBroadcastAddress "192.168.70.9/15" 189 + => [ 192 169 255 255 ] 190 + */ 191 + cidrToBroadcastAddress = cidr: let 192 + subnetMask = cidrToSubnetMask cidr; 193 + networkId = cidrToNetworkId cidr; 194 + in 195 + getBroadcastAddress networkId subnetMask; 196 + 197 + /* 198 + Given a network ID and subnet mask, return the associated broadcast address. 199 + 200 + Type: getBroadcastAddress :: [ Int ] -> [ Int ] -> [ Int ] 201 + 202 + Examples: 203 + getBroadcastAddress [ 192 168 0 0 ] [ 255 254 0 0 ] 204 + => [ 192 169 255 255 ] 205 + */ 206 + getBroadcastAddress = networkId: subnetMask: 207 + lib.zipListsWith (nid: mask: 255 - mask + nid) networkId subnetMask; 208 + 209 + /* 210 + Given a CIDR, return the associated last usable IP address. 211 + 212 + Type: cidrToLastUsableIp :: String -> [ Int ] 213 + 214 + Examples: 215 + cidrToLastUsableIp "192.168.70.9/15" 216 + => [ 192 169 255 254 ] 217 + */ 218 + cidrToLastUsableIp = cidr: let 219 + broadcast = cidrToBroadcastAddress cidr; 220 + in 221 + incrementIp broadcast (-1); 222 + 223 + /* 224 + Increment the last octet of a given IP address. 225 + 226 + Type: incrementIp :: [ Int ] -> Int -> [ Int ] 227 + 228 + Examples: 229 + incrementIp [ 192 168 70 9 ] 3 230 + => [ 192 168 70 12 ] 231 + incrementIp [ 192 168 70 9 ] (-2) 232 + => [ 192 168 70 7 ] 233 + */ 234 + incrementIp = addr: offset: let 235 + lastOctet = lib.last addr; 236 + firstThree = lib.init addr; 237 + in 238 + firstThree ++ [(lastOctet + offset)]; 239 + 240 + /* 241 + Given an IP address and bit mask, return the associated CIDR. 242 + 243 + Type: ipAndBitMaskToCidr :: [ Int ] -> Int -> String 244 + 245 + Examples: 246 + ipAndBitMaskToCidr [ 192 168 70 9 ] 15 247 + => "192.168.70.9/15" 248 + */ 249 + ipAndBitMaskToCidr = addr: bitMask: 250 + lib.concatStringsSep "/" 251 + [ 252 + (prettyIp addr) 253 + (builtins.toString bitMask) 254 + ]; 255 + 256 + /* 257 + Given an IP address and subnet mask, return the associated CIDR. 258 + 259 + Type: ipAndSubnetMaskToCidr :: [ Int ] -> Int -> String 260 + 261 + Examples: 262 + ipAndSubnetMaskToCidr [ 192 168 70 9 ] [ 255 254 0 0 ] 263 + => "192.168.70.9/15" 264 + */ 265 + ipAndSubnetMaskToCidr = addr: subnetMask: 266 + ipAndBitMaskToCidr addr (subnetMaskToBitMask subnetMask); 267 + 268 + /* 269 + Given a CIDR, return an attribute set of: 270 + the IP Address, 271 + the bit mask, 272 + the first usable IP address, 273 + the last usable IP address, 274 + the network ID, 275 + the subnet mask, 276 + the broadcast address. 277 + 278 + Type: getNetworkProperties :: str -> attrset 279 + 280 + Examples: 281 + getNetworkProperties "192.168.70.9/15" 282 + => { 283 + bitMask = 15; 284 + broadcast = "192.169.255.255"; 285 + firstUsableIp = "192.168.0.1"; 286 + ipAddress = "192.168.70.9"; 287 + lastUsableIp = "192.169.255.254"; 288 + networkId = "192.168.0.0"; 289 + subnetMask = "255.254.0.0"; 290 + } 291 + */ 292 + getNetworkProperties = cidr: let 293 + ipAddress = prettyIp (cidrToIpAddress cidr); 294 + bitMask = cidrToBitMask cidr; 295 + firstUsableIp = prettyIp (cidrToFirstUsableIp cidr); 296 + lastUsableIp = prettyIp (cidrToLastUsableIp cidr); 297 + networkId = prettyIp (cidrToNetworkId cidr); 298 + subnetMask = prettyIp (cidrToSubnetMask cidr); 299 + broadcast = prettyIp (cidrToBroadcastAddress cidr); 300 + in {inherit ipAddress bitMask firstUsableIp lastUsableIp networkId subnetMask broadcast;}; 301 + in { 302 + ipv4 = { 303 + inherit 304 + prettyIp 305 + incrementIp 306 + bitMaskToSubnetMask 307 + subnetMaskToBitMask 308 + ipAndBitMaskToCidr 309 + ipAndSubnetMaskToCidr 310 + cidrToIpAddress 311 + cidrToBitMask 312 + cidrToFirstUsableIp 313 + cidrToLastUsableIp 314 + cidrToNetworkId 315 + cidrToSubnetMask 316 + cidrToBroadcastAddress 317 + getNetworkProperties 318 + ; 319 + }; 320 + }
+2 -2
lib/system.nix
··· 2 2 3 3 { 4 4 mkSystem = { system, hostname, user ? "phil", modules ? [], overlays ? [] }: let 5 - inherit (lib.systems.elaborate { inherit system; }) isDarwin isLinux; 5 + inherit (lib.systems.elaborate { inherit system; }) isDarwin; 6 6 7 7 homeDir = if isDarwin then "/Users/${user}" else "/home/${user}"; 8 8 rootDir = if isDarwin then "/var/root" else "/root"; ··· 21 21 ../machines/common.nix 22 22 ../machines/${hostname}/configuration.nix 23 23 ../modules/default.nix 24 - ({ config, ... }: { 24 + ({ ... }: { 25 25 inherit age; 26 26 27 27 nixpkgs = {
+37 -8
machines/cola/configuration.nix
··· 1 - { config, pkgs, user, ... }: 1 + { user, ... }: 2 2 3 3 { 4 4 imports = [ 5 5 ./hardware.nix 6 - ./kernel.nix 7 - ./network.nix 8 6 ./zfs.nix 9 - ../../modules/router 10 - ../../modules/server 11 - ../../modules/games 12 7 ]; 13 8 14 9 users.users."${user}" = { ··· 17 12 hashedPassword = "$6$DEmCOeiSFe6ymGox$WMWddbT9PkkfDT6JS4WuJsM3mQHI0e9kg0t42UowO79dWAcSU0K//KKlcebSosoMRz5mUEw5TFvbrv1aRHqYa/"; 18 13 }; 19 14 20 - time.timeZone = "Europe/London"; 15 + modules = { 16 + router = { 17 + enable = true; 18 + interfaces = { 19 + external = { 20 + name = "extern0"; 21 + macAddress = "1c:1b:0d:eb:ab:15"; 22 + }; 23 + internal = { 24 + name = "intern0"; 25 + macAddress = "1c:1b:0d:eb:ab:14"; 26 + cidr = "10.0.0.1/24"; 27 + }; 28 + }; 29 + dnsmasq.leases = [ 30 + { macAddress = "98:ed:7e:c6:57:b2"; ipAddress = "10.0.0.102"; } # eero router 31 + { macAddress = "c4:f1:74:51:4c:f2"; ipAddress = "10.0.0.124"; } # eero router 32 + { macAddress = "5c:61:99:7a:16:40"; ipAddress = "10.0.0.103"; } # brother printer 33 + { macAddress = "24:e8:53:95:e4:02"; ipAddress = "10.0.0.96"; } # tv 34 + { macAddress = "34:7e:5c:31:4f:fa"; ipAddress = "10.0.0.56"; } # sonos 35 + { macAddress = "e8:9c:25:6c:40:6f"; ipAddress = "10.0.0.150"; } # pepper-pc 36 + ]; 37 + nftables.blockForward = [ 38 + "ec:e5:12:1d:23:40" # tado 39 + ]; 40 + }; 21 41 22 - i18n.defaultLocale = "en_GB.UTF-8"; 42 + server = { 43 + enable = true; 44 + caddy.exposeFolders.files = "/share/files"; 45 + hd-idle.enable = true; 46 + home-assistant.enable = true; 47 + jellyfin.enable = true; 48 + tailscale.enable = true; 49 + vaultwarden.enable = true; 50 + }; 51 + }; 23 52 24 53 system.stateVersion = "23.05"; 25 54 }
+1 -1
machines/cola/hardware.nix
··· 1 - { config, lib, pkgs, nixos-hardware, modulesPath, ... }: 1 + { config, lib, nixos-hardware, modulesPath, ... }: 2 2 3 3 { 4 4 imports = [
-66
machines/cola/kernel.nix
··· 1 - { ... }: 2 - 3 - { 4 - boot.initrd.systemd = { 5 - enable = true; 6 - network.enable = true; 7 - }; 8 - 9 - boot.kernel.sysctl = { 10 - # networking 11 - "net.core.somaxconn" = 4096; 12 - "net.core.netdev_max_backlog" = 2000; 13 - 14 - "net.core.rmem_default" = 1048576; 15 - "net.core.rmem_max" = 26214400; 16 - "net.core.wmem_default" = 1048576; 17 - "net.core.wmem_max" = 16777216; 18 - "net.core.optmem_max" = 65536; 19 - 20 - "net.ipv4.tcp_rmem" = "4096 1048576 2097152"; 21 - "net.ipv4.tcp_wmem" = "4096 65536 16777216"; 22 - 23 - "net.ipv4.tcp_max_syn_backlog" = 8192; 24 - 25 - "net.ipv4.udp_rmem_min" = 8192; 26 - "net.ipv4.udp_wmem_min" = 8192; 27 - 28 - "net.ipv4.tcp_fastopen" = 3; 29 - 30 - "net.ipv4.tcp_max_tw_buckets" = 2000000; 31 - "net.ipv4.tcp_tw_reuse" = true; 32 - "net.ipv4.tcp_slow_start_after_idle" = false; 33 - "net.ipv4.tcp_mtu_probing" = true; 34 - 35 - "net.ipv4.tcp_rfc1337" = true; 36 - "net.ipv4.tcp_fin_timeout" = 10; 37 - 38 - "net.ipv4.tcp_keepalive_time" = 60; 39 - "net.ipv4.tcp_keepalive_intvl" = 10; 40 - "net.ipv4.tcp_keepalive_probes" = 6; 41 - 42 - "net.core.default_qdisc" = "cake"; 43 - "net.ipv4.tcp_congestion_control" = "bbr"; 44 - "net.ipv4.tcp_syncookies" = true; 45 - 46 - "net.ipv6.conf.all.forwarding" = true; 47 - "net.ipv6.conf.all.use_tempaddr" = false; 48 - "net.ipv6.conf.all.autoconf" = false; 49 - "net.ipv6.conf.all.accept_ra" = false; 50 - 51 - # security 52 - "kernel.kptr_restrict" = 2; 53 - "kernel.dmesg_restrict" = 0; 54 - "kernel.sysrq" = 4; 55 - "kernel.unprivileged_bpf_disabled" = true; 56 - "kernel.perf_event_paranoid" = 3; 57 - "kernel.yama.ptrace_scope" = 2; 58 - "kernel.kexec_load_disabled" = true; 59 - "net.core.bpf_jit_harden" = 2; 60 - "dev.tty.ldisc_autoload" = false; 61 - 62 - # containers 63 - "kernel.unprivileged_userns_clone" = true; 64 - "net.ipv4.ping_group_range" = "0 65536"; 65 - }; 66 - }
-63
machines/cola/network.nix
··· 1 - { ... }: 2 - 3 - { 4 - services.irqbalance.enable = true; 5 - 6 - networking = { 7 - useNetworkd = true; 8 - nameservers = [ "127.0.0.1" ]; 9 - timeServers = [ "time.cloudflare.com" ]; 10 - nftables.enable = true; 11 - firewall = { 12 - enable = true; 13 - trustedInterfaces = [ "lo" "intern0" ]; 14 - }; 15 - }; 16 - 17 - systemd.network = { 18 - enable = true; 19 - 20 - links."10-extern0" = { 21 - matchConfig.PermanentMACAddress = "1c:1b:0d:eb:ab:15"; 22 - linkConfig = { 23 - Description = "External Network Interface"; 24 - Name = "extern0"; 25 - MACAddress = "64:20:9f:16:70:a6"; 26 - MTUBytes = "1500"; 27 - }; 28 - }; 29 - 30 - links."11-intern0" = { 31 - matchConfig.PermanentMACAddress = "1c:1b:0d:eb:ab:14"; 32 - linkConfig = { 33 - Description = "Internal Network Interface"; 34 - Name = "intern0"; 35 - MTUBytes = "1500"; 36 - }; 37 - }; 38 - 39 - networks."10-extern0" = { 40 - name = "extern0"; 41 - networkConfig = { 42 - DHCP = "ipv4"; 43 - DNS = "127.0.0.1"; 44 - IPForward = true; 45 - }; 46 - dhcpV4Config = { 47 - UseDNS = false; 48 - UseDomains = false; 49 - UseNTP = false; 50 - }; 51 - }; 52 - 53 - networks."11-intern0" = { 54 - name = "intern0"; 55 - networkConfig = { 56 - Address = "10.0.0.1/24"; 57 - DHCPServer = false; 58 - IPForward = true; 59 - ConfigureWithoutCarrier = true; 60 - }; 61 - }; 62 - }; 63 - }
+33
machines/ramune/configuration.nix
··· 1 + { user, ... }: 2 + 3 + { 4 + imports = [ 5 + ./hardware.nix 6 + ]; 7 + 8 + users.users."${user}" = { 9 + isNormalUser = true; 10 + extraGroups = [ "wheel" ]; 11 + hashedPassword = "$6$DEmCOeiSFe6ymGox$WMWddbT9PkkfDT6JS4WuJsM3mQHI0e9kg0t42UowO79dWAcSU0K//KKlcebSosoMRz5mUEw5TFvbrv1aRHqYa/"; 12 + }; 13 + 14 + modules = { 15 + router = { 16 + enable = true; 17 + interfaces = { 18 + external = { 19 + name = "extern0"; 20 + macAddress = "5c:1b:f4:7f:dc:cd"; 21 + }; 22 + /*internal = { 23 + name = "intern0"; 24 + macAddress = "9c:bf:0d:00:23:5d"; 25 + cidr = "10.0.1.1/24"; 26 + };*/ 27 + }; 28 + }; 29 + }; 30 + 31 + system.stateVersion = "24.11"; 32 + } 33 +
+66
machines/ramune/hardware.nix
··· 1 + { lib, apple-silicon, modulesPath, ... }: 2 + 3 + { 4 + imports = [ 5 + apple-silicon.nixosModules.apple-silicon-support 6 + (modulesPath + "/installer/scan/not-detected.nix") 7 + ]; 8 + 9 + networking.hostId = "e8618fd7"; 10 + 11 + boot = { 12 + supportedFilesystems = [ "btrfs" ]; 13 + loader = { 14 + systemd-boot.enable = true; 15 + efi.canTouchEfiVariables = false; 16 + }; 17 + }; 18 + 19 + fileSystems."/" = { 20 + device = "/dev/disk/by-label/NIXROOT"; 21 + fsType = "btrfs"; 22 + options = [ "subvol=@root" "noatime" ]; 23 + }; 24 + 25 + fileSystems."/nix" = { 26 + device = "/dev/disk/by-label/NIXROOT"; 27 + fsType = "btrfs"; 28 + options = [ "subvol=@nix" "noatime" "compress=zstd" ]; 29 + }; 30 + 31 + fileSystems."/home" = { 32 + device = "/dev/disk/by-label/NIXROOT"; 33 + fsType = "btrfs"; 34 + options = [ "subvol=@home" "noatime" ]; 35 + }; 36 + 37 + fileSystems."/var/log" = { 38 + device = "/dev/disk/by-label/NIXROOT"; 39 + fsType = "btrfs"; 40 + options = [ "subvol=@log" "noatime" ]; 41 + }; 42 + 43 + fileSystems."/swap" = { 44 + device = "/dev/disk/by-label/NIXROOT"; 45 + fsType = "btrfs"; 46 + options = [ "subvol=@swap" ]; 47 + }; 48 + 49 + fileSystems."/boot" = { 50 + device = "/dev/disk/by-uuid/E617-1CEC"; 51 + fsType = "vfat"; 52 + options = [ "fmask=0022" "dmask=0022" ]; 53 + }; 54 + 55 + swapDevices = [ 56 + { device = "/swap/swapfile"; } 57 + ]; 58 + 59 + nixpkgs = { 60 + hostPlatform = lib.mkDefault "aarch64-linux"; 61 + config.allowUnfree = true; 62 + overlays = [ apple-silicon.overlays.apple-silicon-overlay ]; 63 + }; 64 + 65 + hardware.asahi.useExperimentalGPUDriver = true; 66 + }
+1
machines/ramune/home.nix
··· 1 + { ... }: {}
+1
modules/base/default.nix
··· 5 5 ./nix-config.nix 6 6 ./shell.nix 7 7 ./linux.nix 8 + ./gpg.nix 8 9 ]; 9 10 }
+23
modules/base/gpg.nix
··· 1 + { lib, config, pkgs, ... }: 2 + 3 + with lib; 4 + let 5 + cfg = config.modules.gpg; 6 + in { 7 + options.modules.gpg = { 8 + enable = mkEnableOption "GnuPG"; 9 + }; 10 + 11 + config = mkIf cfg.enable { 12 + environment.systemPackages = [ 13 + pkgs.gnupg 14 + ]; 15 + 16 + programs.gnupg = { 17 + agent = { 18 + enable = true; 19 + enableSSHSupport = true; 20 + }; 21 + }; 22 + }; 23 + }
+8 -6
modules/base/linux.nix
··· 1 - { lib, config, pkgs, helpers, ... } @ inputs: 1 + { lib, pkgs, helpers, ... } @ inputs: 2 2 3 + with lib; 3 4 let 4 5 inherit (import ../../lib/colors.nix inputs) hex; 5 - inherit (lib) mkDefault mkForce; 6 6 in helpers.linuxAttrs { 7 7 environment.systemPackages = [ pkgs.sbctl ]; 8 8 ··· 31 31 }; 32 32 33 33 boot = { 34 - bootspec.enable = lib.mkDefault true; 35 - consoleLogLevel = lib.mkDefault 2; 34 + bootspec.enable = mkDefault true; 35 + consoleLogLevel = mkDefault 2; 36 36 37 37 loader = { 38 38 timeout = mkDefault 2; ··· 44 44 systemd.enable = true; 45 45 }; 46 46 47 - kernelPackages = lib.mkDefault pkgs.linuxPackages_latest; 47 + kernelPackages = mkDefault pkgs.linuxPackages_latest; 48 48 49 49 kernelParams = [ 50 50 "quiet" ··· 64 64 }; 65 65 }; 66 66 67 + time.timeZone = "Europe/London"; 68 + 67 69 i18n = { 68 70 defaultLocale = "en_GB.UTF-8"; 69 71 extraLocaleSettings = { ··· 90 92 91 93 services.dbus.enable = true; 92 94 93 - virtualisation.oci-containers.backend = mkForce "podman"; 95 + virtualisation.oci-containers.backend = mkDefault "podman"; 94 96 }
+4 -1
modules/default.nix
··· 3 3 { 4 4 imports = [ 5 5 ./base 6 + ./desktop 6 7 ./development 7 - ./gpg.nix 8 + ./fonts 8 9 ./nvim 10 + ./router 11 + ./server 9 12 ]; 10 13 }
+39 -19
modules/desktop/default.nix
··· 1 - { lib, user, helpers, ... }: 1 + { lib, config, user, helpers, ... }: 2 + 3 + with lib; 4 + let 5 + cfg = config.modules.desktop; 6 + in { 7 + options.modules.desktop = { 8 + enable = mkOption { 9 + default = false; 10 + example = true; 11 + description = "Whether to enable Desktop options."; 12 + type = types.bool; 13 + }; 14 + }; 2 15 3 - helpers.linuxAttrs { 16 + config.modules.server = { 17 + enable = if helpers.isLinux then (mkDefault false) else (mkForce false); 18 + }; 19 + } // helpers.linuxAttrs { 4 20 imports = [ 5 21 ./services.nix 6 22 ./session.nix 7 23 ./xdg.nix 8 - ./fonts 24 + ./fonts.nix 9 25 ]; 10 26 11 - users.users."${user}".extraGroups = [ "video" ]; 27 + config = mkIf cfg.enable { 28 + users.users."${user}".extraGroups = [ "video" ]; 12 29 13 - services = { 14 - fwupd.enable = true; 15 - pipewire = { 16 - enable = true; 17 - wireplumber.enable = true; 18 - alsa.enable = true; 19 - alsa.support32Bit = true; 20 - pulse.enable = true; 21 - jack.enable = true; 30 + services = { 31 + fwupd.enable = true; 32 + pipewire = { 33 + enable = true; 34 + wireplumber.enable = true; 35 + pulse.enable = true; 36 + jack.enable = true; 37 + alsa = { 38 + enable = true; 39 + support32Bit = true; 40 + }; 41 + }; 42 + }; 43 + 44 + hardware = { 45 + pulseaudio.enable = lib.mkForce false; 46 + steam-hardware.enable = true; 22 47 }; 23 - }; 24 48 25 - hardware = { 26 - pulseaudio.enable = lib.mkForce false; 27 - steam-hardware.enable = true; 49 + security.rtkit.enable = true; 28 50 }; 29 - 30 - security.rtkit.enable = true; 31 51 }
+36
modules/desktop/fonts.nix
··· 1 + { lib, config, pkgs, ... }: 2 + 3 + with lib; 4 + let 5 + cfg = config.modules.desktop; 6 + in { 7 + options.modules.desktop.fonts = { 8 + enable = mkOption { 9 + default = cfg.enable; 10 + example = true; 11 + description = "Whether to enable default fonts."; 12 + type = types.bool; 13 + }; 14 + }; 15 + 16 + config = mkIf cfg.fonts.enable { 17 + fonts = { 18 + fontDir.enable = true; 19 + 20 + packages = with pkgs; [ 21 + noto-fonts 22 + noto-fonts-cjk 23 + noto-fonts-emoji 24 + roboto-mono 25 + inter 26 + ]; 27 + 28 + fontconfig.defaultFonts = { 29 + serif = [ "Noto Serif" "Noto Color Emoji" ]; 30 + sansSerif = [ "Inter" "Noto Color Emoji" ]; 31 + monospace = [ "Dank Mono" "Roboto Mono" "Noto Color Emoji" ]; 32 + emoji = [ "Noto Color Emoji" ]; 33 + }; 34 + }; 35 + }; 36 + }
-67
modules/desktop/fonts/default.nix
··· 1 - { config, pkgs, lib, helpers, ... } @ inputs: 2 - 3 - let 4 - inherit (pkgs) stdenv; 5 - 6 - fontsDir = if helpers.isDarwin then 7 - "/Library/Fonts" else "/usr/share/fonts/nonfree"; 8 - 9 - in lib.mkMerge [ 10 - { 11 - age.secrets."DankMono-Regular.otf" = { 12 - symlink = false; 13 - file = ./encrypt/DankMono-Regular.otf.age; 14 - path = "${fontsDir}/DankMono-Regular.otf"; 15 - mode = "755"; 16 - }; 17 - 18 - age.secrets."DankMono-Italic.otf" = { 19 - symlink = false; 20 - file = ./encrypt/DankMono-Italic.otf.age; 21 - path = "${fontsDir}/DankMono-Italic.otf"; 22 - mode = "755"; 23 - }; 24 - 25 - age.secrets."DankMono-Bold.otf" = { 26 - symlink = false; 27 - file = ./encrypt/DankMono-Bold.otf.age; 28 - path = "${fontsDir}/DankMono-Bold.otf"; 29 - mode = "755"; 30 - }; 31 - 32 - age.secrets."codicon.otf" = { 33 - symlink = false; 34 - file = ./encrypt/codicon.otf.age; 35 - path = "${fontsDir}/codicon.otf"; 36 - mode = "755"; 37 - }; 38 - 39 - age.secrets."faicon.ttf" = { 40 - symlink = false; 41 - file = ./encrypt/faicon.ttf.age; 42 - path = "${fontsDir}/faicon.ttf"; 43 - mode = "755"; 44 - }; 45 - } 46 - 47 - (helpers.linuxAttrs { 48 - fonts = { 49 - fontDir.enable = true; 50 - 51 - packages = with pkgs; [ 52 - noto-fonts 53 - noto-fonts-cjk 54 - noto-fonts-emoji 55 - roboto-mono 56 - inter 57 - ]; 58 - 59 - fontconfig.defaultFonts = { 60 - serif = [ "Noto Serif" "Noto Color Emoji" ]; 61 - sansSerif = [ "Inter" "Noto Color Emoji" ]; 62 - monospace = [ "Dank Mono" "Roboto Mono" "Noto Color Emoji" ]; 63 - emoji = [ "Noto Color Emoji" ]; 64 - }; 65 - }; 66 - }) 67 - ]
modules/desktop/fonts/encrypt/DankMono-Bold.otf.age modules/fonts/encrypt/DankMono-Bold.otf.age
modules/desktop/fonts/encrypt/DankMono-Italic.otf.age modules/fonts/encrypt/DankMono-Italic.otf.age
modules/desktop/fonts/encrypt/DankMono-Regular.otf.age modules/fonts/encrypt/DankMono-Regular.otf.age
modules/desktop/fonts/encrypt/codicon.otf.age modules/fonts/encrypt/codicon.otf.age
modules/desktop/fonts/encrypt/faicon.ttf.age modules/fonts/encrypt/faicon.ttf.age
+21 -7
modules/desktop/services.nix
··· 1 - { user, ... }: 1 + { lib, config, user, ... }: 2 2 3 - { 4 - users.users."${user}".extraGroups = [ "video" ]; 3 + with lib; 4 + let 5 + cfg = config.modules.desktop; 6 + in { 7 + options.modules.desktop.services = { 8 + enable = mkOption { 9 + default = cfg.enable; 10 + example = true; 11 + description = "Whether to enable services."; 12 + type = types.bool; 13 + }; 14 + }; 5 15 6 - services = { 7 - printing.enable = true; 8 - flatpak.enable = true; 9 - colord.enable = true; 16 + config = mkIf cfg.services.enable { 17 + users.users."${user}".extraGroups = [ "video" ]; 18 + 19 + services = { 20 + printing.enable = true; 21 + flatpak.enable = true; 22 + colord.enable = true; 23 + }; 10 24 }; 11 25 }
+46 -31
modules/desktop/session.nix
··· 1 - { pkgs, lib, user, ... }: 1 + { lib, config, pkgs, user, ... }: 2 2 3 - { 4 - boot.plymouth.enable = true; 3 + with lib; 4 + let 5 + cfg = config.modules.desktop; 6 + in { 7 + options.modules.desktop.session = { 8 + enable = mkOption { 9 + default = cfg.enable; 10 + example = true; 11 + description = "Whether to enable session and desktop environment."; 12 + type = types.bool; 13 + }; 14 + }; 5 15 6 - services.desktopManager.plasma6.enable = true; 16 + config = mkIf cfg.session.enable { 17 + boot.plymouth.enable = true; 7 18 8 - services.displayManager = { 9 - defaultSession = "plasma"; 10 - autoLogin = { 11 - inherit user; 12 - enable = true; 19 + services = { 20 + desktopManager.plasma6.enable = true; 21 + displayManager = { 22 + defaultSession = "plasma"; 23 + autoLogin = { 24 + inherit user; 25 + enable = true; 26 + }; 27 + sddm = { 28 + enable = true; 29 + enableHidpi = true; 30 + wayland.enable = true; 31 + }; 32 + }; 13 33 }; 14 - sddm = { 15 - enable = true; 16 - enableHidpi = true; 17 - wayland.enable = true; 34 + 35 + environment.plasma6 = { 36 + excludePackages = with pkgs.kdePackages; [ 37 + elisa 38 + gwenview 39 + oxygen 40 + oxygen-sounds 41 + khelpcenter 42 + konsole 43 + ]; 18 44 }; 19 - }; 20 45 21 - environment.plasma6 = { 22 - excludePackages = with pkgs.kdePackages; [ 23 - elisa 24 - gwenview 25 - oxygen 26 - oxygen-sounds 27 - khelpcenter 28 - konsole 29 - ]; 30 - }; 46 + security = { 47 + polkit.enable = true; 48 + }; 31 49 32 - security = { 33 - polkit.enable = true; 34 - }; 35 - 36 - xdg.portal = { 37 - enable = true; 38 - xdgOpenUsePortal = false; 50 + xdg.portal = { 51 + enable = true; 52 + xdgOpenUsePortal = false; 53 + }; 39 54 }; 40 55 }
+34 -20
modules/desktop/xdg.nix
··· 1 - { lib, user, ... }: 1 + { lib, config, user, ... }: 2 2 3 - { 4 - home-manager.users.${user} = { ... }: { 5 - systemd.user.sessionVariables = { 6 - "NIXOS_OZONE_WL" = lib.mkDefault "1"; 7 - "MOZ_ENABLE_WAYLAND" = lib.mkDefault "1"; 8 - "QT_WAYLAND_DISABLE_WINDOWDECORATIONS" = lib.mkDefault "1"; 9 - "QT_WAYLAND_FORCE_DPI" = lib.mkDefault "physical"; 10 - "QT_QPA_PLATFORM" = lib.mkDefault "wayland-egl"; 3 + with lib; 4 + let 5 + cfg = config.modules.desktop; 6 + in { 7 + options.modules.desktop.xdg = { 8 + enable = mkOption { 9 + default = cfg.enable; 10 + example = true; 11 + description = "Whether to enable services."; 12 + type = types.bool; 11 13 }; 14 + }; 12 15 13 - xdg = { 14 - mimeApps = { 15 - enable = true; 16 + config = mkIf cfg.xdg.enable { 17 + home-manager.users.${user} = { ... }: { 18 + systemd.user.sessionVariables = { 19 + "NIXOS_OZONE_WL" = lib.mkDefault "1"; 20 + "MOZ_ENABLE_WAYLAND" = lib.mkDefault "1"; 21 + "QT_WAYLAND_DISABLE_WINDOWDECORATIONS" = lib.mkDefault "1"; 22 + "QT_WAYLAND_FORCE_DPI" = lib.mkDefault "physical"; 23 + "QT_QPA_PLATFORM" = lib.mkDefault "wayland-egl"; 16 24 }; 17 - userDirs = { 18 - enable = true; 19 - createDirectories = true; 25 + 26 + xdg = { 27 + mimeApps = { 28 + enable = true; 29 + }; 30 + userDirs = { 31 + enable = true; 32 + createDirectories = true; 33 + }; 34 + systemDirs.data = [ 35 + "/usr/share" 36 + "/var/lib/flatpak/exports/share" 37 + "$HOME/.local/share/flatpak/exports/share" 38 + ]; 20 39 }; 21 - systemDirs.data = [ 22 - "/usr/share" 23 - "/var/lib/flatpak/exports/share" 24 - "$HOME/.local/share/flatpak/exports/share" 25 - ]; 26 40 }; 27 41 }; 28 42 }
+23 -5
modules/development/cocoapods.nix
··· 1 - { pkgs, helpers, ... }: 1 + { lib, config, pkgs, helpers, ... }: 2 2 3 - helpers.darwinAttrs { 4 - environment.systemPackages = [ 5 - pkgs.cocoapods 6 - ]; 3 + with lib; 4 + let 5 + cfg = config.modules.development; 6 + in { 7 + options.modules.development.cocoapods = { 8 + enable = mkOption { 9 + default = cfg.enable; 10 + example = true; 11 + description = "Whether to enable Cocoapods."; 12 + type = types.bool; 13 + }; 14 + }; 15 + 16 + config.modules.development.cocoapods = { 17 + enable = if helpers.isDarwin then (mkDefault true) else (mkForce false); 18 + }; 19 + } // helpers.darwinAttrs { 20 + config = mkIf cfg.cocoapods.enable { 21 + environment.systemPackages = [ 22 + pkgs.cocoapods 23 + ]; 24 + }; 7 25 }
+12 -1
modules/development/default.nix
··· 1 - { 1 + { lib, ... }: 2 + 3 + with lib; { 4 + options.modules.development = { 5 + enable = mkOption { 6 + default = false; 7 + example = true; 8 + description = "Whether to enable Development options."; 9 + type = types.bool; 10 + }; 11 + }; 12 + 2 13 imports = [ 3 14 ./js.nix 4 15 ./cocoapods.nix
+28 -14
modules/development/js.nix
··· 1 - { pkgs, ... }: 1 + { lib, config, pkgs, ... }: 2 2 3 - { 4 - environment.variables = { 5 - PNPM_HOME = "$HOME/.local/share/pnpm"; 6 - BUN_RUNTIME_TRANSPILER_CACHE_PATH = "$HOME/.cache/bun/install/cache/@t@"; 7 - COREPACK_ENABLE_AUTO_PIN = "0"; 3 + with lib; 4 + let 5 + cfg = config.modules.development; 6 + in { 7 + options.modules.development.js = { 8 + enable = mkOption { 9 + default = cfg.enable; 10 + example = true; 11 + description = "Whether to enable JS tools."; 12 + type = types.bool; 13 + }; 8 14 }; 9 15 10 - environment.systemPackages = (with pkgs; [ 11 - bun 12 - corepack_22 13 - nodejs_22 14 - ]); 16 + config = mkIf cfg.js.enable { 17 + environment.variables = { 18 + PNPM_HOME = "$HOME/.local/share/pnpm"; 19 + BUN_RUNTIME_TRANSPILER_CACHE_PATH = "$HOME/.cache/bun/install/cache/@t@"; 20 + COREPACK_ENABLE_AUTO_PIN = "0"; 21 + }; 22 + 23 + environment.systemPackages = (with pkgs; [ 24 + bun 25 + corepack_22 26 + nodejs_22 27 + ]); 15 28 16 - environment.interactiveShellInit = '' 17 - export PATH=./node_modules/.bin:$HOME/.local/share/pnpm:$PATH 18 - ''; 29 + environment.interactiveShellInit = '' 30 + export PATH=./node_modules/.bin:$HOME/.local/share/pnpm:$PATH 31 + ''; 32 + }; 19 33 }
+53
modules/fonts/default.nix
··· 1 + { config, lib, helpers, ... }: 2 + 3 + with lib; 4 + let 5 + cfg = config.modules.fonts; 6 + fontsPath = if helpers.isDarwin then "/Library/Fonts" else "/usr/share/fonts/nonfree"; 7 + in { 8 + options.modules.fonts = { 9 + enable = mkOption { 10 + default = false; 11 + example = true; 12 + description = "Whether to enable fonts options."; 13 + type = types.bool; 14 + }; 15 + }; 16 + 17 + config = mkIf cfg.enable { 18 + age.secrets."DankMono-Regular.otf" = { 19 + symlink = false; 20 + file = ./encrypt/DankMono-Regular.otf.age; 21 + path = "${fontsPath}/DankMono-Regular.otf"; 22 + mode = "755"; 23 + }; 24 + 25 + age.secrets."DankMono-Italic.otf" = { 26 + symlink = false; 27 + file = ./encrypt/DankMono-Italic.otf.age; 28 + path = "${fontsPath}/DankMono-Italic.otf"; 29 + mode = "755"; 30 + }; 31 + 32 + age.secrets."DankMono-Bold.otf" = { 33 + symlink = false; 34 + file = ./encrypt/DankMono-Bold.otf.age; 35 + path = "${fontsPath}/DankMono-Bold.otf"; 36 + mode = "755"; 37 + }; 38 + 39 + age.secrets."codicon.otf" = { 40 + symlink = false; 41 + file = ./encrypt/codicon.otf.age; 42 + path = "${fontsPath}/codicon.otf"; 43 + mode = "755"; 44 + }; 45 + 46 + age.secrets."faicon.ttf" = { 47 + symlink = false; 48 + file = ./encrypt/faicon.ttf.age; 49 + path = "${fontsPath}/faicon.ttf"; 50 + mode = "755"; 51 + }; 52 + }; 53 + }
-7
modules/games/default.nix
··· 1 - { helpers, ... }: 2 - 3 - helpers.linuxAttrs { 4 - imports = [ 5 - ./enshrouded-server.nix 6 - ]; 7 - }
-51
modules/games/enshrouded-server.nix
··· 1 - { lib, config, pkgs, ... }: 2 - 3 - { 4 - ids.gids.steam = lib.mkDefault 10000; 5 - ids.uids.steam = lib.mkDefault 10000; 6 - 7 - users = { 8 - groups.steam.gid = config.ids.gids.steam; 9 - users.steam = { 10 - uid = config.ids.uids.steam; 11 - group = "steam"; 12 - createHome = true; 13 - home = "/var/lib/steam"; 14 - isSystemUser = true; 15 - }; 16 - }; 17 - 18 - virtualisation.oci-containers = { 19 - containers.enshrouded = rec { 20 - autoStart = false; 21 - volumes = [ 22 - "/var/lib/steam/enshrouded:/home/steam/enshrouded/savegame" 23 - "/etc/localtime:/etc/localtime:ro" 24 - ]; 25 - environment = { 26 - TZ = "Europe/London"; 27 - SERVER_IP = "0.0.0.0"; 28 - SERVER_NAME = "London Boroughs"; 29 - SERVER_PASSWORD = "deepdickgalactic"; 30 - GAME_PORT = "15636"; 31 - QUERY_PORT = "15637"; 32 - SERVER_SLOTS = "8"; 33 - }; 34 - image = "docker.io/sknnr/enshrouded-dedicated-server:proton-latest"; 35 - ports = [ 36 - "15636:15636/udp" 37 - "15637:15637/udp" 38 - ]; 39 - extraOptions = [ 40 - "--userns=keep-id" 41 - "--cap-add=NET_RAW" 42 - "--runtime=runc" 43 - ]; 44 - }; 45 - }; 46 - 47 - networking.firewall.allowedUDPPorts = [ 48 - 15636 49 - 15637 50 - ]; 51 - }
-14
modules/gpg.nix
··· 1 - { pkgs, ... }: 2 - 3 - { 4 - environment.systemPackages = [ 5 - pkgs.gnupg 6 - ]; 7 - 8 - programs.gnupg = { 9 - agent = { 10 - enable = true; 11 - enableSSHSupport = true; 12 - }; 13 - }; 14 - }
+19 -10
modules/nvim/default.nix
··· 1 - { pkgs, ... } @ inputs: 1 + { lib, config, pkgs, ... } @ inputs: 2 2 3 + with lib; 3 4 let 4 5 inherit (import ../../lib/colors.nix inputs) colors mkVimHardlineColors; 5 6 inherit (import ./theme.nix inputs) my-theme; 6 7 7 - importContents = /*lua*/ '' 8 - ''; 8 + cfg = config.modules.nvim; 9 9 10 10 initContents = " 11 11 \nlua <<EOF\n" + /* lua */ '' ··· 71 71 neovimPkg = pkgs.neovim-unwrapped; 72 72 neovim = pkgs.wrapNeovimUnstable neovimPkg neovimConfig; 73 73 in { 74 - environment.variables = { EDITOR = "vim"; }; 74 + options.modules.nvim = { 75 + enable = mkEnableOption "Neovim"; 76 + useCustomConfig = mkEnableOption "Custom Configuration"; 77 + }; 75 78 76 - environment.systemPackages = with pkgs; [ 77 - ripgrep 78 - fd 79 - bat 80 - neovim 81 - ]; 79 + config = mkIf cfg.enable { 80 + environment.variables = { EDITOR = "vim"; }; 81 + 82 + environment.systemPackages = with pkgs; if cfg.useCustomConfig then [ 83 + ripgrep 84 + fd 85 + bat 86 + neovim 87 + ] else [ 88 + neovimPkg 89 + ]; 90 + }; 82 91 }
-9
modules/router/avahi.nix
··· 1 - { ... }: 2 - 3 - { 4 - services.avahi = { 5 - enable = true; 6 - allowInterfaces = [ "intern0" ]; 7 - denyInterfaces = [ "extern0" ]; 8 - }; 9 - }
+21 -6
modules/router/default.nix
··· 1 - { helpers, ... }: 1 + { lib, helpers, ... }: 2 2 3 - helpers.linuxAttrs { 3 + with lib; { 4 + options.modules.router = { 5 + enable = mkOption { 6 + default = false; 7 + example = true; 8 + description = "Whether to enable Router options."; 9 + type = types.bool; 10 + }; 11 + }; 12 + 13 + config.modules.router = { 14 + enable = if helpers.isLinux then (mkDefault false) else (mkForce false); 15 + }; 16 + } // helpers.linuxAttrs { 4 17 imports = [ 5 - ./ntp.nix 6 - ./stubby.nix 18 + ./network.nix 19 + ./timeserver.nix 20 + ./dnsOverTLS.nix 7 21 ./dnsmasq.nix 8 22 ./nftables.nix 9 - ./miniupnpd.nix 10 - ./avahi.nix 23 + ./upnp.nix 24 + ./mdns.nix 25 + ./kernel.nix 11 26 ]; 12 27 }
+47
modules/router/dnsOverTLS.nix
··· 1 + { lib, config, ... }: 2 + 3 + with lib; 4 + let 5 + cfg = config.modules.router; 6 + in { 7 + options.modules.router = { 8 + dnsOverTLS = { 9 + enable = mkOption { 10 + default = cfg.enable; 11 + description = "Whether to enable Stubby DNS proxy"; 12 + type = types.bool; 13 + }; 14 + 15 + port = mkOption { 16 + default = 53000; 17 + description = "Port for Stubby"; 18 + type = types.int; 19 + }; 20 + }; 21 + }; 22 + 23 + config = mkIf cfg.dnsOverTLS.enable { 24 + services.stubby = { 25 + enable = true; 26 + settings = { 27 + resolution_type = "GETDNS_RESOLUTION_STUB"; 28 + dns_transport_list = [ "GETDNS_TRANSPORT_TLS" ]; 29 + tls_authentication = "GETDNS_AUTHENTICATION_REQUIRED"; 30 + tls_query_padding_blocksize = 128; 31 + edns_client_subnet_private = 1; 32 + round_robin_upstreams = 1; 33 + tls_connection_retries = 5; 34 + listen_addresses = [ 35 + "127.0.0.1@${toString cfg.dnsOverTLS.port}" 36 + "0::1@${toString cfg.dnsOverTLS.port}" 37 + ]; 38 + appdata_dir = "/var/cache/stubby"; 39 + trust_anchors_backoff_time = 2500; 40 + upstream_recursive_servers = [ 41 + { address_data = "1.1.1.1"; tls_auth_name = "cloudflare-dns.com"; } 42 + { address_data = "1.0.0.1"; tls_auth_name = "cloudflare-dns.com"; } 43 + ]; 44 + }; 45 + }; 46 + }; 47 + }
+120 -65
modules/router/dnsmasq.nix
··· 1 - { config, lib, ... }: 1 + { lib, config, ... } @ inputs: 2 + 3 + with lib; 4 + let 5 + inherit (import ../../lib/ipv4.nix inputs) ipv4; 6 + 7 + cfg = config.modules.router; 8 + intern = cfg.interfaces.internal; 9 + extern = cfg.interfaces.external; 10 + 11 + leaseType = types.submodule { 12 + options = { 13 + macAddress = mkOption { 14 + type = types.str; 15 + example = "00:00:00:00:00:00"; 16 + }; 17 + ipAddress = mkOption { 18 + type = types.str; 19 + example = "10.0.0.10"; 20 + }; 21 + }; 22 + }; 23 + 24 + dnsServer = if cfg.dnsOverTLS.enable 25 + then [ "127.0.0.1#${toString cfg.dnsOverTLS.port}" ] 26 + else [ "1.1.1.1" "1.0.0.1" ]; 27 + 28 + dhcpHost = builtins.map (lease: "${lease.macAddress},${lease.ipAddress}") cfg.dnsmasq.leases; 29 + 30 + dhcpIPv4Range = let 31 + subnetMask = ipv4.prettyIp (ipv4.cidrToSubnetMask intern.cidr); 32 + firstIP = ipv4.prettyIp (ipv4.incrementIp (ipv4.cidrToFirstUsableIp intern.cidr) 1); 33 + lastIP = ipv4.prettyIp (ipv4.cidrToLastUsableIp intern.cidr); 34 + in "${firstIP}, ${lastIP}, ${subnetMask}, 12h"; 35 + 36 + localDomains = builtins.map (host: "/${host}/${cfg.address}") cfg.dnsmasq.localDomains; 37 + in { 38 + options.modules.router = { 39 + dnsmasq = { 40 + enable = mkOption { 41 + default = cfg.enable; 42 + description = "Whether to enable DNSMasq"; 43 + type = types.bool; 44 + }; 45 + 46 + leases = mkOption { 47 + default = []; 48 + type = types.listOf leaseType; 49 + description = "List of reserved IP address leases"; 50 + }; 2 51 3 - { 4 - services.resolved.extraConfig = lib.mkDefault '' 5 - [Resolve] 6 - DNSStubListener=no 7 - ''; 52 + localDomains = mkOption { 53 + default = []; 54 + type = types.listOf types.str; 55 + }; 56 + }; 57 + }; 8 58 9 - services.dnsmasq = { 10 - enable = true; 11 - alwaysKeepRunning = true; 12 - settings = { 13 - server = if config.services.stubby.enable then [ "127.0.0.1#53000" ] else [ "1.1.1.1" "1.0.0.1" ]; 59 + config = mkIf cfg.dnsmasq.enable { 60 + modules.router.dnsmasq.localDomains = [ 61 + "time.apple.com" 62 + "time1.apple.com" 63 + "time2.apple.com" 64 + "time3.apple.com" 65 + "time4.apple.com" 66 + "time5.apple.com" 67 + "time6.apple.com" 68 + "time7.apple.com" 69 + "time.euro.apple.com" 70 + "time.windows.com" 71 + "0.android.pool.ntp.org" 72 + "1.android.pool.ntp.org" 73 + "2.android.pool.ntp.org" 74 + "3.android.pool.ntp.org" 75 + ]; 76 + 77 + networking.nameservers = [ "127.0.0.1" ]; 78 + 79 + services.resolved.extraConfig = mkDefault '' 80 + [Resolve] 81 + DNSStubListener=no 82 + ''; 83 + 84 + services.dnsmasq = { 85 + enable = true; 86 + alwaysKeepRunning = true; 87 + settings = { 88 + server = dnsServer; 14 89 15 - # never forward plain names (without a dot or domain part) 16 - domain-needed = true; 17 - # never forward addresses in the non-routed address spaces. 18 - bogus-priv = true; 19 - # filter useless windows-originated DNS requests 20 - filterwin2k = true; 21 - # never read nameservers from /etc/resolv.conf 22 - no-resolv = true; 90 + # never forward plain names (without a dot or domain part) 91 + domain-needed = true; 92 + # never forward addresses in the non-routed address spaces. 93 + bogus-priv = true; 94 + # filter useless windows-originated DNS requests 95 + filterwin2k = true; 96 + # never read nameservers from /etc/resolv.conf 97 + no-resolv = true; 23 98 24 - cache-size = 5000; 25 - no-negcache = true; 99 + cache-size = 5000; 100 + no-negcache = true; 26 101 27 - expand-hosts = true; 28 - addn-hosts = "/etc/hosts"; 102 + expand-hosts = true; 103 + addn-hosts = "/etc/hosts"; 29 104 30 - dhcp-range = [ 31 - "10.0.0.2, 10.0.0.255, 255.255.255.0, 12h" 32 - "tag:intern0, ::1, constructor:intern0, ra-names, slaac, 12h" 33 - ]; 34 - dhcp-option = [ 35 - "option6:information-refresh-time, 6h" 36 - "option:router,10.0.0.1" 37 - "option:ntp-server,10.0.0.1" 38 - "ra-param=intern0,high,0,0" 39 - ]; 105 + dhcp-host = dhcpHost; 40 106 41 - dhcp-host = [ 42 - "98:ed:7e:c6:57:b2,10.0.0.102" # eero router 43 - "c4:f1:74:51:4c:f2,10.0.0.124" # eero router 44 - "5c:61:99:7a:16:40,10.0.0.103" # brother printer 45 - "24:e8:53:95:e4:02,10.0.0.96" # tv 46 - "34:7e:5c:31:4f:fa,10.0.0.56" # sonos 47 - "e8:9c:25:6c:40:6f,10.0.0.150" # pepper-pc 48 - ]; 107 + # listen only on intern0 by excluding extern0 108 + except-interface = extern.name; 49 109 50 - # listen only on intern0 by excluding extern0 51 - except-interface = "extern0"; 110 + # set the DHCP server to authoritative and rapic commit mode 111 + dhcp-authoritative = true; 112 + dhcp-rapid-commit = true; 52 113 53 - # set the DHCP server to authoritative and rapic commit mode 54 - dhcp-authoritative = true; 55 - dhcp-rapid-commit = true; 114 + # Detect attempts by Verisign to send queries to unregistered hosts 115 + bogus-nxdomain = "64.94.110.11"; 56 116 57 - # Detect attempts by Verisign to send queries to unregistered hosts 58 - bogus-nxdomain = "64.94.110.11"; 117 + address = localDomains; 118 + } // (optionalAttrs (intern != null) { 119 + dhcp-range = [ 120 + dhcpIPv4Range 121 + "tag:${intern.name}, ::1, constructor:${intern.name}, ra-names, slaac, 12h" 122 + ]; 59 123 60 - address = [ 61 - "/cola.fable-pancake.ts.net/10.0.0.1" 62 - "/time.apple.com/10.0.0.1" 63 - "/time1.apple.com/10.0.0.1" 64 - "/time2.apple.com/10.0.0.1" 65 - "/time3.apple.com/10.0.0.1" 66 - "/time4.apple.com/10.0.0.1" 67 - "/time5.apple.com/10.0.0.1" 68 - "/time6.apple.com/10.0.0.1" 69 - "/time7.apple.com/10.0.0.1" 70 - "/time.euro.apple.com/10.0.0.1" 71 - "/time.windows.com/10.0.0.1" 72 - "/0.android.pool.ntp.org/10.0.0.1" 73 - "/1.android.pool.ntp.org/10.0.0.1" 74 - "/2.android.pool.ntp.org/10.0.0.1" 75 - "/3.android.pool.ntp.org/10.0.0.1" 76 - ]; 124 + dhcp-option = [ 125 + "option6:information-refresh-time, 6h" 126 + "option:router,${cfg.address}" 127 + "ra-param=${intern.name},high,0,0" 128 + ] ++ ( 129 + if cfg.timeserver.enable then [ "option:ntp-server,${cfg.address}" ] else [] 130 + ); 131 + }); 77 132 }; 78 133 }; 79 134 }
+73
modules/router/kernel.nix
··· 1 + { lib, config, ... }: 2 + 3 + with lib; 4 + let 5 + cfg = config.modules.router; 6 + in { 7 + options.modules.router = { 8 + tweakKernel = mkOption { 9 + default = cfg.enable; 10 + description = "Whether to tweak kernel configuration"; 11 + type = types.bool; 12 + }; 13 + }; 14 + 15 + config = mkIf cfg.enable { 16 + boot.initrd.systemd = { 17 + enable = true; 18 + network.enable = true; 19 + }; 20 + 21 + boot.kernel.sysctl = mkIf cfg.tweakKernel { 22 + "net.core.somaxconn" = 4096; 23 + "net.core.netdev_max_backlog" = 2000; 24 + 25 + "net.core.rmem_default" = 1048576; 26 + "net.core.rmem_max" = 26214400; 27 + "net.core.wmem_default" = 1048576; 28 + "net.core.wmem_max" = 16777216; 29 + "net.core.optmem_max" = 65536; 30 + 31 + "net.ipv4.tcp_rmem" = "4096 1048576 2097152"; 32 + "net.ipv4.tcp_wmem" = "4096 65536 16777216"; 33 + 34 + "net.ipv4.tcp_max_syn_backlog" = 8192; 35 + 36 + "net.ipv4.udp_rmem_min" = 8192; 37 + "net.ipv4.udp_wmem_min" = 8192; 38 + 39 + "net.ipv4.tcp_fastopen" = 3; 40 + 41 + "net.ipv4.tcp_max_tw_buckets" = 2000000; 42 + "net.ipv4.tcp_tw_reuse" = true; 43 + "net.ipv4.tcp_slow_start_after_idle" = false; 44 + "net.ipv4.tcp_mtu_probing" = true; 45 + 46 + "net.ipv4.tcp_rfc1337" = true; 47 + "net.ipv4.tcp_fin_timeout" = 10; 48 + 49 + "net.ipv4.tcp_keepalive_time" = 60; 50 + "net.ipv4.tcp_keepalive_intvl" = 10; 51 + "net.ipv4.tcp_keepalive_probes" = 6; 52 + 53 + "net.core.default_qdisc" = "cake"; 54 + "net.ipv4.tcp_congestion_control" = "bbr"; 55 + "net.ipv4.tcp_syncookies" = true; 56 + 57 + "net.ipv6.conf.all.forwarding" = true; 58 + "net.ipv6.conf.all.use_tempaddr" = false; 59 + "net.ipv6.conf.all.autoconf" = false; 60 + "net.ipv6.conf.all.accept_ra" = false; 61 + 62 + "kernel.kptr_restrict" = 2; 63 + "kernel.dmesg_restrict" = 0; 64 + "kernel.sysrq" = 4; 65 + "kernel.unprivileged_bpf_disabled" = true; 66 + "kernel.perf_event_paranoid" = 3; 67 + "kernel.yama.ptrace_scope" = 2; 68 + "kernel.kexec_load_disabled" = true; 69 + "net.core.bpf_jit_harden" = 2; 70 + "dev.tty.ldisc_autoload" = false; 71 + }; 72 + }; 73 + }
+24
modules/router/mdns.nix
··· 1 + { lib, config, ... }: 2 + 3 + with lib; 4 + let 5 + cfg = config.modules.router; 6 + intern = cfg.interfaces.internal; 7 + extern = cfg.interfaces.external; 8 + in { 9 + options.modules.router = { 10 + mdns.enable = mkOption { 11 + default = cfg.enable; 12 + description = "Whether to enable mDNS Discovery Service"; 13 + type = types.bool; 14 + }; 15 + }; 16 + 17 + config = mkIf (cfg.mdns.enable && intern != null) { 18 + services.avahi = { 19 + enable = true; 20 + allowInterfaces = if intern != null then [ intern.name ] else []; 21 + denyInterfaces = [ extern.name ]; 22 + }; 23 + }; 24 + }
-20
modules/router/miniupnpd.nix
··· 1 - { ... }: 2 - 3 - { 4 - services.miniupnpd = { 5 - enable = true; 6 - upnp = true; 7 - natpmp = true; 8 - internalIPs = [ "intern0" ]; 9 - externalInterface = "extern0"; 10 - appendConfig = '' 11 - ext_ip=137.220.98.13 12 - secure_mode=yes 13 - notify_interval=60 14 - clean_ruleset_interval=600 15 - uuid=78b8b903-83c1-4036-8fcd-f64aee25baca 16 - allow 1024-65535 10.0.0.0/24 1024-65535 17 - deny 0-65535 0.0.0.0/0 0-65535 18 - ''; 19 - }; 20 - }
+111
modules/router/network.nix
··· 1 + { lib, config, ... } @ inputs: 2 + 3 + with lib; 4 + let 5 + inherit (import ../../lib/ipv4.nix inputs) ipv4; 6 + 7 + cfg = config.modules.router; 8 + 9 + interfaceType = types.submodule { 10 + options = { 11 + name = mkOption { 12 + type = types.str; 13 + example = "eth0"; 14 + }; 15 + macAddress = mkOption { 16 + type = types.str; 17 + example = "00:00:00:00:00:00"; 18 + }; 19 + cidr = mkOption { 20 + type = types.str; 21 + default = "0.0.0.0/0"; 22 + example = "10.0.0.1/24"; 23 + }; 24 + }; 25 + }; 26 + 27 + extern = cfg.interfaces.external; 28 + intern = cfg.interfaces.internal; 29 + in { 30 + options.modules.router = { 31 + address = mkOption { 32 + type = types.str; 33 + default = if intern != null 34 + then ipv4.prettyIp (ipv4.cidrToIpAddress intern.cidr) 35 + else "127.0.0.1"; 36 + example = "127.0.0.1"; 37 + }; 38 + interfaces = { 39 + external = mkOption { 40 + type = interfaceType; 41 + }; 42 + internal = mkOption { 43 + type = types.nullOr interfaceType; 44 + default = null; 45 + }; 46 + }; 47 + }; 48 + 49 + config = mkIf cfg.enable { 50 + services.irqbalance.enable = true; 51 + 52 + networking.useNetworkd = true; 53 + 54 + networking.firewall = mkIf (intern != null) { 55 + trustedInterfaces = [ "lo" intern.name ]; 56 + }; 57 + 58 + systemd.network = { 59 + enable = true; 60 + 61 + links = { 62 + "10-${extern.name}" = { 63 + matchConfig.PermanentMACAddress = extern.macAddress; 64 + linkConfig = { 65 + Description = "External Network Interface"; 66 + Name = extern.name; 67 + # MACAddress = "64:20:9f:16:70:a6"; 68 + MTUBytes = "1500"; 69 + }; 70 + }; 71 + } // (optionalAttrs (intern != null) { 72 + "11-${intern.name}" = { 73 + matchConfig.PermanentMACAddress = intern.macAddress; 74 + linkConfig = { 75 + Description = "Internal Network Interface"; 76 + Name = intern.name; 77 + MTUBytes = "1500"; 78 + }; 79 + }; 80 + }); 81 + 82 + networks = { 83 + "10-${extern.name}" = { 84 + name = extern.name; 85 + networkConfig = { 86 + DHCP = "ipv4"; 87 + DNS = if cfg.dnsmasq.enable then "127.0.0.1" else "1.1.1.1"; 88 + IPv4Forwarding = true; 89 + IPv6Forwarding = true; 90 + }; 91 + dhcpV4Config = { 92 + UseDNS = false; 93 + UseDomains = false; 94 + UseNTP = !cfg.timeserver.enable; 95 + }; 96 + }; 97 + } // (optionalAttrs (intern != null) { 98 + "11-${intern.name}" = { 99 + name = intern.name; 100 + networkConfig = { 101 + Address = cfg.address; 102 + DHCPServer = false; 103 + IPv4Forwarding = true; 104 + IPv6Forwarding = true; 105 + ConfigureWithoutCarrier = true; 106 + }; 107 + }; 108 + }); 109 + }; 110 + }; 111 + }
+153 -103
modules/router/nftables.nix
··· 1 1 { config, lib, ... }: 2 2 3 + with lib; 3 4 let 5 + cfg = config.modules.router; 6 + 7 + intern = cfg.interfaces.internal; 8 + extern = cfg.interfaces.external; 9 + 4 10 trustedInterfaces = 5 - lib.strings.concatMapStringsSep ", " lib.strings.escapeNixIdentifier config.networking.firewall.trustedInterfaces; 11 + strings.concatMapStringsSep ", " strings.escapeNixIdentifier config.networking.firewall.trustedInterfaces; 12 + 13 + capturePortsRules = 14 + strings.concatStringsSep "\n" 15 + (builtins.map (port: " iifname { ${trustedInterfaces} } udp dport ${toString port} redirect to ${toString port}") cfg.nftables.capturePorts); 16 + 17 + blockForwardRules = 18 + if intern != null then 19 + strings.concatStringsSep "\n" 20 + (builtins.map (mac: " iifname ${intern0} oifname != ${intern0} ether saddr = ${mac} drop") cfg.nftables.blockForward) 21 + else ""; 6 22 in { 7 - networking.firewall.checkReversePath = "loose"; 23 + options.modules.router = { 24 + nftables = { 25 + enable = mkOption { 26 + default = cfg.enable; 27 + description = "Whether to enable Router NFTables config"; 28 + type = types.bool; 29 + }; 8 30 9 - networking.nftables = { 10 - enable = true; 11 - checkRuleset = false; 12 - flushRuleset = true; 31 + capturePorts = mkOption { 32 + default = [ 53 123 ]; 33 + description = "Ports to capture and redirect to router"; 34 + type = types.listOf types.int; 35 + }; 13 36 14 - tables.filter = { 15 - family = "inet"; 16 - content = '' 17 - chain prerouting { 18 - type nat hook prerouting priority 0; policy accept; 19 - iifname { ${trustedInterfaces} } udp dport 53 redirect to 53 20 - iifname { ${trustedInterfaces} } udp dport 123 redirect to 123 21 - } 37 + blockForward = mkOption { 38 + default = []; 39 + description = "MAC Addresses of devices to block internet access for"; 40 + type = types.listOf types.str; 41 + }; 42 + }; 43 + }; 22 44 23 - chain postrouting { 24 - type nat hook postrouting priority 0; policy accept; 25 - oifname != { ${trustedInterfaces} } masquerade 26 - } 45 + config = mkIf cfg.nftables.enable { 46 + networking.useNetworkd = mkDefault true; 47 + networking.firewall = { 48 + enable = mkDefault true; 49 + checkReversePath = "loose"; 50 + }; 27 51 28 - chain input { 29 - type filter hook input priority 0; 30 - ct state { established, related } accept 31 - ct state invalid drop 32 - iifname { ${trustedInterfaces} } accept 33 - iifname { ${trustedInterfaces} } pkttype { broadcast, multicast } accept 34 - tcp flags & (fin|syn|rst|ack) != syn ct state new counter drop 35 - tcp flags & (fin|syn|rst|psh|ack|urg) == fin|syn|rst|psh|ack|urg counter drop 36 - tcp flags & (fin|syn|rst|psh|ack|urg) == 0x0 counter drop 37 - ip protocol icmp \ 38 - icmp type { destination-unreachable, echo-reply, echo-request, source-quench, time-exceeded } \ 39 - accept 40 - ip6 nexthdr icmpv6 accept 41 - udp dport 546 ct state { new, untracked } accept 42 - udp dport dhcpv6-client accept 43 - udp dport { http, https } ct state new accept 44 - tcp dport { http, https } ct state new accept 45 - udp dport 41641 ct state new accept 46 - reject with icmpx type port-unreachable 47 - } 52 + networking.nftables = { 53 + enable = mkForce true; 54 + checkRuleset = false; 55 + flushRuleset = true; 48 56 49 - chain forward { 50 - type filter hook forward priority 0; policy drop; 51 - ip6 nexthdr ipv6-icmp accept 52 - udp dport dhcpv6-client accept 53 - iifname intern0 oifname != intern0 ether saddr == ec:e5:12:1d:23:40 drop # drop tado internet traffic 54 - iifname { ${trustedInterfaces} } accept 55 - oifname { ${trustedInterfaces} } ct state { established, related } accept 56 - ct state invalid drop 57 - } 57 + tables.filter = { 58 + family = "inet"; 59 + content = '' 60 + chain prerouting { 61 + type nat hook prerouting priority 0; policy accept; 62 + ${capturePortsRules} 63 + } 58 64 59 - chain output { 60 - type filter hook output priority 0; policy accept; 61 - ip6 nexthdr ipv6-icmp accept 62 - udp dport dhcpv6-client accept 63 - iifname lo accept 64 - ct state invalid drop 65 - } 66 - ''; 67 - }; 65 + chain postrouting { 66 + type nat hook postrouting priority 0; policy accept; 67 + oifname != { ${trustedInterfaces} } masquerade 68 + } 68 69 69 - tables.arp_filter = { 70 - family = "arp"; 71 - content = '' 72 - chain input { 73 - type filter hook input priority 0; policy accept; 74 - iifname != { ${trustedInterfaces} } limit rate 1/second burst 2 packets accept 75 - } 70 + chain input { 71 + type filter hook input priority 0; 72 + ct state { established, related } accept 73 + ct state invalid drop 74 + iifname { ${trustedInterfaces} } accept 75 + iifname { ${trustedInterfaces} } pkttype { broadcast, multicast } accept 76 + tcp flags & (fin|syn|rst|ack) != syn ct state new counter drop 77 + tcp flags & (fin|syn|rst|psh|ack|urg) == fin|syn|rst|psh|ack|urg counter drop 78 + tcp flags & (fin|syn|rst|psh|ack|urg) == 0x0 counter drop 79 + ip protocol icmp \ 80 + icmp type { destination-unreachable, echo-reply, echo-request, source-quench, time-exceeded } \ 81 + accept 82 + ip6 nexthdr icmpv6 accept 83 + udp dport 546 ct state { new, untracked } accept 84 + udp dport dhcpv6-client accept 85 + udp dport { http, https } ct state new accept 86 + tcp dport { http, https } ct state new accept 87 + udp dport 41641 ct state new accept 88 + reject with icmpx type port-unreachable 89 + } 76 90 77 - chain output { 78 - type filter hook output priority 0; policy accept; 79 - } 80 - ''; 81 - }; 91 + chain forward { 92 + type filter hook forward priority 0; policy drop; 93 + ip6 nexthdr ipv6-icmp accept 94 + udp dport dhcpv6-client accept 95 + ${blockForwardRules} 96 + iifname { ${trustedInterfaces} } accept 97 + oifname { ${trustedInterfaces} } ct state { established, related } accept 98 + ct state invalid drop 99 + } 82 100 83 - tables.tagging = { 84 - family = "netdev"; 85 - content = '' 86 - chain lan { 87 - type filter hook ingress device intern0 priority -150; policy accept; 88 - jump tags 89 - } 101 + chain output { 102 + type filter hook output priority 0; policy accept; 103 + ip6 nexthdr ipv6-icmp accept 104 + udp dport dhcpv6-client accept 105 + iifname lo accept 106 + ct state invalid drop 107 + } 108 + ''; 109 + }; 90 110 91 - chain wan { 92 - type filter hook ingress device extern0 priority -149; policy accept; 93 - jump tags 94 - } 111 + tables.arp_filter = { 112 + family = "arp"; 113 + content = '' 114 + chain input { 115 + type filter hook input priority 0; policy accept; 116 + iifname != { ${trustedInterfaces} } limit rate 1/second burst 2 packets accept 117 + } 95 118 96 - chain tags { 97 - ip dscp set cs0 98 - ip6 dscp set cs0 119 + chain output { 120 + type filter hook output priority 0; policy accept; 121 + } 122 + ''; 123 + }; 99 124 100 - ip protocol udp udp sport ntp ip dscp set cs5 101 - ip6 nexthdr udp udp sport ntp ip6 dscp set cs5 125 + tables.tagging = { 126 + family = "netdev"; 127 + content = let 128 + internChain = if intern != null then '' 129 + chain lan { 130 + type filter hook ingress device ${intern.name} priority -150; policy accept; 131 + jump tags 132 + } 133 + '' else ""; 102 134 103 - ip saddr {1.1.1.1, 1.0.0.1} ip dscp set cs5 104 - ip daddr {1.1.1.1, 1.0.0.1} ip dscp set cs5 135 + externChain = '' 136 + chain wan { 137 + type filter hook ingress device ${extern.name} priority -149; policy accept; 138 + jump tags 139 + } 140 + ''; 141 + in '' 142 + ${internChain} 143 + ${externChain} 105 144 106 - tcp dport {http, https} ip dscp set cs3 107 - tcp sport {http, https} ip dscp set cs3 108 - ip6 nexthdr tcp tcp dport {http, https} ip6 dscp set cs3 109 - ip6 nexthdr tcp tcp sport {http, https} ip6 dscp set cs3 145 + chain tags { 146 + ip dscp set cs0 147 + ip6 dscp set cs0 110 148 111 - udp dport 41641 ip dscp set cs4 # tailscale 112 - udp sport 41641 ip dscp set cs4 # tailscale 149 + ip protocol udp udp sport ntp ip dscp set cs5 150 + ip6 nexthdr udp udp sport ntp ip6 dscp set cs5 113 151 114 - # mark some VOIP traffic as flash override (low delay) 115 - udp dport {3478-3479, 19302-19309} ip dscp set cs4 116 - udp sport {3478-3479, 19302-19309} ip dscp set cs4 117 - ip6 nexthdr udp udp dport {3478-3479, 19302-19309} ip6 dscp set cs4 118 - ip6 nexthdr udp udp sport {3478-3479, 19302-19309} ip6 dscp set cs4 119 - udp dport {7000-9000, 27000-27200} ip dscp set cs4 120 - udp sport {7000-9000, 27000-27200} ip dscp set cs4 121 - ip6 nexthdr udp udp dport {7000-9000, 27000-27200} ip6 dscp set cs4 122 - ip6 nexthdr udp udp sport {7000-9000, 27000-27200} ip6 dscp set cs4 123 - } 124 - ''; 152 + ip saddr {1.1.1.1, 1.0.0.1} ip dscp set cs5 153 + ip daddr {1.1.1.1, 1.0.0.1} ip dscp set cs5 154 + 155 + tcp dport {http, https} ip dscp set cs3 156 + tcp sport {http, https} ip dscp set cs3 157 + ip6 nexthdr tcp tcp dport {http, https} ip6 dscp set cs3 158 + ip6 nexthdr tcp tcp sport {http, https} ip6 dscp set cs3 159 + 160 + udp dport 41641 ip dscp set cs4 # tailscale 161 + udp sport 41641 ip dscp set cs4 # tailscale 162 + 163 + # mark some VOIP traffic as flash override (low delay) 164 + udp dport {3478-3479, 19302-19309} ip dscp set cs4 165 + udp sport {3478-3479, 19302-19309} ip dscp set cs4 166 + ip6 nexthdr udp udp dport {3478-3479, 19302-19309} ip6 dscp set cs4 167 + ip6 nexthdr udp udp sport {3478-3479, 19302-19309} ip6 dscp set cs4 168 + udp dport {7000-9000, 27000-27200} ip dscp set cs4 169 + udp sport {7000-9000, 27000-27200} ip dscp set cs4 170 + ip6 nexthdr udp udp dport {7000-9000, 27000-27200} ip6 dscp set cs4 171 + ip6 nexthdr udp udp sport {7000-9000, 27000-27200} ip6 dscp set cs4 172 + } 173 + ''; 174 + }; 125 175 }; 126 176 }; 127 177 }
-12
modules/router/ntp.nix
··· 1 - { ... }: 2 - 3 - { 4 - services.ntp = { 5 - enable = true; 6 - extraConfig = '' 7 - interface listen lo 8 - interface listen intern0 9 - interface ignore extern0 10 - ''; 11 - }; 12 - }
-23
modules/router/stubby.nix
··· 1 - { ... }: 2 - 3 - { 4 - services.stubby = { 5 - enable = true; 6 - settings = { 7 - resolution_type = "GETDNS_RESOLUTION_STUB"; 8 - dns_transport_list = [ "GETDNS_TRANSPORT_TLS" ]; 9 - tls_authentication = "GETDNS_AUTHENTICATION_REQUIRED"; 10 - tls_query_padding_blocksize = 128; 11 - edns_client_subnet_private = 1; 12 - round_robin_upstreams = 1; 13 - tls_connection_retries = 5; 14 - listen_addresses = [ "127.0.0.1@53000" "0::1@53000" ]; 15 - appdata_dir = "/var/cache/stubby"; 16 - trust_anchors_backoff_time = 2500; 17 - upstream_recursive_servers = [ 18 - { address_data = "1.1.1.1"; tls_auth_name = "cloudflare-dns.com"; } 19 - { address_data = "1.0.0.1"; tls_auth_name = "cloudflare-dns.com"; } 20 - ]; 21 - }; 22 - }; 23 - }
+32
modules/router/timeserver.nix
··· 1 + { lib, config, ... }: 2 + 3 + with lib; 4 + let 5 + cfg = config.modules.router; 6 + 7 + listenInterfaces = 8 + strings.concatStringsSep "\n" 9 + (builtins.map (ifname: "interface listen ${ifname}") config.networking.firewall.trustedInterfaces); 10 + 11 + ntpExtraConfig = '' 12 + ${listenInterfaces} 13 + interface ignore ${cfg.interfaces.external.name} 14 + ''; 15 + in { 16 + options.modules.router = { 17 + timeserver.enable = mkOption { 18 + default = cfg.enable; 19 + description = "Whether to enable NTP Service"; 20 + type = types.bool; 21 + }; 22 + }; 23 + 24 + config = mkIf cfg.timeserver.enable { 25 + networking.timeServers = [ "time.cloudflare.com" ]; 26 + 27 + services.ntp = { 28 + enable = true; 29 + extraConfig = ntpExtraConfig; 30 + }; 31 + }; 32 + }
+36
modules/router/upnp.nix
··· 1 + { lib, config, ... }: 2 + 3 + with lib; 4 + let 5 + cfg = config.modules.router; 6 + extern = cfg.interfaces.external; 7 + intern = cfg.interfaces.internal; 8 + in { 9 + options.modules.router = { 10 + upnp = { 11 + enable = mkOption { 12 + default = cfg.enable; 13 + description = "Whether to enable UPNP"; 14 + type = types.bool; 15 + }; 16 + }; 17 + }; 18 + 19 + config = mkIf (cfg.upnp.enable && intern != null) { 20 + services.miniupnpd = { 21 + enable = true; 22 + upnp = true; 23 + natpmp = true; 24 + internalIPs = if intern != null then [ intern.name ] else []; 25 + externalInterface = extern.name; 26 + appendConfig = '' 27 + secure_mode=yes 28 + notify_interval=60 29 + clean_ruleset_interval=600 30 + uuid=78b8b903-83c1-4036-8fcd-f64aee25baca 31 + allow 1024-65535 ${intern.cidr} 1024-65535 32 + deny 0-65535 ${extern.cidr} 0-65535 33 + ''; 34 + }; 35 + }; 36 + }
+94 -46
modules/server/caddy.nix
··· 1 - { config, ... }: 1 + { lib, config, hostname, ... }: 2 2 3 - { 4 - services.tailscale.permitCertUid = config.services.caddy.user; 3 + with lib; 4 + let 5 + cfg = config.modules.server; 5 6 6 - services.caddy = { 7 - enable = config.services.tailscale.enable; 8 - email = "phil@kitten.sh"; 9 - extraConfig = '' 10 - (network_paths) { 11 - handle_path /home { 12 - redir * https://cola.fable-pancake.ts.net:8123 13 - } 7 + domain = config.networking.domain; 8 + tailscaleEnabled = cfg.tailscale.enable; 9 + vaultwardenEnabled = cfg.vaultwarden.enable; 10 + jellyfinEnabled = cfg.jellyfin.enable; 11 + hassEnabled = cfg.home-assistant.enable; 14 12 15 - handle_path /vault { 16 - redir * /vault/ 17 - } 13 + vaultwardenHandlerConfig = if vaultwardenEnabled then '' 14 + handle_path /vault { 15 + redir * /vault/ 16 + } 18 17 19 - handle_path /vault/* { 20 - reverse_proxy /notifications/hub/negotiate 127.0.0.1:8000 21 - reverse_proxy /notifications/hub 127.0.0.1:8001 22 - reverse_proxy localhost:8000 { 23 - header_up X-Real-IP {remote_host} 24 - } 25 - } 18 + handle_path /vault/* { 19 + reverse_proxy /notifications/hub/negotiate 127.0.0.1:8000 20 + reverse_proxy /notifications/hub 127.0.0.1:8001 21 + reverse_proxy localhost:8000 { 22 + header_up X-Real-IP {remote_host} 23 + } 24 + } 25 + '' else ""; 26 26 27 - handle_path /media { 28 - redir * /media/ 29 - } 27 + jellyfinHandlerConfig = if jellyfinEnabled then '' 28 + handle_path /media { 29 + redir * /media/ 30 + } 31 + 32 + reverse_proxy /media/* localhost:8096 { 33 + header_up X-Real-IP {remote_host} 34 + } 35 + '' else ""; 36 + 37 + hassHandlerConfig = if hassEnabled && tailscaleEnabled then '' 38 + handle_path /home { 39 + redir * https://${hostname}.${domain}:8123 40 + } 41 + '' else ""; 42 + 43 + tailscaleConfig = if tailscaleEnabled then '' 44 + ${hostname}.${domain} { 45 + import network_paths 46 + } 47 + '' else ""; 30 48 31 - reverse_proxy /media/* localhost:8096 { 32 - header_up X-Real-IP {remote_host} 49 + exposeConfig = let 50 + configs = attrsets.mapAttrs (name: root: '' 51 + handle_path /${name} { 52 + redir * /${name}/ 53 + } 54 + 55 + handle_path /${name}/* { 56 + file_server { 57 + browse 58 + root ${root} 59 + hide .* 33 60 } 34 61 35 - handle_path /files { 36 - redir * /files/ 62 + @file path *.* 63 + 64 + handle_path /* { 65 + header @file +Content-Disposition attachment 37 66 } 67 + } 68 + '') cfg.caddy.exposeFolders; 69 + in string.concatStringsSep "\n\n" configs; 70 + in { 71 + options.modules.server.caddy = { 72 + enable = mkOption { 73 + default = cfg.enable; 74 + example = true; 75 + description = "Whether to enable Caddy."; 76 + type = types.bool; 77 + }; 38 78 39 - handle_path /files/* { 40 - file_server { 41 - browse 42 - root /share/files 43 - hide .* 44 - } 79 + exposeFolders = mkOption { 80 + default = {}; 81 + description = "Folders to expose via Cadddy."; 82 + example = { files = "/share/files"; }; 83 + type = types.nullOr types.attrsOf types.str; 84 + }; 85 + }; 45 86 46 - @file path *.* 87 + config = mkIf cfg.caddy.enable { 88 + services.tailscale = mkIf tailscaleEnabled { 89 + permitCertUid = config.services.caddy.user; 90 + }; 47 91 48 - handle_path /* { 49 - header @file +Content-Disposition attachment 50 - } 92 + services.caddy = { 93 + enable = true; 94 + email = "phil@kitten.sh"; 95 + extraConfig = '' 96 + (network_paths) { 97 + ${vaultwardenHandlerConfig} 98 + ${jellyfinHandlerConfig} 99 + ${hassHandlerConfig} 100 + ${exposeConfig} 51 101 } 52 - } 53 102 54 - cola.fable-pancake.ts.net { 55 - import network_paths 56 - } 103 + ${tailscaleConfig} 57 104 58 - :80 { 59 - import network_paths 60 - } 61 - ''; 105 + :80 { 106 + import network_paths 107 + } 108 + ''; 109 + }; 62 110 }; 63 111 }
+15 -2
modules/server/default.nix
··· 1 - { helpers, ... }: 1 + { lib, helpers, ... }: 2 + 3 + with lib; { 4 + options.modules.server = { 5 + enable = mkOption { 6 + default = false; 7 + example = true; 8 + description = "Whether to enable Server options."; 9 + type = types.bool; 10 + }; 11 + }; 2 12 3 - helpers.linuxAttrs { 13 + config.modules.server = { 14 + enable = if helpers.isLinux then (mkDefault false) else (mkForce false); 15 + }; 16 + } // helpers.linuxAttrs { 4 17 imports = [ 5 18 ./sshd.nix 6 19 ./tailscale.nix
+24 -9
modules/server/hd-idle.nix
··· 1 - { pkgs, ... }: 1 + { lib, config, pkgs, ... }: 2 + 3 + with lib; 4 + let 5 + cfgRoot = config.modules.server; 6 + cfg = config.modules.server.hd-idle; 7 + in { 8 + options.modules.server.hd-idle = { 9 + enable = mkOption { 10 + default = false; 11 + example = true; 12 + description = "Whether to enable hard-drive idling."; 13 + type = types.bool; 14 + }; 15 + }; 2 16 3 - { 4 - systemd.services.hd-idle = { 5 - description = "External HD spin down daemon"; 6 - wantedBy = [ "multi-user.target" ]; 7 - serviceConfig = { 8 - Type = "simple"; 9 - Restart = "always"; 10 - ExecStart = "${pkgs.hd-idle}/bin/hd-idle -i 600"; 17 + config = mkIf (cfg.enable && cfgRoot.enable) { 18 + systemd.services.hd-idle = { 19 + description = "External HD spin down daemon"; 20 + wantedBy = [ "multi-user.target" ]; 21 + serviceConfig = { 22 + Type = "simple"; 23 + Restart = "always"; 24 + ExecStart = "${pkgs.hd-idle}/bin/hd-idle -i 600"; 25 + }; 11 26 }; 12 27 }; 13 28 }
+71 -41
modules/server/home-assistant.nix
··· 1 - { config, ... }: 1 + { lib, config, pkgs, ... }: 2 2 3 - { 4 - users = { 5 - groups.hass.gid = config.ids.gids.hass; 6 - users.hass = { 7 - uid = config.ids.uids.hass; 8 - group = "hass"; 3 + with lib; 4 + let 5 + inherit (pkgs) stdenv; 6 + 7 + cfgRoot = config.modules.server; 8 + cfg = config.modules.server.home-assistant; 9 + 10 + containerImage = if stdenv.isAarch64 11 + then "ghcr.io/home-assistant/home-assistant:${cfg.revision}" 12 + else "ghcr.io/home-assistant/aarch64-home-assistant:${cfg.revision}"; 13 + in { 14 + options.modules.server.home-assistant = { 15 + enable = mkOption { 16 + default = false; 17 + example = true; 18 + description = "Whether to enable Home Assistant."; 19 + type = types.bool; 20 + }; 21 + 22 + revision = mkOption { 23 + default = "2024.9.2"; 24 + example = "2024.9.2"; 25 + description = "Home Assistant Revision"; 26 + type = types.str; 9 27 }; 10 28 }; 11 29 12 - virtualisation.oci-containers = { 13 - containers.hass = rec { 14 - autoStart = true; 15 - volumes = [ 16 - "/var/lib/home-assistant:/config" 17 - "/etc/localtime:/etc/localtime:ro" 18 - "/sys:/sys:ro" 19 - ]; 20 - user = "${environment.PUID}:${environment.PGID}"; 21 - environment = { 22 - TZ = "Europe/London"; 23 - PUID = "${toString config.ids.uids.hass}"; 24 - PGID = "${toString config.ids.gids.hass}"; 25 - UMASK = "007"; 30 + config = mkIf (cfg.enable && cfgRoot.enable) { 31 + modules.server.podman.enable = mkDefault true; 32 + 33 + users = { 34 + groups.hass.gid = config.ids.gids.hass; 35 + users.hass = { 36 + uid = config.ids.uids.hass; 37 + group = "hass"; 26 38 }; 27 - image = "ghcr.io/home-assistant/home-assistant:stable"; 28 - extraOptions = [ 29 - "--cap-drop=ALL" 30 - "--cap-add=CHOWN" 31 - "--cap-add=DAC_OVERRIDE" 32 - "--cap-add=FSETID" 33 - "--cap-add=FOWNER" 34 - "--cap-add=SETGID" 35 - "--cap-add=SETUID" 36 - "--cap-add=SYS_CHROOT" 37 - "--cap-add=KILL" 38 - "--cap-add=NET_RAW" 39 - "--cap-add=NET_ADMIN" 40 - "--security-opt=no-new-privileges" 41 - "--userns=keep-id" 42 - "--hostuser=hass" 43 - "--runtime=runc" 44 - "--device=/dev/ttyUSB0" 45 - "--network=host" 46 - ]; 39 + }; 40 + 41 + virtualisation.oci-containers = { 42 + containers.hass = rec { 43 + autoStart = true; 44 + volumes = [ 45 + "/var/lib/home-assistant:/config" 46 + "/etc/localtime:/etc/localtime:ro" 47 + "/sys:/sys:ro" 48 + ]; 49 + user = "${environment.PUID}:${environment.PGID}"; 50 + environment = { 51 + TZ = "Europe/London"; 52 + PUID = "${toString config.ids.uids.hass}"; 53 + PGID = "${toString config.ids.gids.hass}"; 54 + UMASK = "007"; 55 + }; 56 + image = containerImage; 57 + extraOptions = [ 58 + "--cap-drop=ALL" 59 + "--cap-add=CHOWN" 60 + "--cap-add=DAC_OVERRIDE" 61 + "--cap-add=FSETID" 62 + "--cap-add=FOWNER" 63 + "--cap-add=SETGID" 64 + "--cap-add=SETUID" 65 + "--cap-add=SYS_CHROOT" 66 + "--cap-add=KILL" 67 + "--cap-add=NET_RAW" 68 + "--cap-add=NET_ADMIN" 69 + "--security-opt=no-new-privileges" 70 + "--userns=keep-id" 71 + "--hostuser=hass" 72 + "--runtime=runc" 73 + "--device=/dev/ttyUSB0" 74 + "--network=host" 75 + ]; 76 + }; 47 77 }; 48 78 }; 49 79 }
+60 -43
modules/server/jellyfin.nix
··· 1 - { lib, pkgs, user, ... }: 1 + { lib, config, pkgs, user, ... }: 2 2 3 + with lib; 3 4 let 5 + cfgRoot = config.modules.server; 6 + cfg = config.modules.server.jellyfin; 7 + 4 8 group = "share"; 5 9 in { 6 - hardware.graphics.enable = lib.mkDefault true; 10 + options.modules.server.jellyfin = { 11 + enable = mkOption { 12 + default = false; 13 + example = true; 14 + description = "Whether to enable Jellyfin server."; 15 + type = types.bool; 16 + }; 7 17 8 - age.secrets."rclone.conf" = { 9 - symlink = true; 10 - path = "/run/secrets/rclone.conf"; 11 - file = ./encrypt/rclone.conf.age; 12 - owner = "jellyfin"; 13 - group = "${group}"; 18 + sync = mkEnableOption "Whether to sync files from remotes"; 14 19 }; 15 20 16 - users.users."${user}".extraGroups = [ "${group}" ]; 21 + config = mkIf (cfg.enable && cfgRoot.enable) { 22 + hardware.graphics.enable = mkDefault true; 17 23 18 - users.groups.share.gid = 1001; 24 + users.users."${user}".extraGroups = [ "${group}" ]; 19 25 20 - services.jellyfin = { 21 - enable = true; 22 - openFirewall = false; 23 - group = "${group}"; 24 - }; 26 + users.groups.share.gid = 1001; 27 + 28 + services.jellyfin = { 29 + enable = true; 30 + openFirewall = false; 31 + group = "${group}"; 32 + }; 25 33 26 - systemd.services."rclone-sync@" = { 27 - wants = [ "network-online.target" ]; 28 - description = "Sync files between different remotes via rclone"; 29 - stopIfChanged = false; 30 - serviceConfig = { 31 - Type = "simple"; 32 - User = "jellyfin"; 33 - Group = "${group}"; 34 - ExecStart = '' 35 - ${pkgs.rclone}/bin/rclone \ 36 - --config /run/secrets/rclone.conf \ 37 - -P copy \ 38 - -u \ 39 - --max-age 24h \ 40 - --multi-thread-streams 0 \ 41 - putio:%I /share/media/%I 42 - ''; 34 + age.secrets."rclone.conf" = mkIf cfg.sync { 35 + symlink = true; 36 + path = "/run/secrets/rclone.conf"; 37 + file = ./encrypt/rclone.conf.age; 38 + owner = "jellyfin"; 39 + group = "${group}"; 40 + }; 41 + 42 + systemd.services."rclone-sync@" = mkIf cfg.sync { 43 + wants = [ "network-online.target" ]; 44 + description = "Sync files between different remotes via rclone"; 45 + stopIfChanged = false; 46 + serviceConfig = { 47 + Type = "simple"; 48 + User = "jellyfin"; 49 + Group = "${group}"; 50 + ExecStart = '' 51 + ${pkgs.rclone}/bin/rclone \ 52 + --config /run/secrets/rclone.conf \ 53 + -P copy \ 54 + -u \ 55 + --max-age 24h \ 56 + --multi-thread-streams 0 \ 57 + putio:%I /share/media/%I 58 + ''; 59 + }; 43 60 }; 44 - }; 45 61 46 - systemd.timers."rclone-sync@" = { 47 - description = "Sync files between different remotes via rclone periodically"; 48 - timerConfig = { 49 - OnBootSec = "15min"; 50 - OnUnitActiveSec="8h"; 51 - Persistent = true; 62 + systemd.timers."rclone-sync@" = mkIf cfg.sync { 63 + description = "Sync files between different remotes via rclone periodically"; 64 + timerConfig = { 65 + OnBootSec = "15min"; 66 + OnUnitActiveSec="8h"; 67 + Persistent = true; 68 + }; 52 69 }; 53 - }; 54 70 55 - systemd.targets.rclone-sync = { 56 - wantedBy = [ "multi-user.target" ]; 57 - wants = [ "rclone-sync@movies.timer" "rclone-sync@series.timer" ]; 71 + systemd.targets.rclone-sync = mkIf cfg.sync { 72 + wantedBy = [ "multi-user.target" ]; 73 + wants = [ "rclone-sync@movies.timer" "rclone-sync@series.timer" ]; 74 + }; 58 75 }; 59 76 }
+32 -8
modules/server/podman.nix
··· 1 - { ... }: 1 + { lib, config, ... }: 2 2 3 - { 4 - networking.firewall.trustedInterfaces = [ "podman0" ]; 3 + with lib; 4 + let 5 + cfgRoot = config.modules.server; 6 + cfg = config.modules.server.podman; 7 + in { 8 + options.modules.server.podman = { 9 + enable = mkOption { 10 + default = false; 11 + example = true; 12 + description = "Whether to enable Podman."; 13 + type = types.bool; 14 + }; 5 15 6 - virtualisation.podman = { 7 - enable = true; 8 - autoPrune.enable = true; 9 - defaultNetwork.settings = { 10 - dns_enabled = true; 16 + tweakKernel = mkEnableOption "Whether to tweak kernel configuration"; 17 + }; 18 + 19 + config = mkIf (cfg.enable && cfgRoot.enable) { 20 + networking.firewall.trustedInterfaces = [ "podman0" ]; 21 + virtualisation = { 22 + oci-containers.backend = "podman"; 23 + podman = { 24 + enable = true; 25 + autoPrune.enable = true; 26 + defaultNetwork.settings = { 27 + dns_enabled = true; 28 + }; 29 + }; 30 + }; 31 + 32 + boot.kernel.sysctl = mkIf cfg.tweakKernel { 33 + "kernel.unprivileged_userns_clone" = true; 34 + "net.ipv4.ping_group_range" = "0 65536"; 11 35 }; 12 36 }; 13 37 }
+22 -8
modules/server/sshd.nix
··· 1 - { user, ... }: 1 + { lib, config, user, ... }: 2 2 3 - { 4 - users.users."${user}".openssh.authorizedKeys.keys = [ 5 - "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDZVcY+kkbEtIiYjSyIMeIJNZjUpK+kgpMQEIOqtG5GIkTV5rb9DOoruAYy1/2HPpAaDUl7ISorBc4g0v/98cEaF04PIOWpm+HctLhPNyI0f7TClQIFNU8PLO5bMzAvIdJQmJavd42cVZmz44N8C12nL3mzCIaLGsVW/iAc2H2viHoOT3ZYxhq1f0kaDhLYjaserNgLqX12E3q5f3z1HkAg2ivRt5NHs4t4N5L6dqS/GnLAaK9rT1yCuIPQT4+XvKycaos/dMLWSPzz3ROV9mATg2uzQx9DiQd7s0pQ4UjUNL/XHlVj0TnQAS6fioVlkfb6dAxzIm9D+O4NI6b2m23Jo2XXoChKkRtVbBX/bJH8YZS2QdIlwlm57yyEbipCFjha8/GH2LUSqEkAZpbDFkIl77aSDX/D+l5svXIZke3PUmL9VX31UglP6/1hqFjMNvZHMbf+bjpjw2UILPph3QogMw8LeSfndFDDtkCDuP25MyjWi4h2QGVc8ibtQnDu3Lj8HhdQ2dOXPuHgMnty9YZXWfGaStIIsS26ZiXbkvRG5e8rlIXQbz8V1aS9851ODOeoXAU87aAG8MKiWJgtrcJRtBcZJHTZHk/I/fSKsyARWz8xtfrIOsCLSWWiY0lpCUYTCrZ4uh9jFEkYda9S8efh7QmOLXraqn6Gw+psKiU9Fw==" 6 - ]; 3 + with lib; 4 + let 5 + cfg = config.modules.server; 6 + in { 7 + options.modules.server.sshd = { 8 + enable = mkOption { 9 + default = cfg.enable; 10 + example = true; 11 + description = "Whether to enable SSH server."; 12 + type = types.bool; 13 + }; 14 + }; 15 + 16 + config = mkIf cfg.sshd.enable { 17 + users.users."${user}".openssh.authorizedKeys.keys = [ 18 + "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDZVcY+kkbEtIiYjSyIMeIJNZjUpK+kgpMQEIOqtG5GIkTV5rb9DOoruAYy1/2HPpAaDUl7ISorBc4g0v/98cEaF04PIOWpm+HctLhPNyI0f7TClQIFNU8PLO5bMzAvIdJQmJavd42cVZmz44N8C12nL3mzCIaLGsVW/iAc2H2viHoOT3ZYxhq1f0kaDhLYjaserNgLqX12E3q5f3z1HkAg2ivRt5NHs4t4N5L6dqS/GnLAaK9rT1yCuIPQT4+XvKycaos/dMLWSPzz3ROV9mATg2uzQx9DiQd7s0pQ4UjUNL/XHlVj0TnQAS6fioVlkfb6dAxzIm9D+O4NI6b2m23Jo2XXoChKkRtVbBX/bJH8YZS2QdIlwlm57yyEbipCFjha8/GH2LUSqEkAZpbDFkIl77aSDX/D+l5svXIZke3PUmL9VX31UglP6/1hqFjMNvZHMbf+bjpjw2UILPph3QogMw8LeSfndFDDtkCDuP25MyjWi4h2QGVc8ibtQnDu3Lj8HhdQ2dOXPuHgMnty9YZXWfGaStIIsS26ZiXbkvRG5e8rlIXQbz8V1aS9851ODOeoXAU87aAG8MKiWJgtrcJRtBcZJHTZHk/I/fSKsyARWz8xtfrIOsCLSWWiY0lpCUYTCrZ4uh9jFEkYda9S8efh7QmOLXraqn6Gw+psKiU9Fw==" 19 + ]; 7 20 8 - services.openssh = { 9 - enable = true; 10 - openFirewall = false; 21 + services.openssh = { 22 + enable = true; 23 + openFirewall = false; 24 + }; 11 25 }; 12 26 }
+42 -19
modules/server/tailscale.nix
··· 1 - { user, ... }: 1 + { lib, config, hostname, ... }: 2 + 3 + with lib; 4 + let 5 + cfgRoot = config.modules.server; 6 + cfg = config.modules.server.tailscale; 7 + address = config.modules.router.address; 8 + in { 9 + options.modules.server.tailscale = { 10 + enable = mkOption { 11 + default = false; 12 + example = true; 13 + description = "Whether to enable Tailscale."; 14 + type = types.bool; 15 + }; 2 16 3 - { 4 - networking = { 5 - domain = "fable-pancake.ts.net"; 6 - firewall.trustedInterfaces = [ "tailscale0" ]; 7 - hosts."10.0.0.1" = [ "cola.fable-pancake.ts.net" "cola" ]; 17 + authKeySecret = { 18 + description = "Age Secret of auth keyfile for Tailscale."; 19 + type = types.path; 20 + }; 8 21 }; 9 22 10 - age.secrets."tailscale" = { 11 - symlink = true; 12 - path = "/run/secrets/tailscale"; 13 - file = ./encrypt/tailscale.age; 14 - }; 23 + config = mkIf (cfg.enable && cfgRoot.enable) { 24 + modules.router.dnsmasq.localDomains = [ "${hostname}.fable-pancake.ts.net" ]; 25 + 26 + networking = { 27 + domain = "fable-pancake.ts.net"; 28 + firewall.trustedInterfaces = [ "tailscale0" ]; 29 + hosts."${address}" = [ "${hostname}.fable-pancake.ts.net" hostname ]; 30 + }; 15 31 16 - services.tailscale = { 17 - enable = true; 18 - useRoutingFeatures = "both"; 19 - extraUpFlags = [ "--advertise-exit-node" "--ssh" ]; 20 - extraDaemonFlags = [ "--no-logs-no-support" ]; 21 - authKeyFile = "/run/secrets/tailscale"; 22 - }; 32 + age.secrets."tailscale" = { 33 + symlink = true; 34 + path = "/run/secrets/tailscale"; 35 + file = cfg.authKeySecret; 36 + }; 23 37 24 - systemd.services.tailscaled.serviceConfig.Environment = [ "TS_DEBUG_DISABLE_PORTLIST=true" ]; 38 + services.tailscale = { 39 + enable = true; 40 + useRoutingFeatures = "both"; 41 + extraUpFlags = [ "--advertise-exit-node" "--ssh" ]; 42 + extraDaemonFlags = [ "--no-logs-no-support" ]; 43 + authKeyFile = "/run/secrets/tailscale"; 44 + }; 45 + 46 + systemd.services.tailscaled.serviceConfig.Environment = [ "TS_DEBUG_DISABLE_PORTLIST=true" ]; 47 + }; 25 48 }
+31 -16
modules/server/vaultwarden.nix
··· 1 - { ... }: 1 + { lib, config, hostname, ... }: 2 2 3 - { 4 - services.vaultwarden = { 5 - enable = true; 6 - dbBackend = "sqlite"; 7 - config = { 8 - IP_HEADER = "X-Real-IP"; 9 - ADMIN_TOKEN = "$argon2id$v=19$m=65540,t=3,p=4$+5A5H6YiN6OxyrFggkrft8Mm+sxgh/tL3USbaYFZ/h8$qj8NjE+COL4WXjmjkPWSQk7iLfhaBfBtV6k06Bql3CQ"; 10 - PASSWORD_HINTS_ALLOWED = "false"; 11 - SIGNUPS_ALLOWED = "false"; 12 - DOMAIN = "https://cola.fable-pancake.ts.net"; 13 - WEBSOCKET_ADDRESS = "127.0.0.1"; 14 - ROCKET_ADDRESS = "127.0.0.1"; 15 - WEBSOCKET_PORT = "8001"; 16 - ROCKET_PORT = "8000"; 17 - ROCKET_LIMITS = "{json=10485760}"; 3 + with lib; 4 + let 5 + cfgRoot = config.modules.server; 6 + cfg = config.modules.server.vaultwarden; 7 + in { 8 + options.modules.server.vaultwarden = { 9 + enable = mkOption { 10 + default = false; 11 + example = true; 12 + description = "Whether to enable Vaultwarden."; 13 + type = types.bool; 14 + }; 15 + }; 16 + 17 + config = mkIf (cfg.enable && cfgRoot.enable) { 18 + services.vaultwarden = { 19 + enable = true; 20 + dbBackend = "sqlite"; 21 + config = { 22 + IP_HEADER = "X-Real-IP"; 23 + ADMIN_TOKEN = "$argon2id$v=19$m=65540,t=3,p=4$+5A5H6YiN6OxyrFggkrft8Mm+sxgh/tL3USbaYFZ/h8$qj8NjE+COL4WXjmjkPWSQk7iLfhaBfBtV6k06Bql3CQ"; 24 + PASSWORD_HINTS_ALLOWED = "false"; 25 + SIGNUPS_ALLOWED = "false"; 26 + DOMAIN = "https://${hostname}.fable-pancake.ts.net"; 27 + WEBSOCKET_ADDRESS = "127.0.0.1"; 28 + ROCKET_ADDRESS = "127.0.0.1"; 29 + WEBSOCKET_PORT = "8001"; 30 + ROCKET_PORT = "8000"; 31 + ROCKET_LIMITS = "{json=10485760}"; 32 + }; 18 33 }; 19 34 }; 20 35 }
+5 -5
secrets.nix
··· 6 6 { 7 7 "./modules/base/encrypt/nix-access-tokens.conf.age".publicKeys = keys; 8 8 9 - "./modules/desktop/fonts/encrypt/DankMono-Regular.otf.age".publicKeys = keys; 10 - "./modules/desktop/fonts/encrypt/DankMono-Bold.otf.age".publicKeys = keys; 11 - "./modules/desktop/fonts/encrypt/DankMono-Italic.otf.age".publicKeys = keys; 12 - "./modules/desktop/fonts/encrypt/codicon.otf.age".publicKeys = keys; 13 - "./modules/desktop/fonts/encrypt/faicon.ttf.age".publicKeys = keys; 9 + "./modules/fonts/encrypt/DankMono-Regular.otf.age".publicKeys = keys; 10 + "./modules/fonts/encrypt/DankMono-Bold.otf.age".publicKeys = keys; 11 + "./modules/fonts/encrypt/DankMono-Italic.otf.age".publicKeys = keys; 12 + "./modules/fonts/encrypt/codicon.otf.age".publicKeys = keys; 13 + "./modules/fonts/encrypt/faicon.ttf.age".publicKeys = keys; 14 14 15 15 "./modules/server/encrypt/tailscale.age".publicKeys = keys; 16 16 "./modules/server/encrypt/rclone.conf.age".publicKeys = keys;