From deb0eb8dc4672f800fdf0606b0d276d33e0a20bb Mon Sep 17 00:00:00 2001 From: Jeremy Fleischman Date: Thu, 31 Aug 2023 22:15:46 -0700 Subject: [PATCH] mcaptcha package and module with nixos tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This completes https://github.com/ngi-nix/ngipkgs/issues/17 Co-authored-by: Shahar "Dawn" Or Co-authored-by: Rohit Co-authored-by: Matúš Ferech Co-authored-by: Alejandro Sanchez Medina --- all-packages.nix | 2 + modules/all-modules.nix | 1 + modules/mcaptcha.nix | 268 ++++++++++++++++++ pkgs/mcaptcha-cache/default.nix | 43 +++ pkgs/mcaptcha/default.nix | 180 ++++++++++++ pkgs/mcaptcha/no-build-script.patch | 12 + ...r-setting-cookie-secret-with-env-var.patch | 19 ++ tests/mcaptcha/default.nix | 103 +++++++ 8 files changed, 628 insertions(+) create mode 100644 modules/mcaptcha.nix create mode 100644 pkgs/mcaptcha-cache/default.nix create mode 100644 pkgs/mcaptcha/default.nix create mode 100644 pkgs/mcaptcha/no-build-script.patch create mode 100644 pkgs/mcaptcha/support-for-setting-cookie-secret-with-env-var.patch create mode 100644 tests/mcaptcha/default.nix diff --git a/all-packages.nix b/all-packages.nix index b1a4e8745..f1eafc0ab 100644 --- a/all-packages.nix +++ b/all-packages.nix @@ -19,6 +19,8 @@ libgnunetchat = callPackage ./pkgs/libgnunetchat {}; librecast = callPackage ./pkgs/librecast {inherit lcrq;}; + mcaptcha = callPackage ./pkgs/mcaptcha {}; + mcaptcha-cache = callPackage ./pkgs/mcaptcha-cache {}; pretalx = callPackage ./pkgs/pretalx {}; pretalx-frontend = callPackage ./pkgs/pretalx/frontend.nix {}; pretalx-full = callPackage ./pkgs/pretalx { diff --git a/modules/all-modules.nix b/modules/all-modules.nix index c97954e9e..743306539 100644 --- a/modules/all-modules.nix +++ b/modules/all-modules.nix @@ -3,6 +3,7 @@ # Refer to . #liberaforms = import ./liberaforms.nix; flarum = import ./flarum.nix; + mcaptcha = import ./mcaptcha.nix; pretalx = import ./pretalx.nix; rosenpass = import ./rosenpass.nix; unbootable = import ./unbootable.nix; diff --git a/modules/mcaptcha.nix b/modules/mcaptcha.nix new file mode 100644 index 000000000..64983d6b2 --- /dev/null +++ b/modules/mcaptcha.nix @@ -0,0 +1,268 @@ +{ + config, + lib, + options, + pkgs, + ... +}: +with builtins; +with lib; let + cfg = config.services.mcaptcha; + + # mCaptcha has no support for defaults. Every option must be specified. + # The module-provided defaults below are based on + # https://github.com/mCaptcha/mCaptcha/blob/f337ee0643d88723776e1de4e5588dfdb6c0c574/config/default.toml + settings = { + debug = false; + source_code = "https://github.com/mCaptcha/mCaptcha"; + commercial = false; + allow_demo = false; + allow_registration = true; + + server = { + port = cfg.server.port; + domain = cfg.server.host; + ip = cfg.server.bindAddress; + proxy_has_tls = false; + }; + + database = + { + pool = 4; + database_type = "postgres"; + } + // lib.optionalAttrs (!cfg.database.createLocally) { + username = cfg.database.user; + hostname = cfg.database.host; + port = cfg.database.port; + name = cfg.database.name; + }; + + captcha = { + gc = 30; + runners = 4; + queue_length = 2000; + enable_stats = true; + + default_difficulty_strategy = { + avg_traffic_difficulty = 50000; + peak_sustainable_traffic_difficulty = 3000000; + broke_my_site_traffic_difficulty = 5000000; + duration = 30; + }; + }; + + redis = { + pool = 4; + }; + }; + + configFile = (pkgs.formats.toml {}).generate "mcaptcha.config.toml" (lib.recursiveUpdate settings cfg.extraSettings); +in { + options.services.mcaptcha.enable = mkEnableOption "Enable mCaptcha server."; + options.services.mcaptcha.package = mkPackageOption pkgs "mcaptcha" {}; + + options.services.mcaptcha.extraSettings = mkOption { + type = types.attrs; + description = '' + Extra settings. Best sources of documentation for settings seem to be + https://github.com/mCaptcha/mCaptcha/blob/master/config/default.toml + https://github.com/mCaptcha/mCaptcha/blob/master/docs/CONFIGURATION.md + ''; + default = {}; + }; + + options.services.mcaptcha.user = mkOption { + type = types.str; + description = "User account to run under."; + default = "mcaptcha"; + }; + + options.services.mcaptcha.group = mkOption { + type = types.str; + description = "Group for the user mCaptcha runs under."; + default = "mcaptcha"; + }; + + options.services.mcaptcha.database.createLocally = mkOption { + type = types.bool; + description = "Whether to create and use a local databse instance"; + default = false; + }; + + options.services.mcaptcha.database.passwordFile = mkOption { + type = types.nullOr types.path; + description = '' + Path to a file containing a database password. + + Ignored when `database.createLocally`. + ''; + default = null; + example = "/run/secrets/mcaptcha/database"; + }; + + options.services.mcaptcha.database.name = mkOption { + type = types.str; + description = "Applies both when `database.createLocally` is set and not."; + default = "mcaptcha"; + }; + + options.services.mcaptcha.database.user = mkOption { + type = types.str; + description = "Ignored when `database.createLocally`."; + example = "mcaptcha"; + }; + + options.services.mcaptcha.database.host = mkOption { + type = types.str; + description = "Ignored when `database.createLocally`."; + example = "localhost"; + }; + + options.services.mcaptcha.database.port = mkOption { + type = types.int; + description = "Ignored when `database.createLocally`."; + example = 5432; + }; + + options.services.mcaptcha.server.cookieSecretFile = mkOption { + type = types.path; + description = "Path to a file containing a cookie secret."; + example = "/run/secrets/mcaptcha/cookie-secret"; + }; + + options.services.mcaptcha.captcha.saltFile = mkOption { + type = types.path; + description = "Path to a file containing a salt."; + example = "/run/secrets/mcaptcha/salt"; + }; + + options.services.mcaptcha.redis.createLocally = mkOption { + type = types.bool; + description = "Whether to create a Redis instance locally."; + default = false; + }; + + options.services.mcaptcha.redis.host = mkOption { + type = types.str; + description = "Ignored when `redis.createLocally`."; + example = "redis.example.com"; + }; + + options.services.mcaptcha.redis.port = mkOption { + type = types.int; + description = "Applies both when `redis.createLocally` is set and not."; + default = 6379; + }; + + options.services.mcaptcha.redis.user = mkOption { + type = types.str; + description = "Ignored when `redis.createLocally`."; + default = "default"; + example = "mcaptcha"; + }; + + options.services.mcaptcha.redis.passwordFile = mkOption { + type = types.path; + description = '' + Path to a file containing the Redis server password. + + Ignored when `redis.createLocally`."; + ''; + example = "/run/secrets/mcaptcha/redis-secret"; + }; + + options.services.mcaptcha.server.port = mkOption { + type = types.int; + description = "Web server port."; + default = 7000; + }; + + options.services.mcaptcha.server.host = mkOption { + type = types.str; + description = "Web server host."; + default = "localhost"; + example = "example.com"; + }; + + options.services.mcaptcha.server.bindAddress = mkOption { + type = types.str; + description = "Web server IP addresses to bind to."; + default = "127.0.0.1"; + example = "0.0.0.0"; + }; + + config = mkIf cfg.enable { + systemd.services.mcaptcha.description = "mCaptcha: a CAPTCHA system that gives attackers a run for their money"; + + systemd.services.mcaptcha.script = let + serverCookieSecret = "export MCAPTCHA_SERVER_COOKIE_SECRET=$(< ${cfg.server.cookieSecretFile})"; + captchaSalt = "export MCAPTCHA_CAPTCHA_SALT=$(< ${cfg.captcha.saltFile})"; + databaseLocalUrl = ''export DATABASE_URL="postgres:///${cfg.database.name}?host=/run/postgresql"''; + databasePassword = "export MCAPTCHA_DATABASE_PASSWORD=$(< ${cfg.database.passwordFile})"; + redisLocalUrl = ''export MCAPTCHA_REDIS_URL="redis://${cfg.redis.host}:${builtins.toString cfg.redis.port}"''; + redisRemoteUrl = '' + redis_user=$(${pkgs.urlencode}/bin/urlencode -e userinfo ${lib.escapeShellArg cfg.redis.user}) + redis_pass=$(${pkgs.urlencode}/bin/urlencode -e userinfo < ${cfg.redis.passwordFile}) + export MCAPTCHA_REDIS_URL="redis://$redis_user:$redis_pass@${cfg.redis.host}:${builtins.toString cfg.redis.port}" + ''; + exec = "exec ${cfg.package}/bin/mcaptcha"; + in + concatStringsSep "\n" [ + serverCookieSecret + captchaSalt + ( + if cfg.database.createLocally + then databaseLocalUrl + else databasePassword + ) + ( + if cfg.redis.createLocally + then redisLocalUrl + else redisRemoteUrl + ) + exec + ]; + + systemd.services.mcaptcha.environment.MCAPTCHA_CONFIG = builtins.toString configFile; + systemd.services.mcaptcha.after = ["syslog.target"] ++ lib.optionals cfg.database.createLocally ["postgresql.service"]; + systemd.services.mcaptcha.bindsTo = lib.optionals cfg.database.createLocally ["postgresql.service"]; + systemd.services.mcaptcha.wants = ["network-online.target"]; + systemd.services.mcaptcha.wantedBy = ["multi-user.target"]; + # Settings modeled after https://github.com/mCaptcha/mCaptcha/blob/f337ee0643d88723776e1de4e5588dfdb6c0c574/docs/DEPLOYMENT.md#6-systemd-service-configuration + systemd.services.mcaptcha.serviceConfig.User = cfg.user; + systemd.services.mcaptcha.serviceConfig.Type = "simple"; + systemd.services.mcaptcha.serviceConfig.Restart = "on-failure"; + systemd.services.mcaptcha.serviceConfig.RestartSec = 1; + systemd.services.mcaptcha.serviceConfig.SuccessExitStatus = "3 4"; + systemd.services.mcaptcha.serviceConfig.RestartForceExitStatus = "3 4"; + systemd.services.mcaptcha.serviceConfig.SystemCallArchitectures = "native"; + systemd.services.mcaptcha.serviceConfig.MemoryDenyWriteExecute = true; + systemd.services.mcaptcha.serviceConfig.NoNewPrivileges = true; + + users.users."${cfg.user}" = { + isSystemUser = true; + group = cfg.group; + }; + + users.groups."${cfg.group}" = {}; + + services.postgresql = lib.mkIf cfg.database.createLocally { + enable = true; + ensureDatabases = [cfg.database.name]; + ensureUsers = [ + { + name = cfg.user; + ensurePermissions = {"DATABASE ${cfg.database.name}" = "ALL PRIVILEGES";}; + } + ]; + }; + + services.redis.servers.mcaptcha = lib.mkIf cfg.redis.createLocally { + enable = true; + port = cfg.redis.port; + extraParams = ["--loadmodule" "${pkgs.mcaptcha-cache}/lib/libcache.so"]; + }; + services.mcaptcha.redis.host = lib.mkIf cfg.redis.createLocally "127.0.0.1"; + }; +} diff --git a/pkgs/mcaptcha-cache/default.nix b/pkgs/mcaptcha-cache/default.nix new file mode 100644 index 000000000..67124e3f8 --- /dev/null +++ b/pkgs/mcaptcha-cache/default.nix @@ -0,0 +1,43 @@ +{ + rustPlatform, + fetchFromGitHub, + lib, +}: let + src = fetchFromGitHub { + owner = "mCaptcha"; + repo = "cache"; + rev = "67d6c701baa804849abc53a78422a6da01358487"; + # NOTE: Avoiding this typo fix (which caused a bug in libmcaptcha) + # https://github.com/mCaptcha/cache/commit/f30bc54e6374cf5fad07af8f3d38bbe5fbbb4b20 + # until this is merged https://github.com/mCaptcha/libmcaptcha/pull/12 + sha256 = "sha256-whRLgYkoBoVQiZwrmwBwqgHzPqqXC6g3na3YrH4/xVo="; + }; +in + rustPlatform.buildRustPackage rec { + inherit src; + pname = "cache"; + version = "unstable-2023-03-08"; + + cargoLock = { + lockFile = src + "/Cargo.lock"; + outputHashes = { + "libmcaptcha-0.1.4" = "sha256-KwFT0Px5ZQGa26fjkiaT8lKc8ASVdfL/67E0hnaHl7I="; + }; + }; + + nativeBuildInputs = [rustPlatform.bindgenHook]; + + # We are unable to figure out the following error 🤷 + # + # ``` + # warning: `mcaptcha-cache` (lib test) generated 4 warnings + # Finished test [unoptimized + debuginfo] target(s) in 0.03s + # Running unittests src/lib.rs (target/debug/deps/cache-e9c2ad24991bfc21) + # thread panicked while processing panic. aborting. + # error: test failed, to rerun pass `--lib` + # + # Caused by: + # process didn't exit successfully: `/mcaptcha-cache/target/debug/deps/cache-e9c2ad24991bfc21` (signal: 6, SIGABRT: process abort signal) + # ``` + doCheck = false; + } diff --git a/pkgs/mcaptcha/default.nix b/pkgs/mcaptcha/default.nix new file mode 100644 index 000000000..deda63be0 --- /dev/null +++ b/pkgs/mcaptcha/default.nix @@ -0,0 +1,180 @@ +{ + mkYarnPackage, + fetchYarnDeps, + fetchFromGitHub, + rustPlatform, + lib, + openssl, + pkg-config, + perl, + nixosTests, +}: let + rootSrc = fetchFromGitHub { + # TODO: switch owner to 'mCaptcha' after this PR is merged: + # https://github.com/mCaptcha/libmcaptcha/pull/13 + owner = "ngi-nix"; + repo = "mCaptcha"; + rev = "ngipkgs-patches"; + sha256 = "sha256-+0SK18F+YgHrYEwKlvySJ0p0Nf99CPloUKV3l2j9u7M="; + }; + + releaseDate = "2023-07-04"; + + frontend = let + src = rootSrc; + in + mkYarnPackage { + inherit src; + + offlineCache = fetchYarnDeps { + yarnLock = src + "/yarn.lock"; + sha256 = "sha256-GyWjQdFJ+hEuR4PebhYzFwiuyMyamRY5GPaJ7rK9Rsc="; + }; + + buildPhase = '' + export HOME=$(mktemp -d) + yarn --offline build + ''; + + doCheck = true; + checkPhase = '' + CI=true yarn test + ''; + + # Copied from `make frontend` in the mCaptcha Makefile: + # https://github.com/mCaptcha/mCaptcha/blob/f337ee0643d88723776e1de4e5588dfdb6c0c574/Makefile#L130-L147 + installPhase = '' + runHook preInstall + + yarn run sass -s \ + compressed templates/main.scss \ + ./static/cache/bundle/css/main.css + yarn run sass -s \ + compressed templates/mobile.scss \ + ./static/cache/bundle/css/mobile.css + yarn run sass -s \ + compressed templates/widget/main.scss \ + ./static/cache/bundle/css/widget.css + + patchShebangs deps/vanilla/scripts + deps/vanilla/scripts/librejs.sh + deps/vanilla/scripts/cachebust.sh + + mv deps/vanilla/static/cache/bundle $out + + runHook postInstall + ''; + + # Note that "true" disables the dist phase, as this is all handled in + # installPhase above. + distPhase = "true"; + }; + + openapi = let + src = rootSrc + "/docs/openapi"; + in + mkYarnPackage { + inherit src; + + offlineCache = fetchYarnDeps { + yarnLock = src + "/yarn.lock"; + sha256 = "sha256-mdd5AwO4WO/RoR/ycR+miJvRXgu1K6WXvMZtyyV/0Tc="; + }; + + buildPhase = '' + export HOME=$(mktemp -d) + yarn --offline build + ''; + + installPhase = '' + runHook preInstall + mv deps/mcaptcha/dist $out + runHook postInstall + ''; + + # Note that "true" disables the dist phase, as this is all handled in + # installPhase above. + distPhase = "true"; + }; + + cache-bust = let + src = rootSrc + "/utils/cache-bust"; + in + rustPlatform.buildRustPackage { + inherit src; + pname = "cache-bust"; + version = "unstable-${releaseDate}"; + + cargoLock = { + lockFile = src + "/Cargo.lock"; + outputHashes = { + "cache-buster-0.2.0" = "sha256-FT+GV2c+jZyVyC2pEtDm52Aurbf6tOZeBqzHq3NZptw="; + }; + }; + }; + + mcaptcha = let + src = rootSrc; + in + rustPlatform.buildRustPackage { + inherit src; + pname = "mcaptcha"; + version = "unstable-${releaseDate}"; + + cargoLock = { + lockFile = src + "/Cargo.lock"; + outputHashes = { + "actix-auth-middleware-0.2.0" = "sha256-sLd2Fsa02bXE+CTzTcByTF2PAnzn5YEYGekCmw+AG4E="; + "actix-web-codegen-4.0.0" = "sha256-2MKgeCa9C5WL0TtvQSTvz2YMBBgzn7tnkFL7c7KJFSs="; + "argon2-creds-0.2.2" = "sha256-A5xkcVvi+xfdQ0vBdqJgtlIbiNmOz6weSB3ho6kAz+A="; + "cache-buster-0.2.0" = "sha256-FT+GV2c+jZyVyC2pEtDm52Aurbf6tOZeBqzHq3NZptw="; + "libmcaptcha-0.2.3" = "sha256-oedAXrasZ3YAj6PiscdasAQ0RlG9YcFAuFRtxicmkhY="; + "pow_sha256-0.3.1" = "sha256-gprbSYL0tjKQlQe/lJwFZM0avSQT92nAwUnr61t0X0g="; + }; + }; + + patches = [ + # https://github.com/mCaptcha/mCaptcha/issues/105 + ./support-for-setting-cookie-secret-with-env-var.patch + # build.rs does some impure stuff with git to inject a commit hash and + # a compilation date. That isn't nix-compatible, so we remove it, and + # simulate its effects (see GIT_HASH and COMPILED_DATE below). + ./no-build-script.patch + ]; + + # Remove the build.rs mentioned in no-build-script.patch above. (This is + # just less likely to run into future conflicts vs if we put it in the + # patch file). + postPatch = '' + rm build.rs + ''; + + # Setting these variables to simulate the behavior of the (impure) + # build.rs script we've removed. + GIT_HASH = src.rev; + COMPILED_DATE = releaseDate; + + preBuild = '' + ln -s ${openapi} docs/openapi/dist + ln -s ${frontend} static/cache/bundle + (cd utils/cache-bust && ${cache-bust}/bin/cache-bust) + ''; + + # Most of the tests are database integration tests + doCheck = false; + + # Get openssl-sys to use pkg-config + OPENSSL_NO_VENDOR = 1; + + nativeBuildInputs = [pkg-config perl]; + + buildInputs = [openssl]; + + meta = { + license = lib.licenses.agpl3Plus; + }; + + passthru.tests.mcaptcha = nixosTests.mcaptcha; + }; +in + mcaptcha diff --git a/pkgs/mcaptcha/no-build-script.patch b/pkgs/mcaptcha/no-build-script.patch new file mode 100644 index 000000000..9d969edda --- /dev/null +++ b/pkgs/mcaptcha/no-build-script.patch @@ -0,0 +1,12 @@ +diff --git a/Cargo.toml b/Cargo.toml +index 25058093..a7d13eb7 100644 +--- a/Cargo.toml ++++ b/Cargo.toml +@@ -9,7 +9,6 @@ license = "AGPLv3 or later version" + authors = ["realaravinth "] + edition = "2021" + default-run = "mcaptcha" +-build = "build.rs" + + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + diff --git a/pkgs/mcaptcha/support-for-setting-cookie-secret-with-env-var.patch b/pkgs/mcaptcha/support-for-setting-cookie-secret-with-env-var.patch new file mode 100644 index 000000000..2addcb94e --- /dev/null +++ b/pkgs/mcaptcha/support-for-setting-cookie-secret-with-env-var.patch @@ -0,0 +1,19 @@ +diff --git a/src/settings.rs b/src/settings.rs +index e64b8a67..a2ab0e0f 100644 +--- a/src/settings.rs ++++ b/src/settings.rs +@@ -164,6 +164,14 @@ impl Settings { + + s.merge(Environment::with_prefix("MCAPTCHA").separator("_"))?; + ++ // Incomplete workaround for https://github.com/mCaptcha/mCaptcha/issues/105. There are ++ // other settings that still don't work, but this is the only one we need in order to securely ++ // package mCaptcha on NixOS. ++ if let Ok(val) = env::var("MCAPTCHA_SERVER_COOKIE_SECRET") { ++ s.set("server.cookie_secret", val).unwrap(); ++ log::info!("Overriding [server].cookie_secret with environment variable"); ++ } ++ + check_url(&s); + + if let Ok(val) = env::var("PORT") { diff --git a/tests/mcaptcha/default.nix b/tests/mcaptcha/default.nix new file mode 100644 index 000000000..3f98812c2 --- /dev/null +++ b/tests/mcaptcha/default.nix @@ -0,0 +1,103 @@ +{modules, ...}: let + port = 7000; + urlRoot = "http://localhost:${builtins.toString port}"; +in { + name = "mcaptcha tests"; + + nodes.create_locally = {pkgs, ...}: { + imports = [ + modules.default + modules.mcaptcha + ]; + + services.mcaptcha.enable = true; + services.mcaptcha.server.port = port; + services.mcaptcha.server.cookieSecretFile = pkgs.writeText "cookie-secret" "mcaptcha-cookie-secret-dm0tdGVzdC1ydW4tbWNhcHRjaGEtdGVzdHM"; + services.mcaptcha.captcha.saltFile = pkgs.writeText "salt" "asdl;kjfhjawehfpa;osdkjasdvjaksndfpoanjdfainsdfaijdsfajlkjdsaf;ajsdfweroire"; + services.mcaptcha.database.createLocally = true; + services.mcaptcha.redis.createLocally = true; + + # the following two lines are for debugging + networking.firewall.enable = false; + services.mcaptcha.server.bindAddress = "0.0.0.0"; + }; + + nodes.bring_your_own_services = { + pkgs, + config, + ... + }: { + imports = [ + modules.default + modules.mcaptcha + ]; + + services.mcaptcha.enable = true; + services.mcaptcha.server.port = port; + services.mcaptcha.server.host = "localhost"; + services.mcaptcha.server.cookieSecretFile = pkgs.writeText "cookie-secret" "mcaptcha-cookie-secret-dm0tdGVzdC1ydW4tbWNhcHRjaGEtdGVzdHM"; + services.mcaptcha.captcha.saltFile = pkgs.writeText "salt" "asdl;kjfhjawehfpa;osdkjasdvjaksndfpoanjdfainsdfaijdsfajlkjdsaf;ajsdfweroire"; + + services.mcaptcha.database.name = "my_mcaptcha"; + services.mcaptcha.database.user = "role_mcaptcha"; + services.mcaptcha.database.host = "my_own_services"; + services.mcaptcha.database.port = 5432; + services.mcaptcha.database.passwordFile = pkgs.writeText "db-password" "mcaptcha-db-secret"; + + services.mcaptcha.redis.passwordFile = pkgs.writeText "redis-secret" "(*&(*):ps@r}"; + services.mcaptcha.redis.host = "my_own_services"; + }; + + nodes.my_own_services = {pkgs, ...}: { + imports = [ + modules.default + ]; + networking.firewall.enable = false; + services.postgresql.enable = true; + services.postgresql.enableTCPIP = true; + services.postgresql.initialScript = pkgs.writeText "postgresql-init-script" '' + CREATE ROLE role_mcaptcha WITH LOGIN PASSWORD 'mcaptcha-db-secret'; + CREATE DATABASE my_mcaptcha; + GRANT ALL PRIVILEGES ON DATABASE my_mcaptcha TO role_mcaptcha; + ''; + services.postgresql.authentication = '' + #type database DBuser auth-method + host all all 0.0.0.0/0 md5 + ''; + services.redis.servers.mcaptcha.enable = true; + services.redis.servers.mcaptcha.port = 6379; + services.redis.servers.mcaptcha.bind = null; + services.redis.servers.mcaptcha.extraParams = [ + "--loadmodule" + "${pkgs.mcaptcha-cache}/lib/libcache.so" + ]; + services.redis.servers.mcaptcha.requirePass = "(*&(*):ps@r}"; + }; + + testScript = {nodes, ...}: '' + import json + + def check_mcaptcha_node(node): + node.start() + + node.wait_for_unit("mcaptcha.service") + node.wait_until_succeeds("curl --fail --connect-timeout 2 ${urlRoot}", timeout=60) + + node.succeed("curl --fail --connect-timeout 10 ${urlRoot}/widget") + + json_str = node.succeed("curl --fail --connect-timeout 10 ${urlRoot}/api/v1/meta/health") + health_data = json.loads(json_str) + assert health_data == {'db': True, 'redis': True} + + + with subtest("mcaptcha.create_locally"): + check_mcaptcha_node(create_locally) + + my_own_services.start() + my_own_services.wait_for_unit("redis-mcaptcha.service") + my_own_services.wait_for_unit("postgresql.service") + + with subtest("mcaptcha.bring_your_own_services"): + check_mcaptcha_node(bring_your_own_services) + ''; +}