commit 0366dc389ad0daaf12adf79b03509b7eee85fad5
parent 1f5ebac39729e033c10113e0c4e66e9958e9fef3
Author: Katja (ctucx) <git@ctu.cx>
Date: Tue, 4 Mar 2025 11:32:09 +0100
parent 1f5ebac39729e033c10113e0c4e66e9958e9fef3
Author: Katja (ctucx) <git@ctu.cx>
Date: Tue, 4 Mar 2025 11:32:09 +0100
modules: refactor
15 files changed, 501 insertions(+), 488 deletions(-)
D
|
229
-------------------------------------------------------------------------------
D
|
173
-------------------------------------------------------------------------------
A
|
229
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
|
174
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
diff --git a/flake.nix b/flake.nix @@ -14,6 +14,9 @@ ); }; + ctucxModules.darwin = loadDir ./modules/darwin; + ctucxModules.nixos = loadDir ./modules/nixos; + ctucxConfig.common = loadDir ./configurations/common; ctucxConfig.darwin = inputs.nixpkgs.lib.recursiveUpdate ctucxConfig.common (loadDir ./configurations/darwin); ctucxConfig.nixos = inputs.nixpkgs.lib.recursiveUpdate ctucxConfig.common (loadDir ./configurations/nixos); @@ -33,7 +36,11 @@ }; modules = [ + inputs.lixModule.nixosModules.default + inputs.homeManager.darwinModules.default + inputs.agenix.darwinModules.default ctucxConfig.darwin.default + ctucxModules.darwin.default ./machines/blechkasten ]; }; @@ -51,13 +58,21 @@ specialArgs = { inherit inputs; + ctucxConfig = ctucxConfig.nixos; }; }; defaults = { imports = [ + inputs.lixModule.nixosModules.default + inputs.impermanence.nixosModules.default + inputs.homeManager.nixosModules.default + inputs.agenix.nixosModules.default + inputs.lanzaboote.nixosModules.lanzaboote + inputs.simpleNixosMailserver.nixosModules.default + inputs.ctucxThings.nixosModule + ctucxModules.nixos.default ctucxConfig.nixos.default - ./modules ]; }; @@ -77,7 +92,7 @@ dns = inputs.dnsNix; std = inputs.nixStd.lib; unstable = inputs.nixpkgsUnstable.legacyPackages.${prev.system}; - ctucxConfig = ctucxConfig; + inherit ctucxConfig ctucxModules; }) inputs.colmena.overlay
diff --git a/modules/darwin/default.nix b/modules/darwin/default.nix @@ -0,0 +1,19 @@ + +{ lib ... }: + +{ + + imports = [ + ./quirks.nix + ./hidutil.nix + ./syncthing.nix + ./skhd.nix + ]; + + options = { + networking.primaryIP = lib.mkOption { type = lib.types.str; default = ""; }; + networking.primaryIP4 = lib.mkOption { type = lib.types.str; default = ""; }; + networking.secondaryIP4 = lib.mkOption { type = lib.types.str; default = ""; }; + }; + +}
diff --git a/modules/default.nix b/modules/default.nix @@ -1,49 +0,0 @@ - -{ inputs, lib, currentSystem, ... }: - -{ - - disabledModules = [ - "services/misc/gitolite.nix" - "services/web-apps/gotosocial.nix" - ]; - - imports = (builtins.concatLists [ - [ - inputs.lixModule.nixosModules.default - ] - - (if (currentSystem == "x86_64-linux") then [ - inputs.impermanence.nixosModules.default - inputs.homeManager.nixosModules.default - inputs.agenix.nixosModules.default - inputs.lanzaboote.nixosModules.lanzaboote - inputs.simpleNixosMailserver.nixosModules.default - inputs.ctucxThings.nixosModule - - ./linux/restic-backups.nix - ./linux/vnstati - ./linux/gitolite.nix - ./linux/email-notify.nix - ./linux/dns.nix - ./linux/gotosocial.nix - ./linux/gnome.nix - ] else []) - - (if (currentSystem == "aarch64-darwin") then [ - inputs.homeManager.darwinModules.default - inputs.agenix.darwinModules.default - ./darwin/quirks.nix - ./darwin/hidutil.nix - ./darwin/syncthing.nix - ./darwin/skhd.nix - ] else []) - ]); - - options = { - networking.primaryIP = lib.mkOption { type = lib.types.str; default = ""; }; - networking.primaryIP4 = lib.mkOption { type = lib.types.str; default = ""; }; - networking.secondaryIP4 = lib.mkOption { type = lib.types.str; default = ""; }; - }; - -}
diff --git a/modules/linux/dns.nix b/modules/linux/dns.nix @@ -1,229 +0,0 @@ -{ currentSystem, nodes, config, lib, pkgs, ... }: - -# -# this module requires lix' experimental `pipe-operator` feature! -# - -with lib; - -let - cfg = config.dns; - - filterDNSServerAddresses = nodes: isPrimary: lib.flatten ( - nodes - |> lib.filterAttrs (hostName: nodeCfg: nodeCfg.config.dns.enable && nodeCfg.config.dns.primary == isPrimary) - |> lib.mapAttrsToList ( - hostName: nodeCfg: [ - (lib.mkIf (nodeCfg.config.networking.primaryIP != "") nodeCfg.config.networking.primaryIP) - (lib.mkIf (nodeCfg.config.networking.primaryIP4 != "") nodeCfg.config.networking.primaryIP4) - ] - ) - ); - - filterDNSServerSecondaries = nodes: ( - nodes - |> lib.filterAttrs (hostName: nodeCfg: nodeCfg.config.dns.enable && !nodeCfg.config.dns.primary) - |> lib.mapAttrs( - hostName: nodeCfg: { - address = [ - (lib.mkIf (nodeCfg.config.networking.primaryIP != "") nodeCfg.config.networking.primaryIP) - (lib.mkIf (nodeCfg.config.networking.primaryIP4 != "") nodeCfg.config.networking.primaryIP4) - ]; - } - ) - ); - -in { - - options.dns = { - enable = mkEnableOption "nix-powered DNS"; - - primary = lib.mkOption { - type = lib.types.bool; - default = true; - }; - - zonesDir = lib.mkOption { - type = lib.types.str; - default = "nixZones"; - }; - - dataDir = lib.mkOption { - type = lib.types.str; - default = "/var/lib/knot"; - }; - - keyFiles = lib.mkOption { - type = types.listOf types.path; - default = []; - }; - - # contains dns entries defined on the local host - zones = mkOption { - type = lib.types.attrsOf pkgs.dns.lib.types.subzone; - default = {}; - }; - - # contains dns entries defined on the local host and on remote hosts, merged together - allZones = mkOption { - type = lib.types.attrsOf pkgs.dns.lib.types.zone; - default = {}; - }; - - extraZones = mkOption { - type = (pkgs.formats.yaml { }).type; - default = {}; - }; - - extraACL = mkOption { - type = (pkgs.formats.yaml { }).type; - default = {}; - }; - }; - - config = mkIf cfg.enable { - networking.firewall.allowedTCPPorts = [ 53 ]; - networking.firewall.allowedUDPPorts = [ 53 ]; - - # serve records defined in all host configs - dns.allZones = mkMerge ( - nodes - |> mapAttrsToList ( name: host: host.config.dns.zones ) - ); - - environment.etc = lib.mkIf cfg.primary ( - cfg.allZones - |> lib.mapAttrs' (name: zone: { - name = "${cfg.zonesDir}/${name}.zone"; - value = { source = pkgs.dns.util."${currentSystem}".writeZone name zone; }; - }) - ); - - services.knot = let - primaryAddresses = filterDNSServerAddresses nodes true; - secondaryAddresses = filterDNSServerAddresses nodes false; - secondaries = filterDNSServerSecondaries nodes; - in { - enable = true; - keyFiles = lib.mkIf (cfg.keyFiles != []) cfg.keyFiles; - settings = { - log.syslog.any = "info"; - - server.listen = [ - (lib.mkIf (config.networking.primaryIP != "") "${config.networking.primaryIP}@53") - (lib.mkIf (config.networking.primaryIP4 != "") "${config.networking.primaryIP4}@53") - ]; - - mod-rrl.default.rate-limit = 200; - mod-rrl.default.slip = 2; - - remote = { primary.address = primaryAddresses; } // secondaries; - - acl = { - allowTransfer.address = secondaryAddresses; - allowTransfer.action = "transfer"; - - allowNotify.address = primaryAddresses; - allowNotify.action = "notify"; - } // cfg.extraACL; - - template = let - notify = { - acl = "allowTransfer"; - notify = builtins.attrNames secondaries; - }; - - catalog = { - catalog-role = "member"; - catalog-zone = "catalog."; - }; - - in { - - default = { - semantic-checks = true; - global-module = "mod-rrl/default"; - }; - - notifyZone = notify; - extraZone = notify // catalog; - primaryZone = notify // catalog // { - storage = "${cfg.dataDir}/nixZones"; - - journal-content = "all"; - - zonefile-sync = -1; - zonefile-load = "difference-no-serial"; - }; - - secondaryZone = { - master = "primary"; - acl = "allowNotify"; - - journal-content = "all"; - - zonefile-sync = -1; - zonefile-load = "none"; - }; - }; - - zone = if !cfg.primary then { - "catalog.".catalog-role = "interpret"; - "catalog.".catalog-template = "secondaryZone"; - "catalog.".template = "secondaryZone"; - } else { - "catalog.".catalog-role = "generate"; - "catalog.".template = "notifyZone"; - } // (lib.mapAttrs (name: zone: { - template = "primaryZone"; - }) cfg.allZones) // (lib.mapAttrs (name: zone: zone // { - template = "extraZone"; - acl = lib.mkIf (builtins.hasAttr "acl" zone) (lib.flatten [ [ "allowTransfer" ] zone.acl ]); - }) cfg.extraZones); - }; - }; - - systemd.tmpfiles.settings = { - knotDataDir."${cfg.dataDir}".d = { - group = "knot"; - user = "knot"; - mode = "770"; - age = "-"; - }; - - knotZones."${cfg.dataDir}/nixZones".d = lib.mkIf cfg.primary { - group = "knot"; - user = "knot"; - mode = "770"; - age = "-"; - }; - }; - - systemd.services.knot = lib.mkIf cfg.primary { - reloadTriggers = ( - cfg.allZones - |> lib.mapAttrsToList (name: zone: pkgs.dns.util."${currentSystem}".writeZone name zone) - ) ++ ( - cfg.extraZones - |> lib.mapAttrsToList (name: zone: (if (builtins.hasAttr "storage" zone) then "${zone.storage}/${zone.file}" else "${zone.file}")) - ); - - preStart = '' - set -euo pipefail - - cp --dereference /etc/${cfg.zonesDir}/* ${cfg.dataDir}/nixZones - chmod -R 770 ${cfg.dataDir}/nixZones - ''; - - serviceConfig.ExecReload = lib.mkForce (pkgs.writeShellScript "knot-reload" '' - set -eou pipefail - - cp --dereference /etc/${cfg.zonesDir}/* ${cfg.dataDir}/nixZones - chmod -R 770 ${cfg.dataDir}/nixZones - - ${config.services.knot.package}/bin/knotc reload - ''); - }; - }; - -}
diff --git a/modules/linux/email-notify.nix b/modules/linux/email-notify.nix @@ -1,35 +0,0 @@ -{ pkgs, lib, config, ... }: - -{ - - options.services.email-notify.enable = lib.mkEnableOption "Enable a service which can be used to send emails"; - - config = lib.mkIf config.services.email-notify.enable { - age.secrets.password-leah-at-f2k1-de.file = ../../secrets/passwords/leah-at-f2k1-de.age; - - programs.msmtp = { - enable = true; - setSendmail = false; - accounts = { - default = { - auth = true; - tls = true; - host = "rx300.kunbox.net"; - port = 587; - user = "leah@f2k1.de"; - from = "${config.networking.fqdn} <leah@f2k1.de>"; - passwordeval = "cat ${config.age.secrets.password-leah-at-f2k1-de.path}"; - }; - }; - }; - - systemd.services."email-notify@" = { - serviceConfig = { - ExecStart = '' - ${pkgs.runtimeShell} -c "{ echo -n 'Subject:[${config.networking.fqdn}] Service failed: %i\n\n' & ${pkgs.systemd}/bin/systemctl status %i;} | ${pkgs.msmtp}/bin/msmtp -v notify@ctu.cx" - ''; - }; - }; - }; - -}
diff --git a/modules/linux/gotosocial.nix b/modules/linux/gotosocial.nix @@ -1,173 +0,0 @@ -{ options, config, pkgs, lib, ... }: - -with lib; - -let - cfg = config.services.gotosocial; - settingsFormat = pkgs.formats.json {}; - -in { - - options = { - services.gotosocial = with lib; { - enable = mkEnableOption "GoToSocial ActivityPub Server"; - - package = mkOption { - type = types.package; - default = pkgs.gotosocial; - }; - - user = mkOption { - type = types.str; - default = "gotosocial"; - }; - - group = mkOption { - type = types.str; - default = "gotosocial"; - }; - - stateDir = mkOption { - type = types.str; - default = "/var/lib/gotosocial"; - readOnly = true; - }; - - environmentFile = lib.mkOption { - type = lib.types.nullOr lib.types.path; - default = null; - }; - - settings = lib.mkOption { - type = lib.types.submodule { - freeformType = settingsFormat.type; - - options.host = lib.mkOption { - type = lib.types.nullOr lib.types.str; - default = null; - description = '' - Hostname that this server will be reachable at. Defaults to localhost for local testing, - but you should *definitely* change this when running for real, or your server won't work at all. - DO NOT change this after your server has already run once, or you will break things! - ''; - }; - - options.port = lib.mkOption { - type = lib.types.port; - default = 8080; - description = '' - Int. Listen port for the GoToSocial webserver + API. If you're running behind a reverse proxy and/or in a docker, - container, just set this to whatever you like (or leave the default), and make sure it's forwarded properly. - If you are running with built-in letsencrypt enabled, and running GoToSocial directly on a host machine, you will - probably want to set this to 443 (standard https port), unless you have other services already using that port. - This *MUST NOT* be the same as the letsencrypt port specified below, unless letsencrypt is turned off. - ''; - }; - }; - - default = {}; - description = '' - Configuration for GoToSocial, see - <link xlink:href="https://docs.gotosocial.org/en/latest/"> - for supported values. - ''; - }; - }; - }; - - config = lib.mkIf cfg.enable (let - configFile = settingsFormat.generate "gotosocial-config.yaml" cfg.settings; - in { - assertions = [ - { - assertion = cfg.settings.host != null; - message = "You have to define a hostname for GoToSocial, it cannot be changed later without starting over!"; - } - ]; - - services.gotosocial.settings = { # Defaults - user = lib.mkDefault "gotosocial"; - group = lib.mkDefault "gotosocial"; - - storage-local-base-path = lib.mkDefault "/var/lib/gotosocial"; # SystemD StateDirectory - - web-template-base-dir = lib.mkDefault "${cfg.package}/share/web/template/"; - web-asset-base-dir = lib.mkDefault "${cfg.package}/share/web/assets/"; - }; - - users = { - users."${cfg.user}" = { - home = cfg.stateDir; - group = cfg.group; - isSystemUser = true; - }; - groups."${cfg.group}" = {}; - }; - - environment.etc."gotosocial.yaml".source = configFile; - - environment.systemPackages = [ - (pkgs.writeShellScriptBin "gotosocial" '' - exec ${cfg.package}/bin/gotosocial --config-path ${configFile} "$@" - '') - ]; - - systemd.services = { - gotosocial = { - description = "GoToSocial ActivityPub Server"; - after = [ "network-online.target" ]; - wantedBy = [ "multi-user.target" ]; - onFailure = [ "email-notify@%i.service" ]; - - serviceConfig = { - User = cfg.user; - Group = cfg.group; - - Type = "exec"; - WorkingDirectory = "~"; - StateDirectory = lib.mkIf (cfg.settings.storage-local-base-path != "/var/lib/gotosocial") "gotosocial"; - ReadOnlyPaths = [ cfg.package ]; - ReadWritePaths = [ cfg.settings.storage-local-base-path ]; - StateDirectoryMode = "750"; - - Restart = "always"; - RestartSec = 3; - - EnvironmentFile = lib.mkIf (cfg.environmentFile != null) cfg.environmentFile; - ExecStart = "${cfg.package}/bin/gotosocial --config-path ${configFile} server start"; - - NoNewPrivileges = true; - PrivateTmp = true; - PrivateDevices = false; - - RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6"; - RestrictNamespaces = true; - RestrictRealtime = true; - - ProtectSystem = "full"; - ProtectControlGroups = true; - ProtectKernelModules = true; - ProtectKernelTunables = true; - - DevicePolicy = "closed"; - LockPersonality = true; - SystemCallFilter = "~@clock @debug @module @mount @obsolete @reboot @setuid @swap"; - - CapabilityBoundingSet = [ - "~CAP_RAWIO CAP_MKNOD" - "~CAP_AUDIT_CONTROL CAP_AUDIT_READ CAP_AUDIT_WRITE" - "~CAP_SYS_BOOT CAP_SYS_TIME CAP_SYS_MODULE CAP_SYS_PACCT" - "~CAP_LEASE CAP_LINUX_IMMUTABLE CAP_IPC_LOCK" - "~CAP_BLOCK_SUSPEND CAP_WAKE_ALARM" - "~CAP_SYS_TTY_CONFIG" - "~CAP_MAC_ADMIN CAP_MAC_OVERRIDE" - "~CAP_NET_ADMIN CAP_NET_BROADCAST CAP_NET_RAW" - "~CAP_SYS_ADMIN CAP_SYS_PTRACE CAP_SYSLOG" - ]; - }; - }; - }; - - }); - -}
diff --git a/modules/nixos/default.nix b/modules/nixos/default.nix @@ -0,0 +1,27 @@ + +{ lib, ... }: + +{ + + disabledModules = [ + "services/misc/gitolite.nix" + "services/web-apps/gotosocial.nix" + ]; + + imports = [ + ./vnstati + ./restic-backups.nix + ./gitolite.nix + ./email-notify.nix + ./dns.nix + ./gotosocial.nix + ./gnome.nix + ]; + + options = { + networking.primaryIP = lib.mkOption { type = lib.types.str; default = ""; }; + networking.primaryIP4 = lib.mkOption { type = lib.types.str; default = ""; }; + networking.secondaryIP4 = lib.mkOption { type = lib.types.str; default = ""; }; + }; + +}
diff --git a/modules/nixos/dns.nix b/modules/nixos/dns.nix @@ -0,0 +1,229 @@ +{ nodes, config, lib, pkgs, ... }: + +# +# this module requires lix' experimental `pipe-operator` feature! +# + +with lib; + +let + cfg = config.dns; + + filterDNSServerAddresses = nodes: isPrimary: lib.flatten ( + nodes + |> lib.filterAttrs (hostName: nodeCfg: nodeCfg.config.dns.enable && nodeCfg.config.dns.primary == isPrimary) + |> lib.mapAttrsToList ( + hostName: nodeCfg: [ + (lib.mkIf (nodeCfg.config.networking.primaryIP != "") nodeCfg.config.networking.primaryIP) + (lib.mkIf (nodeCfg.config.networking.primaryIP4 != "") nodeCfg.config.networking.primaryIP4) + ] + ) + ); + + filterDNSServerSecondaries = nodes: ( + nodes + |> lib.filterAttrs (hostName: nodeCfg: nodeCfg.config.dns.enable && !nodeCfg.config.dns.primary) + |> lib.mapAttrs( + hostName: nodeCfg: { + address = [ + (lib.mkIf (nodeCfg.config.networking.primaryIP != "") nodeCfg.config.networking.primaryIP) + (lib.mkIf (nodeCfg.config.networking.primaryIP4 != "") nodeCfg.config.networking.primaryIP4) + ]; + } + ) + ); + +in { + + options.dns = { + enable = mkEnableOption "nix-powered DNS"; + + primary = lib.mkOption { + type = lib.types.bool; + default = true; + }; + + zonesDir = lib.mkOption { + type = lib.types.str; + default = "nixZones"; + }; + + dataDir = lib.mkOption { + type = lib.types.str; + default = "/var/lib/knot"; + }; + + keyFiles = lib.mkOption { + type = types.listOf types.path; + default = []; + }; + + # contains dns entries defined on the local host + zones = mkOption { + type = lib.types.attrsOf pkgs.dns.lib.types.subzone; + default = {}; + }; + + # contains dns entries defined on the local host and on remote hosts, merged together + allZones = mkOption { + type = lib.types.attrsOf pkgs.dns.lib.types.zone; + default = {}; + }; + + extraZones = mkOption { + type = (pkgs.formats.yaml { }).type; + default = {}; + }; + + extraACL = mkOption { + type = (pkgs.formats.yaml { }).type; + default = {}; + }; + }; + + config = mkIf cfg.enable { + networking.firewall.allowedTCPPorts = [ 53 ]; + networking.firewall.allowedUDPPorts = [ 53 ]; + + # serve records defined in all host configs + dns.allZones = mkMerge ( + nodes + |> mapAttrsToList ( name: host: host.config.dns.zones ) + ); + + environment.etc = lib.mkIf cfg.primary ( + cfg.allZones + |> lib.mapAttrs' (name: zone: { + name = "${cfg.zonesDir}/${name}.zone"; + value = { source = pkgs.dns.util."${config.nixpkgs.system}".writeZone name zone; }; + }) + ); + + services.knot = let + primaryAddresses = filterDNSServerAddresses nodes true; + secondaryAddresses = filterDNSServerAddresses nodes false; + secondaries = filterDNSServerSecondaries nodes; + in { + enable = true; + keyFiles = lib.mkIf (cfg.keyFiles != []) cfg.keyFiles; + settings = { + log.syslog.any = "info"; + + server.listen = [ + (lib.mkIf (config.networking.primaryIP != "") "${config.networking.primaryIP}@53") + (lib.mkIf (config.networking.primaryIP4 != "") "${config.networking.primaryIP4}@53") + ]; + + mod-rrl.default.rate-limit = 200; + mod-rrl.default.slip = 2; + + remote = { primary.address = primaryAddresses; } // secondaries; + + acl = { + allowTransfer.address = secondaryAddresses; + allowTransfer.action = "transfer"; + + allowNotify.address = primaryAddresses; + allowNotify.action = "notify"; + } // cfg.extraACL; + + template = let + notify = { + acl = "allowTransfer"; + notify = builtins.attrNames secondaries; + }; + + catalog = { + catalog-role = "member"; + catalog-zone = "catalog."; + }; + + in { + + default = { + semantic-checks = true; + global-module = "mod-rrl/default"; + }; + + notifyZone = notify; + extraZone = notify // catalog; + primaryZone = notify // catalog // { + storage = "${cfg.dataDir}/nixZones"; + + journal-content = "all"; + + zonefile-sync = -1; + zonefile-load = "difference-no-serial"; + }; + + secondaryZone = { + master = "primary"; + acl = "allowNotify"; + + journal-content = "all"; + + zonefile-sync = -1; + zonefile-load = "none"; + }; + }; + + zone = if !cfg.primary then { + "catalog.".catalog-role = "interpret"; + "catalog.".catalog-template = "secondaryZone"; + "catalog.".template = "secondaryZone"; + } else { + "catalog.".catalog-role = "generate"; + "catalog.".template = "notifyZone"; + } // (lib.mapAttrs (name: zone: { + template = "primaryZone"; + }) cfg.allZones) // (lib.mapAttrs (name: zone: zone // { + template = "extraZone"; + acl = lib.mkIf (builtins.hasAttr "acl" zone) (lib.flatten [ [ "allowTransfer" ] zone.acl ]); + }) cfg.extraZones); + }; + }; + + systemd.tmpfiles.settings = { + knotDataDir."${cfg.dataDir}".d = { + group = "knot"; + user = "knot"; + mode = "770"; + age = "-"; + }; + + knotZones."${cfg.dataDir}/nixZones".d = lib.mkIf cfg.primary { + group = "knot"; + user = "knot"; + mode = "770"; + age = "-"; + }; + }; + + systemd.services.knot = lib.mkIf cfg.primary { + reloadTriggers = ( + cfg.allZones + |> lib.mapAttrsToList (name: zone: pkgs.dns.util."${config.nixpkgs.system}".writeZone name zone) + ) ++ ( + cfg.extraZones + |> lib.mapAttrsToList (name: zone: (if (builtins.hasAttr "storage" zone) then "${zone.storage}/${zone.file}" else "${zone.file}")) + ); + + preStart = '' + set -euo pipefail + + cp --dereference /etc/${cfg.zonesDir}/* ${cfg.dataDir}/nixZones + chmod -R 770 ${cfg.dataDir}/nixZones + ''; + + serviceConfig.ExecReload = lib.mkForce (pkgs.writeShellScript "knot-reload" '' + set -eou pipefail + + cp --dereference /etc/${cfg.zonesDir}/* ${cfg.dataDir}/nixZones + chmod -R 770 ${cfg.dataDir}/nixZones + + ${config.services.knot.package}/bin/knotc reload + ''); + }; + }; + +}
diff --git a/modules/nixos/email-notify.nix b/modules/nixos/email-notify.nix @@ -0,0 +1,35 @@ +{ pkgs, lib, config, ... }: + +{ + + options.services.email-notify.enable = lib.mkEnableOption "Enable a service which can be used to send emails"; + +# config = lib.mkIf config.services.email-notify.enable { +# age.secrets.password-leah-at-f2k1-de.file = ../../secrets/passwords/leah-at-f2k1-de.age; +# +# programs.msmtp = { +# enable = true; +# setSendmail = false; +# accounts = { +# default = { +# auth = true; +# tls = true; +# host = "rx300.kunbox.net"; +# port = 587; +# user = "leah@f2k1.de"; +# from = "${config.networking.fqdn} <leah@f2k1.de>"; +# passwordeval = "cat ${config.age.secrets.password-leah-at-f2k1-de.path}"; +# }; +# }; +# }; +# +# systemd.services."email-notify@" = { +# serviceConfig = { +# ExecStart = '' +# ${pkgs.runtimeShell} -c "{ echo -n 'Subject:[${config.networking.fqdn}] Service failed: %i\n\n' & ${pkgs.systemd}/bin/systemctl status %i;} | ${pkgs.msmtp}/bin/msmtp -v notify@ctu.cx" +# ''; +# }; +# }; +# }; + +}
diff --git a/modules/nixos/gotosocial.nix b/modules/nixos/gotosocial.nix @@ -0,0 +1,174 @@ +{ options, config, pkgs, lib, ... }: + +with lib; + +let + cfg = config.services.gotosocial; + settingsFormat = pkgs.formats.json {}; + +in { + + options = { + services.gotosocial = with lib; { + enable = mkEnableOption "GoToSocial ActivityPub Server"; + + package = mkOption { + type = types.package; + default = pkgs.gotosocial; + }; + + user = mkOption { + type = types.str; + default = "gotosocial"; + }; + + group = mkOption { + type = types.str; + default = "gotosocial"; + }; + + stateDir = mkOption { + type = types.str; + default = "/var/lib/gotosocial"; + readOnly = true; + }; + + environmentFile = lib.mkOption { + type = lib.types.nullOr lib.types.path; + default = null; + }; + + settings = lib.mkOption { + type = lib.types.submodule { + freeformType = settingsFormat.type; + + options.host = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + description = '' + Hostname that this server will be reachable at. Defaults to localhost for local testing, + but you should *definitely* change this when running for real, or your server won't work at all. + DO NOT change this after your server has already run once, or you will break things! + ''; + }; + + options.port = lib.mkOption { + type = lib.types.port; + default = 8080; + description = '' + Int. Listen port for the GoToSocial webserver + API. If you're running behind a reverse proxy and/or in a docker, + container, just set this to whatever you like (or leave the default), and make sure it's forwarded properly. + If you are running with built-in letsencrypt enabled, and running GoToSocial directly on a host machine, you will + probably want to set this to 443 (standard https port), unless you have other services already using that port. + This *MUST NOT* be the same as the letsencrypt port specified below, unless letsencrypt is turned off. + ''; + }; + }; + + default = {}; + description = '' + Configuration for GoToSocial, see + <link xlink:href="https://docs.gotosocial.org/en/latest/"> + for supported values. + ''; + }; + }; + }; + + config = lib.mkIf cfg.enable (let + configFile = settingsFormat.generate "gotosocial-config.yaml" cfg.settings; + in { + assertions = [ + { + assertion = cfg.settings.host != null; + message = "You have to define a hostname for GoToSocial, it cannot be changed later without starting over!"; + } + ]; + + services.gotosocial.settings = { # Defaults + user = lib.mkDefault "gotosocial"; + group = lib.mkDefault "gotosocial"; + + storage-local-base-path = lib.mkDefault "/var/lib/gotosocial"; # SystemD StateDirectory + + web-template-base-dir = lib.mkDefault "${cfg.package}/share/web/template/"; + web-asset-base-dir = lib.mkDefault "${cfg.package}/share/web/assets/"; + }; + + users = { + users."${cfg.user}" = { + home = cfg.stateDir; + group = cfg.group; + isSystemUser = true; + }; + groups."${cfg.group}" = {}; + }; + + environment.etc."gotosocial.yaml".source = configFile; + + environment.systemPackages = [ + (pkgs.writeShellScriptBin "gotosocial" '' + exec ${cfg.package}/bin/gotosocial --config-path ${configFile} "$@" + '') + ]; + + systemd.services = { + gotosocial = { + description = "GoToSocial ActivityPub Server"; + wants = [ "network-online.target" ]; + after = [ "network-online.target" ]; + wantedBy = [ "multi-user.target" ]; + onFailure = [ "email-notify@%i.service" ]; + + serviceConfig = { + User = cfg.user; + Group = cfg.group; + + Type = "exec"; + WorkingDirectory = "~"; + StateDirectory = lib.mkIf (cfg.settings.storage-local-base-path != "/var/lib/gotosocial") "gotosocial"; + ReadOnlyPaths = [ cfg.package ]; + ReadWritePaths = [ cfg.settings.storage-local-base-path ]; + StateDirectoryMode = "750"; + + Restart = "always"; + RestartSec = 3; + + EnvironmentFile = lib.mkIf (cfg.environmentFile != null) cfg.environmentFile; + ExecStart = "${cfg.package}/bin/gotosocial --config-path ${configFile} server start"; + + NoNewPrivileges = true; + PrivateTmp = true; + PrivateDevices = false; + + RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6"; + RestrictNamespaces = true; + RestrictRealtime = true; + + ProtectSystem = "full"; + ProtectControlGroups = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + + DevicePolicy = "closed"; + LockPersonality = true; + SystemCallFilter = "~@clock @debug @module @mount @obsolete @reboot @setuid @swap"; + + CapabilityBoundingSet = [ + "~CAP_RAWIO CAP_MKNOD" + "~CAP_AUDIT_CONTROL CAP_AUDIT_READ CAP_AUDIT_WRITE" + "~CAP_SYS_BOOT CAP_SYS_TIME CAP_SYS_MODULE CAP_SYS_PACCT" + "~CAP_LEASE CAP_LINUX_IMMUTABLE CAP_IPC_LOCK" + "~CAP_BLOCK_SUSPEND CAP_WAKE_ALARM" + "~CAP_SYS_TTY_CONFIG" + "~CAP_MAC_ADMIN CAP_MAC_OVERRIDE" + "~CAP_NET_ADMIN CAP_NET_BROADCAST CAP_NET_RAW" + "~CAP_SYS_ADMIN CAP_SYS_PTRACE CAP_SYSLOG" + ]; + }; + }; + }; + + }); + +}