diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 40e5d59..df9d62b 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -9,6 +9,3 @@ fd138d45e6a2cad89fead6e9f246ba282070d6b7 # Switch to alejandra formatting 046a88905ddfa7f9edc3291c310dbb985dee34f9 - -# Apply wide linting -63b3cbe00be80ccb4b221aad64eb657ae5c96d70 diff --git a/checks/default.nix b/checks/default.nix deleted file mode 100644 index f3db4ce..0000000 --- a/checks/default.nix +++ /dev/null @@ -1,61 +0,0 @@ -{ - self, - nixpkgs, - deploy-rs, - system, - ... -}: -let - pkgs = nixpkgs.legacyPackages.${system}; - - statix' = pkgs.statix.overrideAttrs (old: { - patches = old.patches ++ [ - (pkgs.fetchpatch { - url = "https://github.com/oppiliappan/statix/commit/925dec39bb705acbbe77178b4d658fe1b752abbb.patch"; - hash = "sha256-0wacO6wuYJ4ufN9PGucRVJucFdFFNF+NoHYIrLXsCWs="; - }) - ]; - }); - - runNuCheck = - { - name, - packages, - check, - }: - pkgs.stdenvNoCC.mkDerivation { - inherit name; - - src = nixpkgs.lib.cleanSourceWith { - src = self; - filter = nixpkgs.lib.cleanSourceFilter; - }; - - dontPatch = true; - dontConfigure = true; - dontBuild = true; - dontInstall = true; - dontFixup = true; - doCheck = true; - - checkInputs = nixpkgs.lib.singleton pkgs.nushell ++ packages; - - checkPhase = '' - nu ${check} - ''; - }; -in -nixpkgs.lib.recursiveUpdate { - lints = runNuCheck { - name = "lints"; - - packages = [ - pkgs.deadnix - pkgs.nixfmt-rfc-style - pkgs.shellcheck - statix' - ]; - - check = ./lints.nu; - }; -} (deploy-rs.lib.${system}.deployChecks self.deploy) diff --git a/checks/lints.nu b/checks/lints.nu deleted file mode 100644 index ffc2047..0000000 --- a/checks/lints.nu +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env nu - -let shell_files = ls **/*.sh | get name -let nix_files = ls **/*.nix | where name !~ "hardware-configuration.nix|_sources" | get name - -let linters = [ - ([shellcheck] ++ $shell_files) - ([nixfmt --check --strict] ++ $nix_files) - ([deadnix --fail] ++ $nix_files) - ([statix check] ++ $nix_files) -] - -mkdir $env.out - -def run-linter [linterArgs: list<string>] { - print $'Running ($linterArgs.0)...' - - let exit_code = try { - ^$linterArgs.0 ...($linterArgs | skip 1) - $env.LAST_EXIT_CODE - } catch {|e| $e.exit_code} - - [$linterArgs.0, $exit_code] -} - -let results = $linters | each {|linter| run-linter $linter} - -print 'Linter results:' - -let success = $results | each {|result| - match $result.1 { - 0 => {print $'(ansi green)($result.0)(ansi reset)'} - _ => {print $'(ansi red)($result.0)(ansi reset)'} - } - - $result.1 -} | math sum - -exit $success diff --git a/configuration/default.nix b/configuration/default.nix index 0377e9c..f874733 100644 --- a/configuration/default.nix +++ b/configuration/default.nix @@ -1,5 +1,7 @@ { config, + pkgs, + lib, modulesPath, flake-inputs, ... @@ -19,8 +21,8 @@ ./services/crowdsec.nix ./services/foundryvtt.nix ./services/gitea.nix - ./services/immich.nix ./services/metrics + ./services/minecraft.nix ./services/nextcloud.nix ./services/webserver.nix ./services/wireguard.nix @@ -30,7 +32,13 @@ ./sops.nix ]; - nixpkgs.overlays = [ (_: prev: { local = import ../pkgs { pkgs = prev; }; }) ]; + nixpkgs.overlays = [ + (final: prev: { + local = import ../pkgs { + pkgs = prev; + }; + }) + ]; nix = { extraOptions = '' @@ -63,8 +71,6 @@ 8448 # starbound 21025 - # Minecraft - 25565 config.services.coturn.listening-port config.services.coturn.tls-listening-port @@ -73,9 +79,6 @@ ]; allowedUDPPorts = [ - # More minecraft - 25565 - config.services.coturn.listening-port config.services.coturn.tls-listening-port config.services.coturn.alt-listening-port diff --git a/configuration/hardware-specific/hetzner/disko.nix b/configuration/hardware-specific/hetzner/disko.nix index 7e1acd7..cc15471 100644 --- a/configuration/hardware-specific/hetzner/disko.nix +++ b/configuration/hardware-specific/hetzner/disko.nix @@ -80,17 +80,6 @@ inherit mountOptions; mountpoint = "/var"; }; - "/volume/var/lib/private/matrix-conduit" = { - mountOptions = [ - # Explicitly don't compress here, since - # conduwuit's database does compression by - # itself, and relies on being able to read the - # raw file data from disk (which is impossible - # if btrfs compresses it) - "noatime" - ]; - mountpoint = "/var/lib/private/matrix-conduit"; - }; "/volume/nix-store" = { inherit mountOptions; mountpoint = "/nix"; diff --git a/configuration/hardware-specific/vm.nix b/configuration/hardware-specific/vm.nix index 71870fb..db563fe 100644 --- a/configuration/hardware-specific/vm.nix +++ b/configuration/hardware-specific/vm.nix @@ -6,35 +6,26 @@ boot.kernelParams = [ "nomodeset" ]; networking.hostName = "testvm"; - - services = { - # Sets the base domain for nginx to a local domain so that we can - # easily test locally with the VM. - nginx.domain = "dev.local"; - - # Don't run this - batteryManager.enable = lib.mkForce false; - - openssh.hostKeys = lib.mkForce [ - { - type = "rsa"; - bits = 4096; - path = "/etc/staging.key"; - } - ]; - }; + # Sets the base domain for nginx to a local domain so that we can + # easily test locally with the VM. + services.nginx.domain = "dev.local"; # Use the staging secrets sops.defaultSopsFile = lib.mkOverride 99 ../../keys/staging.yaml; systemd.network.networks."10-eth0" = { matchConfig.Name = "eth0"; - gateway = [ "192.168.9.1" ]; + gateway = [ + "192.168.9.1" + ]; networkConfig = { Address = "192.168.9.2/24"; }; }; + # Don't run this + services.batteryManager.enable = lib.mkForce false; + # Both so we have a predictable key for the staging env, as well as # to have a static key for decrypting the sops secrets for the # staging env. @@ -43,6 +34,14 @@ source = ../../keys/hosts/staging.key; }; + services.openssh.hostKeys = lib.mkForce [ + { + type = "rsa"; + bits = 4096; + path = "/etc/staging.key"; + } + ]; + virtualisation.vmVariant = { virtualisation = { memorySize = 3941; diff --git a/configuration/nginx.nix b/configuration/nginx.nix index 3abef7f..3ec3bd9 100644 --- a/configuration/nginx.nix +++ b/configuration/nginx.nix @@ -1,50 +1,41 @@ { config, lib, ... }: { - services = { - nginx = { - enable = true; - recommendedTlsSettings = true; - recommendedOptimisation = true; - recommendedGzipSettings = true; - recommendedProxySettings = true; - clientMaxBodySize = "10G"; + services.nginx = { + enable = true; + recommendedTlsSettings = true; + recommendedOptimisation = true; + recommendedGzipSettings = true; + recommendedProxySettings = true; + clientMaxBodySize = "10G"; - statusPage = true; # For metrics, should be accessible only from localhost + statusPage = true; # For metrics, should be accessible only from localhost - commonHttpConfig = '' - log_format upstream_time '$remote_addr - $remote_user [$time_local] ' - '"$request" $status $body_bytes_sent ' - '"$http_referer" "$http_user_agent" ' - 'rt=$request_time uct="$upstream_connect_time" ' - 'uht="$upstream_header_time" urt="$upstream_response_time"'; - ''; - }; - - logrotate.settings = - { - # Override the default, just keep fewer logs - nginx.rotate = 6; - } - // lib.mapAttrs' ( - virtualHost: _: - lib.nameValuePair "/var/log/nginx/${virtualHost}/access.log" { - frequency = "daily"; - rotate = 2; - compress = true; - delaycompress = true; - su = "${config.services.nginx.user} ${config.services.nginx.group}"; - postrotate = "[ ! -f /var/run/nginx/nginx.pid ] || kill -USR1 `cat /var/run/nginx/nginx.pid`"; - } - ) config.services.nginx.virtualHosts; - - backups.acme = { - user = "acme"; - paths = lib.mapAttrsToList ( - virtualHost: _: "/var/lib/acme/${virtualHost}" - ) config.services.nginx.virtualHosts; - }; + commonHttpConfig = '' + log_format upstream_time '$remote_addr - $remote_user [$time_local] ' + '"$request" $status $body_bytes_sent ' + '"$http_referer" "$http_user_agent" ' + 'rt=$request_time uct="$upstream_connect_time" ' + 'uht="$upstream_header_time" urt="$upstream_response_time"'; + ''; }; + services.logrotate.settings = + { + # Override the default, just keep fewer logs + nginx.rotate = 6; + } + // lib.mapAttrs' ( + virtualHost: _: + lib.nameValuePair "/var/log/nginx/${virtualHost}/access.log" { + frequency = "daily"; + rotate = 2; + compress = true; + delaycompress = true; + su = "${config.services.nginx.user} ${config.services.nginx.group}"; + postrotate = "[ ! -f /var/run/nginx/nginx.pid ] || kill -USR1 `cat /var/run/nginx/nginx.pid`"; + } + ) config.services.nginx.virtualHosts; + systemd.tmpfiles.rules = lib.mapAttrsToList ( virtualHost: _: # @@ -75,4 +66,11 @@ systemd.services.nginx.serviceConfig.SupplementaryGroups = [ config.security.acme.certs."tlater.net".group ]; + + services.backups.acme = { + user = "acme"; + paths = lib.mapAttrsToList ( + virtualHost: _: "/var/lib/acme/${virtualHost}" + ) config.services.nginx.virtualHosts; + }; } diff --git a/configuration/services/backups.nix b/configuration/services/backups.nix index baa61e3..81e3554 100644 --- a/configuration/services/backups.nix +++ b/configuration/services/backups.nix @@ -57,7 +57,7 @@ in ''; type = types.attrsOf ( types.submodule ( - { name, ... }: + { config, name, ... }: { options = { user = lib.mkOption { @@ -246,7 +246,7 @@ in }; } // lib.mapAttrs' ( - name: _: + name: backup: lib.nameValuePair "backup-${name}" { wantedBy = [ "timers.target" ]; timerConfig = { diff --git a/configuration/services/conduit/default.nix b/configuration/services/conduit/default.nix index 4ba5271..c7e4ab4 100644 --- a/configuration/services/conduit/default.nix +++ b/configuration/services/conduit/default.nix @@ -1,5 +1,4 @@ { - pkgs, config, lib, ... @@ -17,166 +16,159 @@ in ./matrix-hookshot.nix ]; - services = { - matrix-conduit = { - enable = true; - package = pkgs.matrix-continuwuity; - settings.global = { - address = "127.0.0.1"; - server_name = domain; - new_user_displayname_suffix = "🦆"; - allow_check_for_updates = true; + services.matrix-conduit = { + enable = true; + settings.global = { + address = "127.0.0.1"; + server_name = domain; + database_backend = "rocksdb"; - # Set up delegation: https://docs.conduit.rs/delegation.html#automatic-recommended - # This is primarily to make sliding sync work - well_known = { - client = "https://${domain}"; - server = "${domain}:443"; - }; - - turn_uris = - let - address = "${config.services.coturn.realm}:${toString config.services.coturn.listening-port}"; - tls-address = "${config.services.coturn.realm}:${toString config.services.coturn.tls-listening-port}"; - in - [ - "turn:${address}?transport=udp" - "turn:${address}?transport=tcp" - "turns:${tls-address}?transport=udp" - "turns:${tls-address}?transport=tcp" - ]; + # Set up delegation: https://docs.conduit.rs/delegation.html#automatic-recommended + # This is primarily to make sliding sync work + well_known = { + client = "https://${domain}"; + server = "${domain}:443"; }; - }; - coturn = { - enable = true; - no-cli = true; - use-auth-secret = true; - static-auth-secret-file = config.sops.secrets."turn/secret".path; - realm = turn-realm; - relay-ips = [ "116.202.158.55" ]; - - # SSL config - pkey = "${config.security.acme.certs."tlater.net".directory}/key.pem"; - cert = "${config.security.acme.certs."tlater.net".directory}/fullchain.pem"; - - # Based on suggestions from - # https://github.com/matrix-org/synapse/blob/develop/docs/turn-howto.md - # and - # https://www.foxypossibilities.com/2018/05/19/setting-up-a-turn-sever-for-matrix-on-nixos/ - no-tcp-relay = true; - secure-stun = true; - extraConfig = '' - # Deny various local IP ranges, see - # https://www.rtcsec.com/article/cve-2020-26262-bypass-of-coturns-access-control-protection/ - no-multicast-peers - denied-peer-ip=0.0.0.0-0.255.255.255 - denied-peer-ip=10.0.0.0-10.255.255.255 - denied-peer-ip=100.64.0.0-100.127.255.255 - denied-peer-ip=127.0.0.0-127.255.255.255 - denied-peer-ip=169.254.0.0-169.254.255.255 - denied-peer-ip=172.16.0.0-172.31.255.255 - denied-peer-ip=192.0.0.0-192.0.0.255 - denied-peer-ip=192.0.2.0-192.0.2.255 - denied-peer-ip=192.88.99.0-192.88.99.255 - denied-peer-ip=192.168.0.0-192.168.255.255 - denied-peer-ip=198.18.0.0-198.19.255.255 - denied-peer-ip=198.51.100.0-198.51.100.255 - denied-peer-ip=203.0.113.0-203.0.113.255 - denied-peer-ip=240.0.0.0-255.255.255.255 denied-peer-ip=::1 - denied-peer-ip=64:ff9b::-64:ff9b::ffff:ffff - denied-peer-ip=::ffff:0.0.0.0-::ffff:255.255.255.255 - denied-peer-ip=100::-100::ffff:ffff:ffff:ffff - denied-peer-ip=2001::-2001:1ff:ffff:ffff:ffff:ffff:ffff:ffff - denied-peer-ip=2002::-2002:ffff:ffff:ffff:ffff:ffff:ffff:ffff - denied-peer-ip=fc00::-fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff - denied-peer-ip=fe80::-febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff - - # *Allow* any IP addresses that we explicitly set as relay IPs - ${concatMapStringsSep "\n" (ip: "allowed-peer-ip=${ip}") config.services.coturn.relay-ips} - - # Various other security settings - no-tlsv1 - no-tlsv1_1 - - # Monitoring - prometheus - ''; - }; - - nginx.virtualHosts."${domain}" = { - useACMEHost = "tlater.net"; - - listen = [ - { - addr = "0.0.0.0"; - port = 80; - } - { - addr = "[::0]"; - port = 80; - } - { - addr = "0.0.0.0"; - port = 443; - ssl = true; - } - { - addr = "[::0]"; - port = 443; - ssl = true; - } - { - addr = "0.0.0.0"; - port = 8448; - ssl = true; - } - { - addr = "[::0]"; - port = 8448; - ssl = true; - } - ]; - - forceSSL = true; - enableHSTS = true; - extraConfig = '' - merge_slashes off; - ''; - - locations = { - "/_matrix" = { - proxyPass = "http://${cfg.settings.global.address}:${toString cfg.settings.global.port}"; - # Recommended by conduit - extraConfig = '' - proxy_buffering off; - ''; - }; - "/.well-known/matrix" = { - proxyPass = "http://${cfg.settings.global.address}:${toString cfg.settings.global.port}"; - }; - }; - }; - - backups.conduit = { - user = "root"; - paths = [ "/var/lib/private/matrix-conduit/" ]; - # Other services store their data in conduit, so no other services - # need to be shut down currently. - pauseServices = [ "conduit.service" ]; + turn_uris = + let + address = "${config.services.coturn.realm}:${toString config.services.coturn.listening-port}"; + tls-address = "${config.services.coturn.realm}:${toString config.services.coturn.tls-listening-port}"; + in + [ + "turn:${address}?transport=udp" + "turn:${address}?transport=tcp" + "turns:${tls-address}?transport=udp" + "turns:${tls-address}?transport=tcp" + ]; }; }; - systemd.services.conduit.serviceConfig = { - ExecStart = lib.mkForce "${config.services.matrix-conduit.package}/bin/conduwuit"; - # Pass in the TURN secret via EnvironmentFile, not supported by - # upstream module currently. - # - # See also https://gitlab.com/famedly/conduit/-/issues/314 - EnvironmentFile = config.sops.secrets."turn/env".path; - }; + # Pass in the TURN secret via EnvironmentFile, not supported by + # upstream module currently. + # + # See also https://gitlab.com/famedly/conduit/-/issues/314 + systemd.services.conduit.serviceConfig.EnvironmentFile = config.sops.secrets."turn/env".path; systemd.services.coturn.serviceConfig.SupplementaryGroups = [ config.security.acme.certs."tlater.net".group ]; + + services.coturn = { + enable = true; + no-cli = true; + use-auth-secret = true; + static-auth-secret-file = config.sops.secrets."turn/secret".path; + realm = turn-realm; + relay-ips = [ "116.202.158.55" ]; + + # SSL config + pkey = "${config.security.acme.certs."tlater.net".directory}/key.pem"; + cert = "${config.security.acme.certs."tlater.net".directory}/fullchain.pem"; + + # Based on suggestions from + # https://github.com/matrix-org/synapse/blob/develop/docs/turn-howto.md + # and + # https://www.foxypossibilities.com/2018/05/19/setting-up-a-turn-sever-for-matrix-on-nixos/ + no-tcp-relay = true; + secure-stun = true; + extraConfig = '' + # Deny various local IP ranges, see + # https://www.rtcsec.com/article/cve-2020-26262-bypass-of-coturns-access-control-protection/ + no-multicast-peers + denied-peer-ip=0.0.0.0-0.255.255.255 + denied-peer-ip=10.0.0.0-10.255.255.255 + denied-peer-ip=100.64.0.0-100.127.255.255 + denied-peer-ip=127.0.0.0-127.255.255.255 + denied-peer-ip=169.254.0.0-169.254.255.255 + denied-peer-ip=172.16.0.0-172.31.255.255 + denied-peer-ip=192.0.0.0-192.0.0.255 + denied-peer-ip=192.0.2.0-192.0.2.255 + denied-peer-ip=192.88.99.0-192.88.99.255 + denied-peer-ip=192.168.0.0-192.168.255.255 + denied-peer-ip=198.18.0.0-198.19.255.255 + denied-peer-ip=198.51.100.0-198.51.100.255 + denied-peer-ip=203.0.113.0-203.0.113.255 + denied-peer-ip=240.0.0.0-255.255.255.255 denied-peer-ip=::1 + denied-peer-ip=64:ff9b::-64:ff9b::ffff:ffff + denied-peer-ip=::ffff:0.0.0.0-::ffff:255.255.255.255 + denied-peer-ip=100::-100::ffff:ffff:ffff:ffff + denied-peer-ip=2001::-2001:1ff:ffff:ffff:ffff:ffff:ffff:ffff + denied-peer-ip=2002::-2002:ffff:ffff:ffff:ffff:ffff:ffff:ffff + denied-peer-ip=fc00::-fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff + denied-peer-ip=fe80::-febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff + + # *Allow* any IP addresses that we explicitly set as relay IPs + ${concatMapStringsSep "\n" (ip: "allowed-peer-ip=${ip}") config.services.coturn.relay-ips} + + # Various other security settings + no-tlsv1 + no-tlsv1_1 + + # Monitoring + prometheus + ''; + }; + + services.nginx.virtualHosts."${domain}" = { + useACMEHost = "tlater.net"; + + listen = [ + { + addr = "0.0.0.0"; + port = 80; + } + { + addr = "[::0]"; + port = 80; + } + { + addr = "0.0.0.0"; + port = 443; + ssl = true; + } + { + addr = "[::0]"; + port = 443; + ssl = true; + } + { + addr = "0.0.0.0"; + port = 8448; + ssl = true; + } + { + addr = "[::0]"; + port = 8448; + ssl = true; + } + ]; + + forceSSL = true; + enableHSTS = true; + extraConfig = '' + merge_slashes off; + ''; + + locations = { + "/_matrix" = { + proxyPass = "http://${cfg.settings.global.address}:${toString cfg.settings.global.port}"; + # Recommended by conduit + extraConfig = '' + proxy_buffering off; + ''; + }; + "/.well-known/matrix" = { + proxyPass = "http://${cfg.settings.global.address}:${toString cfg.settings.global.port}"; + }; + }; + }; + + services.backups.conduit = { + user = "root"; + paths = [ "/var/lib/private/matrix-conduit/" ]; + # Other services store their data in conduit, so no other services + # need to be shut down currently. + pauseServices = [ "conduit.service" ]; + }; } diff --git a/configuration/services/conduit/matrix-hookshot.nix b/configuration/services/conduit/matrix-hookshot.nix index 6b788b2..6f11728 100644 --- a/configuration/services/conduit/matrix-hookshot.nix +++ b/configuration/services/conduit/matrix-hookshot.nix @@ -29,29 +29,16 @@ let }; # Encryption support - # TODO(tlater): Enable when - # https://github.com/matrix-org/matrix-hookshot/issues/1060 is - # fixed - # extraSettings = { - # "de.sorunome.msc2409.push_ephemeral" = true; - # push_ephemeral = true; - # "org.matrix.msc3202" = true; - # }; + extraSettings = { + "de.sorunome.msc2409.push_ephemeral" = true; + push_ephemeral = true; + "org.matrix.msc3202" = true; + }; runtimeRegistration = "${cfg.registrationFile}"; }; in { - # users = { - # users.matrix-hookshot = { - # home = "/run/matrix-hookshot"; - # group = "matrix-hookshot"; - # isSystemUser = true; - # }; - - # groups.matrix-hookshot = { }; - # }; - systemd.services.matrix-hookshot = { serviceConfig = { Type = lib.mkForce "exec"; @@ -62,7 +49,6 @@ in # Some library in matrix-hookshot wants a home directory Environment = [ "HOME=/run/matrix-hookshot" ]; - # User = "matrix-hookshot"; DynamicUser = true; StateDirectory = "matrix-hookshot"; RuntimeDirectory = "matrix-hookshot"; @@ -76,11 +62,7 @@ in ProtectKernelModules = true; ProtectKernelLogs = true; ProtectControlGroups = true; - RestrictAddressFamilies = [ - # "AF_UNIX" - "AF_INET" - "AF_INET6" - ]; + RestrictAddressFamilies = [ "AF_INET AF_INET6" ]; LockPersonality = true; RestrictRealtime = true; ProtectProc = "invisible"; @@ -89,15 +71,12 @@ in }; }; - # services.redis.servers.matrix-hookshot = { - # enable = true; - # user = "matrix-hookshot"; - # }; - services.matrix-hookshot = { enable = true; - serviceDependencies = [ "conduit.service" ]; + serviceDependencies = [ + "conduit.service" + ]; registrationFile = "/run/matrix-hookshot/registration.yaml"; @@ -112,8 +91,6 @@ in bot.displayname = "Hookshot"; - # cache.redisUri = "redis://${config.services.redis.servers.matrix-hookshot.unixSocket}"; - generic = { enabled = true; outbound = false; @@ -123,10 +100,7 @@ in allowJsTransformationFunctions = true; }; - # TODO(tlater): Enable when - # https://github.com/matrix-org/matrix-hookshot/issues/1060 is - # fixed - # encryption.storagePath = "/var/lib/matrix-hookshot/cryptostore"; + encryption.storagePath = "/var/lib/matrix-hookshot/cryptostore"; permissions = [ { @@ -152,15 +126,19 @@ in listeners = [ { port = 9000; - resources = [ "webhooks" ]; + resources = [ + "webhooks" + ]; } { port = 9001; - resources = [ "metrics" ]; + resources = [ + "metrics" + ]; } ]; - metrics.enabled = true; + metrics.enable = true; }; }; } diff --git a/configuration/services/crowdsec.nix b/configuration/services/crowdsec.nix index b736047..6860354 100644 --- a/configuration/services/crowdsec.nix +++ b/configuration/services/crowdsec.nix @@ -8,7 +8,21 @@ security.crowdsec = { enable = true; - parserWhitelist = [ "10.45.249.2" ]; + parserWhitelist = [ + "10.45.249.2" + ]; + + extraConfig."postoverflows/s01-whitelist/matrix-whitelist.yaml" = { + name = "tetsumaki/matrix"; + description = "custom matrix whitelist"; + whitelist = { + reason = "whitelist false positive for matrix"; + expression = [ + "evt.Overflow.Alert.Events[0].GetMeta('target_fqdn') == '${config.services.matrix-conduit.settings.global.server_name}'" + "evt.Overflow.Alert.GetScenario() in ['crowdsecurity/http-probing', 'crowdsecurity/http-crawl-non_statics']" + ]; + }; + }; extraGroups = [ "systemd-journal" @@ -19,19 +33,25 @@ { source = "journalctl"; labels.type = "syslog"; - journalctl_filter = [ "SYSLOG_IDENTIFIER=Nextcloud" ]; + journalctl_filter = [ + "SYSLOG_IDENTIFIER=Nextcloud" + ]; } { source = "journalctl"; labels.type = "syslog"; - journalctl_filter = [ "SYSLOG_IDENTIFIER=sshd-session" ]; + journalctl_filter = [ + "SYSLOG_IDENTIFIER=sshd-session" + ]; } { labels.type = "nginx"; filenames = - [ "/var/log/nginx/*.log" ] + [ + "/var/log/nginx/*.log" + ] ++ lib.mapAttrsToList ( vHost: _: "/var/log/nginx/${vHost}/access.log" ) config.services.nginx.virtualHosts; @@ -47,36 +67,4 @@ }; }; }; - - # Add whitelists for matrix - systemd.tmpfiles.settings."10-matrix" = - let - stateDir = config.security.crowdsec.stateDirectory; - in - { - "${stateDir}/config/postoverflows".d = { - user = "crowdsec"; - group = "crowdsec"; - mode = "0700"; - }; - - "${stateDir}/config/postoverflows/s01-whitelist".d = { - user = "crowdsec"; - group = "crowdsec"; - mode = "0700"; - }; - - "${stateDir}/config/postoverflows/s01-whitelist/matrix-whitelist.yaml"."L+".argument = - ((pkgs.formats.yaml { }).generate "crowdsec-matrix-whitelist.yaml" { - name = "tetsumaki/matrix"; - description = "custom matrix whitelist"; - whitelist = { - reason = "whitelist false positive for matrix"; - expression = [ - "evt.Overflow.Alert.Events[0].GetMeta('target_fqdn') == '${config.services.matrix-conduit.settings.global.server_name}'" - "evt.Overflow.Alert.GetScenario() in ['crowdsecurity/http-probing', 'crowdsecurity/http-crawl-non_statics']" - ]; - }; - }).outPath; - }; } diff --git a/configuration/services/foundryvtt.nix b/configuration/services/foundryvtt.nix index 6c475a3..614b818 100644 --- a/configuration/services/foundryvtt.nix +++ b/configuration/services/foundryvtt.nix @@ -11,39 +11,37 @@ in { imports = [ flake-inputs.foundryvtt.nixosModules.foundryvtt ]; - services = { - foundryvtt = { - enable = true; - hostName = domain; - minifyStaticFiles = true; - proxySSL = true; - proxyPort = 443; - package = flake-inputs.foundryvtt.packages.${pkgs.system}.foundryvtt_13; - }; - - nginx.virtualHosts."${domain}" = - let - inherit (config.services.foundryvtt) port; - in - { - forceSSL = true; - useACMEHost = "tlater.net"; - enableHSTS = true; - - locations."/" = { - proxyWebsockets = true; - proxyPass = "http://localhost:${toString port}"; - }; - }; - - backups.foundryvtt = { - user = "foundryvtt"; - paths = [ config.services.foundryvtt.dataDir ]; - pauseServices = [ "foundryvtt.service" ]; - }; + services.foundryvtt = { + enable = true; + hostName = domain; + minifyStaticFiles = true; + proxySSL = true; + proxyPort = 443; + package = flake-inputs.foundryvtt.packages.${pkgs.system}.foundryvtt_11; }; # Want to start it manually when I need it, not have it constantly # running systemd.services.foundryvtt.wantedBy = lib.mkForce [ ]; + + services.nginx.virtualHosts."${domain}" = + let + inherit (config.services.foundryvtt) port; + in + { + forceSSL = true; + useACMEHost = "tlater.net"; + enableHSTS = true; + + locations."/" = { + proxyWebsockets = true; + proxyPass = "http://localhost:${toString port}"; + }; + }; + + services.backups.foundryvtt = { + user = "foundryvtt"; + paths = [ config.services.foundryvtt.dataDir ]; + pauseServices = [ "foundryvtt.service" ]; + }; } diff --git a/configuration/services/gitea.nix b/configuration/services/gitea.nix index 613d30c..da01cde 100644 --- a/configuration/services/gitea.nix +++ b/configuration/services/gitea.nix @@ -8,68 +8,24 @@ let domain = "gitea.${config.services.nginx.domain}"; in { - services = { - forgejo = { - enable = true; - database.type = "postgres"; + services.forgejo = { + enable = true; + database.type = "postgres"; - settings = { - server = { - DOMAIN = domain; - HTTP_ADDR = "127.0.0.1"; - ROOT_URL = "https://${domain}/"; - SSH_PORT = 2222; - }; - - metrics = { - ENABLED = true; - TOKEN = "#metricstoken#"; - }; - service.DISABLE_REGISTRATION = true; - session.COOKIE_SECURE = true; - }; - }; - - # Set up SSL - nginx.virtualHosts."${domain}" = - let - httpAddress = config.services.forgejo.settings.server.HTTP_ADDR; - httpPort = config.services.forgejo.settings.server.HTTP_PORT; - in - { - forceSSL = true; - useACMEHost = "tlater.net"; - enableHSTS = true; - - locations."/".proxyPass = "http://${httpAddress}:${toString httpPort}"; - locations."/metrics" = { - extraConfig = '' - access_log off; - allow 127.0.0.1; - ${lib.optionalString config.networking.enableIPv6 "allow ::1;"} - deny all; - ''; - }; + settings = { + server = { + DOMAIN = domain; + HTTP_ADDR = "127.0.0.1"; + ROOT_URL = "https://${domain}/"; + SSH_PORT = 2222; }; - backups.forgejo = { - user = "forgejo"; - paths = [ - "/var/lib/forgejo/forgejo-db.sql" - "/var/lib/forgejo/repositories/" - "/var/lib/forgejo/data/" - "/var/lib/forgejo/custom/" - # Conf is backed up via nix - ]; - preparation = { - packages = [ config.services.postgresql.package ]; - text = "pg_dump ${config.services.forgejo.database.name} --file=/var/lib/forgejo/forgejo-db.sql"; + metrics = { + ENABLED = true; + TOKEN = "#metricstoken#"; }; - cleanup = { - packages = [ pkgs.coreutils ]; - text = "rm /var/lib/forgejo/forgejo-db.sql"; - }; - pauseServices = [ "forgejo.service" ]; + service.DISABLE_REGISTRATION = true; + session.COOKIE_SECURE = true; }; }; @@ -80,4 +36,46 @@ in runConfig = "${config.services.forgejo.customDir}/conf/app.ini"; in [ "+${replaceSecretBin} '#metricstoken#' '${secretPath}' '${runConfig}'" ]; + + # Set up SSL + services.nginx.virtualHosts."${domain}" = + let + httpAddress = config.services.forgejo.settings.server.HTTP_ADDR; + httpPort = config.services.forgejo.settings.server.HTTP_PORT; + in + { + forceSSL = true; + useACMEHost = "tlater.net"; + enableHSTS = true; + + locations."/".proxyPass = "http://${httpAddress}:${toString httpPort}"; + locations."/metrics" = { + extraConfig = '' + access_log off; + allow 127.0.0.1; + ${lib.optionalString config.networking.enableIPv6 "allow ::1;"} + deny all; + ''; + }; + }; + + services.backups.forgejo = { + user = "forgejo"; + paths = [ + "/var/lib/forgejo/forgejo-db.sql" + "/var/lib/forgejo/repositories/" + "/var/lib/forgejo/data/" + "/var/lib/forgejo/custom/" + # Conf is backed up via nix + ]; + preparation = { + packages = [ config.services.postgresql.package ]; + text = "pg_dump ${config.services.forgejo.database.name} --file=/var/lib/forgejo/forgejo-db.sql"; + }; + cleanup = { + packages = [ pkgs.coreutils ]; + text = "rm /var/lib/forgejo/forgejo-db.sql"; + }; + pauseServices = [ "forgejo.service" ]; + }; } diff --git a/configuration/services/immich.nix b/configuration/services/immich.nix deleted file mode 100644 index 516ea3e..0000000 --- a/configuration/services/immich.nix +++ /dev/null @@ -1,67 +0,0 @@ -{ - pkgs, - config, - lib, - ... -}: -let - hostName = "immich.${config.services.nginx.domain}"; -in -{ - services = { - immich = { - enable = true; - settings.server.externalDomain = "https://${hostName}"; - - environment.IMMICH_TELEMETRY_INCLUDE = "all"; - }; - - nginx.virtualHosts.${hostName} = - let - local = "http://${config.services.immich.host}:${toString config.services.immich.port}"; - in - { - forceSSL = true; - useACMEHost = "tlater.net"; - enableHSTS = true; - - locations."/" = { - proxyPass = local; - proxyWebsockets = true; - }; - locations."/metrics" = { - extraConfig = '' - access_log off; - allow 127.0.0.1; - ${lib.optionalString config.networking.enableIPv6 "allow ::1;"} - deny all; - ''; - }; - }; - - backups.immich = - let - db-dump = "${config.services.immich.mediaLocation}/immich-db.sql"; - in - { - user = "immich"; - paths = [ config.services.immich.mediaLocation ]; - - preparation = { - packages = [ config.services.postgresql.package ]; - text = '' - pg_dump ${config.services.immich.database.name} --clean --if-exists --file=${db-dump} - ''; - }; - - cleanup = { - packages = [ pkgs.coreutils ]; - text = "rm ${db-dump}"; - }; - pauseServices = [ - "immich-server.service" - "immich-machine-learning.service" - ]; - }; - }; -} diff --git a/configuration/services/metrics/exporters.nix b/configuration/services/metrics/exporters.nix index 52c2a46..78ba684 100644 --- a/configuration/services/metrics/exporters.nix +++ b/configuration/services/metrics/exporters.nix @@ -74,7 +74,7 @@ in listenAddress = "127.0.0.1"; group = "nginx"; - settings.namespaces = lib.mapAttrsToList (name: _: { + settings.namespaces = lib.mapAttrsToList (name: virtualHost: { inherit name; metrics_override.prefix = "nginxlog"; namespace_label = "vhost"; @@ -97,6 +97,4 @@ in # - postgres (?) # - ssl_exporter (?) }; - - services.dbus.implementation = "broker"; } diff --git a/configuration/services/metrics/grafana.nix b/configuration/services/metrics/grafana.nix index b30806c..b872833 100644 --- a/configuration/services/metrics/grafana.nix +++ b/configuration/services/metrics/grafana.nix @@ -1,4 +1,9 @@ -{ pkgs, config, ... }: +{ + pkgs, + config, + flake-inputs, + ... +}: let domain = "metrics.${config.services.nginx.domain}"; in @@ -30,7 +35,7 @@ in declarativePlugins = [ pkgs.grafanaPlugins.victoriametrics-metrics-datasource - pkgs.grafanaPlugins.victoriametrics-logs-datasource + flake-inputs.nixpkgs-unstable.legacyPackages.${pkgs.system}.grafanaPlugins.victoriametrics-logs-datasource ]; provision = { diff --git a/configuration/services/metrics/options.nix b/configuration/services/metrics/options.nix index e1b0761..d69ecfb 100644 --- a/configuration/services/metrics/options.nix +++ b/configuration/services/metrics/options.nix @@ -38,7 +38,7 @@ in services.victoriametrics.scrapeConfigs = mkOption { type = types.attrsOf ( types.submodule ( - { name, ... }: + { name, self, ... }: { options = { job_name = mkOption { @@ -106,37 +106,35 @@ in # module is an intractable mess wantedBy = [ "multi-user.target" ]; after = [ "network.target" ]; - serviceConfig = { - Restart = mkDefault "always"; - PrivateTmp = mkDefault true; - WorkingDirectory = mkDefault /tmp; - DynamicUser = mkDefault true; - # Hardening - CapabilityBoundingSet = mkDefault [ "" ]; - DeviceAllow = [ "" ]; - LockPersonality = true; - MemoryDenyWriteExecute = true; - NoNewPrivileges = true; - PrivateDevices = mkDefault true; - ProtectClock = mkDefault true; - ProtectControlGroups = true; - ProtectHome = true; - ProtectHostname = true; - ProtectKernelLogs = true; - ProtectKernelModules = true; - ProtectKernelTunables = true; - ProtectSystem = mkDefault "strict"; - RemoveIPC = true; - RestrictAddressFamilies = [ - "AF_INET" - "AF_INET6" - ]; - RestrictNamespaces = true; - RestrictRealtime = true; - RestrictSUIDSGID = true; - SystemCallArchitectures = "native"; - UMask = "0077"; - }; + serviceConfig.Restart = mkDefault "always"; + serviceConfig.PrivateTmp = mkDefault true; + serviceConfig.WorkingDirectory = mkDefault /tmp; + serviceConfig.DynamicUser = mkDefault true; + # Hardening + serviceConfig.CapabilityBoundingSet = mkDefault [ "" ]; + serviceConfig.DeviceAllow = [ "" ]; + serviceConfig.LockPersonality = true; + serviceConfig.MemoryDenyWriteExecute = true; + serviceConfig.NoNewPrivileges = true; + serviceConfig.PrivateDevices = mkDefault true; + serviceConfig.ProtectClock = mkDefault true; + serviceConfig.ProtectControlGroups = true; + serviceConfig.ProtectHome = true; + serviceConfig.ProtectHostname = true; + serviceConfig.ProtectKernelLogs = true; + serviceConfig.ProtectKernelModules = true; + serviceConfig.ProtectKernelTunables = true; + serviceConfig.ProtectSystem = mkDefault "strict"; + serviceConfig.RemoveIPC = true; + serviceConfig.RestrictAddressFamilies = [ + "AF_INET" + "AF_INET6" + ]; + serviceConfig.RestrictNamespaces = true; + serviceConfig.RestrictRealtime = true; + serviceConfig.RestrictSUIDSGID = true; + serviceConfig.SystemCallArchitectures = "native"; + serviceConfig.UMask = "0077"; } exporter.serviceOpts ] @@ -146,7 +144,7 @@ in { vmagent-scrape-exporters = let - inherit (config.services.victoriametrics) listenAddress; + listenAddress = config.services.victoriametrics.listenAddress; vmAddr = (lib.optionalString (lib.hasPrefix ":" listenAddress) "127.0.0.1") + listenAddress; promscrape = yaml.generate "prometheus.yml" { scrape_configs = lib.mapAttrsToList ( @@ -155,7 +153,7 @@ in inherit (scrape) job_name; static_configs = scrape.static_configs - ++ lib.optional (scrape.targets != [ ]) { inherit (scrape) targets; }; + ++ lib.optional (scrape.targets != [ ]) { targets = scrape.targets; }; } scrape.extraSettings ) config.services.victoriametrics.scrapeConfigs; }; @@ -214,7 +212,7 @@ in services.victoriametrics.scrapeConfigs = let - allExporters = lib.mapAttrs (_: exporter: { inherit (exporter) listenAddress port; }) ( + allExporters = lib.mapAttrs (name: exporter: { inherit (exporter) listenAddress port; }) ( (lib.filterAttrs ( name: exporter: # A bunch of deprecated exporters that need to be ignored diff --git a/configuration/services/metrics/victorialogs.nix b/configuration/services/metrics/victorialogs.nix index 413659a..ed74c59 100644 --- a/configuration/services/metrics/victorialogs.nix +++ b/configuration/services/metrics/victorialogs.nix @@ -1,22 +1,37 @@ -{ config, lib, ... }: +{ + config, + pkgs, + lib, + ... +}: let cfg = config.services.victorialogs; + pkg = pkgs.victoriametrics; + dirname = "victorialogs"; in { - options.services.victorialogs.bindAddress = lib.mkOption { - readOnly = true; - type = lib.types.str; - description = '' - Final address on which victorialogs listens. - ''; - }; + options.services.victorialogs = + let + inherit (lib.types) str; + in + { + listenAddress = lib.mkOption { + default = ":9428"; + type = str; + }; + + bindAddress = lib.mkOption { + readOnly = true; + type = str; + description = '' + Final address on which victorialogs listens. + ''; + }; + }; config = { - services.victorialogs = { - enable = true; - bindAddress = - (lib.optionalString (lib.hasPrefix ":" cfg.listenAddress) "127.0.0.1") + cfg.listenAddress; - }; + services.victorialogs.bindAddress = + (lib.optionalString (lib.hasPrefix ":" cfg.listenAddress) "127.0.0.1") + cfg.listenAddress; services.journald.upload = { enable = true; @@ -25,6 +40,71 @@ in NetworkTimeoutSec = "20s"; }; }; + systemd.services."systemd-journal-upload".after = [ "victorialogs.service" ]; + + systemd.services.victorialogs = { + description = "VictoriaLogs log database"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + startLimitBurst = 5; + + serviceConfig = { + ExecStart = lib.escapeShellArgs [ + "${pkg}/bin/victoria-logs" + "-storageDataPath=/var/lib/${dirname}" + "-httpListenAddr=${cfg.listenAddress}" + ]; + + DynamicUser = true; + RestartSec = 1; + Restart = "on-failure"; + RuntimeDirectory = dirname; + RuntimeDirectoryMode = "0700"; + StateDirectory = dirname; + StateDirectoryMode = "0700"; + + LimitNOFILE = 1048576; + + # Hardening + DeviceAllow = [ "/dev/null rw" ]; + DevicePolicy = "strict"; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateTmp = true; + PrivateUsers = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + ProtectSystem = "full"; + RemoveIPC = true; + RestrictAddressFamilies = [ + "AF_INET" + "AF_INET6" + "AF_UNIX" + ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ + "@system-service" + "~@privileged" + ]; + }; + + postStart = lib.mkBefore '' + until ${lib.getBin pkgs.curl}/bin/curl -s -o /dev/null http://${cfg.bindAddress}/ping; do + sleep 1; + done + ''; + }; }; } diff --git a/configuration/services/metrics/victoriametrics.nix b/configuration/services/metrics/victoriametrics.nix index f37b8b0..4a78d46 100644 --- a/configuration/services/metrics/victoriametrics.nix +++ b/configuration/services/metrics/victoriametrics.nix @@ -84,16 +84,9 @@ in in [ "${address}:${toString port}" ]; - immich.targets = [ - "127.0.0.1:8081" - "127.0.0.1:8082" - ]; - # Configured in the hookshot listeners, but it's hard to filter # the correct values out of that config. matrixHookshot.targets = [ "127.0.0.1:9001" ]; - - victorialogs.targets = [ config.services.victorialogs.bindAddress ]; }; }; } diff --git a/configuration/services/minecraft.nix b/configuration/services/minecraft.nix new file mode 100644 index 0000000..0477f44 --- /dev/null +++ b/configuration/services/minecraft.nix @@ -0,0 +1,83 @@ +{ + pkgs, + lib, + config, + ... +}: +{ + services.minecraft-server = { + enable = true; + eula = true; + # jvmOpts are set using a file for forge + # jvmOpts = "-Xmx8G -Xms8G"; + openFirewall = true; + + declarative = true; + + whitelist = { + tlater = "140d177a-966f-41b8-a4c0-e305babd291b"; + romino25 = "59cd1648-14a4-4bcf-8f5a-2e1bde678f2c"; + lasi25 = "0ab6e3d1-544a-47e7-8538-2e6c248e49a4"; + }; + + serverProperties = { + allow-flight = true; + difficulty = "hard"; + motd = "tlater.net"; + spawn-protection = 1; + white-list = true; + enable-query = true; + enable-status = true; + + # Allows the server to write chunks without hogging the main + # thread... + sync-chunk-writes = false; + # Disables chat reporting, because we don't need any of that + # drama on a lil' friends-only server. + enforce-secure-profile = false; + }; + + package = pkgs.writeShellApplication { + name = "minecraft-server"; + runtimeInputs = with pkgs; [ jdk17_headless ]; + + text = '' + exec /var/lib/minecraft/run.sh $@ + ''; + }; + }; + + systemd.services.minecraft-server = { + path = with pkgs; [ jdk17_headless ]; + + # Since we read from our own HTTP server, we need to wait for it + # to be up + after = [ "nginx.service" ]; + + serviceConfig = { + # Use packwiz to install mods + ExecStartPre = [ + "${pkgs.jdk17_headless}/bin/java -jar ${config.services.minecraft-server.dataDir}/packwiz-installer-bootstrap.jar -g -s server 'https://minecraft.${config.services.nginx.domain}/cobblemon-pack/pack.toml'" + ]; + # Forge requires some bonus JVM options, which they include in a + # little `run.sh` script + ExecStart = lib.mkForce "${config.services.minecraft-server.dataDir}/run.sh --nogui"; + }; + }; + + systemd.tmpfiles.settings."10-minecraft" = { + "/srv/minecraft".d = { + user = "nginx"; + group = "minecraft"; + mode = "0775"; + }; + }; + + services.nginx.virtualHosts."minecraft.${config.services.nginx.domain}" = { + forceSSL = true; + useACMEHost = "tlater.net"; + enableHSTS = true; + + root = "/srv/minecraft"; + }; +} diff --git a/configuration/services/nextcloud.nix b/configuration/services/nextcloud.nix index 4af77a9..b5cb691 100644 --- a/configuration/services/nextcloud.nix +++ b/configuration/services/nextcloud.nix @@ -5,99 +5,97 @@ ... }: let - nextcloud = pkgs.nextcloud31; + nextcloud = pkgs.nextcloud30; hostName = "nextcloud.${config.services.nginx.domain}"; in { - services = { - nextcloud = { - inherit hostName; + services.nextcloud = { + inherit hostName; - package = nextcloud; - phpPackage = lib.mkForce ( - pkgs.php.override { - packageOverrides = _: prev: { - extensions = prev.extensions // { - pgsql = prev.extensions.pgsql.overrideAttrs (_: { - configureFlags = [ "--with-pgsql=${lib.getDev config.services.postgresql.package.pg_config}" ]; - }); - pdo_pgsql = prev.extensions.pdo_pgsql.overrideAttrs (_: { - configureFlags = [ "--with-pdo-pgsql=${lib.getDev config.services.postgresql.package.pg_config}" ]; - }); - }; + package = nextcloud; + phpPackage = lib.mkForce ( + pkgs.php.override { + packageOverrides = final: prev: { + extensions = prev.extensions // { + pgsql = prev.extensions.pgsql.overrideAttrs (old: { + configureFlags = [ "--with-pgsql=${lib.getDev config.services.postgresql.package}" ]; + }); + pdo_pgsql = prev.extensions.pdo_pgsql.overrideAttrs (old: { + configureFlags = [ "--with-pdo-pgsql=${lib.getDev config.services.postgresql.package}" ]; + }); }; - } - ); - enable = true; - maxUploadSize = "2G"; - https = true; + }; + } + ); + enable = true; + maxUploadSize = "2G"; + https = true; - configureRedis = true; + configureRedis = true; - config = { - dbtype = "pgsql"; - dbhost = "/run/postgresql"; + config = { + dbtype = "pgsql"; + dbhost = "/run/postgresql"; - adminuser = "tlater"; - adminpassFile = config.sops.secrets."nextcloud/tlater".path; - }; - - settings = { - default_phone_region = "AT"; - overwriteprotocol = "https"; - }; - - phpOptions = { - "opcache.interned_strings_buffer" = "16"; - }; - - extraApps = { - inherit (config.services.nextcloud.package.packages.apps) - calendar - contacts - cookbook - news - ; - }; + adminuser = "tlater"; + adminpassFile = config.sops.secrets."nextcloud/tlater".path; }; - # Set up SSL - nginx.virtualHosts."${hostName}" = { - forceSSL = true; - useACMEHost = "tlater.net"; - # The upstream module already adds HSTS + settings = { + default_phone_region = "AT"; + overwriteprotocol = "https"; }; - backups.nextcloud = { - user = "nextcloud"; - paths = [ - "/var/lib/nextcloud/nextcloud-db.sql" - "/var/lib/nextcloud/data/" - "/var/lib/nextcloud/config/config.php" - ]; - preparation = { - packages = [ - config.services.postgresql.package - config.services.nextcloud.occ - ]; - text = '' - nextcloud-occ maintenance:mode --on - pg_dump ${config.services.nextcloud.config.dbname} --file=/var/lib/nextcloud/nextcloud-db.sql - ''; - }; - cleanup = { - packages = [ - pkgs.coreutils - config.services.nextcloud.occ - ]; - text = '' - rm /var/lib/nextcloud/nextcloud-db.sql - nextcloud-occ maintenance:mode --off - ''; - }; + phpOptions = { + "opcache.interned_strings_buffer" = "16"; + }; + + extraApps = { + inherit (config.services.nextcloud.package.packages.apps) + calendar + contacts + cookbook + news + ; }; }; # Ensure that this service doesn't start before postgres is ready systemd.services.nextcloud-setup.after = [ "postgresql.service" ]; + + # Set up SSL + services.nginx.virtualHosts."${hostName}" = { + forceSSL = true; + useACMEHost = "tlater.net"; + # The upstream module already adds HSTS + }; + + services.backups.nextcloud = { + user = "nextcloud"; + paths = [ + "/var/lib/nextcloud/nextcloud-db.sql" + "/var/lib/nextcloud/data/" + "/var/lib/nextcloud/config/config.php" + ]; + preparation = { + packages = [ + config.services.postgresql.package + config.services.nextcloud.occ + ]; + text = '' + nextcloud-occ maintenance:mode --on + pg_dump ${config.services.nextcloud.config.dbname} --file=/var/lib/nextcloud/nextcloud-db.sql + ''; + }; + cleanup = { + packages = [ + pkgs.coreutils + config.services.nextcloud.occ + ]; + text = '' + rm /var/lib/nextcloud/nextcloud-db.sql + nextcloud-occ maintenance:mode --off + ''; + }; + }; } diff --git a/configuration/services/webserver.nix b/configuration/services/webserver.nix index 864f6c0..e6b49b3 100644 --- a/configuration/services/webserver.nix +++ b/configuration/services/webserver.nix @@ -1,6 +1,6 @@ { config, ... }: let - inherit (config.services.nginx) domain; + domain = config.services.nginx.domain; in { services.tlaternet-webserver = { diff --git a/flake.lock b/flake.lock index 885b579..37c3d5f 100644 --- a/flake.lock +++ b/flake.lock @@ -300,11 +300,11 @@ ] }, "locked": { - "lastModified": 1747742835, - "narHash": "sha256-kYL4GCwwznsypvsnA20oyvW8zB/Dvn6K5G/tgMjVMT4=", + "lastModified": 1739841949, + "narHash": "sha256-lSOXdgW/1zi/SSu7xp71v+55D5Egz8ACv0STkj7fhbs=", "owner": "nix-community", "repo": "disko", - "rev": "df522e787fdffc4f32ed3e1fca9ed0968a384d62", + "rev": "15dbf8cebd8e2655a883b74547108e089f051bf0", "type": "github" }, "original": { @@ -595,11 +595,11 @@ ] }, "locked": { - "lastModified": 1746877938, - "narHash": "sha256-N9J96pSPg4vbozV+ZZ++dwLnMIf2Le6ONNMO0kZCj1M=", + "lastModified": 1739712626, + "narHash": "sha256-u3m+awbdL+0BKk8IWidsWMr+R0ian3GZMUlH7623kd8=", "owner": "reckenrode", "repo": "nix-foundryvtt", - "rev": "f1b401831d796dd94cf5a11b65fd169a199d4ff0", + "rev": "a7fa493ba2c623cf90e83756b62285b3b58f18d2", "type": "github" }, "original": { @@ -744,18 +744,34 @@ "type": "github" } }, - "nixpkgs_2": { + "nixpkgs-unstable": { "locked": { - "lastModified": 1748085680, - "narHash": "sha256-XG90Q/040NiV70gAVvoYbXg1lULbiwIzfkWmwSINyGQ=", + "lastModified": 1740215764, + "narHash": "sha256-wzBbGGZ6i1VVBA/cDJaLfuuGYCUriD7fwsLgJJHRVRk=", "owner": "nixos", "repo": "nixpkgs", - "rev": "4e6eeca5ed45465087274fc9dc6bc2011254a0f3", + "rev": "8465e233b0668cf162c608a92e62e8d78c1ba7e4", "type": "github" }, "original": { "owner": "nixos", - "ref": "nixos-25.05-small", + "ref": "nixos-unstable-small", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1740162160, + "narHash": "sha256-SSYxFhqCOb3aiPb6MmN68yEzBIltfom8IgRz7phHscM=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "11415c7ae8539d6292f2928317ee7a8410b28bb9", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-24.11-small", "repo": "nixpkgs", "type": "github" } @@ -1006,6 +1022,7 @@ "disko": "disko", "foundryvtt": "foundryvtt", "nixpkgs": "nixpkgs_2", + "nixpkgs-unstable": "nixpkgs-unstable", "sonnenshift": "sonnenshift", "sops-nix": "sops-nix", "tlaternet-webserver": "tlaternet-webserver" @@ -1079,11 +1096,11 @@ ] }, "locked": { - "lastModified": 1747603214, - "narHash": "sha256-lAblXm0VwifYCJ/ILPXJwlz0qNY07DDYdLD+9H+Wc8o=", + "lastModified": 1739262228, + "narHash": "sha256-7JAGezJ0Dn5qIyA2+T4Dt/xQgAbhCglh6lzCekTVMeU=", "owner": "Mic92", "repo": "sops-nix", - "rev": "8d215e1c981be3aa37e47aeabd4e61bb069548fd", + "rev": "07af005bb7d60c7f118d9d9f5530485da5d1e975", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index da8455f..b31d108 100644 --- a/flake.nix +++ b/flake.nix @@ -2,7 +2,8 @@ description = "tlater.net host configuration"; inputs = { - nixpkgs.url = "github:nixos/nixpkgs/nixos-25.05-small"; + nixpkgs.url = "github:nixos/nixpkgs/nixos-24.11-small"; + nixpkgs-unstable.url = "github:nixos/nixpkgs/nixos-unstable-small"; disko = { url = "github:nix-community/disko"; inputs.nixpkgs.follows = "nixpkgs"; @@ -91,7 +92,7 @@ ######### # Tests # ######### - checks.${system} = import ./checks (inputs // { inherit system; }); + checks = builtins.mapAttrs (system: deployLib: deployLib.deployChecks self.deploy) deploy-rs.lib; ########################### # Garbage collection root # @@ -116,6 +117,8 @@ run-vm = { type = "app"; program = + let + in (pkgs.writeShellScript "" '' ${vm.config.system.build.vm.outPath}/bin/run-testvm-vm '').outPath; diff --git a/modules/crowdsec/default.nix b/modules/crowdsec/default.nix index 44e6bc5..cc14939 100644 --- a/modules/crowdsec/default.nix +++ b/modules/crowdsec/default.nix @@ -31,6 +31,22 @@ let ${lib.concatMapStringsSep "\n---\n" builtins.toJSON cfg.acquisitions} --- ''; + + extraConfigs = pkgs.symlinkJoin { + name = "crowdsec-extra-configs"; + paths = lib.mapAttrsToList ( + path: settings: + (settingsFormat.generate path settings).overrideAttrs (old: { + patchPhase = '' + mkdir -p "$out/${dirOf path}/" + out="$out/${dirOf path}/" + + echo $out + exit 1 + ''; + }) + ) cfg.extraConfig; + }; in { imports = [ ./remediations ]; @@ -38,6 +54,7 @@ in options.security.crowdsec = let inherit (lib.types) + attrsOf nullOr listOf package @@ -85,6 +102,16 @@ in ''; }; + extraConfig = lib.mkOption { + type = attrsOf (settingsFormat.type); + default = { + "parsers/s02-enrich/nixos-whitelist.yaml" = cfg.parserWhitelist; + }; + description = '' + Set of additional configurations to install. + ''; + }; + acquisitions = lib.mkOption { type = listOf settingsFormat.type; default = [ ]; @@ -247,7 +274,10 @@ in online_client = { # By default, we don't let crowdsec phone home, since # this is usually within NixOS users' concerns. - sharing = lib.mkDefault false; + # + # TODO: Enable when this option becomes available + # (1.6.4, current nixpkgs-unstable) + # sharing = lib.mkDefault false; credentials_path = cfg.centralApiCredentials; }; }; @@ -264,7 +294,9 @@ in }; }; - systemd.packages = [ cfg.package ]; + systemd.packages = [ + cfg.package + ]; environment = { systemPackages = [ @@ -295,33 +327,6 @@ in group = "crowdsec"; mode = "0700"; }; - - "${cfg.stateDirectory}/config/parsers".d = lib.mkIf (cfg.parserWhitelist != [ ]) { - user = "crowdsec"; - group = "crowdsec"; - mode = "0700"; - }; - - "${cfg.stateDirectory}/config/parsers/s02-enrich".d = lib.mkIf (cfg.parserWhitelist != [ ]) { - user = "crowdsec"; - group = "crowdsec"; - mode = "0700"; - }; - - "${cfg.stateDirectory}/config/parsers/s02-enrich/nixos-whitelist.yaml" = - lib.mkIf (cfg.parserWhitelist != [ ]) - { - "L+".argument = - (settingsFormat.generate "crowdsec-nixos-whitelist.yaml" { - name = "nixos/parser-whitelist"; - description = "Parser whitelist generated by the crowdsec NixOS module"; - whitelist = { - reason = "Filtered by NixOS whitelist"; - ip = lib.lists.filter (ip: !(lib.hasInfix "/" ip)) cfg.parserWhitelist; - cidr = lib.lists.filter (ip: lib.hasInfix "/" ip) cfg.parserWhitelist; - }; - }).outPath; - }; }; services = { @@ -331,6 +336,8 @@ in description = "Crowdsec database and config preparation"; script = '' + cp --copy-contents --recursive ${extraConfigs}/. ${cfg.stateDirectory}/config + if [ ! -e '${cfg.settings.config_paths.simulation_path}' ]; then cp '${cfg.package}/share/crowdsec/config/simulation.yaml' '${cfg.settings.config_paths.simulation_path}' fi diff --git a/modules/crowdsec/remediations/cs-firewall-bouncer.nix b/modules/crowdsec/remediations/cs-firewall-bouncer.nix index 42accc6..aa70552 100644 --- a/modules/crowdsec/remediations/cs-firewall-bouncer.nix +++ b/modules/crowdsec/remediations/cs-firewall-bouncer.nix @@ -6,11 +6,10 @@ ... }: let - inherit (flake-inputs.self.packages.${pkgs.system}) crowdsec-firewall-bouncer; - crowdsecCfg = config.security.crowdsec; cfg = crowdsecCfg.remediationComponents.firewallBouncer; settingsFormat = pkgs.formats.yaml { }; + crowdsec-firewall-bouncer = flake-inputs.self.packages.${pkgs.system}.crowdsec-firewall-bouncer; in { options.security.crowdsec.remediationComponents.firewallBouncer = { @@ -32,7 +31,9 @@ in security.crowdsec.remediationComponents.firewallBouncer.settings = { mode = lib.mkDefault "${if config.networking.nftables.enable then "nftables" else "iptables"}"; log_mode = "stdout"; - iptables_chains = [ "nixos-fw" ]; + iptables_chains = [ + "nixos-fw" + ]; # Don't let users easily override this; unfortunately we need to # set up this key through substitution at runtime. @@ -77,7 +78,9 @@ in requiredBy = [ "crowdsec.service" ]; path = - lib.optionals (cfg.settings.mode == "ipset" || cfg.settings.mode == "iptables") [ pkgs.ipset ] + lib.optionals (cfg.settings.mode == "ipset" || cfg.settings.mode == "iptables") [ + pkgs.ipset + ] ++ lib.optional (cfg.settings.mode == "iptables") pkgs.iptables ++ lib.optional (cfg.settings.mode == "nftables") pkgs.nftables; }; diff --git a/modules/crowdsec/remediations/default.nix b/modules/crowdsec/remediations/default.nix index c3c0790..7df6ade 100644 --- a/modules/crowdsec/remediations/default.nix +++ b/modules/crowdsec/remediations/default.nix @@ -1 +1,5 @@ -{ imports = [ ./cs-firewall-bouncer.nix ]; } +{ + imports = [ + ./cs-firewall-bouncer.nix + ]; +} diff --git a/pkgs/crowdsec/_sources/generated.json b/pkgs/crowdsec/_sources/generated.json index f3c8f01..ac251aa 100644 --- a/pkgs/crowdsec/_sources/generated.json +++ b/pkgs/crowdsec/_sources/generated.json @@ -21,7 +21,7 @@ }, "crowdsec-hub": { "cargoLocks": null, - "date": "2025-05-17", + "date": "2025-02-22", "extract": null, "name": "crowdsec-hub", "passthru": null, @@ -33,10 +33,10 @@ "name": null, "owner": "crowdsecurity", "repo": "hub", - "rev": "850614b9fcd4298f559b422c5ac685a69aa2e5ff", - "sha256": "sha256-96MMwFN5KongQA3YJVSuk7Kanbr1gR94CCyiflmez2k=", + "rev": "f9883cd6c7d1913c13e4a3a69d9a0b887a7d57df", + "sha256": "sha256-45pUln7Qj5luY9I9BE2qhzjH7kv4IbYvNoEX3/4AVVg=", "type": "github" }, - "version": "850614b9fcd4298f559b422c5ac685a69aa2e5ff" + "version": "f9883cd6c7d1913c13e4a3a69d9a0b887a7d57df" } } \ No newline at end of file diff --git a/pkgs/crowdsec/_sources/generated.nix b/pkgs/crowdsec/_sources/generated.nix index 19a7f5a..9c63cc5 100644 --- a/pkgs/crowdsec/_sources/generated.nix +++ b/pkgs/crowdsec/_sources/generated.nix @@ -14,14 +14,14 @@ }; crowdsec-hub = { pname = "crowdsec-hub"; - version = "850614b9fcd4298f559b422c5ac685a69aa2e5ff"; + version = "f9883cd6c7d1913c13e4a3a69d9a0b887a7d57df"; src = fetchFromGitHub { owner = "crowdsecurity"; repo = "hub"; - rev = "850614b9fcd4298f559b422c5ac685a69aa2e5ff"; + rev = "f9883cd6c7d1913c13e4a3a69d9a0b887a7d57df"; fetchSubmodules = false; - sha256 = "sha256-96MMwFN5KongQA3YJVSuk7Kanbr1gR94CCyiflmez2k="; + sha256 = "sha256-45pUln7Qj5luY9I9BE2qhzjH7kv4IbYvNoEX3/4AVVg="; }; - date = "2025-05-17"; + date = "2025-02-22"; }; } diff --git a/pkgs/crowdsec/hub.nix b/pkgs/crowdsec/hub.nix index 1b8c9b3..d057ca8 100644 --- a/pkgs/crowdsec/hub.nix +++ b/pkgs/crowdsec/hub.nix @@ -1 +1,4 @@ -{ sources }: sources.crowdsec-hub.src +{ + sources, +}: +sources.crowdsec-hub.src