refactor owl

Ryan Gibb 2c13bf5a e73afd4f

+14 -85
flake.lock
··· 58 58 "type": "gitlab" 59 59 } 60 60 }, 61 - "colour-guesser": { 62 - "inputs": { 63 - "flake-utils": "flake-utils_2", 64 - "nix-filter": "nix-filter", 65 - "nixpkgs": [ 66 - "nixpkgs" 67 - ] 68 - }, 69 - "locked": { 70 - "lastModified": 1713180220, 71 - "narHash": "sha256-hTdD8t3/hvaexQXDFL2lsXgyxg93IrTFszXeCrBcmEY=", 72 - "ref": "develop", 73 - "rev": "6ecf853ca91af2e6ddcecd64e6eaa1214aaf87fa", 74 - "revCount": 11, 75 - "type": "git", 76 - "url": "ssh://git@github.com/ryangibb/colour-guesser.git" 77 - }, 78 - "original": { 79 - "ref": "develop", 80 - "type": "git", 81 - "url": "ssh://git@github.com/ryangibb/colour-guesser.git" 82 - } 83 - }, 84 61 "darwin": { 85 62 "inputs": { 86 63 "nixpkgs": [ ··· 152 129 }, 153 130 "eon": { 154 131 "inputs": { 155 - "flake-utils": "flake-utils_3", 132 + "flake-utils": "flake-utils_2", 156 133 "nixpkgs": [ 157 134 "nixpkgs" 158 135 ], ··· 287 264 }, 288 265 "flake-utils_2": { 289 266 "inputs": { 290 - "systems": "systems_2" 291 - }, 292 - "locked": { 293 - "lastModified": 1681202837, 294 - "narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=", 295 - "owner": "numtide", 296 - "repo": "flake-utils", 297 - "rev": "cfacdce06f30d2b68473a46042957675eebb3401", 298 - "type": "github" 299 - }, 300 - "original": { 301 - "id": "flake-utils", 302 - "type": "indirect" 303 - } 304 - }, 305 - "flake-utils_3": { 306 - "inputs": { 307 - "systems": "systems_5" 267 + "systems": "systems_4" 308 268 }, 309 269 "locked": { 310 270 "lastModified": 1731533236, ··· 320 280 "type": "github" 321 281 } 322 282 }, 323 - "flake-utils_4": { 283 + "flake-utils_3": { 324 284 "locked": { 325 285 "lastModified": 1676283394, 326 286 "narHash": "sha256-XX2f9c3iySLCw54rJ/CZs+ZK6IQy7GXNY4nSOyu2QG4=", ··· 334 294 "type": "indirect" 335 295 } 336 296 }, 337 - "flake-utils_5": { 297 + "flake-utils_4": { 338 298 "inputs": { 339 - "systems": "systems_6" 299 + "systems": "systems_5" 340 300 }, 341 301 "locked": { 342 302 "lastModified": 1694529238, ··· 352 312 "type": "github" 353 313 } 354 314 }, 355 - "flake-utils_6": { 315 + "flake-utils_5": { 356 316 "locked": { 357 317 "lastModified": 1638122382, 358 318 "narHash": "sha256-sQzZzAbvKEqN9s0bzWuYmRaA03v40gaJ4+iL1LXjaeI=", ··· 367 327 "type": "github" 368 328 } 369 329 }, 370 - "flake-utils_7": { 330 + "flake-utils_6": { 371 331 "inputs": { 372 - "systems": "systems_7" 332 + "systems": "systems_6" 373 333 }, 374 334 "locked": { 375 335 "lastModified": 1692799911, ··· 387 347 }, 388 348 "fn06-website": { 389 349 "inputs": { 390 - "flake-utils": "flake-utils_4", 350 + "flake-utils": "flake-utils_3", 391 351 "nixpkgs": [ 392 352 "nixpkgs" 393 353 ] ··· 505 465 }, 506 466 "hyperbib-eeg": { 507 467 "inputs": { 508 - "flake-utils": "flake-utils_5", 468 + "flake-utils": "flake-utils_4", 509 469 "nixpkgs": [ 510 470 "nixpkgs" 511 471 ], ··· 529 489 }, 530 490 "i3-workspace-history": { 531 491 "inputs": { 532 - "flake-utils": "flake-utils_7", 492 + "flake-utils": "flake-utils_6", 533 493 "gomod2nix": "gomod2nix", 534 494 "nixpkgs": [ 535 495 "nixpkgs" ··· 625 585 "type": "github" 626 586 } 627 587 }, 628 - "nix-filter": { 629 - "locked": { 630 - "lastModified": 1681154353, 631 - "narHash": "sha256-MCJ5FHOlbfQRFwN0brqPbCunLEVw05D/3sRVoNVt2tI=", 632 - "owner": "numtide", 633 - "repo": "nix-filter", 634 - "rev": "f529f42792ade8e32c4be274af6b6d60857fbee7", 635 - "type": "github" 636 - }, 637 - "original": { 638 - "owner": "numtide", 639 - "repo": "nix-filter", 640 - "type": "github" 641 - } 642 - }, 643 588 "nix-formatter-pack": { 644 589 "inputs": { 645 590 "nixpkgs": [ ··· 1023 968 "opam-nix_2": { 1024 969 "inputs": { 1025 970 "flake-compat": "flake-compat_4", 1026 - "flake-utils": "flake-utils_6", 971 + "flake-utils": "flake-utils_5", 1027 972 "mirage-opam-overlays": "mirage-opam-overlays_2", 1028 973 "nixpkgs": [ 1029 974 "hyperbib-eeg", ··· 1162 1107 "inputs": { 1163 1108 "agenix": "agenix", 1164 1109 "alec-website": "alec-website", 1165 - "colour-guesser": "colour-guesser", 1166 1110 "deploy-rs": "deploy-rs", 1167 1111 "eilean": "eilean", 1168 1112 "eon": "eon", ··· 1311 1255 "type": "github" 1312 1256 } 1313 1257 }, 1314 - "systems_7": { 1315 - "locked": { 1316 - "lastModified": 1681028828, 1317 - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 1318 - "owner": "nix-systems", 1319 - "repo": "default", 1320 - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 1321 - "type": "github" 1322 - }, 1323 - "original": { 1324 - "owner": "nix-systems", 1325 - "repo": "default", 1326 - "type": "github" 1327 - } 1328 - }, 1329 1258 "tangled": { 1330 1259 "inputs": { 1331 1260 "gitignore": "gitignore", ··· 1395 1324 }, 1396 1325 "utils": { 1397 1326 "inputs": { 1398 - "systems": "systems_3" 1327 + "systems": "systems_2" 1399 1328 }, 1400 1329 "locked": { 1401 1330 "lastModified": 1701680307, ··· 1413 1342 }, 1414 1343 "utils_2": { 1415 1344 "inputs": { 1416 - "systems": "systems_4" 1345 + "systems": "systems_3" 1417 1346 }, 1418 1347 "locked": { 1419 1348 "lastModified": 1709126324,
-2
flake.nix
··· 15 15 eilean.url = "github:RyanGibb/eilean-nix/main"; 16 16 alec-website.url = "github:alexanderhthompson/website"; 17 17 fn06-website.url = "github:RyanGibb/fn06"; 18 - colour-guesser.url = "git+ssh://git@github.com/ryangibb/colour-guesser.git?ref=develop"; 19 18 i3-workspace-history.url = "github:RyanGibb/i3-workspace-history"; 20 19 hyperbib-eeg.url = "github:RyanGibb/hyperbib?ref=nixify"; 21 20 nix-rpi5.url = "gitlab:vriska/nix-rpi5?ref=main"; ··· 33 32 alec-website.inputs.nixpkgs.follows = "nixpkgs"; 34 33 fn06-website.inputs.nixpkgs.follows = "nixpkgs"; 35 34 eon.inputs.nixpkgs.follows = "nixpkgs"; 36 - colour-guesser.inputs.nixpkgs.follows = "nixpkgs"; 37 35 i3-workspace-history.inputs.nixpkgs.follows = "nixpkgs"; 38 36 hyperbib-eeg.inputs.nixpkgs.follows = "nixpkgs"; 39 37 nix-rpi5.inputs.nixpkgs.follows = "nixpkgs";
+1 -513
hosts/owl/default.nix
··· 1 1 { 2 2 pkgs, 3 - config, 4 3 lib, 5 - eon, 6 4 ... 7 5 }@inputs: 8 6 9 - let 10 - vpnRecords = [ 11 - { 12 - name = "nix-cache.vpn.${config.networking.domain}."; 13 - type = "A"; 14 - value = "100.64.0.9"; 15 - } 16 - { 17 - name = "jellyfin.vpn.${config.networking.domain}."; 18 - type = "A"; 19 - value = "100.64.0.9"; 20 - } 21 - { 22 - name = "nextcloud.vpn.${config.networking.domain}."; 23 - type = "A"; 24 - value = "100.64.0.9"; 25 - } 26 - { 27 - name = "transmission.vpn.${config.networking.domain}."; 28 - type = "A"; 29 - value = "100.64.0.9"; 30 - } 31 - { 32 - name = "owntracks.vpn.${config.networking.domain}."; 33 - type = "A"; 34 - value = "100.64.0.9"; 35 - } 36 - { 37 - name = "immich.vpn.${config.networking.domain}."; 38 - type = "A"; 39 - value = "100.64.0.9"; 40 - } 41 - { 42 - name = "audiobookshelf.vpn.${config.networking.domain}."; 43 - type = "A"; 44 - value = "100.64.0.9"; 45 - } 46 - ]; 47 - in 48 7 { 49 8 imports = [ 50 9 ./hardware-configuration.nix 51 10 ./minimal.nix 52 - ../../modules/colour-guesser.nix 11 + ./services.nix 53 12 ../../modules/ryan-website.nix 54 13 ../../modules/alec-website.nix 55 14 ../../modules/fn06-website.nix ··· 57 16 ]; 58 17 59 18 environment.systemPackages = with pkgs; [ 60 - # nix 61 19 nixd 62 20 nixfmt-rfc-style 63 21 ]; 64 22 65 - age.secrets.eon-capnp = { 66 - file = ../../secrets/eon-capnp.age; 67 - mode = "770"; 68 - owner = "eon"; 69 - group = "eon"; 70 - }; 71 - age.secrets.eon-sirref-primary = { 72 - file = ../../secrets/eon-sirref-primary.cap.age; 73 - mode = "770"; 74 - owner = "eon"; 75 - group = "eon"; 76 - }; 77 - services.eon = { 78 - capnpSecretKeyFile = config.age.secrets.eon-capnp.path; 79 - primaries = [ config.age.secrets.eon-sirref-primary.path ]; 80 - prod = true; 81 - capnpAddress = "135.181.100.27"; 82 - logLevel = 0; 83 - }; 84 - 85 - security.acme-eon = { 86 - acceptTerms = true; 87 - package = eon.defaultPackage.${config.nixpkgs.hostPlatform.system}; 88 - defaults.email = "${config.custom.username}@${config.networking.domain}"; 89 - defaults.capFile = "/var/lib/eon/caps/domain/freumh.org.cap"; 90 - certs = { 91 - "fn06.org".capFile = "/var/lib/eon/caps/domain/fn06.org.cap"; 92 - "capybara.fn06.org".capFile = "/var/lib/eon/caps/domain/fn06.org.cap"; 93 - }; 94 - }; 95 - 96 - eilean = { 97 - username = config.custom.username; 98 - serverIpv4 = "135.181.100.27"; 99 - serverIpv6 = "2a01:4f9:c011:87ad:0:0:0:0"; 100 - acme-eon = true; 101 - fail2ban.enable = true; 102 - }; 103 - networking.domain = lib.mkDefault "freumh.org"; 104 - eilean.publicInterface = "enp1s0"; 105 - eilean.mailserver.enable = true; 106 - eilean.radicale = { 107 - enable = true; 108 - users = null; 109 - }; 110 - age.secrets.matrix-shared-secret = { 111 - file = ../../secrets/matrix-shared-secret.age; 112 - mode = "770"; 113 - owner = "${config.systemd.services.matrix-synapse.serviceConfig.User}"; 114 - group = "${config.systemd.services.matrix-synapse.serviceConfig.Group}"; 115 - }; 116 - eilean.matrix = { 117 - enable = true; 118 - registrationSecretFile = config.age.secrets.matrix-shared-secret.path; 119 - bridges.whatsapp = true; 120 - bridges.signal = true; 121 - bridges.instagram = true; 122 - bridges.messenger = true; 123 - }; 124 - eilean.turn.enable = true; 125 - eilean.mastodon.enable = true; 126 - eilean.headscale.enable = true; 127 - #eilean.dns.enable = lib.mkForce false; 128 - 129 - systemd.services.matrix-as-meta = { 130 - # voice messages need `ffmpeg` 131 - path = [ pkgs.ffmpeg ]; 132 - }; 133 - 134 - custom = { 135 - freumh.enable = true; 136 - rmfakecloud.enable = true; 137 - website = { 138 - ryan = { 139 - enable = true; 140 - cname = "vps"; 141 - }; 142 - alec = { 143 - enable = true; 144 - cname = "vps"; 145 - }; 146 - fn06 = { 147 - enable = true; 148 - cname = "vps"; 149 - domain = "fn06.org"; 150 - }; 151 - colour-guesser = { 152 - # enable = true; 153 - cname = "vps"; 154 - }; 155 - }; 156 - }; 157 - 158 - eilean.dns.nameservers = [ "ns1" ]; 159 - eilean.services.dns.zones = { 160 - ${config.networking.domain} = { 161 - ttl = 300; 162 - soa = { 163 - serial = 2018011660; 164 - refresh = 300; 165 - }; 166 - records = [ 167 - { 168 - name = "@"; 169 - type = "TXT"; 170 - value = "google-site-verification=rEvwSqf7RYKRQltY412qMtTuoxPp64O3L7jMotj9Jnc"; 171 - } 172 - { 173 - name = "_atproto.ryan"; 174 - type = "TXT"; 175 - value = "did=did:plc:3lfhu6ehlynzjgehef6alnvg"; 176 - } 177 - 178 - { 179 - name = "teapot"; 180 - type = "CNAME"; 181 - value = "vps"; 182 - } 183 - 184 - { 185 - name = "@"; 186 - type = "NS"; 187 - value = "ns1.sirref.org."; 188 - } 189 - 190 - { 191 - name = "@"; 192 - type = "A"; 193 - value = config.eilean.serverIpv4; 194 - } 195 - { 196 - name = "@"; 197 - type = "AAAA"; 198 - value = config.eilean.serverIpv6; 199 - } 200 - { 201 - name = "vps"; 202 - type = "A"; 203 - value = config.eilean.serverIpv4; 204 - } 205 - { 206 - name = "vps"; 207 - type = "AAAA"; 208 - value = config.eilean.serverIpv6; 209 - } 210 - 211 - { 212 - name = "@"; 213 - type = "LOC"; 214 - value = "52 12 40.4 N 0 5 31.9 E 22m 10m 10m 10m"; 215 - } 216 - 217 - { 218 - name = "ns.cl"; 219 - type = "A"; 220 - value = "128.232.113.136"; 221 - } 222 - { 223 - name = "cl"; 224 - type = "NS"; 225 - value = "ns.cl"; 226 - } 227 - 228 - { 229 - name = "ns1.eilean"; 230 - type = "A"; 231 - value = "65.109.10.223"; 232 - } 233 - { 234 - name = "eilean"; 235 - type = "NS"; 236 - value = "ns1.eilean"; 237 - } 238 - 239 - { 240 - name = "shrew"; 241 - type = "CNAME"; 242 - value = "vps"; 243 - } 244 - 245 - { 246 - name = "knot"; 247 - type = "CNAME"; 248 - value = "vps"; 249 - } 250 - 251 - # generate with 252 - # sudo openssl x509 -in /var/lib/acme/mail.freumh.org/fullchain.pem -pubkey -noout | openssl pkey -pubin -outform der | sha256sum | awk '{print "3 1 1", $1}' 253 - { 254 - name = "_25._tcp.mail"; 255 - type = "TLSA"; 256 - value = "3 1 1 2f0fd413f063c75141937dd196a9f4ab66139d599e0dcf2a7ce6d557647e26a6"; 257 - } 258 - # generate with 259 - # for i in r3 e1 r4-cross-signed e2 260 - # openssl x509 -in ~/downloads/lets-encrypt-$i.pem -pubkey -noout | openssl pkey -pubin -outform der | sha256sum | awk '{print "2 1 1", $1}' 261 - # LE R3 262 - { 263 - name = "_25._tcp.mail"; 264 - type = "TLSA"; 265 - value = "2 1 1 8d02536c887482bc34ff54e41d2ba659bf85b341a0a20afadb5813dcfbcf286d"; 266 - } 267 - # LE E1 268 - { 269 - name = "_25._tcp.mail"; 270 - type = "TLSA"; 271 - value = "2 1 1 276fe8a8c4ec7611565bf9fce6dcace9be320c1b5bea27596b2204071ed04f10"; 272 - } 273 - # LE R4 274 - { 275 - name = "_25._tcp.mail"; 276 - type = "TLSA"; 277 - value = "2 1 1 e5545e211347241891c554a03934cde9b749664a59d26d615fe58f77990f2d03"; 278 - } 279 - # LE E2 280 - { 281 - name = "_25._tcp.mail"; 282 - type = "TLSA"; 283 - value = "2 1 1 bd936e72b212ef6f773102c6b77d38f94297322efc25396bc3279422e0c89270"; 284 - } 285 - ] ++ vpnRecords; 286 - }; 287 - "fn06.org" = { 288 - soa.serial = 1706745602; 289 - records = [ 290 - { 291 - name = "@"; 292 - type = "NS"; 293 - value = "ns1"; 294 - } 295 - { 296 - name = "@"; 297 - type = "NS"; 298 - value = "ns2"; 299 - } 300 - 301 - { 302 - name = "ns1"; 303 - type = "A"; 304 - value = config.eilean.serverIpv4; 305 - } 306 - { 307 - name = "ns1"; 308 - type = "AAAA"; 309 - value = config.eilean.serverIpv6; 310 - } 311 - { 312 - name = "ns2"; 313 - type = "A"; 314 - value = config.eilean.serverIpv4; 315 - } 316 - { 317 - name = "ns2"; 318 - type = "AAAA"; 319 - value = config.eilean.serverIpv6; 320 - } 321 - 322 - { 323 - name = "@"; 324 - type = "A"; 325 - value = config.eilean.serverIpv4; 326 - } 327 - { 328 - name = "@"; 329 - type = "AAAA"; 330 - value = config.eilean.serverIpv6; 331 - } 332 - 333 - { 334 - name = "www.fn06.org."; 335 - type = "CNAME"; 336 - value = "fn06.org."; 337 - } 338 - 339 - { 340 - name = "@"; 341 - type = "LOC"; 342 - value = "52 12 40.4 N 0 5 31.9 E 22m 10m 10m 10m"; 343 - } 344 - 345 - { 346 - name = "capybara.fn06.org."; 347 - type = "CNAME"; 348 - value = "fn06.org."; 349 - } 350 - 351 - { 352 - name = "jellyfin.${config.networking.domain}."; 353 - type = "AAAA"; 354 - value = "2a00:23c6:aa22:e401:8dff:9b9a:cb3c:3fcb"; 355 - } 356 - { 357 - name = "jellyseerr.${config.networking.domain}."; 358 - type = "AAAA"; 359 - value = "2a00:23c6:aa22:e401:8dff:9b9a:cb3c:3fcb"; 360 - } 361 - { 362 - name = "calibre.${config.networking.domain}."; 363 - type = "AAAA"; 364 - value = "2a00:23c6:aa22:e401:8dff:9b9a:cb3c:3fcb"; 365 - } 366 - ]; 367 - }; 368 - }; 369 - services.bind.zones.${config.networking.domain}.extraConfig = 370 - '' 371 - dnssec-policy default; 372 - inline-signing yes; 373 - journal "${config.services.bind.directory}/${config.networking.domain}.signed.jnl"; 374 - '' 375 - + 376 - # dig ns org +short | xargs dig +short 377 - # replace with `checkds true;` in bind 9.20 378 - '' 379 - parental-agents { 380 - 199.19.56.1; 381 - 199.249.112.1; 382 - 199.19.54.1; 383 - 199.249.120.1; 384 - 199.19.53.1; 385 - 199.19.57.1; 386 - }; 387 - ''; 388 - 389 - services.nginx.commonHttpConfig = '' 390 - add_header Strict-Transport-Security max-age=31536000 always; 391 - add_header X-Frame-Options SAMEORIGIN always; 392 - add_header X-Content-Type-Options nosniff always; 393 - add_header Content-Security-Policy "default-src 'self' 'unsafe-inline' 'unsafe-eval' blob:; base-uri 'self'; frame-src 'self'; frame-ancestors 'self'; form-action 'self';" always; 394 - add_header Referrer-Policy 'same-origin'; 395 - ''; 396 - services.nginx.virtualHosts."teapot.${config.networking.domain}" = { 397 - extraConfig = '' 398 - return 418; 399 - ''; 400 - }; 401 - age.secrets.website-phd = { 402 - file = ../../secrets/website-phd.age; 403 - mode = "770"; 404 - owner = "${config.systemd.services.nginx.serviceConfig.User}"; 405 - group = "${config.systemd.services.nginx.serviceConfig.Group}"; 406 - }; 407 - services.nginx.virtualHosts."${config.custom.website.ryan.domain}" = { 408 - locations."/phd/" = { 409 - basicAuthFile = config.age.secrets.website-phd.path; 410 - }; 411 - }; 412 - 413 - security.acme-eon.nginxCerts = [ 414 - "capybara.fn06.org" 415 - "shrew.freumh.org" 416 - "knot.freumh.org" 417 - ]; 418 - services.nginx.virtualHosts."capybara.fn06.org" = { 419 - forceSSL = true; 420 - locations."/" = { 421 - proxyPass = '' 422 - http://100.64.0.10:8123 423 - ''; 424 - proxyWebsockets = true; 425 - }; 426 - }; 427 - services.nginx.virtualHosts."shrew.freumh.org" = { 428 - forceSSL = true; 429 - locations."/" = { 430 - # need to specify ip or there's a bootstrap problem with headscale 431 - proxyPass = '' 432 - http://100.64.0.6:8123 433 - ''; 434 - proxyWebsockets = true; 435 - }; 436 - }; 437 - 438 - services.mastodon = { 439 - webProcesses = lib.mkForce 1; 440 - webThreads = lib.mkForce 1; 441 - sidekiqThreads = lib.mkForce 1; 442 - streamingProcesses = lib.mkForce 1; 443 - }; 444 - 445 - boot.kernel.sysctl = { 446 - "net.ipv4.ip_forward" = 1; 447 - "net.ipv6.conf.all.forwarding" = 1; 448 - }; 449 - 450 - services.headscale.settings.dns = { 451 - extra_records = vpnRecords; 452 - base_domain = "vpn.freumh.org"; 453 - nameservers.global = config.networking.nameservers; 454 - }; 455 - 456 - age.secrets.restic-owl.file = ../../secrets/restic-owl.age; 457 - services.restic.backups.${config.networking.hostName} = { 458 - repository = "rest:http://100.64.0.9:8000/${config.networking.hostName}/"; 459 - passwordFile = config.age.secrets.restic-owl.path; 460 - initialize = true; 461 - paths = [ 462 - "/var/" 463 - "/run/" 464 - "/etc/" 465 - ]; 466 - timerConfig = { 467 - OnCalendar = "03:00"; 468 - randomizedDelaySec = "1hr"; 469 - }; 470 - }; 471 - 472 23 nix = { 473 24 gc = { 474 25 automatic = true; ··· 478 29 }; 479 30 }; 480 31 481 - age.secrets.email-ryan.file = ../../secrets/email-ryan.age; 482 - age.secrets.email-system.file = ../../secrets/email-system.age; 483 - eilean.mailserver.systemAccountPasswordFile = config.age.secrets.email-system.path; 484 - mailserver.loginAccounts = { 485 - "${config.eilean.username}@${config.networking.domain}" = { 486 - passwordFile = config.age.secrets.email-ryan.path; 487 - aliases = [ 488 - "dns@${config.networking.domain}" 489 - "postmaster@${config.networking.domain}" 490 - ]; 491 - sieveScript = '' 492 - require ["fileinto", "mailbox"]; 493 - 494 - if header :contains ["to", "cc"] ["ai-control@ietf.org"] { 495 - fileinto :create "lists.aietf"; 496 - stop; 497 - } 498 - ''; 499 - }; 500 - "misc@${config.networking.domain}" = { 501 - passwordFile = config.age.secrets.email-ryan.path; 502 - catchAll = [ "${config.networking.domain}" ]; 503 - }; 504 - "system@${config.networking.domain}" = { 505 - aliases = [ "nas@${config.networking.domain}" ]; 506 - }; 507 - }; 508 - 509 - services.minecraft-server = { 510 - enable = true; 511 - package = pkgs.overlay-unstable.minecraft-server; 512 - eula = true; 513 - openFirewall = true; 514 - }; 515 - 516 - networking.firewall.allowedTCPPorts = [ 7001 ]; 517 - 518 32 services.openssh.openFirewall = true; 519 - 520 - age.secrets.tangled = { 521 - file = ../../secrets/tangled.age; 522 - mode = "660"; 523 - owner = "git"; 524 - group = "git"; 525 - }; 526 - services.tangled-knotserver = { 527 - enable = true; 528 - repo.mainBranch = "master"; 529 - server.hostname = "knot.freumh.org"; 530 - server = { 531 - secretFile = config.age.secrets.tangled.path; 532 - listenAddr = "127.0.0.1:5555"; 533 - internalListenAddr = "127.0.0.1:5444"; 534 - }; 535 - }; 536 - services.nginx.virtualHosts."knot.freumh.org" = { 537 - forceSSL = true; 538 - locations."/" = { 539 - proxyPass = '' 540 - http://${config.services.tangled-knotserver.server.listenAddr} 541 - ''; 542 - proxyWebsockets = true; 543 - }; 544 - }; 545 33 }
-3
hosts/owl/minimal.nix
··· 1 1 { 2 - pkgs, 3 2 config, 4 - lib, 5 - eilean, 6 3 ... 7 4 }: 8 5
+482
hosts/owl/services.nix
··· 1 + { 2 + pkgs, 3 + config, 4 + lib, 5 + eon, 6 + ... 7 + }: 8 + 9 + let 10 + vpnRecords = [ 11 + { 12 + name = "nix-cache.vpn.${config.networking.domain}."; 13 + type = "A"; 14 + value = "100.64.0.9"; 15 + } 16 + { 17 + name = "jellyfin.vpn.${config.networking.domain}."; 18 + type = "A"; 19 + value = "100.64.0.9"; 20 + } 21 + { 22 + name = "nextcloud.vpn.${config.networking.domain}."; 23 + type = "A"; 24 + value = "100.64.0.9"; 25 + } 26 + { 27 + name = "transmission.vpn.${config.networking.domain}."; 28 + type = "A"; 29 + value = "100.64.0.9"; 30 + } 31 + { 32 + name = "owntracks.vpn.${config.networking.domain}."; 33 + type = "A"; 34 + value = "100.64.0.9"; 35 + } 36 + { 37 + name = "immich.vpn.${config.networking.domain}."; 38 + type = "A"; 39 + value = "100.64.0.9"; 40 + } 41 + { 42 + name = "audiobookshelf.vpn.${config.networking.domain}."; 43 + type = "A"; 44 + value = "100.64.0.9"; 45 + } 46 + ]; 47 + in 48 + { 49 + # eilean 50 + networking.domain = lib.mkDefault "freumh.org"; 51 + eilean = { 52 + username = config.custom.username; 53 + serverIpv4 = "135.181.100.27"; 54 + serverIpv6 = "2a01:4f9:c011:87ad:0:0:0:0"; 55 + publicInterface = "enp1s0"; 56 + fail2ban.enable = true; 57 + }; 58 + 59 + # eon 60 + age.secrets.eon-capnp = { 61 + file = ../../secrets/eon-capnp.age; 62 + mode = "660"; 63 + owner = "eon"; 64 + group = "eon"; 65 + }; 66 + age.secrets.eon-sirref-primary = { 67 + file = ../../secrets/eon-sirref-primary.cap.age; 68 + mode = "660"; 69 + owner = "eon"; 70 + group = "eon"; 71 + }; 72 + services.eon = { 73 + capnpSecretKeyFile = config.age.secrets.eon-capnp.path; 74 + primaries = [ config.age.secrets.eon-sirref-primary.path ]; 75 + prod = true; 76 + capnpAddress = "135.181.100.27"; 77 + logLevel = 0; 78 + }; 79 + 80 + # certificates 81 + eilean.acme-eon = true; 82 + security.acme-eon = { 83 + acceptTerms = true; 84 + package = eon.defaultPackage.${config.nixpkgs.hostPlatform.system}; 85 + defaults.email = "${config.custom.username}@${config.networking.domain}"; 86 + defaults.capFile = "/var/lib/eon/caps/domain/freumh.org.cap"; 87 + certs = { 88 + "fn06.org".capFile = "/var/lib/eon/caps/domain/fn06.org.cap"; 89 + "capybara.fn06.org".capFile = "/var/lib/eon/caps/domain/fn06.org.cap"; 90 + }; 91 + }; 92 + security.acme-eon.nginxCerts = [ 93 + "capybara.fn06.org" 94 + "shrew.freumh.org" 95 + "knot.freumh.org" 96 + ]; 97 + 98 + # VPN 99 + eilean.headscale.enable = true; 100 + services.headscale.settings.dns = { 101 + extra_records = vpnRecords; 102 + base_domain = "vpn.freumh.org"; 103 + nameservers.global = config.networking.nameservers; 104 + }; 105 + boot.kernel.sysctl = { 106 + "net.ipv4.ip_forward" = 1; 107 + "net.ipv6.conf.all.forwarding" = 1; 108 + }; 109 + 110 + # websites 111 + custom = { 112 + freumh.enable = true; 113 + rmfakecloud.enable = true; 114 + website = { 115 + ryan = { 116 + enable = true; 117 + cname = "vps"; 118 + }; 119 + alec = { 120 + enable = true; 121 + cname = "vps"; 122 + }; 123 + fn06 = { 124 + enable = true; 125 + cname = "vps"; 126 + domain = "fn06.org"; 127 + }; 128 + }; 129 + }; 130 + services.nginx.commonHttpConfig = '' 131 + add_header Strict-Transport-Security max-age=31536000 always; 132 + add_header X-Frame-Options SAMEORIGIN always; 133 + add_header X-Content-Type-Options nosniff always; 134 + add_header Content-Security-Policy "default-src 'self' 'unsafe-inline' 'unsafe-eval' blob:; base-uri 'self'; frame-src 'self'; frame-ancestors 'self'; form-action 'self';" always; 135 + add_header Referrer-Policy 'same-origin'; 136 + ''; 137 + services.nginx.virtualHosts."teapot.${config.networking.domain}" = { 138 + extraConfig = '' 139 + return 418; 140 + ''; 141 + }; 142 + 143 + # mailserver 144 + eilean.mailserver.enable = true; 145 + age.secrets.email-ryan.file = ../../secrets/email-ryan.age; 146 + age.secrets.email-system.file = ../../secrets/email-system.age; 147 + eilean.mailserver.systemAccountPasswordFile = config.age.secrets.email-system.path; 148 + mailserver.loginAccounts = { 149 + "${config.eilean.username}@${config.networking.domain}" = { 150 + passwordFile = config.age.secrets.email-ryan.path; 151 + aliases = [ 152 + "dns@${config.networking.domain}" 153 + "postmaster@${config.networking.domain}" 154 + ]; 155 + sieveScript = '' 156 + require ["fileinto", "mailbox"]; 157 + 158 + if header :contains ["to", "cc"] ["ai-control@ietf.org"] { 159 + fileinto :create "lists.aietf"; 160 + stop; 161 + } 162 + ''; 163 + }; 164 + "misc@${config.networking.domain}" = { 165 + passwordFile = config.age.secrets.email-ryan.path; 166 + catchAll = [ "${config.networking.domain}" ]; 167 + }; 168 + "system@${config.networking.domain}" = { 169 + aliases = [ "nas@${config.networking.domain}" ]; 170 + }; 171 + }; 172 + 173 + # CalDAV calendar server 174 + eilean.radicale = { 175 + enable = true; 176 + users = null; 177 + }; 178 + 179 + # matrix 180 + age.secrets.matrix-shared-secret = { 181 + file = ../../secrets/matrix-shared-secret.age; 182 + mode = "770"; 183 + owner = "${config.systemd.services.matrix-synapse.serviceConfig.User}"; 184 + group = "${config.systemd.services.matrix-synapse.serviceConfig.Group}"; 185 + }; 186 + eilean.matrix = { 187 + enable = true; 188 + registrationSecretFile = config.age.secrets.matrix-shared-secret.path; 189 + bridges.whatsapp = true; 190 + bridges.signal = true; 191 + bridges.instagram = true; 192 + bridges.messenger = true; 193 + }; 194 + eilean.turn.enable = true; 195 + systemd.services.matrix-as-meta = { 196 + path = [ pkgs.ffmpeg ]; 197 + }; 198 + 199 + # mastodon 200 + eilean.mastodon.enable = true; 201 + services.mastodon = { 202 + webProcesses = lib.mkForce 1; 203 + webThreads = lib.mkForce 1; 204 + sidekiqThreads = lib.mkForce 1; 205 + streamingProcesses = lib.mkForce 1; 206 + }; 207 + 208 + # restic 209 + age.secrets.restic-owl.file = ../../secrets/restic-owl.age; 210 + services.restic.backups.${config.networking.hostName} = { 211 + repository = "rest:http://100.64.0.9:8000/${config.networking.hostName}/"; 212 + passwordFile = config.age.secrets.restic-owl.path; 213 + initialize = true; 214 + paths = [ 215 + "/var/" 216 + "/run/" 217 + "/etc/" 218 + ]; 219 + timerConfig = { 220 + OnCalendar = "03:00"; 221 + randomizedDelaySec = "1hr"; 222 + }; 223 + }; 224 + 225 + # reverse proxy 226 + services.nginx.virtualHosts."capybara.fn06.org" = { 227 + forceSSL = true; 228 + locations."/" = { 229 + proxyPass = '' 230 + http://100.64.0.10:8123 231 + ''; 232 + proxyWebsockets = true; 233 + }; 234 + }; 235 + services.nginx.virtualHosts."shrew.freumh.org" = { 236 + forceSSL = true; 237 + locations."/" = { 238 + proxyPass = '' 239 + http://100.64.0.6:8123 240 + ''; 241 + proxyWebsockets = true; 242 + }; 243 + }; 244 + 245 + # tangled 246 + age.secrets.tangled = { 247 + file = ../../secrets/tangled.age; 248 + mode = "660"; 249 + owner = "git"; 250 + group = "git"; 251 + }; 252 + services.tangled-knotserver = { 253 + enable = true; 254 + repo.mainBranch = "master"; 255 + server.hostname = "knot.freumh.org"; 256 + server = { 257 + secretFile = config.age.secrets.tangled.path; 258 + listenAddr = "127.0.0.1:5555"; 259 + internalListenAddr = "127.0.0.1:5444"; 260 + }; 261 + }; 262 + services.nginx.virtualHosts."knot.freumh.org" = { 263 + forceSSL = true; 264 + locations."/" = { 265 + proxyPass = '' 266 + http://${config.services.tangled-knotserver.server.listenAddr} 267 + ''; 268 + proxyWebsockets = true; 269 + }; 270 + }; 271 + 272 + # minecraft server 273 + services.minecraft-server = { 274 + enable = true; 275 + package = pkgs.overlay-unstable.minecraft-server; 276 + eula = true; 277 + openFirewall = true; 278 + }; 279 + 280 + # DNS records 281 + eilean.dns.nameservers = [ "ns1" ]; 282 + eilean.services.dns.zones = { 283 + ${config.networking.domain} = { 284 + ttl = 300; 285 + soa = { 286 + serial = 2018011660; 287 + refresh = 300; 288 + }; 289 + records = [ 290 + { 291 + name = "@"; 292 + type = "TXT"; 293 + value = "google-site-verification=rEvwSqf7RYKRQltY412qMtTuoxPp64O3L7jMotj9Jnc"; 294 + } 295 + { 296 + name = "_atproto.ryan"; 297 + type = "TXT"; 298 + value = "did=did:plc:3lfhu6ehlynzjgehef6alnvg"; 299 + } 300 + 301 + { 302 + name = "teapot"; 303 + type = "CNAME"; 304 + value = "vps"; 305 + } 306 + 307 + { 308 + name = "@"; 309 + type = "ns"; 310 + value = "ns1.sirref.org."; 311 + } 312 + 313 + { 314 + name = "vps"; 315 + type = "A"; 316 + value = config.eilean.serverIpv4; 317 + } 318 + { 319 + name = "vps"; 320 + type = "AAAA"; 321 + value = config.eilean.serverIpv6; 322 + } 323 + 324 + { 325 + name = "@"; 326 + type = "LOC"; 327 + value = "52 12 40.4 N 0 5 31.9 E 22m 10m 10m 10m"; 328 + } 329 + 330 + { 331 + name = "ns.cl"; 332 + type = "A"; 333 + value = "128.232.113.136"; 334 + } 335 + { 336 + name = "cl"; 337 + type = "NS"; 338 + value = "ns.cl"; 339 + } 340 + 341 + { 342 + name = "ns1.eilean"; 343 + type = "A"; 344 + value = "65.109.10.223"; 345 + } 346 + { 347 + name = "eilean"; 348 + type = "NS"; 349 + value = "ns1.eilean"; 350 + } 351 + 352 + { 353 + name = "shrew"; 354 + type = "CNAME"; 355 + value = "vps"; 356 + } 357 + 358 + { 359 + name = "knot"; 360 + type = "CNAME"; 361 + value = "vps"; 362 + } 363 + 364 + # generate with 365 + # sudo openssl x509 -in /var/lib/acme/mail.freumh.org/fullchain.pem -pubkey -noout | openssl pkey -pubin -outform der | sha256sum | awk '{print "3 1 1", $1}' 366 + { 367 + name = "_25._tcp.mail"; 368 + type = "TLSA"; 369 + value = "3 1 1 2f0fd413f063c75141937dd196a9f4ab66139d599e0dcf2a7ce6d557647e26a6"; 370 + } 371 + # generate with 372 + # for i in r3 e1 r4-cross-signed e2 373 + # openssl x509 -in ~/downloads/lets-encrypt-$i.pem -pubkey -noout | openssl pkey -pubin -outform der | sha256sum | awk '{print "2 1 1", $1}' 374 + # LE R3 375 + { 376 + name = "_25._tcp.mail"; 377 + type = "TLSA"; 378 + value = "2 1 1 8d02536c887482bc34ff54e41d2ba659bf85b341a0a20afadb5813dcfbcf286d"; 379 + } 380 + # LE E1 381 + { 382 + name = "_25._tcp.mail"; 383 + type = "TLSA"; 384 + value = "2 1 1 276fe8a8c4ec7611565bf9fce6dcace9be320c1b5bea27596b2204071ed04f10"; 385 + } 386 + # LE R4 387 + { 388 + name = "_25._tcp.mail"; 389 + type = "TLSA"; 390 + value = "2 1 1 e5545e211347241891c554a03934cde9b749664a59d26d615fe58f77990f2d03"; 391 + } 392 + # LE E2 393 + { 394 + name = "_25._tcp.mail"; 395 + type = "TLSA"; 396 + value = "2 1 1 bd936e72b212ef6f773102c6b77d38f94297322efc25396bc3279422e0c89270"; 397 + } 398 + 399 + { 400 + name = "jellyfin"; 401 + type = "AAAA"; 402 + value = "2a00:23c6:aa22:e401:8dff:9b9a:cb3c:3fcb"; 403 + } 404 + { 405 + name = "jellyseerr"; 406 + type = "AAAA"; 407 + value = "2a00:23c6:aa22:e401:8dff:9b9a:cb3c:3fcb"; 408 + } 409 + { 410 + name = "calibre"; 411 + type = "AAAA"; 412 + value = "2a00:23c6:aa22:e401:8dff:9b9a:cb3c:3fcb"; 413 + } 414 + ] ++ vpnRecords; 415 + }; 416 + "fn06.org" = { 417 + soa.serial = 1706745602; 418 + records = [ 419 + { 420 + name = "@"; 421 + type = "NS"; 422 + value = "ns1"; 423 + } 424 + { 425 + name = "@"; 426 + type = "NS"; 427 + value = "ns2"; 428 + } 429 + 430 + { 431 + name = "ns1"; 432 + type = "A"; 433 + value = config.eilean.serverIpv4; 434 + } 435 + { 436 + name = "ns1"; 437 + type = "AAAA"; 438 + value = config.eilean.serverIpv6; 439 + } 440 + { 441 + name = "ns2"; 442 + type = "A"; 443 + value = config.eilean.serverIpv4; 444 + } 445 + { 446 + name = "ns2"; 447 + type = "AAAA"; 448 + value = config.eilean.serverIpv6; 449 + } 450 + 451 + { 452 + name = "@"; 453 + type = "A"; 454 + value = config.eilean.serverIpv4; 455 + } 456 + { 457 + name = "@"; 458 + type = "AAAA"; 459 + value = config.eilean.serverIpv6; 460 + } 461 + 462 + { 463 + name = "www.fn06.org."; 464 + type = "CNAME"; 465 + value = "fn06.org."; 466 + } 467 + 468 + { 469 + name = "@"; 470 + type = "LOC"; 471 + value = "52 12 40.4 N 0 5 31.9 E 22m 10m 10m 10m"; 472 + } 473 + 474 + { 475 + name = "capybara.fn06.org."; 476 + type = "CNAME"; 477 + value = "fn06.org."; 478 + } 479 + ]; 480 + }; 481 + }; 482 + }
-57
modules/colour-guesser.nix
··· 1 - { 2 - pkgs, 3 - config, 4 - lib, 5 - options, 6 - colour-guesser, 7 - ... 8 - }: 9 - 10 - let 11 - cfg = config.custom.website.colour-guesser; 12 - in 13 - { 14 - options = { 15 - custom.website.colour-guesser = { 16 - enable = lib.mkEnableOption "Colour Guesser"; 17 - domain = lib.mkOption { 18 - type = lib.types.str; 19 - default = "colour-guesser.${config.networking.domain}"; 20 - }; 21 - cname = lib.mkOption { 22 - type = lib.types.str; 23 - default = null; 24 - description = '' 25 - CNAME to create DNS records for. 26 - Ignored if null 27 - ''; 28 - }; 29 - }; 30 - }; 31 - 32 - config = lib.mkIf cfg.enable { 33 - security.acme-eon.nginxCerts = [ cfg.domain ]; 34 - services.nginx = { 35 - enable = true; 36 - recommendedProxySettings = true; 37 - virtualHosts."${cfg.domain}" = { 38 - forceSSL = true; 39 - root = "${colour-guesser.packages.${pkgs.stdenv.hostPlatform.system}.default}"; 40 - }; 41 - }; 42 - 43 - # requires dns module 44 - eilean.services.dns.zones.${config.networking.domain}.records = [ 45 - { 46 - name = "${cfg.domain}."; 47 - type = "CNAME"; 48 - value = cfg.cname; 49 - } 50 - { 51 - name = "www.${cfg.domain}."; 52 - type = "CNAME"; 53 - value = cfg.cname; 54 - } 55 - ]; 56 - }; 57 - }