{ config, pkgs, lib, ... }: with lib; let ipv4 = { address = "95.216.74.104"; gateway = "95.216.74.65"; netmask = "255.255.255.192"; prefixLength = 26; # https://www.pawprint.net/designresources/netmask-converter.php }; ipv6 = { address = "2a01:4f9:2b:a98::"; gateway = "fe80::1"; prefixLength = 64; }; networkInterface = "eth0"; hostName = "cobalt"; hostId = "712ae82a"; hostAddr = "cobalt.exotic.sh"; sefidelKeys = [ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILN14b5Fu+StHeMXq4ClyLG4G+/vCAfS7adxceEFria/ openpgp:0x1D5BCD11" ]; maintainerKeys = [ ] ++ sefidelKeys; poorObfuscation = y: x: "${x}@${y}"; in { deployment = { targetHost = hostAddr; targetPort = 22; targetUser = "root"; }; imports = [ ./hardware-configuration.nix ]; boot.supportedFilesystems = [ "zfs" ]; networking.hostId = hostId; boot.loader.grub.enable = true; # boot.loader.grub.version = 2; boot.loader.grub.efiSupport = false; # boot.loader.grub.device = "nodev"; # This should be done automatically, but explicitly declare it just in case. boot.loader.grub.copyKernels = true; # Make sure that you've listed all of the boot partitions here. boot.loader.grub.mirroredBoots = [ { path = "/boot"; devices = [ "/dev/disk/by-id/ata-ST4000NM0245-1Z2107_ZC17GW7G" ]; } { path = "/boot-fallback"; devices = [ "/dev/disk/by-id/ata-ST4000NM0245-1Z2107_ZC17GWB2" ]; } ]; # Boot normally when one of the boot partitions are missing fileSystems."/boot".options = [ "nofail" ]; fileSystems."/boot-fallback".options = [ "nofail" ]; # Erase your darlings boot.initrd.postDeviceCommands = lib.mkAfter '' zfs rollback -r rpool/local/root@blank ''; # NOTE: replace these to boot.initrd.availableKernelModules? boot.kernelModules = [ "e1000e" ]; boot.initrd.kernelModules = [ "e1000e" ]; boot.kernelParams = [ # See for documentation. # ip=::::::::: # The server ip refers to the NFS server -- not needed in this case. "ip=${ipv4.address}::${ipv4.gateway}:${ipv4.netmask}:${hostName}-initrd:${networkInterface}:off:8.8.8.8" ]; boot.initrd.network.enable = true; boot.initrd.network.ssh = { enable = true; # Using the same port as the actual SSH will cause clients to throw errors # related to host key mismatch. port = 2222; # This takes 'path's, not 'string's. hostKeys = [ /boot/initrd-ssh-key /boot-fallback/initrd-ssh-key ]; # Public ssh key to log into the initrd ssh authorizedKeys = maintainerKeys; }; boot.initrd.network.postCommands = '' cat < /root/.profile if pgrep -x "zfs" > /dev/null then zfs load-key -a killall zfs else echo "ZFS is not running -- this could be a sign of failure." fi EOF ''; hardware.enableRedistributableFirmware = true; networking.hostName = hostName; networking.useDHCP = false; networking.interfaces.${networkInterface} = { ipv4 = { addresses = [{ address = ipv4.address; prefixLength = ipv4.prefixLength; }]; }; ipv6 = { addresses = [{ address = ipv6.address; prefixLength = ipv6.prefixLength; }]; }; }; networking.defaultGateway = ipv4.gateway; networking.defaultGateway6 = { address = ipv6.gateway; interface = networkInterface; }; networking.nameservers = [ "8.8.8.8" ]; networking.firewall.enable = true; time.timeZone = "UTC"; sops.secrets.root-password.neededForUsers = true; sops.secrets.sefidel-password.neededForUsers = true; users.users.root.hashedPasswordFile = config.sops.secrets.root-password.path; users.users.root.openssh.authorizedKeys.keys = maintainerKeys; users.users.sefidel = { isNormalUser = true; shell = pkgs.zsh; hashedPasswordFile = config.sops.secrets.sefidel-password.path; openssh.authorizedKeys.keys = sefidelKeys; extraGroups = [ "wheel" ]; }; programs.zsh.enable = true; services.openssh.enable = true; services.openssh.settings.PermitRootLogin = "prohibit-password"; environment.systemPackages = with pkgs; [ bsd-finger ]; sops.secrets.borg-cobalt-rolling-pass = { }; sops.secrets.grafana-admin-pass = { owner = "grafana"; }; sops.secrets.acme-envs = { owner = "acme"; }; sops.secrets.matrix-server-key = { owner = "matrix-synapse"; }; sops.secrets.matrix-shared-secret = { owner = "matrix-synapse"; }; sops.secrets.synapse-extra-config = { owner = "matrix-synapse"; }; sops.secrets.sliding-sync-secret = { }; sops.secrets.mjolnir-password = { owner = "mjolnir"; }; sops.secrets.mautrix-envs = { }; sops.secrets.turn-secret = { }; # sops.secrets.openldap-admin-key = { # owner = "openldap"; # }; sops.secrets.searx-env = { }; sops.secrets.freshrss-admin-pass = { owner = "freshrss"; }; services.openssh.knownHosts."hk-s020.rsync.net".publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILcPl9x9JfRFwsn09NnDw/xBZbAN80ZQck+h6AqlVqPH"; nix.flakes.enable = true; modules = { security.enable = true; persistence = { enable = true; storagePath = "/persist"; }; cachix.enable = true; sops.enable = true; services.backup = { enable = true; paths = [ "/persist" "/home" ]; exclude = [ # Rust build files "/home/**/target" # Static backup created in /persist/var/backup/postgresql "/persist/var/lib/postgresql" ]; repo = "20963@hk-s020.rsync.net:rolling/exotic/cobalt"; repoKeyPath = config.sops.secrets.borg-cobalt-rolling-pass.path; sshKeyPath = "/persist/ssh/ssh_host_ed25519_key"; rsyncNet = true; }; services.tailscale.enable = true; services.metrics = { enable = true; domain = "metrics.exotic.sh"; tls.acmeHost = "exotic.sh"; secrets.adminPassword = config.sops.secrets.grafana-admin-pass.path; }; services.coredns.enable = false; services.nginx.enable = true; services.acme = { enable = true; email = poorObfuscation "exotic.sh" "postmaster"; certs = { "exotic.sh" = { subDomains = [ "git" "matrix" "*.labs" "social" "bouncer" "meet" "chat" "cinny" "turn" "metrics" "mail" "webmail" "todo" "rss" "rss-bridge" ]; }; "nand.moe" = { subDomains = [ ]; }; "sefidel.net" = { subDomains = [ ]; }; }; secrets.acme-credentials = config.sops.secrets.acme-envs.path; }; services.gitolite = { enable = true; adminPubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILN14b5Fu+StHeMXq4ClyLG4G+/vCAfS7adxceEFria/ openpgp:0x1D5BCD11"; }; services.gitDaemon.enable = true; services.cgit = { enable = true; domain = "exotic.sh"; realHost = "git.exotic.sh"; settings.description = "Out of sync since Y2K"; }; services.fail2ban.enable = true; services.postgresql.enable = true; services.matrix-homeserver = { enable = true; domain = "exotic.sh"; realHost = "matrix.exotic.sh"; slidingSyncHost = "slidingsync.labs.exotic.sh"; turn = { enable = true; domain = "turn.exotic.sh"; shared_secret = "$TURN_SECRET"; # dendrite envs }; secrets = { matrix-server-key = config.sops.secrets.matrix-server-key.path; matrix-shared-secret = config.sops.secrets.matrix-shared-secret.path; extra-config-path = config.sops.secrets.synapse-extra-config; sliding-sync-secret = config.sops.secrets.sliding-sync-secret.path; }; }; services.coturn = { enable = true; domain = "turn.exotic.sh"; tls.acmeHost = "exotic.sh"; shared_secret = config.sops.secrets.turn-secret.path; }; services.matrix-moderation = { enable = true; domain = config.modules.services.matrix-homeserver.domain; secrets.userPassword = config.sops.secrets.mjolnir-password.path; }; services.matrix-bridge = { enable = true; domain = config.modules.services.matrix-homeserver.domain; secrets.mautrix-envs = config.sops.secrets.mautrix-envs.path; }; # TODO: replace all URLs to be relative to the core homeserver service services.element-web = { enable = true; hostName = "chat.exotic.sh"; matrix = { baseUrl = "https://matrix.exotic.sh"; serverName = "exotic.sh"; }; tls.acmeHost = "exotic.sh"; jitsi.domain = "meet.exotic.sh"; }; services.cinny-web = { enable = true; hostName = "cinny.exotic.sh"; matrix.serverName = config.modules.services.element-web.matrix.serverName; tls.acmeHost = config.modules.services.element-web.tls.acmeHost; }; services.akkoma = { enable = true; domain = "exotic.sh"; realHost = "social.exotic.sh"; instanceName = "exotic.sh social"; }; services.soju = { enable = true; hostName = "bouncer.exotic.sh"; tls.enable = true; tls.acmeHost = "exotic.sh"; }; services.vikunja = { enable = true; domain = "exotic.sh"; realHost = "todo.exotic.sh"; }; services.rss = rec { enable = true; domain = "exotic.sh"; realHost = "rss.exotic.sh"; bridge = { enable = true; inherit domain; realHost = "rss-bridge.exotic.sh"; whitelist = [ "*" ]; }; secrets.admin-password = config.sops.secrets.freshrss-admin-pass.path; }; services.searx = { enable = true; domain = "exotic.sh"; realHost = "searx.labs.exotic.sh"; secrets.searx-env = config.sops.secrets.searx-env.path; }; services.obsidian-livesync = { enable = true; domain = "exotic.sh"; realHost = "obsidian-livesync.labs.exotic.sh"; }; # TODO: replace with dovecot.nix? services.nixos-mailserver = { enable = true; webmail = { enable = true; domain = "exotic.sh"; realHost = "webmail.exotic.sh"; }; }; services.jitsi = { enable = true; hostName = "meet.exotic.sh"; tls.acmeHost = "exotic.sh"; }; services.ldap = { enable = false; dc = "exotic"; tld = "sh"; tls.acmeHost = "exotic.sh"; secrets.rootPass = config.sops.secrets.openldap-admin-key.path; }; services.sefidel-web.enable = true; }; # This value determines the NixOS release from which the default # settings for stateful data, like file locations and database versions # on your system were taken. It‘s perfectly fine and recommended to leave # this value at the release version of the first install of this system. # Before changing this value read the documentation for this option # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). system.stateVersion = "23.05"; # Did you read the comment? }