commit 80800c10ba8796bde888922206f7c62b0990f6b8
parent 1ff6e0f8f2caf6eac76822359efdc3740c287e15
Author: Katja (ctucx) <git@ctu.cx>
Date: Mon, 3 Mar 2025 18:09:06 +0100
parent 1ff6e0f8f2caf6eac76822359efdc3740c287e15
Author: Katja (ctucx) <git@ctu.cx>
Date: Mon, 3 Mar 2025 18:09:06 +0100
move webservices to `configurations/nixos/websites`
17 files changed, 625 insertions(+), 646 deletions(-)
A
|
114
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
|
251
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
|
63
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
|
139
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
D
|
118
-------------------------------------------------------------------------------
D
|
251
-------------------------------------------------------------------------------
D
|
144
-------------------------------------------------------------------------------
diff --git a/configurations/nixos/websites/dav.ctu.cx.nix b/configurations/nixos/websites/dav.ctu.cx.nix @@ -0,0 +1,48 @@ +{ config, lib, pkgs, ... }: + +{ + + dns.zones."ctu.cx".subdomains.dav.CNAME = [ "${config.networking.fqdn}." ]; + + age.secrets = { + restic-radicale.file = ./. + "/../../../secrets/${config.networking.hostName}/restic/radicale.age"; + radicale-users = { + file = ./. + "/../../../secrets/${config.networking.hostName}/radicale-users.age"; + owner = "radicale"; + }; + }; + + restic-backups.radicale = { + user = "radicale"; + passwordFile = config.age.secrets.restic-radicale.path; + paths = [ "/var/lib/radicale" ]; + }; + + systemd.services.radicale.onFailure = [ "email-notify@%i.service" ]; + + services = { + radicale = { + enable = true; + settings = { + server.hosts = [ "[::1]:5232" ]; + web.type = "internal"; + storage.filesystem_folder = "/var/lib/radicale/collections"; + headers.Access-Control-Allow-Origin = "*"; + auth.type = "htpasswd"; + auth.htpasswd_filename = config.age.secrets.radicale-users.path; + auth.htpasswd_encryption = "plain"; + }; + }; + + nginx = { + enable = true; + virtualHosts."dav.ctu.cx" = { + useACMEHost = "${config.networking.hostName}.${config.networking.domain}"; + forceSSL = true; + kTLS = true; + locations."/".proxyPass = "http://[::1]:5232/"; + }; + }; + }; + +}
diff --git a/configurations/nixos/websites/fedi.home.ctu.cx.nix b/configurations/nixos/websites/fedi.home.ctu.cx.nix @@ -0,0 +1,114 @@ +{ pkgs, lib, config, ... }: + +{ + + dns.zones."ctu.cx".subdomains."fedi.home".AAAA = [ config.networking.primaryIP ]; + + age.secrets.restic-gotosocial.file = ./. + "/../../../secrets/${config.networking.hostName}/restic/gotosocial.age"; + + systemd.services.restic-backup-gotosocial.serviceConfig.ReadWritePaths = [ "/var/lib/gotosocial" ]; + + restic-backups.gotosocial = { + user = "gotosocial"; + passwordFile = config.age.secrets.restic-gotosocial.path; + sqliteDatabases = [ "/var/lib/gotosocial/db.sqlite" ]; + paths = [ "/var/lib/gotosocial/storage" "/var/lib/gotosocial/backup.json" ]; + runBeforeBackup = '' + ${pkgs.gotosocial}/bin/gotosocial --config-path /etc/gotosocial.yaml admin export --path /var/lib/gotosocial/backup.json + ''; + }; + + systemd.services.gotosocial.serviceConfig = { + Group = lib.mkForce config.services.nginx.group; + }; + + services.gotosocial = { + enable = true; + group = "nginx"; + settings = { + application-name = "ctucx.gts"; + + host = "fedi.home.ctu.cx"; + account-domain = "fedi.home.ctu.cx"; + protocol = "https"; + + bind-address = "[::1]"; + port = 8085; + + trusted-proxies = [ "::1/128" "172.17.0.0/24" ]; + + db-type = "sqlite"; + db-address = "/var/lib/gotosocial/db.sqlite"; + + accounts-allow-custom-css = true; + accounts-registration-open = false; + + instance-expose-peers = true; + instance-expose-suspended = true; + instance-expose-suspended-web = true; + + instance-languages = [ "de" "en-us" ]; + + storage-backend = "local"; + storage-local-base-path = "/var/lib/gotosocial/storage"; + + media-remote-max-size = 0; + media-remote-cache-days = 3; + media-cleanup-from = "02:00"; + }; + }; + + services.nginx.appendHttpConfig = '' + proxy_cache_path /var/cache/nginx keys_zone=gotosocial_ap_public_responses:10m inactive=1w; + ''; + + services.nginx.virtualHosts."fedi.home.ctu.cx" = { + useACMEHost = "${config.networking.hostName}.${config.networking.domain}"; + forceSSL = true; + kTLS = true; + locations = { + "= /".return = "307 /@leah"; + + "/" = { + proxyPass = "http://${toString config.services.gotosocial.settings.bind-address}:${toString config.services.gotosocial.settings.port}"; + proxyWebsockets = true; + }; + + "~ /.well-known/(webfinger|host-meta)$" = { + proxyPass = "http://${toString config.services.gotosocial.settings.bind-address}:${toString config.services.gotosocial.settings.port}"; + extraConfig = '' + proxy_cache gotosocial_ap_public_responses; + proxy_cache_background_update on; + proxy_cache_key $scheme://$host$uri$is_args$query_string; + proxy_cache_valid 200 10m; + proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504 http_429; + proxy_cache_lock on; + add_header X-Cache-Status $upstream_cache_status; + ''; + }; + + "~ ^\/users\/(?:[a-z0-9_\.]+)\/main-key$" = { + proxyPass = "http://${toString config.services.gotosocial.settings.bind-address}:${toString config.services.gotosocial.settings.port}"; + extraConfig = '' + proxy_cache gotosocial_ap_public_responses; + proxy_cache_background_update on; + proxy_cache_key $scheme://$host$uri; + proxy_cache_valid 200 604800s; + proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504 http_429; + proxy_cache_lock on; + + add_header X-Cache-Status $upstream_cache_status; + ''; + }; + + "/assets/".extraConfig = '' + alias ${config.services.gotosocial.package}/share/web/assets/; + autoindex off; + expires max; + add_header Cache-Control "public, immutable"; + ''; + + }; + }; + +}
diff --git a/configurations/nixos/websites/git.ctu.cx.nix b/configurations/nixos/websites/git.ctu.cx.nix @@ -0,0 +1,251 @@ +{ config, lib, pkgs, ... }: + +let + stagitFunctions = pkgs.writeShellScript "stagitFunctions" '' + export LC_CTYPE="en_US.UTF-8" + + is_public_and_listed() { + if [ ! -f "$1/git-daemon-export-ok" ]; then + return 1 + fi + return 0 + } + + is_forced_update() { + test "$oldrev" = "0000000000000000000000000000000000000000" && return 1 + test "$newrev" = "0000000000000000000000000000000000000000" && return 1 + + hasrevs="$(${pkgs.git}/bin/git rev-list "$oldrev" "^$newrev" | ${pkgs.gnused}/bin/sed 1q)" + if test -n "$hasrevs"; then + return 0 + fi + return 1 + } + + build_stagit_repo() { + reponame="$(basename "$1" ".git")" + printf "[%s] Generate stagit HTML pages... " "$reponame" + + mkdir -p "/var/lib/gitolite/stagit-cache" + mkdir -p "/var/lib/stagit/$reponame" + + cd "/var/lib/stagit/$reponame" || return 1 + + # build repo pages + ${pkgs.stagit}/bin/stagit -c "/var/lib/gitolite/stagit-cache/$reponame" -n 'ctucx.git' -h 'https://git.ctu.cx/' -s 'git@${config.networking.hostName}.ctu.cx:' "$1" + + # set correct permissions + chown git:git -R /var/lib/stagit/$reponame; + chmod 755 -R /var/lib/stagit/$reponame; + + echo "done" + } + + build_stagit_index() { + printf "Generating stagit index... " + + # set assets if not already there + ln -sf "${pkgs.stagit}/share/doc/stagit/style.css" "/var/lib/stagit/style.css" 2> /dev/null + + # generate index arguments + args="-n 'ctucx.git' -e 'git@ctu.cx'" + + for category in "nix" "etc" "nimlang" "nimlang libraries" "archive"; do + args="$args -c '$category'" + for repo in "$HOME/repositories/"*.git/; do + repo="''${repo%/}" + is_public_and_listed "$repo" || continue + + [ "$(${pkgs.gawk}/bin/awk -F '=' '/category/ {print $2}' $repo/config | ${pkgs.gnused}/bin/sed -e 's/^[[:space:]]*//')" = "$category" ] && args="$args $repo" + done + done + + # build index + echo "$args" | xargs ${pkgs.stagit}/bin/stagit-index > /var/lib/stagit/index.html + + # set correct permissions + chown git:git /var/lib/stagit/index.html; + chmod 755 /var/lib/stagit/index.html; + + echo "done" + } + + + update_stagit_repo() { + repo="$(pwd)" + reponame="$(basename "$repo" ".git")" + + cd "$repo" || return 1 + is_public_and_listed "$repo" || return 0 + + # if forced update, remove directory and cache file + is_forced_update && printf "[%s] Forced update, trigger complete regeneration of stagit-pages... \n" "$reponame" && rm -rf "/var/lib/stagit/$reponame" "/var/lib/gitolite/stagit-cache/$reponame" + + build_stagit_repo "$repo" + build_stagit_index + } + + ''; + + rebuildWebdir = '' + source ${stagitFunctions} + + # clear webdir + rm -rf /var/lib/stagit/* + + # clear cache + rm -rf /var/lib/gitolite/stagit-cache/* + + # generate pages per repo + for repo in "$HOME/repositories/"*.git/; do + repo="''${repo%/}" + is_public_and_listed "$repo" || continue + + build_stagit_repo "$repo" + done + + # generate index page + build_stagit_index + ''; + +in { + + services.openssh.settings.Macs = [ + "hmac-sha2-512" + "hmac-sha2-512-etm@openssh.com" + "hmac-sha2-256-etm@openssh.com" + "umac-128-etm@openssh.com" + ]; + + dns.zones."ctu.cx".subdomains = { + cgit.CNAME = [ "${config.networking.fqdn}." ]; + git.CNAME = [ "${config.networking.fqdn}." ]; + }; + + age.secrets.restic-gitolite.file = ./. + "/../../../secrets/${config.networking.hostName}/restic/gitolite.age"; + + restic-backups.gitolite = { + user = "git"; + passwordFile = config.age.secrets.restic-gitolite.path; + paths = [ "/var/lib/gitolite" ]; + }; + + services = { + gitolite = { + enable = true; + user = "git"; + group = "git"; + adminPubkey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDb2eZ2ymt+Zsf0eTlmjW2jPdS013lbde1+EGkgu6bz9lVTR8aawshF2HcoaWp5a5dJr3SKyihDM8hbWSYB3qyTHihNGyCArqSvAtZRw301ailRVHGqiwUITTfcg1533TtmWvlJZgOIFM1VvSAfdueDRRRzbygmn749fS9nhUTDzLtjqX5LvhpqhzsD+eOqPrV6Ne8E1e42JxQb5AJPY1gj9mk6eAarvtEHQYEe+/hp9ERjtCdN5DfuOJnqfaKS0ytPj/NbQskbX/TMgeUVio11iC2NbXsnAtzMmtbLX4mxlDQrR6aZmU/rHQ4aeJqI/Tj2rrF46icri7s0tnnit1OjT5PSxXgifcOtn06qoxYZMT1x+Dyrt40vNkGmxmxCnirm8B+6MKXgd/Ys+7tnOm1ht8TmLm96x6KdOiF3Zq/tMxhPAzp8JriTKSo7k7U9XxStFghTbhhBNc7OX89ZbpalLEnvbQiz87gZxhcx8cLvzIjslOHmZOSWC5Pgr4wwuj3Akq63i4ya6/BzM6v4UoBuDAB6fz3NHKL4R5X20la7Pvt7OBysQkGClWfj6ipMR1bFE2mfYtlMioXNgTjC+NCpEl1+81MH7dv2565Hk8CLV8FMxv6GujbAZGjjcM47lpWM1cBQvpBMUA/lLkyiCPK0YxNWAB7Co+jYDl6CR0Ubew== cardno:6445161"; + + extraGitoliteRc = '' + $RC{GIT_CONFIG_KEYS} = ".*"; + $RC{UMASK} = 0027; + + push(@{$RC{ENABLE}}, 'cgit'); + push(@{$RC{ENABLE}}, 'symbolic-ref'); + push(@{$RC{ENABLE}}, 'rebuild-webdir'); + push(@{$RC{ENABLE}}, 'rebuild-webdir'); + + $RC{NON_CORE} = "rebuild-webdir-trigger POST_COMPILE rebuild-stagit"; + ''; + + triggers.rebuild-webdir = rebuildWebdir; + commands.rebuild-webdir = rebuildWebdir; + commonHooks.post-receive = '' + # update stagit pages + source ${stagitFunctions} + update_stagit_repo "$1" + ''; + }; + + fcgiwrap = { + instances.git = { + process.user = "git"; + process.group = "git"; + socket.user = "nginx"; + socket.group = "nginx"; + }; + }; + + nginx = { + enable = true; + virtualHosts = { + "cgit.ctu.cx" = { + useACMEHost = "${config.networking.hostName}.${config.networking.domain}"; + forceSSL = true; + kTLS = true; + locations = { + "~ '^/[a-zA-Z0-9._-]+/(git-(receive|upload)-pack|HEAD|info/refs|objects/(info/(http-)?alternates|packs)|[0-9a-f]{2}/[0-9a-f]{38}|pack/pack-[0-9a-f]{40}\.(pack|idx))$'".return = "307 https://git.ctu.cx$request_uri"; + "~ '^/([a-zA-Z0-9_.]+)/*$'".return = "307 https://git.ctu.cx/$1"; + "~ '^/([a-zA-Z0-9_.]+)/tree/([a-zA-Z0-9_./-]+[a-zA-Z0-9_-])/*$'".return = "307 https://git.ctu.cx/$1/tree/$2.html"; + "~ '^/([a-zA-Z0-9_.]+)/tree/*$'".return = "307 https://git.ctu.cx/$1/tree.html"; + "~ '^/([a-zA-Z0-9_.]+)/log/*$'".return = "307 https://git.ctu.cx/$1/log.html"; + "~ '^/([a-zA-Z0-9_.]+)/commit/*$'".extraConfig = '' + if ($arg_id) { + return 307 https://git.ctu.cx/$1/commit/$arg_id.html; + } + + return 307 https://git.ctu.cx/$1/log.html; + ''; + }; + }; + + "git.ctu.cx" = { + useACMEHost = "${config.networking.hostName}.${config.networking.domain}"; + forceSSL = true; + kTLS = true; + root = "/var/lib/stagit"; + locations = { + "@redir".return = "307 ../log.html"; + "~ '^/([a-zA-Z0-9_.]+)/commit/.*$'".extraConfig = "error_page 404 = @redir;"; + + "~* \.html$".extraConfig = '' + add_header Last-Modified $date_gmt; + add_header Cache-Control 'private no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0'; + if_modified_since off; + expires off; + etag off; + ''; + + "~ '^/[a-zA-Z0-9._-]+/raw'".extraConfig = '' + types { + application/json json; + + application/wasm wasm; + font/woff woff; + font/woff2 woff2; + + application/pdf pdf; + + image/gif gif; + image/jpeg jpeg jpg; + image/png png; + image/svg+xml svg svgz; + image/webp webp; + image/x-icon ico; + } + + default_type text/plain; + try_files $uri =404; + ''; + + "~ '^/[a-zA-Z0-9._-]+/(git-(receive|upload)-pack|HEAD|info/refs|objects/(info/(http-)?alternates|packs)|[0-9a-f]{2}/[0-9a-f]{38}|pack/pack-[0-9a-f]{40}\.(pack|idx))$'".extraConfig = '' + if ($query_string = service=git-receive-pack) { + return 403; + } + + include "${pkgs.nginx}/conf/fastcgi_params"; + fastcgi_param SCRIPT_FILENAME "${pkgs.git}/libexec/git-core/git-http-backend"; + fastcgi_param GIT_PROJECT_ROOT /var/lib/gitolite/repositories; + fastcgi_param PATH_INFO $uri; + fastcgi_pass unix:${config.services.fcgiwrap.instances.git.socket.address}; + ''; + }; + }; + + }; + }; + + }; + +}
diff --git a/configurations/nixos/websites/vault.ctu.cx.nix b/configurations/nixos/websites/vault.ctu.cx.nix @@ -0,0 +1,62 @@ +{ pkgs, config, ... }: + +{ + + dns.zones."ctu.cx".subdomains.vault.CNAME = [ "${config.networking.fqdn}." ]; + + age.secrets = { + restic-vaultwarden.file = ./. + "/../../../secrets/${config.networking.hostName}/restic/vaultwarden.age"; + vaultwarden-secrets = { + file = ./. + "/../../../secrets/${config.networking.hostName}/vaultwarden-secrets.age"; + owner = "vaultwarden"; + group = "vaultwarden"; + }; + }; + + restic-backups.vaultwarden = { + user = "vaultwarden"; + passwordFile = config.age.secrets.restic-vaultwarden.path; + paths = [ "/var/lib/vaultwarden" "/var/backups/vaultwarden"]; + }; + + systemd.services.vaultwarden.onFailure = [ "email-notify@%i.service" ]; + + services = { + vaultwarden = { + enable = true; + dbBackend = "sqlite"; + backupDir = "/var/backups/vaultwarden"; + environmentFile = config.age.secrets.vaultwarden-secrets.path; + config = { + DOMAIN = "https://vault.ctu.cx"; + SIGNUPS_ALLOWED = false; + + PUSH_ENABLED = true; + + SMTP_HOST = "hector.ctu.cx"; + SMTP_FROM = "vaultwarden@ctu.cx"; + SMTP_USERNAME = "vaultwarden@ctu.cx"; + SMTP_PORT = 587; + SMTP_SECURITY = "starttls"; + + ROCKET_ADDRESS = "::1"; + ROCKET_PORT = 8582; + }; + }; + + nginx = { + enable = true; + virtualHosts."vault.ctu.cx" = { + useACMEHost = "${config.networking.hostName}.${config.networking.domain}"; + forceSSL = true; + kTLS = true; + locations."/".proxyPass = "http://[::1]:${toString config.services.vaultwarden.config.ROCKET_PORT}/"; + locations."/notifications/hub" = { + proxyPass = "http://[::1]:${toString config.services.vaultwarden.config.ROCKET_PORT}/"; + proxyWebsockets = true; + }; + }; + }; + }; + +}+ \ No newline at end of file
diff --git a/configurations/nixos/websites/zuggeschmack.de.nix b/configurations/nixos/websites/zuggeschmack.de.nix @@ -0,0 +1,139 @@ +{ pkgs, lib, config, ... }: + +{ + + dns.zones."zuggeschmack.de" = (pkgs.dns.lib.combinators.host config.networking.primaryIP4 config.networking.primaryIP) // { + subdomains."client".CNAME = [ "${config.networking.fqdn}." ]; + }; + + age.secrets = { + restic-gotosocial.file = ./. + "/../../../secrets/${config.networking.hostName}/restic/gotosocial.age"; + gotosocial-env.file = ./. + "/../../../secrets/${config.networking.hostName}/gotosocial-env.age"; + }; + + systemd.services.restic-backup-gotosocial.serviceConfig.ReadWritePaths = [ "/var/lib/gotosocial" ]; + + restic-backups.gotosocial = { + user = "gotosocial"; + passwordFile = config.age.secrets.restic-gotosocial.path; + sqliteDatabases = [ "/var/lib/gotosocial/db.sqlite" ]; + paths = [ "/var/lib/gotosocial/storage" "/var/lib/gotosocial/backup.json" ]; + runBeforeBackup = '' + ${pkgs.gotosocial}/bin/gotosocial --config-path /etc/gotosocial.yaml admin export --path /var/lib/gotosocial/backup.json + ''; + }; + + systemd.services.gotosocial.serviceConfig.Group = lib.mkForce config.services.nginx.group; + + services.gotosocial = { + enable = true; + group = "nginx"; + environmentFile = config.age.secrets.gotosocial-env.path; + settings = { + application-name = "ZugGeschmack.de"; + + host = "zuggeschmack.de"; + account-domain = "zuggeschmack.de"; + protocol = "https"; + + bind-address = "[::1]"; + port = 8085; + + trusted-proxies = [ "::1/128" "172.17.0.0/24" ]; + + db-type = "sqlite"; + db-address = "/var/lib/gotosocial/db.sqlite"; + + accounts-allow-custom-css = true; + accounts-registration-open = true; + + instance-expose-peers = true; + instance-expose-suspended = true; + instance-expose-suspended-web = true; + + instance-languages = [ "de" "en-us" ]; + + storage-backend = "local"; + storage-local-base-path = "/var/lib/gotosocial/storage"; + + media-local-max-size = "50MiB"; + media-remote-max-size = "50MiB"; + + media-remote-cache-days = 3; + media-cleanup-from = "01:00"; + + smtp-host = "hector.ctu.cx"; + smtp-port = 587; + smtp-username = "gts@zuggeschmack.de"; + smtp-from = "gts@zuggeschmack.de"; + }; + }; + + services.nginx.appendHttpConfig = '' + proxy_cache_path /var/cache/nginx keys_zone=gotosocial_ap_public_responses:10m inactive=1w; + ''; + + services.nginx.virtualHosts."zuggeschmack.de" = { + useACMEHost = "${config.networking.hostName}.${config.networking.domain}"; + forceSSL = true; + kTLS = true; + extraConfig = '' + client_max_body_size 50M; + ''; + locations = { + "/" = { + proxyPass = "http://${toString config.services.gotosocial.settings.bind-address}:${toString config.services.gotosocial.settings.port}"; + proxyWebsockets = true; + extraConfig = '' + client_max_body_size 50M; + ''; + }; + + "~ /.well-known/(webfinger|host-meta)$" = { + proxyPass = "http://${toString config.services.gotosocial.settings.bind-address}:${toString config.services.gotosocial.settings.port}"; + extraConfig = '' + proxy_cache gotosocial_ap_public_responses; + proxy_cache_background_update on; + proxy_cache_key $scheme://$host$uri$is_args$query_string; + proxy_cache_valid 200 10m; + proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504 http_429; + proxy_cache_lock on; + add_header X-Cache-Status $upstream_cache_status; + ''; + }; + + "~ ^\/users\/(?:[a-z0-9_\.]+)\/main-key$" = { + proxyPass = "http://${toString config.services.gotosocial.settings.bind-address}:${toString config.services.gotosocial.settings.port}"; + extraConfig = '' + proxy_cache gotosocial_ap_public_responses; + proxy_cache_background_update on; + proxy_cache_key $scheme://$host$uri; + proxy_cache_valid 200 604800s; + proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504 http_429; + proxy_cache_lock on; + + add_header X-Cache-Status $upstream_cache_status; + ''; + }; + + "/assets/".extraConfig = '' + alias ${config.services.gotosocial.package}/share/web/assets/; + autoindex off; + expires max; + add_header Cache-Control "public, immutable"; + ''; + }; + + }; + + services.nginx.virtualHosts."client.zuggeschmack.de" = { + useACMEHost = "${config.networking.hostName}.${config.networking.domain}"; + forceSSL = true; + kTLS = true; + root = pkgs.masto-fe-standalone; + extraConfig = '' + try_files $uri $uri/ /index.html; + ''; + }; + +}
diff --git a/machines/briefkasten/default.nix b/machines/briefkasten/default.nix @@ -14,13 +14,11 @@ ctucxConfig.websites."music.home.ctu.cx" ctucxConfig.websites."audiobooks.home.ctu.cx" + ctucxConfig.websites."fedi.home.ctu.cx" # syncthing (and it's backup) ./syncthing.nix - # fedi server - ./gotosocial.nix - ./smarthome ./scanner-sftp.nix
diff --git a/machines/briefkasten/gotosocial.nix b/machines/briefkasten/gotosocial.nix @@ -1,118 +0,0 @@ -{ pkgs, lib, config, ... }: - -let - gotosocial = pkgs.callPackage ../../pkgs/gotosocial {}; - -in { - - dns.zones."ctu.cx".subdomains."fedi.home".AAAA = [ config.networking.primaryIP ]; - - age.secrets.restic-gotosocial.file = ./. + "/../../secrets/${config.networking.hostName}/restic/gotosocial.age"; - - systemd.services.restic-backup-gotosocial.serviceConfig.ReadWritePaths = [ "/var/lib/gotosocial" ]; - - restic-backups.gotosocial = { - user = "gotosocial"; - passwordFile = config.age.secrets.restic-gotosocial.path; - sqliteDatabases = [ "/var/lib/gotosocial/db.sqlite" ]; - paths = [ "/var/lib/gotosocial/storage" "/var/lib/gotosocial/backup.json" ]; - runBeforeBackup = '' - ${gotosocial}/bin/gotosocial --config-path /etc/gotosocial.yaml admin export --path /var/lib/gotosocial/backup.json - ''; - }; - - systemd.services.gotosocial.serviceConfig = { - Group = lib.mkForce config.services.nginx.group; - }; - - services.gotosocial = { - enable = true; - package = gotosocial; - group = "nginx"; - settings = { - application-name = "ctucx.gts"; - - host = "fedi.home.ctu.cx"; - account-domain = "fedi.home.ctu.cx"; - protocol = "https"; - - bind-address = "[::1]"; - port = 8085; - - trusted-proxies = [ "::1/128" "172.17.0.0/24" ]; - - db-type = "sqlite"; - db-address = "/var/lib/gotosocial/db.sqlite"; - - accounts-allow-custom-css = true; - accounts-registration-open = false; - - instance-expose-peers = true; - instance-expose-suspended = true; - instance-expose-suspended-web = true; - - instance-languages = [ "de" "en-us" ]; - - storage-backend = "local"; - storage-local-base-path = "/var/lib/gotosocial/storage"; - - media-remote-max-size = 0; - media-remote-cache-days = 3; - media-cleanup-from = "02:00"; - }; - }; - - services.nginx.appendHttpConfig = '' - proxy_cache_path /var/cache/nginx keys_zone=gotosocial_ap_public_responses:10m inactive=1w; - ''; - - services.nginx.virtualHosts."fedi.home.ctu.cx" = { - useACMEHost = "${config.networking.hostName}.${config.networking.domain}"; - forceSSL = true; - kTLS = true; - locations = { - "= /".return = "307 /@leah"; - - "/" = { - proxyPass = "http://${toString config.services.gotosocial.settings.bind-address}:${toString config.services.gotosocial.settings.port}"; - proxyWebsockets = true; - }; - - "~ /.well-known/(webfinger|host-meta)$" = { - proxyPass = "http://${toString config.services.gotosocial.settings.bind-address}:${toString config.services.gotosocial.settings.port}"; - extraConfig = '' - proxy_cache gotosocial_ap_public_responses; - proxy_cache_background_update on; - proxy_cache_key $scheme://$host$uri$is_args$query_string; - proxy_cache_valid 200 10m; - proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504 http_429; - proxy_cache_lock on; - add_header X-Cache-Status $upstream_cache_status; - ''; - }; - - "~ ^\/users\/(?:[a-z0-9_\.]+)\/main-key$" = { - proxyPass = "http://${toString config.services.gotosocial.settings.bind-address}:${toString config.services.gotosocial.settings.port}"; - extraConfig = '' - proxy_cache gotosocial_ap_public_responses; - proxy_cache_background_update on; - proxy_cache_key $scheme://$host$uri; - proxy_cache_valid 200 604800s; - proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504 http_429; - proxy_cache_lock on; - - add_header X-Cache-Status $upstream_cache_status; - ''; - }; - - "/assets/".extraConfig = '' - alias ${config.services.gotosocial.package}/share/web/assets/; - autoindex off; - expires max; - add_header Cache-Control "public, immutable"; - ''; - - }; - }; - -}
diff --git a/machines/hector/default.nix b/machines/hector/default.nix @@ -12,28 +12,28 @@ ctucxConfig.websites."things.ctu.cx" ctucxConfig.websites."bikemap.ctu.cx" ctucxConfig.websites."photos.ctu.cx" + ctucxConfig.websites."grocy.ctu.cx" # monitoring - ./prometheus.nix + ctucxConfig.websites."prometheus.ctu.cx" ./grafana - # cal- and card-dav server - ./radicale.nix + # cal-/card-dav server (radicale) + ctucxConfig.websites."dav.ctu.cx" # vaultwarden password-store - ./vaultwarden.nix + ctucxConfig.websites."vault.ctu.cx" # git server (gitolite+stagit) - ./git.nix + ctucxConfig.websites."git.ctu.cx" - # communication - ./fedi + # fediverse server (gotosocial) + ctucxConfig.websites."fedi.ctu.cx" ./matrix ./mail ./syncthing.nix - ./grocy.nix ]; dns.zones."ctu.cx".subdomains."${config.networking.hostName}" = (pkgs.dns.lib.combinators.host config.networking.primaryIP4 config.networking.primaryIP);
diff --git a/machines/hector/fedi/default.nix b/machines/hector/fedi/default.nix @@ -1,9 +0,0 @@ -{ ... }: - -{ - - imports = [ - ./gotosocial.nix - ]; - -}- \ No newline at end of file
diff --git a/machines/hector/git.nix b/machines/hector/git.nix @@ -1,251 +0,0 @@ -{ config, lib, pkgs, ... }: - -let - stagitFunctions = pkgs.writeShellScript "stagitFunctions" '' - export LC_CTYPE="en_US.UTF-8" - - is_public_and_listed() { - if [ ! -f "$1/git-daemon-export-ok" ]; then - return 1 - fi - return 0 - } - - is_forced_update() { - test "$oldrev" = "0000000000000000000000000000000000000000" && return 1 - test "$newrev" = "0000000000000000000000000000000000000000" && return 1 - - hasrevs="$(${pkgs.git}/bin/git rev-list "$oldrev" "^$newrev" | ${pkgs.gnused}/bin/sed 1q)" - if test -n "$hasrevs"; then - return 0 - fi - return 1 - } - - build_stagit_repo() { - reponame="$(basename "$1" ".git")" - printf "[%s] Generate stagit HTML pages... " "$reponame" - - mkdir -p "/var/lib/gitolite/stagit-cache" - mkdir -p "/var/lib/stagit/$reponame" - - cd "/var/lib/stagit/$reponame" || return 1 - - # build repo pages - ${pkgs.stagit}/bin/stagit -c "/var/lib/gitolite/stagit-cache/$reponame" -n 'ctucx.git' -h 'https://git.ctu.cx/' -s 'git@${config.networking.hostName}.ctu.cx:' "$1" - - # set correct permissions - chown git:git -R /var/lib/stagit/$reponame; - chmod 755 -R /var/lib/stagit/$reponame; - - echo "done" - } - - build_stagit_index() { - printf "Generating stagit index... " - - # set assets if not already there - ln -sf "${pkgs.stagit}/share/doc/stagit/style.css" "/var/lib/stagit/style.css" 2> /dev/null - - # generate index arguments - args="-n 'ctucx.git' -e 'git@ctu.cx'" - - for category in "nix" "etc" "nimlang" "nimlang libraries" "archive"; do - args="$args -c '$category'" - for repo in "$HOME/repositories/"*.git/; do - repo="''${repo%/}" - is_public_and_listed "$repo" || continue - - [ "$(${pkgs.gawk}/bin/awk -F '=' '/category/ {print $2}' $repo/config | ${pkgs.gnused}/bin/sed -e 's/^[[:space:]]*//')" = "$category" ] && args="$args $repo" - done - done - - # build index - echo "$args" | xargs ${pkgs.stagit}/bin/stagit-index > /var/lib/stagit/index.html - - # set correct permissions - chown git:git /var/lib/stagit/index.html; - chmod 755 /var/lib/stagit/index.html; - - echo "done" - } - - - update_stagit_repo() { - repo="$(pwd)" - reponame="$(basename "$repo" ".git")" - - cd "$repo" || return 1 - is_public_and_listed "$repo" || return 0 - - # if forced update, remove directory and cache file - is_forced_update && printf "[%s] Forced update, trigger complete regeneration of stagit-pages... \n" "$reponame" && rm -rf "/var/lib/stagit/$reponame" "/var/lib/gitolite/stagit-cache/$reponame" - - build_stagit_repo "$repo" - build_stagit_index - } - - ''; - - rebuildWebdir = '' - source ${stagitFunctions} - - # clear webdir - rm -rf /var/lib/stagit/* - - # clear cache - rm -rf /var/lib/gitolite/stagit-cache/* - - # generate pages per repo - for repo in "$HOME/repositories/"*.git/; do - repo="''${repo%/}" - is_public_and_listed "$repo" || continue - - build_stagit_repo "$repo" - done - - # generate index page - build_stagit_index - ''; - -in { - - services.openssh.settings.Macs = [ - "hmac-sha2-512" - "hmac-sha2-512-etm@openssh.com" - "hmac-sha2-256-etm@openssh.com" - "umac-128-etm@openssh.com" - ]; - - dns.zones."ctu.cx".subdomains = { - cgit.CNAME = [ "${config.networking.fqdn}." ]; - git.CNAME = [ "${config.networking.fqdn}." ]; - }; - - age.secrets.restic-gitolite.file = ./. + "/../../secrets/${config.networking.hostName}/restic/gitolite.age"; - - restic-backups.gitolite = { - user = "git"; - passwordFile = config.age.secrets.restic-gitolite.path; - paths = [ "/var/lib/gitolite" ]; - }; - - services = { - gitolite = { - enable = true; - user = "git"; - group = "git"; - adminPubkey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDb2eZ2ymt+Zsf0eTlmjW2jPdS013lbde1+EGkgu6bz9lVTR8aawshF2HcoaWp5a5dJr3SKyihDM8hbWSYB3qyTHihNGyCArqSvAtZRw301ailRVHGqiwUITTfcg1533TtmWvlJZgOIFM1VvSAfdueDRRRzbygmn749fS9nhUTDzLtjqX5LvhpqhzsD+eOqPrV6Ne8E1e42JxQb5AJPY1gj9mk6eAarvtEHQYEe+/hp9ERjtCdN5DfuOJnqfaKS0ytPj/NbQskbX/TMgeUVio11iC2NbXsnAtzMmtbLX4mxlDQrR6aZmU/rHQ4aeJqI/Tj2rrF46icri7s0tnnit1OjT5PSxXgifcOtn06qoxYZMT1x+Dyrt40vNkGmxmxCnirm8B+6MKXgd/Ys+7tnOm1ht8TmLm96x6KdOiF3Zq/tMxhPAzp8JriTKSo7k7U9XxStFghTbhhBNc7OX89ZbpalLEnvbQiz87gZxhcx8cLvzIjslOHmZOSWC5Pgr4wwuj3Akq63i4ya6/BzM6v4UoBuDAB6fz3NHKL4R5X20la7Pvt7OBysQkGClWfj6ipMR1bFE2mfYtlMioXNgTjC+NCpEl1+81MH7dv2565Hk8CLV8FMxv6GujbAZGjjcM47lpWM1cBQvpBMUA/lLkyiCPK0YxNWAB7Co+jYDl6CR0Ubew== cardno:6445161"; - - extraGitoliteRc = '' - $RC{GIT_CONFIG_KEYS} = ".*"; - $RC{UMASK} = 0027; - - push(@{$RC{ENABLE}}, 'cgit'); - push(@{$RC{ENABLE}}, 'symbolic-ref'); - push(@{$RC{ENABLE}}, 'rebuild-webdir'); - push(@{$RC{ENABLE}}, 'rebuild-webdir'); - - $RC{NON_CORE} = "rebuild-webdir-trigger POST_COMPILE rebuild-stagit"; - ''; - - triggers.rebuild-webdir = rebuildWebdir; - commands.rebuild-webdir = rebuildWebdir; - commonHooks.post-receive = '' - # update stagit pages - source ${stagitFunctions} - update_stagit_repo "$1" - ''; - }; - - fcgiwrap = { - instances.git = { - process.user = "git"; - process.group = "git"; - socket.user = "nginx"; - socket.group = "nginx"; - }; - }; - - nginx = { - enable = true; - virtualHosts = { - "cgit.ctu.cx" = { - useACMEHost = "${config.networking.hostName}.${config.networking.domain}"; - forceSSL = true; - kTLS = true; - locations = { - "~ '^/[a-zA-Z0-9._-]+/(git-(receive|upload)-pack|HEAD|info/refs|objects/(info/(http-)?alternates|packs)|[0-9a-f]{2}/[0-9a-f]{38}|pack/pack-[0-9a-f]{40}\.(pack|idx))$'".return = "307 https://git.ctu.cx$request_uri"; - "~ '^/([a-zA-Z0-9_.]+)/*$'".return = "307 https://git.ctu.cx/$1"; - "~ '^/([a-zA-Z0-9_.]+)/tree/([a-zA-Z0-9_./-]+[a-zA-Z0-9_-])/*$'".return = "307 https://git.ctu.cx/$1/tree/$2.html"; - "~ '^/([a-zA-Z0-9_.]+)/tree/*$'".return = "307 https://git.ctu.cx/$1/tree.html"; - "~ '^/([a-zA-Z0-9_.]+)/log/*$'".return = "307 https://git.ctu.cx/$1/log.html"; - "~ '^/([a-zA-Z0-9_.]+)/commit/*$'".extraConfig = '' - if ($arg_id) { - return 307 https://git.ctu.cx/$1/commit/$arg_id.html; - } - - return 307 https://git.ctu.cx/$1/log.html; - ''; - }; - }; - - "git.ctu.cx" = { - useACMEHost = "${config.networking.hostName}.${config.networking.domain}"; - forceSSL = true; - kTLS = true; - root = "/var/lib/stagit"; - locations = { - "@redir".return = "307 ../log.html"; - "~ '^/([a-zA-Z0-9_.]+)/commit/.*$'".extraConfig = "error_page 404 = @redir;"; - - "~* \.html$".extraConfig = '' - add_header Last-Modified $date_gmt; - add_header Cache-Control 'private no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0'; - if_modified_since off; - expires off; - etag off; - ''; - - "~ '^/[a-zA-Z0-9._-]+/raw'".extraConfig = '' - types { - application/json json; - - application/wasm wasm; - font/woff woff; - font/woff2 woff2; - - application/pdf pdf; - - image/gif gif; - image/jpeg jpeg jpg; - image/png png; - image/svg+xml svg svgz; - image/webp webp; - image/x-icon ico; - } - - default_type text/plain; - try_files $uri =404; - ''; - - "~ '^/[a-zA-Z0-9._-]+/(git-(receive|upload)-pack|HEAD|info/refs|objects/(info/(http-)?alternates|packs)|[0-9a-f]{2}/[0-9a-f]{38}|pack/pack-[0-9a-f]{40}\.(pack|idx))$'".extraConfig = '' - if ($query_string = service=git-receive-pack) { - return 403; - } - - include "${pkgs.nginx}/conf/fastcgi_params"; - fastcgi_param SCRIPT_FILENAME "${pkgs.git}/libexec/git-core/git-http-backend"; - fastcgi_param GIT_PROJECT_ROOT /var/lib/gitolite/repositories; - fastcgi_param PATH_INFO $uri; - fastcgi_pass unix:${config.services.fcgiwrap.instances.git.socket.address}; - ''; - }; - }; - - }; - }; - - }; - -}
diff --git a/machines/hector/radicale.nix b/machines/hector/radicale.nix @@ -1,48 +0,0 @@ -{ config, lib, pkgs, ... }: - -{ - - dns.zones."ctu.cx".subdomains.dav.CNAME = [ "${config.networking.fqdn}." ]; - - age.secrets = { - restic-radicale.file = ./. + "/../../secrets/${config.networking.hostName}/restic/radicale.age"; - radicale-users = { - file = ./. + "/../../secrets/${config.networking.hostName}/radicale-users.age"; - owner = "radicale"; - }; - }; - - restic-backups.radicale = { - user = "radicale"; - passwordFile = config.age.secrets.restic-radicale.path; - paths = [ "/var/lib/radicale" ]; - }; - - systemd.services.radicale.onFailure = [ "email-notify@%i.service" ]; - - services = { - radicale = { - enable = true; - settings = { - server.hosts = [ "[::1]:5232" ]; - web.type = "internal"; - storage.filesystem_folder = "/var/lib/radicale/collections"; - headers.Access-Control-Allow-Origin = "*"; - auth.type = "htpasswd"; - auth.htpasswd_filename = config.age.secrets.radicale-users.path; - auth.htpasswd_encryption = "plain"; - }; - }; - - nginx = { - enable = true; - virtualHosts."dav.ctu.cx" = { - useACMEHost = "${config.networking.hostName}.${config.networking.domain}"; - forceSSL = true; - kTLS = true; - locations."/".proxyPass = "http://[::1]:5232/"; - }; - }; - }; - -}
diff --git a/machines/hector/vaultwarden.nix b/machines/hector/vaultwarden.nix @@ -1,62 +0,0 @@ -{ pkgs, config, ... }: - -{ - - dns.zones."ctu.cx".subdomains.vault.CNAME = [ "${config.networking.fqdn}." ]; - - age.secrets = { - restic-vaultwarden.file = ./. + "/../../secrets/${config.networking.hostName}/restic/vaultwarden.age"; - vaultwarden-secrets = { - file = ./. + "/../../secrets/${config.networking.hostName}/vaultwarden-secrets.age"; - owner = "vaultwarden"; - group = "vaultwarden"; - }; - }; - - restic-backups.vaultwarden = { - user = "vaultwarden"; - passwordFile = config.age.secrets.restic-vaultwarden.path; - paths = [ "/var/lib/vaultwarden" "/var/backups/vaultwarden"]; - }; - - systemd.services.vaultwarden.onFailure = [ "email-notify@%i.service" ]; - - services = { - vaultwarden = { - enable = true; - dbBackend = "sqlite"; - backupDir = "/var/backups/vaultwarden"; - environmentFile = config.age.secrets.vaultwarden-secrets.path; - config = { - DOMAIN = "https://vault.ctu.cx"; - SIGNUPS_ALLOWED = false; - - PUSH_ENABLED = true; - - SMTP_HOST = "hector.ctu.cx"; - SMTP_FROM = "vaultwarden@ctu.cx"; - SMTP_USERNAME = "vaultwarden@ctu.cx"; - SMTP_PORT = 587; - SMTP_SECURITY = "starttls"; - - ROCKET_ADDRESS = "::1"; - ROCKET_PORT = 8582; - }; - }; - - nginx = { - enable = true; - virtualHosts."vault.ctu.cx" = { - useACMEHost = "${config.networking.hostName}.${config.networking.domain}"; - forceSSL = true; - kTLS = true; - locations."/".proxyPass = "http://[::1]:${toString config.services.vaultwarden.config.ROCKET_PORT}/"; - locations."/notifications/hub" = { - proxyPass = "http://[::1]:${toString config.services.vaultwarden.config.ROCKET_PORT}/"; - proxyWebsockets = true; - }; - }; - }; - }; - -}- \ No newline at end of file
diff --git a/machines/trabbi/default.nix b/machines/trabbi/default.nix @@ -8,7 +8,7 @@ ctucxConfig.services.prometheus-exporters ctucxConfig.services.dns-server - ./gotosocial.nix + ctucxConfig.websites."zuggeschmack.de" ]; dns.zones."ctu.cx".subdomains."${config.networking.hostName}" = (pkgs.dns.lib.combinators.host config.networking.primaryIP4 config.networking.primaryIP);
diff --git a/machines/trabbi/gotosocial.nix b/machines/trabbi/gotosocial.nix @@ -1,144 +0,0 @@ -{ pkgs, lib, config, ... }: - -let - gotosocial = pkgs.callPackage ../../pkgs/gotosocial {}; - -in { - - dns.zones."zuggeschmack.de" = (pkgs.dns.lib.combinators.host config.networking.primaryIP4 config.networking.primaryIP) // { - subdomains."client".CNAME = [ "${config.networking.fqdn}." ]; - }; - - age.secrets = { - restic-gotosocial.file = ./. + "/../../secrets/${config.networking.hostName}/restic/gotosocial.age"; - gotosocial-env.file = ./. + "/../../secrets/${config.networking.hostName}/gotosocial-env.age"; - }; - - systemd.services.restic-backup-gotosocial.serviceConfig.ReadWritePaths = [ "/var/lib/gotosocial" ]; - - restic-backups.gotosocial = { - user = "gotosocial"; - passwordFile = config.age.secrets.restic-gotosocial.path; - sqliteDatabases = [ "/var/lib/gotosocial/db.sqlite" ]; - paths = [ "/var/lib/gotosocial/storage" "/var/lib/gotosocial/backup.json" ]; - runBeforeBackup = '' - ${gotosocial}/bin/gotosocial --config-path /etc/gotosocial.yaml admin export --path /var/lib/gotosocial/backup.json - ''; - }; - - systemd.services.gotosocial.serviceConfig.Group = lib.mkForce config.services.nginx.group; - - services.gotosocial = { - enable = true; - package = gotosocial; - group = "nginx"; - environmentFile = config.age.secrets.gotosocial-env.path; - settings = { - application-name = "ZugGeschmack.de"; - - host = "zuggeschmack.de"; - account-domain = "zuggeschmack.de"; - protocol = "https"; - - bind-address = "[::1]"; - port = 8085; - - trusted-proxies = [ "::1/128" "172.17.0.0/24" ]; - - db-type = "sqlite"; - db-address = "/var/lib/gotosocial/db.sqlite"; - - accounts-allow-custom-css = true; - accounts-registration-open = true; - - instance-expose-peers = true; - instance-expose-suspended = true; - instance-expose-suspended-web = true; - - instance-languages = [ "de" "en-us" ]; - - storage-backend = "local"; - storage-local-base-path = "/var/lib/gotosocial/storage"; - - media-local-max-size = "50MiB"; - media-remote-max-size = "50MiB"; - - media-remote-cache-days = 3; - media-cleanup-from = "01:00"; - - - smtp-host = "hector.ctu.cx"; - smtp-port = 587; - smtp-username = "gts@zuggeschmack.de"; - smtp-from = "gts@zuggeschmack.de"; - }; - }; - - services.nginx.appendHttpConfig = '' - proxy_cache_path /var/cache/nginx keys_zone=gotosocial_ap_public_responses:10m inactive=1w; - ''; - - services.nginx.virtualHosts."zuggeschmack.de" = { - useACMEHost = "${config.networking.hostName}.${config.networking.domain}"; - forceSSL = true; - kTLS = true; - extraConfig = '' - client_max_body_size 50M; - ''; - locations = { - "/" = { - proxyPass = "http://${toString config.services.gotosocial.settings.bind-address}:${toString config.services.gotosocial.settings.port}"; - proxyWebsockets = true; - extraConfig = '' - client_max_body_size 50M; - ''; - }; - - "~ /.well-known/(webfinger|host-meta)$" = { - proxyPass = "http://${toString config.services.gotosocial.settings.bind-address}:${toString config.services.gotosocial.settings.port}"; - extraConfig = '' - proxy_cache gotosocial_ap_public_responses; - proxy_cache_background_update on; - proxy_cache_key $scheme://$host$uri$is_args$query_string; - proxy_cache_valid 200 10m; - proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504 http_429; - proxy_cache_lock on; - add_header X-Cache-Status $upstream_cache_status; - ''; - }; - - "~ ^\/users\/(?:[a-z0-9_\.]+)\/main-key$" = { - proxyPass = "http://${toString config.services.gotosocial.settings.bind-address}:${toString config.services.gotosocial.settings.port}"; - extraConfig = '' - proxy_cache gotosocial_ap_public_responses; - proxy_cache_background_update on; - proxy_cache_key $scheme://$host$uri; - proxy_cache_valid 200 604800s; - proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504 http_429; - proxy_cache_lock on; - - add_header X-Cache-Status $upstream_cache_status; - ''; - }; - - "/assets/".extraConfig = '' - alias ${config.services.gotosocial.package}/share/web/assets/; - autoindex off; - expires max; - add_header Cache-Control "public, immutable"; - ''; - }; - - }; - - services.nginx.virtualHosts."client.zuggeschmack.de" = { - useACMEHost = "${config.networking.hostName}.${config.networking.domain}"; - forceSSL = true; - kTLS = true; - root = pkgs.masto-fe-standalone; - extraConfig = '' - try_files $uri $uri/ /index.html; - ''; - }; - -}