diff --git a/checks/default.nix b/checks/default.nix index 674b19d..737313d 100644 --- a/checks/default.nix +++ b/checks/default.nix @@ -6,6 +6,8 @@ let in { x86_64-linux = lib.mergeAttrsList [ + flake-inputs.self.nixosConfigurations.hetzner-1.config.serviceTests + { nix = checkLib.mkLint { name = "nix-lints"; diff --git a/configuration/default.nix b/configuration/default.nix index 9881db2..464f7ed 100644 --- a/configuration/default.nix +++ b/configuration/default.nix @@ -1,5 +1,5 @@ { - config, + lib, modulesPath, flake-inputs, ... @@ -9,25 +9,11 @@ flake-inputs.disko.nixosModules.disko flake-inputs.sops-nix.nixosModules.sops flake-inputs.tlaternet-webserver.nixosModules.default - "${modulesPath}/profiles/minimal.nix" - (import ../modules) - ./services/backups.nix - ./services/battery-manager.nix - ./services/conduit - ./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 - # ./services/starbound.nix -- Not currently used - ./services/postgres.nix + ../modules ./nginx + ./services ]; nix = { @@ -39,49 +25,9 @@ settings.trusted-users = [ "@wheel" ]; }; - # Optimization for minecraft servers, see: - # https://bugs.mojang.com/browse/MC-183518 - boot.kernelParams = [ - "highres=off" - "nohz=off" - ]; - networking = { usePredictableInterfaceNames = false; useDHCP = false; - - firewall = { - allowedTCPPorts = [ - # http - 80 - 443 - # ssh - 2222 - # matrix - 8448 - # starbound - 21025 - - config.services.coturn.listening-port - config.services.coturn.tls-listening-port - config.services.coturn.alt-listening-port - config.services.coturn.alt-tls-listening-port - ]; - - allowedUDPPorts = [ - config.services.coturn.listening-port - config.services.coturn.tls-listening-port - config.services.coturn.alt-listening-port - config.services.coturn.alt-tls-listening-port - ]; - - allowedUDPPortRanges = [ - { - from = config.services.coturn.min-port; - to = config.services.coturn.max-port; - } - ]; - }; }; systemd.network.enable = true; @@ -124,7 +70,7 @@ sops.defaultSopsFile = ../keys/production.yaml; # Remove some unneeded packages - environment.defaultPackages = [ ]; + environment.defaultPackages = lib.mkForce [ ]; system.stateVersion = "20.09"; } diff --git a/configuration/nginx/logging.nix b/configuration/nginx/logging.nix index 0c6a955..e41bfae 100644 --- a/configuration/nginx/logging.nix +++ b/configuration/nginx/logging.nix @@ -1,4 +1,10 @@ -{ config, lib, ... }: +{ + flake-inputs, + pkgs, + config, + lib, + ... +}: let hostNames = lib.attrNames config.services.nginx.virtualHosts; logPath = name: "/var/log/nginx/${name}/access.log"; @@ -80,5 +86,55 @@ in }; }; }; + + serviceTests = + let + testHostConfig = + { config, ... }: + { + imports = [ + ./. + ../../modules/serviceTests/mocks.nix + ]; + + networking.firewall.allowedTCPPorts = [ 80 ]; + + services.nginx = { + domain = "testHost"; + virtualHosts."${config.services.nginx.domain}".locations."/".return = "200 ok"; + }; + }; + in + { + nginxMetricsWork = pkgs.testers.runNixOSTest { + name = "nginx-metrics-work"; + node.specialArgs = { inherit flake-inputs; }; + + nodes = { + testHost = testHostConfig; + + client = + { pkgs, ... }: + { + environment.systemPackages = [ pkgs.curl ]; + }; + }; + + testScript = '' + import time + + start_all() + + testHost.wait_for_unit("nginx.service") + client.succeed("curl --max-time 10 http://testHost") + + # Wait a bit for the prometheus exporter to scrape our logs + time.sleep(5) + + res = testHost.succeed("curl localhost:${builtins.toString config.services.prometheus.exporters.nginxlog.port}/metrics") + assert 'nginxlog_http_response_count_total{method="GET",status="200",vhost="testHost"} 1' in res, res + ''; + }; + }; }; } diff --git a/configuration/nginx/ssl.nix b/configuration/nginx/ssl.nix index 7abc38e..dc2b999 100644 --- a/configuration/nginx/ssl.nix +++ b/configuration/nginx/ssl.nix @@ -1,4 +1,5 @@ { + flake-inputs, pkgs, config, lib, @@ -69,5 +70,72 @@ "porkbun/api-key".owner = "acme"; "porkbun/secret-api-key".owner = "acme"; }; + + serviceTests = + let + testHostConfig = + { config, ... }: + { + imports = [ + ./. + ../../modules/serviceTests/mocks.nix + ]; + + networking.firewall.allowedTCPPorts = [ + 80 + 443 + ]; + + security.acme.certs."tlater.net".extraDomainNames = [ config.services.nginx.domain ]; + + services.nginx = { + domain = "testHost"; + + virtualHosts."${config.services.nginx.domain}" = { + useACMEHost = "tlater.net"; + forceSSL = true; + enableHSTS = true; + locations."/".return = "200 ok"; + }; + }; + }; + in + { + hstsIsSet = pkgs.testers.runNixOSTest { + name = "hsts-is-set"; + + node.specialArgs = { inherit flake-inputs; }; + nodes = { + testHost = testHostConfig; + + client = + { pkgs, ... }: + { + environment.systemPackages = [ pkgs.curl ]; + }; + }; + + testScript = '' + start_all() + + testHost.wait_for_unit("nginx.service") + testHost.copy_from_vm("/var/lib/acme/tlater.net/", "certs") + client.copy_from_host(f"{testHost.out_dir}/certs", "/certs") + + client.succeed("curl --max-time 10 http://testHost") + res = client.succeed(" ".join([ + "curl", + "--show-error", + "--silent", + "--dump-header -", + "--cacert /certs/tlater.net/fullchain.pem", + "https://testHost", + "-o /dev/null" + ])) + + assert "strict-transport-security: max-age=15552000; includeSubDomains" in res + ''; + }; + }; }; } diff --git a/configuration/services/conduit/default.nix b/configuration/services/conduit/default.nix index 6e01e81..b6f8f27 100644 --- a/configuration/services/conduit/default.nix +++ b/configuration/services/conduit/default.nix @@ -17,6 +17,36 @@ in ./matrix-hookshot.nix ]; + networking.firewall = { + allowedTCPPorts = [ + # These are for "normal" clients + 80 + 443 + + # Federation happens on 8448 + 8448 + + config.services.coturn.listening-port + config.services.coturn.tls-listening-port + config.services.coturn.alt-listening-port + config.services.coturn.alt-tls-listening-port + ]; + + allowedUDPPorts = [ + config.services.coturn.listening-port + config.services.coturn.tls-listening-port + config.services.coturn.alt-listening-port + config.services.coturn.alt-tls-listening-port + ]; + + allowedUDPPortRanges = [ + { + from = config.services.coturn.min-port; + to = config.services.coturn.max-port; + } + ]; + }; + services = { matrix-conduit = { enable = true; diff --git a/configuration/services/default.nix b/configuration/services/default.nix new file mode 100644 index 0000000..adc4482 --- /dev/null +++ b/configuration/services/default.nix @@ -0,0 +1,18 @@ +{ + imports = [ + ./backups.nix + ./battery-manager.nix + ./conduit + ./crowdsec.nix + ./foundryvtt.nix + ./gitea.nix + ./immich.nix + ./metrics + ./minecraft.nix + ./nextcloud.nix + ./postgres.nix + # ./starbound.nix -- Not currently used + ./webserver.nix + ./wireguard.nix + ]; +} diff --git a/configuration/services/foundryvtt.nix b/configuration/services/foundryvtt.nix index 6c475a3..5c8a21f 100644 --- a/configuration/services/foundryvtt.nix +++ b/configuration/services/foundryvtt.nix @@ -11,6 +11,11 @@ in { imports = [ flake-inputs.foundryvtt.nixosModules.foundryvtt ]; + networking.firewall.allowedTCPPorts = [ + 80 + 443 + ]; + services = { foundryvtt = { enable = true; diff --git a/configuration/services/gitea.nix b/configuration/services/gitea.nix index 613d30c..b4dd719 100644 --- a/configuration/services/gitea.nix +++ b/configuration/services/gitea.nix @@ -8,6 +8,11 @@ let domain = "gitea.${config.services.nginx.domain}"; in { + networking.firewall.allowedTCPPorts = [ + 80 + 443 + ]; + services = { forgejo = { enable = true; diff --git a/configuration/services/immich.nix b/configuration/services/immich.nix index 516ea3e..39673d0 100644 --- a/configuration/services/immich.nix +++ b/configuration/services/immich.nix @@ -8,6 +8,11 @@ let hostName = "immich.${config.services.nginx.domain}"; in { + networking.firewall.allowedTCPPorts = [ + 80 + 443 + ]; + services = { immich = { enable = true; diff --git a/configuration/services/metrics/grafana.nix b/configuration/services/metrics/grafana.nix index f4b6956..765a364 100644 --- a/configuration/services/metrics/grafana.nix +++ b/configuration/services/metrics/grafana.nix @@ -3,6 +3,11 @@ let domain = "metrics.${config.services.nginx.domain}"; in { + networking.firewall.allowedTCPPorts = [ + 80 + 443 + ]; + services.grafana = { enable = true; settings = { diff --git a/configuration/services/nextcloud.nix b/configuration/services/nextcloud.nix index ef2a6ac..77cfa4c 100644 --- a/configuration/services/nextcloud.nix +++ b/configuration/services/nextcloud.nix @@ -9,6 +9,11 @@ let hostName = "nextcloud.${config.services.nginx.domain}"; in { + networking.firewall.allowedTCPPorts = [ + 80 + 443 + ]; + services = { nextcloud = { inherit hostName; diff --git a/configuration/services/starbound.nix b/configuration/services/starbound.nix index a667b57..6b97471 100644 --- a/configuration/services/starbound.nix +++ b/configuration/services/starbound.nix @@ -8,6 +8,8 @@ let inherit (lib) concatStringsSep; in { + networking.firewall.allowedTCPPorts = [ 21025 ]; + # Sadly, steam-run requires some X libs environment.noXlibs = false; diff --git a/configuration/services/webserver.nix b/configuration/services/webserver.nix index 864f6c0..8f08e4f 100644 --- a/configuration/services/webserver.nix +++ b/configuration/services/webserver.nix @@ -3,6 +3,11 @@ let inherit (config.services.nginx) domain; in { + networking.firewall.allowedTCPPorts = [ + 80 + 443 + ]; + services.tlaternet-webserver = { enable = true; listen = { diff --git a/modules/default.nix b/modules/default.nix index 9483c66..1bf5314 100644 --- a/modules/default.nix +++ b/modules/default.nix @@ -1 +1,6 @@ -{ imports = [ ./crowdsec ]; } +{ + imports = [ + ./crowdsec + ./serviceTests/stub.nix + ]; +} diff --git a/modules/serviceTests/mocks.nix b/modules/serviceTests/mocks.nix new file mode 100644 index 0000000..f864253 --- /dev/null +++ b/modules/serviceTests/mocks.nix @@ -0,0 +1,27 @@ +/** + Module containing mock definitions for service test runners. +*/ +{ flake-inputs, lib, ... }: +{ + imports = [ + flake-inputs.sops-nix.nixosModules.sops + ../. + ../../configuration/services/backups.nix + ]; + + sops.defaultSopsFile = ../../keys/staging.yaml; + environment.etc."staging.key" = { + mode = "0400"; + source = ../../keys/hosts/staging.key; + }; + services.openssh = { + enable = true; + hostKeys = lib.mkForce [ + { + type = "rsa"; + bits = 4096; + path = "/etc/staging.key"; + } + ]; + }; +} diff --git a/modules/serviceTests/stub.nix b/modules/serviceTests/stub.nix new file mode 100644 index 0000000..d4a4b78 --- /dev/null +++ b/modules/serviceTests/stub.nix @@ -0,0 +1,20 @@ +/** + Module to make writing service-specific tests easy. +*/ +{ lib, ... }: +let + inherit (lib) mkOption types; +in +{ + options = { + serviceTests = mkOption { + type = types.attrsOf types.package; + + description = '' + NixOS tests to run. + ''; + + default = { }; + }; + }; +}