{ flake, config, pkgs, lib, ... }: let inherit (flake.config.people) user0; inherit (flake.config.services) instances; serviceCfg = flake.config.services.instances.mastodon; smtpCfg = flake.config.services.instances.smtp; hostCfg = flake.config.services.instances.web; host = serviceCfg.domains.url0; dns0 = instances.web.dns.provider0; dns0Path = "dns/${dns0}"; in { # If you need to start fresh for some reason, run these to create the new Admin account: # sudo -u mastodon mastodon-tootctl accounts create nick --email=nick@localhost --confirmed --role=Owner # sudo -u mastodon mastodon-tootctl accounts approve nick # If you fuck up and lose the password, use this: # sudo mastodon-tootctl accounts modify --reset-password nick # If you really fuck up and name yourself wrong, use this shit # sudo mastodon-tootctl accounts modify username --remove-role # nixpkgs.overlays = [ # ( # final: prev: { # mastodon = prev.mastodon.overrideAttrs (oldAttrs: { # postPatch = # (oldAttrs.postPatch or "") # + '' # patch -p1 < ${./chars.patch} # ''; # }); # } # ) # ]; microvm.vms = { ${serviceCfg.name} = { autostart = true; restartIfChanged = true; config = { system.stateVersion = "24.05"; time.timeZone = "America/Winnipeg"; users.users.root.openssh.authorizedKeys.keys = flake.config.people.users.${user0}.sshKeys; services = { ${serviceCfg.name} = { enable = true; localDomain = host; secretKeyBaseFile = "/run/mastodon-secrets/pass"; streamingProcesses = 7; trustedProxy = hostCfg.localhost.address1; automaticMigrations = true; database = { createLocally = true; name = serviceCfg.name; host = "/run/postgresql"; user = serviceCfg.name; passwordFile = "/run/mastodon-secrets/database"; }; extraConfig = { SINGLE_USER_MODE = "true"; SMTP_AUTH_METHOD = "plain"; SMTP_DELIVERY_METHOD = "smtp"; SMTP_ENABLE_STARTTLS_AUTO = "true"; SMTP_SSL = "false"; }; mediaAutoRemove = { enable = true; olderThanDays = 14; }; redis = { createLocally = true; enableUnixSocket = true; }; sidekiqThreads = 25; sidekiqProcesses = { all = { jobClasses = [ ]; threads = null; }; default = { jobClasses = [ "default" ]; threads = 5; }; ingress = { jobClasses = [ "ingress" ]; threads = 5; }; push-pull = { jobClasses = [ "push" "pull" ]; threads = 5; }; mailers = { jobClasses = [ "mailers" ]; threads = 5; }; }; smtp = { authenticate = true; createLocally = false; fromAddress = "upRootNutrition <${smtpCfg.email.address1}>"; host = smtpCfg.hostname; passwordFile = "/run/mastodon-secrets/smtp"; port = smtpCfg.ports.port1; user = smtpCfg.email.address1; }; }; caddy = { enable = true; virtualHosts = { "${serviceCfg.interface.ip}" = { extraConfig = '' handle_path /system/* { file_server * { root /var/lib/mastodon/public-system } } handle /api/v1/streaming/* { reverse_proxy unix//run/mastodon-streaming/streaming.socket } route * { file_server * { root ${pkgs.mastodon}/public pass_thru } reverse_proxy * unix//run/mastodon-web/web.socket } handle_errors { root * ${pkgs.mastodon}/public rewrite 500.html file_server } encode gzip header /* { Strict-Transport-Security "max-age=31536000;" } header /emoji/* Cache-Control "public, max-age=31536000, immutable" header /packs/* Cache-Control "public, max-age=31536000, immutable" header /system/accounts/avatars/* Cache-Control "public, max-age=31536000, immutable" header /system/media_attachments/files/* Cache-Control "public, max-age=31536000, immutable" ''; }; }; }; postgresql = { enable = true; }; openssh = { enable = true; settings = { PasswordAuthentication = false; PermitRootLogin = "prohibit-password"; }; }; }; users.users.${serviceCfg.name}.extraGroups = [ "postgres" ]; users.users.caddy.extraGroups = [ serviceCfg.name ]; networking.firewall.allowedTCPPorts = [ 22 # SSH 80 # Caddy 25 # SMTP 139 # SMTP 587 # SMTP 2525 # SMTP 5432 # Postgres ]; systemd = { services = { systemd-networkd.wantedBy = [ "multi-user.target" ]; copy-secrets-to-tmpfs = { description = "Copy secrets from virtiofs to tmpfs"; wantedBy = [ "multi-user.target" ]; before = [ "mastodon-init-dirs.service" ]; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; }; script = '' mkdir -p /run/mastodon-secrets mkdir -p /run/mastodon-web cp /run/secrets/pass /run/mastodon-secrets/pass cp /run/secrets/smtp /run/mastodon-secrets/smtp cp /run/secrets/database /run/mastodon-secrets/database cp /run/secrets/redis /run/mastodon-secrets/redis chown root:mastodon /run/mastodon-secrets/* chmod 0640 /run/mastodon-secrets/* chown mastodon:mastodon /run/mastodon-web chmod 0755 /run/mastodon-web ''; }; caddy = { after = [ "copy-secrets-to-tmpfs.service" ]; requires = [ "copy-secrets-to-tmpfs.service" ]; serviceConfig.ReadWriteDirectories = lib.mkForce [ "/var/lib/caddy" "/run/mastodon-web" ]; }; }; network = { enable = true; networks."20-lan" = { matchConfig.Name = "enp0s6"; addresses = [ { Address = "${serviceCfg.interface.ip}/24"; } ]; routes = [ { Destination = "${hostCfg.localhost.address1}/0"; Gateway = serviceCfg.interface.gate; } ]; dns = [ "1.1.1.1" "8.8.8.8" ]; }; }; tmpfiles.rules = [ "Z ${serviceCfg.varPaths.path0} 0755 ${serviceCfg.name} ${serviceCfg.name} -" "Z /var/lib/postgresql 0755 postgres postgres -" ]; }; microvm = { vcpu = 4; mem = 8192; hypervisor = "qemu"; interfaces = [ { type = "tap"; id = serviceCfg.interface.id; mac = serviceCfg.interface.mac; } { type = "user"; id = serviceCfg.interface.idUser; mac = serviceCfg.interface.macUser; } ]; forwardPorts = [ { from = "host"; host.port = serviceCfg.interface.ssh; guest.port = 22; } ]; shares = [ { mountPoint = "/nix/.ro-store"; proto = "virtiofs"; source = "/nix/store"; tag = "read_only_nix_store"; } { mountPoint = "/var/lib/${serviceCfg.name}"; proto = "virtiofs"; source = "${serviceCfg.mntPaths.path0}/data"; tag = "${serviceCfg.name}_data"; } { mountPoint = "/var/lib/postgresql"; proto = "virtiofs"; source = "${serviceCfg.mntPaths.path0}/database"; tag = "${serviceCfg.name}_database"; } { mountPoint = "/run/secrets"; proto = "virtiofs"; source = "/run/secrets/${serviceCfg.name}"; tag = "host_secrets"; } ]; }; }; }; }; sops = { secrets = builtins.listToAttrs ( map (secret: { name = "${serviceCfg.name}/${secret}"; value = { owner = "root"; mode = "0600"; }; }) [ "smtp" "database" "redis" "pass" ] ); }; systemd.tmpfiles.rules = [ "d ${serviceCfg.mntPaths.path0} 0751 microvm wheel - -" "d ${serviceCfg.mntPaths.path0}/data 0751 microvm wheel - -" "d ${serviceCfg.mntPaths.path0}/database 0751 microvm wheel - -" ]; services.caddy.virtualHosts."${host}" = { extraConfig = '' reverse_proxy http://${serviceCfg.interface.ip}:80 tls ${serviceCfg.ssl.cert} ${serviceCfg.ssl.key} encode zstd gzip ''; }; users.users.caddy.extraGroups = [ "acme" ]; security.acme.certs."${host}" = { dnsProvider = dns0; environmentFile = config.sops.secrets.${dns0Path}.path; group = "caddy"; }; }