From efd0a0f056b65a0bda4dc6e2b02a6577ce3a9c9d Mon Sep 17 00:00:00 2001 From: Qubasa Date: Mon, 19 Aug 2024 11:33:17 +0200 Subject: [PATCH 1/3] clanModules: Init nginx module. matrix-synapse: don't assume domain names --- clanModules/matrix-synapse/default.nix | 43 ++++++++++---------- clanModules/nginx/README.md | 3 ++ clanModules/nginx/default.nix | 55 ++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 20 deletions(-) create mode 100644 clanModules/nginx/README.md create mode 100644 clanModules/nginx/default.nix diff --git a/clanModules/matrix-synapse/default.nix b/clanModules/matrix-synapse/default.nix index cceb465c1..d623320b9 100644 --- a/clanModules/matrix-synapse/default.nix +++ b/clanModules/matrix-synapse/default.nix @@ -6,25 +6,31 @@ }: let cfg = config.clan.matrix-synapse; - nginx-vhost = "matrix.${config.clan.matrix-synapse.domain}"; element-web = pkgs.runCommand "element-web-with-config" { nativeBuildInputs = [ pkgs.buildPackages.jq ]; } '' cp -r ${pkgs.element-web} $out chmod -R u+w $out - jq '."default_server_config"."m.homeserver" = { "base_url": "https://${nginx-vhost}:443", "server_name": "${config.clan.matrix-synapse.domain}" }' \ + jq '."default_server_config"."m.homeserver" = { "base_url": "https://${cfg.domain.server}:443", "server_name": "${cfg.domain.server}" }' \ > $out/config.json < ${pkgs.element-web}/config.json - ln -s $out/config.json $out/config.${nginx-vhost}.json + ln -s $out/config.json $out/config.${cfg.domain.server}.json ''; in # FIXME: This was taken from upstream. Drop this when our patch is upstream { options.services.matrix-synapse.package = lib.mkOption { readOnly = false; }; options.clan.matrix-synapse = { - domain = lib.mkOption { - type = lib.types.str; - description = "The domain name of the matrix server"; - example = "example.com"; + domain = { + server = lib.mkOption { + type = lib.types.str; + description = "The domain name of the matrix server"; + example = "matrix.example.com"; + }; + client = lib.mkOption { + type = lib.types.str; + description = "The domain name of the matrix client"; + example = "element.example.com"; + }; }; users = lib.mkOption { default = { }; @@ -61,14 +67,14 @@ in "matrix-synapse" "enable" ] "Importing the module will already enable the service.") - - ../postgresql + ../nginx ]; config = { services.matrix-synapse = { enable = true; settings = { - server_name = cfg.domain; + server_name = cfg.domain.server; + enable_registration = false; database = { args.user = "matrix-synapse"; args.database = "matrix-synapse"; @@ -169,18 +175,13 @@ in ]; }; - networking.firewall.allowedTCPPorts = [ - 80 - 443 - ]; - services.nginx = { enable = true; virtualHosts = { - ${cfg.domain} = { + "${cfg.domain.server}" = { locations."= /.well-known/matrix/server".extraConfig = '' add_header Content-Type application/json; - return 200 '${builtins.toJSON { "m.server" = "matrix.${cfg.domain}:443"; }}'; + return 200 '${builtins.toJSON { "m.server" = "${cfg.domain.server}:443"; }}'; ''; locations."= /.well-known/matrix/client".extraConfig = '' add_header Content-Type application/json; @@ -188,7 +189,7 @@ in return 200 '${ builtins.toJSON { "m.homeserver" = { - "base_url" = "https://${nginx-vhost}"; + "base_url" = "https://${cfg.domain.server}"; }; "m.identity_server" = { "base_url" = "https://vector.im"; @@ -196,12 +197,14 @@ in } }'; ''; - }; - ${nginx-vhost} = { forceSSL = true; enableACME = true; locations."/_matrix".proxyPass = "http://localhost:8008"; locations."/_synapse".proxyPass = "http://localhost:8008"; + }; + "${cfg.domain.client}" = { + forceSSL = true; + enableACME = true; locations."/".root = element-web; }; }; diff --git a/clanModules/nginx/README.md b/clanModules/nginx/README.md new file mode 100644 index 000000000..4c8ca5a36 --- /dev/null +++ b/clanModules/nginx/README.md @@ -0,0 +1,3 @@ +--- +description = "Good defaults for the nginx webserver" +--- diff --git a/clanModules/nginx/default.nix b/clanModules/nginx/default.nix new file mode 100644 index 000000000..615ea89cc --- /dev/null +++ b/clanModules/nginx/default.nix @@ -0,0 +1,55 @@ +{ config, lib, ... }: +{ + + imports = [ + (lib.mkRemovedOptionModule [ + "clan" + "nginx" + "enable" + ] "Importing the module will already enable the service.") + + ]; + config = { + networking.firewall.allowedTCPPorts = [ + 443 + 80 + ]; + + services.nginx = { + enable = true; + + statusPage = lib.mkDefault true; + recommendedBrotliSettings = lib.mkDefault true; + recommendedGzipSettings = lib.mkDefault true; + recommendedOptimisation = lib.mkDefault true; + recommendedProxySettings = lib.mkDefault true; + recommendedTlsSettings = lib.mkDefault true; + recommendedZstdSettings = lib.mkDefault true; + + # Nginx sends all the access logs to /var/log/nginx/access.log by default. + # instead of going to the journal! + commonHttpConfig = "access_log syslog:server=unix:/dev/log;"; + + resolver.addresses = + let + isIPv6 = addr: builtins.match ".*:.*:.*" addr != null; + escapeIPv6 = addr: if isIPv6 addr then "[${addr}]" else addr; + cloudflare = [ + "1.1.1.1" + "2606:4700:4700::1111" + ]; + resolvers = + if config.networking.nameservers == [ ] then cloudflare else config.networking.nameservers; + in + map escapeIPv6 resolvers; + + sslDhparam = config.security.dhparams.params.nginx.path; + }; + + security.dhparams = { + enable = true; + params.nginx = { }; + }; + }; + +} From e4821a33cb5f0e939c495133ca7b9980f168da5a Mon Sep 17 00:00:00 2001 From: Qubasa Date: Mon, 19 Aug 2024 11:40:46 +0200 Subject: [PATCH 2/3] clanModules: Init vaultwarden, the bitwarden server --- checks/matrix-synapse/default.nix | 5 +- clanModules/dyndns/default.nix | 7 +- clanModules/flake-module.nix | 2 + clanModules/vaultwarden/README.md | 3 + clanModules/vaultwarden/default.nix | 164 ++++++++++++++++++++++++++++ 5 files changed, 174 insertions(+), 7 deletions(-) create mode 100644 clanModules/vaultwarden/README.md create mode 100644 clanModules/vaultwarden/default.nix diff --git a/checks/matrix-synapse/default.nix b/checks/matrix-synapse/default.nix index 5205abbf5..9bd39e84d 100644 --- a/checks/matrix-synapse/default.nix +++ b/checks/matrix-synapse/default.nix @@ -22,7 +22,10 @@ enableACME = lib.mkForce false; forceSSL = lib.mkForce false; }; - clan.matrix-synapse.domain = "clan.test"; + clan.matrix-synapse.domain = { + server = "clan.test"; + client = "element.clan.test"; + }; clan.matrix-synapse.users.admin.admin = true; clan.matrix-synapse.users.someuser = { }; diff --git a/clanModules/dyndns/default.nix b/clanModules/dyndns/default.nix index 3363b251c..7722005c6 100644 --- a/clanModules/dyndns/default.nix +++ b/clanModules/dyndns/default.nix @@ -143,6 +143,7 @@ in "dyndns" "enable" ] "Just define clan.dyndns.settings to enable it") + ../nginx ]; config = lib.mkMerge [ @@ -158,11 +159,6 @@ in createHome = true; }; - networking.firewall.allowedTCPPorts = lib.mkIf cfg.server.enable [ - 80 - 443 - ]; - services.nginx = lib.mkIf cfg.server.enable { enable = true; virtualHosts = { @@ -249,7 +245,6 @@ in PrivateDevices = "yes"; ProtectKernelModules = "yes"; ProtectKernelTunables = "yes"; - WorkingDirectory = "/var/lib/${name}"; ReadWritePaths = [ "/proc/self" diff --git a/clanModules/flake-module.nix b/clanModules/flake-module.nix index 60e1a32fc..d0f950eda 100644 --- a/clanModules/flake-module.nix +++ b/clanModules/flake-module.nix @@ -15,6 +15,7 @@ moonlight = ./moonlight; mumble = ./mumble; packages = ./packages; + nginx = ./nginx; postgresql = ./postgresql; root-password = ./root-password; single-disk = ./single-disk; @@ -26,6 +27,7 @@ thelounge = ./thelounge; trusted-nix-caches = ./trusted-nix-caches; user-password = ./user-password; + vaultwarden = ./vaultwarden; xfce = ./xfce; zerotier-static-peers = ./zerotier-static-peers; zt-tcp-relay = ./zt-tcp-relay; diff --git a/clanModules/vaultwarden/README.md b/clanModules/vaultwarden/README.md new file mode 100644 index 000000000..d2d0b76b8 --- /dev/null +++ b/clanModules/vaultwarden/README.md @@ -0,0 +1,3 @@ +--- +description = "The server for the password manager bitwarden" +--- diff --git a/clanModules/vaultwarden/default.nix b/clanModules/vaultwarden/default.nix new file mode 100644 index 000000000..7f110c8ec --- /dev/null +++ b/clanModules/vaultwarden/default.nix @@ -0,0 +1,164 @@ +{ + config, + lib, + pkgs, + ... +}: +let + cfg = config.clan.vaultwarden; + module_name = "vaultwarden"; + + admin_pwd_secret = "${module_name}-admin"; + admin_pwd_hash_secret = "${admin_pwd_secret}-hash"; + smtp_pwd_secret = "${module_name}-smtp"; + smtp_pwd_secret_path = + config.clan.core.facts.services."${smtp_pwd_secret}".secret."${smtp_pwd_secret}".path; + admin_secret_cfg_path = + config.clan.core.facts.services."${admin_pwd_secret}".secret."${admin_pwd_hash_secret}".path; +in + +{ + imports = [ + ../postgresql + (lib.mkRemovedOptionModule [ + "clan" + "vaultwarden" + "enable" + ] "Importing the module will already enable the service.") + ../nginx + ]; + + options.clan."${module_name}" = { + domain = lib.mkOption { + type = lib.types.str; + example = "bitwarden.example.com"; + description = "The domain to use for Vaultwarden"; + }; + port = lib.mkOption { + type = lib.types.int; + default = 3011; + description = "The port to use for Vaultwarden"; + }; + allow_signups = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Allow signups for new users"; + }; + + smtp = { + host = lib.mkOption { + type = lib.types.str; + example = "smtp.example.com"; + description = "The email server domain address"; + }; + from = lib.mkOption { + type = lib.types.str; + example = "foobar@example.com"; + description = "From whom the email is coming from"; + }; + username = lib.mkOption { + type = lib.types.str; + example = "foobar@example.com"; + description = "The email server username"; + }; + }; + }; + + config = { + + clan.postgresql.users.vaultwarden = { }; + clan.postgresql.databases.vaultwarden.create.options = { + TEMPLATE = "template0"; + LC_COLLATE = "C"; + LC_CTYPE = "C"; + ENCODING = "UTF8"; + OWNER = "vaultwarden"; + }; + clan.postgresql.databases.vaultwarden.restore.stopOnRestore = [ "vaultwarden" ]; + + services.nginx = { + enable = true; + virtualHosts = { + "${cfg.domain}" = { + forceSSL = true; + enableACME = true; + extraConfig = '' + client_max_body_size 128M; + ''; + locations."/" = { + proxyPass = "http://localhost:${builtins.toString cfg.port}"; + proxyWebsockets = true; + }; + locations."/notifications/hub" = { + proxyPass = "http://localhost:${builtins.toString cfg.port}"; + proxyWebsockets = true; + }; + locations."/notifications/hub/negotiate" = { + proxyPass = "http://localhost:${builtins.toString cfg.port}"; + proxyWebsockets = true; + }; + }; + }; + }; + + clan.core.facts.services = { + "${admin_pwd_secret}" = { + secret."${admin_pwd_secret}" = { }; + secret."${admin_pwd_hash_secret}" = { }; + generator.path = with pkgs; [ + coreutils + pwgen + libargon2 + openssl + ]; + generator.script = '' + ADMIN_PWD=$(pwgen 16 -n1 | tr -d "\n") + ADMIN_HASH=$(echo -n "$ADMIN_PWD" | argon2 "$(openssl rand -base64 32)" -e -id -k 65540 -t 3 -p 4) + + config=" + ADMIN_TOKEN=\"$ADMIN_HASH\" + " + echo -n "$ADMIN_PWD" > $secrets/${admin_pwd_secret} + echo -n "$config" > $secrets/${admin_pwd_hash_secret} + ''; + }; + "${smtp_pwd_secret}" = { + secret."${smtp_pwd_secret}" = { }; + generator.prompt = "${cfg.smtp.from} SMTP password"; + generator.path = with pkgs; [ coreutils ]; + generator.script = '' + config=" + SMTP_PASSWORD="$prompt_value" + " + echo -n "$config" > $secrets/${smtp_pwd_secret} + ''; + }; + }; + + systemd.services."${module_name}" = { + serviceConfig = { + EnvironmentFile = [ smtp_pwd_secret_path ]; + }; + }; + + services."${module_name}" = { + enable = true; + dbBackend = "postgresql"; + environmentFile = admin_secret_cfg_path; # TODO: Make this upstream an array + config = { + SMTP_SECURITY = "force_tls"; + SMTP_HOST = cfg.smtp.host; + SMTP_FROM = cfg.smtp.from; + SMTP_USERNAME = cfg.smtp.username; + DOMAIN = "https://${cfg.domain}"; + SIGNUPS_ALLOWED = cfg.allow_signups; + ROCKET_PORT = builtins.toString cfg.port; + DATABASE_URL = "postgresql://"; # TODO: This should be set upstream if dbBackend is set to postgresql + ENABLE_WEBSOCKET = true; + ROCKET_ADDRESS = "127.0.0.1"; + }; + }; + + }; + +} From 4bf31c2cf6e71691dd84dbf23466736accea562e Mon Sep 17 00:00:00 2001 From: Qubasa Date: Mon, 19 Aug 2024 12:53:15 +0200 Subject: [PATCH 3/3] clanModules: Add acme secret prompt for nginx --- checks/matrix-synapse/default.nix | 3 ++- clanModules/nginx/default.nix | 20 ++++++++++++++++++++ docs/mkdocs.yml | 2 ++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/checks/matrix-synapse/default.nix b/checks/matrix-synapse/default.nix index 9bd39e84d..d470963bb 100644 --- a/checks/matrix-synapse/default.nix +++ b/checks/matrix-synapse/default.nix @@ -22,8 +22,9 @@ enableACME = lib.mkForce false; forceSSL = lib.mkForce false; }; + security.acme.defaults.email = "admin@clan.test"; clan.matrix-synapse.domain = { - server = "clan.test"; + server = "matrix.clan.test"; client = "element.clan.test"; }; clan.matrix-synapse.users.admin.admin = true; diff --git a/clanModules/nginx/default.nix b/clanModules/nginx/default.nix index 615ea89cc..e0bb685cb 100644 --- a/clanModules/nginx/default.nix +++ b/clanModules/nginx/default.nix @@ -1,4 +1,7 @@ { config, lib, ... }: +let + nginx_acme_email = "nginx-acme"; +in { imports = [ @@ -10,6 +13,23 @@ ]; config = { + + clan.core.facts.services."${nginx_acme_email}" = { + public."${nginx_acme_email}" = { }; + generator.prompt = "Please enter your email address for Let's Encrypt certificate generation"; + + generator.script = '' + echo -n $prompt_value | tr -d "\n" > "$facts"/${nginx_acme_email} + ''; + }; + security.acme.acceptTerms = true; + security.acme.defaults.email = lib.mkDefault ( + let + path = config.clan.core.facts.services."${nginx_acme_email}".public."${nginx_acme_email}".path; + in + if builtins.pathExists path then builtins.readFile path else null + ); + networking.firewall.allowedTCPPorts = [ 443 80 diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 647e880e0..2fab52d1e 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -59,6 +59,8 @@ nav: - reference/clanModules/borgbackup.md - reference/clanModules/deltachat.md - reference/clanModules/dyndns.md + - reference/clanModules/nginx.md + - reference/clanModules/vaultwarden.md - reference/clanModules/ergochat.md - reference/clanModules/garage.md - reference/clanModules/golem-provider.md