diff --git a/modules/nixos/_private/default.nix b/modules/nixos/_private/default.nix index ac264ce8..1a6103ca 100644 --- a/modules/nixos/_private/default.nix +++ b/modules/nixos/_private/default.nix @@ -1,6 +1,7 @@ { imports = [ ./extra-arguments.nix + ./shared-setups/server ./state ./suites/archiving.nix ./suites/browsers.nix diff --git a/modules/nixos/_private/shared-setups/server/crowdsec.nix b/modules/nixos/_private/shared-setups/server/crowdsec.nix new file mode 100644 index 00000000..ea75b757 --- /dev/null +++ b/modules/nixos/_private/shared-setups/server/crowdsec.nix @@ -0,0 +1,30 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.shared-setups.server.crowdsec; +in +{ + options.shared-setups.server.crowdsec.enable = + lib.mkEnableOption "typical Crowdsec setup for public-facing servers"; + + config = lib.mkIf cfg.enable { + services.crowdsec = { + enable = true; + settings = { + common = { + daemonize = false; + log_media = "stdout"; + }; + }; + + plugins = { + http = { + settings = { + type = "http"; + log_level = "info"; + }; + }; + }; + }; + }; +} diff --git a/modules/nixos/_private/shared-setups/server/default.nix b/modules/nixos/_private/shared-setups/server/default.nix new file mode 100644 index 00000000..c1ae41c4 --- /dev/null +++ b/modules/nixos/_private/shared-setups/server/default.nix @@ -0,0 +1,8 @@ +{ + imports = [ + ./crowdsec.nix + ./fail2ban.nix + ./firewall.nix + ./nginx.nix + ]; +} diff --git a/modules/nixos/_private/shared-setups/server/fail2ban.nix b/modules/nixos/_private/shared-setups/server/fail2ban.nix new file mode 100644 index 00000000..e8bd21f2 --- /dev/null +++ b/modules/nixos/_private/shared-setups/server/fail2ban.nix @@ -0,0 +1,29 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.shared-setups.server.fail2ban; +in +{ + options.shared-setups.server.fail2ban.enable = + lib.mkEnableOption "typical fail2ban configuration for public-facing servers"; + + config = lib.mkIf cfg.enable { + services.fail2ban = { + enable = true; + bantime-increment = { + enable = true; + factor = "4"; + maxtime = "24h"; + overalljails = true; + }; + extraPackages = with pkgs; [ ipset ]; + + # We're going to be unforgiving with this one since we only have key + # authentication and password authentication is disabled anyways. + jails.sshd.settings = { + enabled = true; + maxretry = 1; + }; + }; + }; +} diff --git a/modules/nixos/_private/shared-setups/server/firewall.nix b/modules/nixos/_private/shared-setups/server/firewall.nix new file mode 100644 index 00000000..3c1e6b28 --- /dev/null +++ b/modules/nixos/_private/shared-setups/server/firewall.nix @@ -0,0 +1,20 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.shared-setups.server.firewall; +in +{ + options.shared-setups.server.firewall.enable = lib.mkEnableOption "typical firewall setup"; + + config = lib.mkIf cfg.enable { + networking = { + nftables.enable = true; + firewall = { + enable = true; + allowedTCPPorts = [ + 22 # Secure Shells. + ]; + }; + }; + }; +} diff --git a/modules/nixos/_private/shared-setups/server/nginx.nix b/modules/nixos/_private/shared-setups/server/nginx.nix new file mode 100644 index 00000000..309d0812 --- /dev/null +++ b/modules/nixos/_private/shared-setups/server/nginx.nix @@ -0,0 +1,86 @@ +# The reverse proxy of choice. Logs should be rotated weekly. +{ config, lib, pkgs, ... }: + +let + cfg = config.shared-setups.server.nginx; +in +{ + options.shared-setups.server.nginx.enable = + lib.mkEnableOption "typical Nginx configuration for public-facing servers"; + + config = lib.mkIf cfg.enable (lib.mkMerge [ + { + # The main server where it will tie all of the services in one neat little + # place. Take note, the virtual hosts definition are all in their respective + # modules. + services.nginx = { + enable = true; + enableReload = true; + + package = pkgs.nginxMainline; + + recommendedOptimisation = true; + recommendedProxySettings = true; + recommendedTlsSettings = true; + + # Some more server-sided compressions. + recommendedBrotliSettings = true; + recommendedGzipSettings = true; + recommendedZstdSettings = true; + + proxyCachePath.apps = { + enable = true; + keysZoneName = "apps"; + }; + + appendConfig = '' + worker_processes auto; + ''; + + # We're avoiding any service to be the default server especially that it + # could be used for enter a service with unencrypted HTTP. So we're setting + # up one with an unresponsive server response. + appendHttpConfig = '' + # https://docs.nginx.com/nginx/admin-guide/content-cache/content-caching/ + proxy_cache_min_uses 5; + proxy_cache_valid 200 302 10m; + proxy_cache_valid 404 1m; + proxy_no_cache $http_pragma $http_authorization; + + server { + listen 80 default_server; + listen [::]:80 default_server; + return 444; + } + ''; + + # This is defined for other services. + upstreams."nginx" = { + extraConfig = '' + zone services 64k; + ''; + servers = { + "localhost:80" = { }; + }; + }; + }; + + networking.firewall.allowedTCPPorts = [ + 80 # HTTP servers. + 443 # HTTPS servers. + ]; + + # Generate a DH parameters for nginx-specific security configurations. + security.dhparams.params.nginx.bits = 4096; + } + + (lib.mkIf config.services.fail2ban.enable { + # Some fail2ban policies to apply for nginx. + services.fail2ban.jails = { + nginx-http-auth.settings = { enabled = true; }; + nginx-botsearch.settings = { enabled = true; }; + nginx-bad-request.settings = { enabled = true; }; + }; + }) + ]); +}