at master 13 kB view raw
1{ 2 pkgs, 3 config, 4 lib, 5 eon, 6 ... 7}: 8 9let 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 ]; 47in 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 "enki.freumh.org" 97 ]; 98 99 # VPN 100 eilean.headscale.enable = true; 101 services.headscale.settings.dns = { 102 extra_records = vpnRecords; 103 base_domain = "vpn.freumh.org"; 104 nameservers.global = config.networking.nameservers; 105 }; 106 boot.kernel.sysctl = { 107 "net.ipv4.ip_forward" = 1; 108 "net.ipv6.conf.all.forwarding" = 1; 109 }; 110 111 # websites 112 custom = { 113 freumh.enable = true; 114 rmfakecloud.enable = true; 115 website = { 116 ryan = { 117 enable = true; 118 cname = "vps"; 119 }; 120 alec = { 121 enable = true; 122 cname = "vps"; 123 }; 124 fn06 = { 125 enable = true; 126 cname = "vps"; 127 domain = "fn06.org"; 128 }; 129 }; 130 }; 131 services.nginx.commonHttpConfig = '' 132 add_header Strict-Transport-Security max-age=31536000 always; 133 add_header X-Frame-Options SAMEORIGIN always; 134 add_header X-Content-Type-Options nosniff always; 135 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; 136 add_header Referrer-Policy 'same-origin'; 137 ''; 138 services.nginx.virtualHosts."teapot.${config.networking.domain}" = { 139 extraConfig = '' 140 return 418; 141 ''; 142 }; 143 144 # mailserver 145 eilean.mailserver.enable = true; 146 age.secrets.email-ryan.file = ../../secrets/email-ryan.age; 147 age.secrets.email-system.file = ../../secrets/email-system.age; 148 eilean.mailserver.systemAccountPasswordFile = config.age.secrets.email-system.path; 149 mailserver.loginAccounts = { 150 "${config.eilean.username}@${config.networking.domain}" = { 151 passwordFile = config.age.secrets.email-ryan.path; 152 aliases = [ 153 "dns@${config.networking.domain}" 154 "postmaster@${config.networking.domain}" 155 ]; 156 sieveScript = '' 157 require ["fileinto", "mailbox"]; 158 159 if header :contains ["to", "cc"] ["ai-control@ietf.org"] { 160 fileinto :create "lists.aietf"; 161 stop; 162 } 163 ''; 164 }; 165 "misc@${config.networking.domain}" = { 166 passwordFile = config.age.secrets.email-ryan.path; 167 catchAll = [ "${config.networking.domain}" ]; 168 }; 169 "system@${config.networking.domain}" = { 170 aliases = [ "nas@${config.networking.domain}" ]; 171 }; 172 }; 173 174 # CalDAV calendar server 175 eilean.radicale = { 176 enable = true; 177 users = null; 178 }; 179 180 # matrix 181 age.secrets.matrix-shared-secret = { 182 file = ../../secrets/matrix-shared-secret.age; 183 mode = "770"; 184 owner = "${config.systemd.services.matrix-synapse.serviceConfig.User}"; 185 group = "${config.systemd.services.matrix-synapse.serviceConfig.Group}"; 186 }; 187 eilean.matrix = { 188 enable = true; 189 registrationSecretFile = config.age.secrets.matrix-shared-secret.path; 190 bridges.whatsapp = true; 191 bridges.signal = true; 192 bridges.instagram = true; 193 bridges.messenger = true; 194 }; 195 eilean.turn.enable = true; 196 systemd.services.matrix-as-meta = { 197 path = [ pkgs.ffmpeg ]; 198 }; 199 200 # mastodon 201 eilean.mastodon.enable = true; 202 services.mastodon = { 203 webProcesses = lib.mkForce 1; 204 webThreads = lib.mkForce 1; 205 sidekiqThreads = lib.mkForce 1; 206 streamingProcesses = lib.mkForce 1; 207 }; 208 209 # restic 210 age.secrets.restic-owl.file = ../../secrets/restic-owl.age; 211 services.restic.backups.${config.networking.hostName} = { 212 repository = "rest:http://100.64.0.9:8000/${config.networking.hostName}/"; 213 passwordFile = config.age.secrets.restic-owl.path; 214 initialize = true; 215 paths = [ 216 "/var/" 217 "/run/" 218 "/etc/" 219 ]; 220 timerConfig = { 221 OnCalendar = "03:00"; 222 randomizedDelaySec = "1hr"; 223 }; 224 }; 225 226 # reverse proxy 227 services.nginx.virtualHosts."capybara.fn06.org" = { 228 forceSSL = true; 229 locations."/" = { 230 proxyPass = '' 231 http://100.64.0.10:8123 232 ''; 233 proxyWebsockets = true; 234 }; 235 }; 236 services.nginx.virtualHosts."shrew.freumh.org" = { 237 forceSSL = true; 238 locations."/" = { 239 proxyPass = '' 240 http://100.64.0.6:8123 241 ''; 242 proxyWebsockets = true; 243 }; 244 }; 245 246 # tangled 247 age.secrets.tangled = { 248 file = ../../secrets/tangled.age; 249 mode = "660"; 250 owner = "git"; 251 group = "git"; 252 }; 253 services.tangled-knotserver = { 254 enable = true; 255 repo.mainBranch = "master"; 256 server.hostname = "knot.freumh.org"; 257 server = { 258 secretFile = config.age.secrets.tangled.path; 259 listenAddr = "127.0.0.1:5555"; 260 internalListenAddr = "127.0.0.1:5444"; 261 }; 262 }; 263 services.nginx.virtualHosts."knot.freumh.org" = { 264 forceSSL = true; 265 locations."/" = { 266 proxyPass = '' 267 http://${config.services.tangled-knotserver.server.listenAddr} 268 ''; 269 proxyWebsockets = true; 270 }; 271 }; 272 273 services.nginx.virtualHosts."enki.freumh.org" = { 274 forceSSL = true; 275 locations."/" = { 276 proxyPass = '' 277 http://localhost:8000 278 ''; 279 proxyWebsockets = true; 280 extraConfig = '' 281 # SSE-specific settings 282 proxy_buffering off; 283 proxy_read_timeout 3600s; 284 proxy_send_timeout 3600s; 285 proxy_connect_timeout 60s; 286 287 # Forward headers 288 proxy_set_header Connection ""; 289 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 290 proxy_set_header X-Forwarded-Proto $scheme; 291 proxy_set_header Host $host; 292 ''; 293 }; 294 }; 295 296 # minecraft server 297 services.minecraft-server = { 298 enable = true; 299 package = pkgs.overlay-unstable.minecraft-server; 300 eula = true; 301 openFirewall = true; 302 }; 303 304 # DNS records 305 eilean.dns.nameservers = [ "ns1" ]; 306 eilean.services.dns.zones = { 307 ${config.networking.domain} = { 308 ttl = 300; 309 soa = { 310 serial = 2018011660; 311 refresh = 300; 312 }; 313 records = [ 314 { 315 name = "@"; 316 type = "TXT"; 317 value = "google-site-verification=rEvwSqf7RYKRQltY412qMtTuoxPp64O3L7jMotj9Jnc"; 318 } 319 { 320 name = "_atproto.ryan"; 321 type = "TXT"; 322 value = "did=did:plc:3lfhu6ehlynzjgehef6alnvg"; 323 } 324 325 { 326 name = "teapot"; 327 type = "CNAME"; 328 value = "vps"; 329 } 330 331 { 332 name = "@"; 333 type = "ns"; 334 value = "ns1.sirref.org."; 335 } 336 337 { 338 name = "vps"; 339 type = "A"; 340 value = config.eilean.serverIpv4; 341 } 342 { 343 name = "vps"; 344 type = "AAAA"; 345 value = config.eilean.serverIpv6; 346 } 347 348 { 349 name = "@"; 350 type = "LOC"; 351 value = "52 12 40.4 N 0 5 31.9 E 22m 10m 10m 10m"; 352 } 353 354 { 355 name = "ns.cl"; 356 type = "A"; 357 value = "128.232.113.136"; 358 } 359 { 360 name = "cl"; 361 type = "NS"; 362 value = "ns.cl"; 363 } 364 365 { 366 name = "ns1.eilean"; 367 type = "A"; 368 value = "65.109.10.223"; 369 } 370 { 371 name = "eilean"; 372 type = "NS"; 373 value = "ns1.eilean"; 374 } 375 376 { 377 name = "shrew"; 378 type = "CNAME"; 379 value = "vps"; 380 } 381 382 { 383 name = "knot"; 384 type = "CNAME"; 385 value = "vps"; 386 } 387 388 { 389 name = "enki"; 390 type = "CNAME"; 391 value = "vps"; 392 } 393 394 { 395 name = "hippo"; 396 type = "A"; 397 value = "128.232.124.251"; 398 } 399 400 # generate with 401 # 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}' 402 { 403 name = "_25._tcp.mail"; 404 type = "TLSA"; 405 value = "3 1 1 2f0fd413f063c75141937dd196a9f4ab66139d599e0dcf2a7ce6d557647e26a6"; 406 } 407 # generate with 408 # for i in r3 e1 r4-cross-signed e2 409 # openssl x509 -in ~/downloads/lets-encrypt-$i.pem -pubkey -noout | openssl pkey -pubin -outform der | sha256sum | awk '{print "2 1 1", $1}' 410 # LE R3 411 { 412 name = "_25._tcp.mail"; 413 type = "TLSA"; 414 value = "2 1 1 8d02536c887482bc34ff54e41d2ba659bf85b341a0a20afadb5813dcfbcf286d"; 415 } 416 # LE E1 417 { 418 name = "_25._tcp.mail"; 419 type = "TLSA"; 420 value = "2 1 1 276fe8a8c4ec7611565bf9fce6dcace9be320c1b5bea27596b2204071ed04f10"; 421 } 422 # LE R4 423 { 424 name = "_25._tcp.mail"; 425 type = "TLSA"; 426 value = "2 1 1 e5545e211347241891c554a03934cde9b749664a59d26d615fe58f77990f2d03"; 427 } 428 # LE E2 429 { 430 name = "_25._tcp.mail"; 431 type = "TLSA"; 432 value = "2 1 1 bd936e72b212ef6f773102c6b77d38f94297322efc25396bc3279422e0c89270"; 433 } 434 435 { 436 name = "jellyfin"; 437 type = "AAAA"; 438 value = "2a00:23c6:aa22:e401:8dff:9b9a:cb3c:3fcb"; 439 } 440 { 441 name = "jellyseerr"; 442 type = "AAAA"; 443 value = "2a00:23c6:aa22:e401:8dff:9b9a:cb3c:3fcb"; 444 } 445 { 446 name = "calibre"; 447 type = "AAAA"; 448 value = "2a00:23c6:aa22:e401:8dff:9b9a:cb3c:3fcb"; 449 } 450 ] ++ vpnRecords; 451 }; 452 "fn06.org" = { 453 soa.serial = 1706745602; 454 records = [ 455 { 456 name = "@"; 457 type = "NS"; 458 value = "ns1"; 459 } 460 { 461 name = "@"; 462 type = "NS"; 463 value = "ns2"; 464 } 465 466 { 467 name = "ns1"; 468 type = "A"; 469 value = config.eilean.serverIpv4; 470 } 471 { 472 name = "ns1"; 473 type = "AAAA"; 474 value = config.eilean.serverIpv6; 475 } 476 { 477 name = "ns2"; 478 type = "A"; 479 value = config.eilean.serverIpv4; 480 } 481 { 482 name = "ns2"; 483 type = "AAAA"; 484 value = config.eilean.serverIpv6; 485 } 486 487 { 488 name = "@"; 489 type = "A"; 490 value = config.eilean.serverIpv4; 491 } 492 { 493 name = "@"; 494 type = "AAAA"; 495 value = config.eilean.serverIpv6; 496 } 497 498 { 499 name = "www.fn06.org."; 500 type = "CNAME"; 501 value = "fn06.org."; 502 } 503 504 { 505 name = "@"; 506 type = "LOC"; 507 value = "52 12 40.4 N 0 5 31.9 E 22m 10m 10m 10m"; 508 } 509 510 { 511 name = "capybara.fn06.org."; 512 type = "CNAME"; 513 value = "fn06.org."; 514 } 515 ]; 516 }; 517 }; 518}