diff --git a/changelog.d/17984.feature b/changelog.d/17984.feature new file mode 100644 index 0000000000..609f3a7cd8 --- /dev/null +++ b/changelog.d/17984.feature @@ -0,0 +1 @@ +Add `recaptcha_private_key_path` and `recaptcha_public_key_path` config option. \ No newline at end of file diff --git a/docs/usage/configuration/config_documentation.md b/docs/usage/configuration/config_documentation.md index 1fff68ed0a..1d48f8110e 100644 --- a/docs/usage/configuration/config_documentation.md +++ b/docs/usage/configuration/config_documentation.md @@ -2357,6 +2357,21 @@ Example configuration: recaptcha_public_key: YOUR_PUBLIC_KEY ``` --- +### `recaptcha_public_key_path` + +*(string|null)* An alternative to [`recaptcha_public_key`](#recaptcha_public_key): allows the public key to be specified in an external file. + +The file should be a plain text file, containing only the public key. Synapse reads the public key from the given file once at startup. + +_Added in Synapse 1.134.0._ + +Defaults to `null`. + +Example configuration: +```yaml +recaptcha_public_key_path: /path/to/key/file +``` +--- ### `recaptcha_private_key` *(string|null)* This homeserver's ReCAPTCHA private key. Must be specified if [`enable_registration_captcha`](#enable_registration_captcha) is enabled. Defaults to `null`. @@ -2366,6 +2381,21 @@ Example configuration: recaptcha_private_key: YOUR_PRIVATE_KEY ``` --- +### `recaptcha_private_key_path` + +*(string|null)* An alternative to [`recaptcha_private_key`](#recaptcha_private_key): allows the private key to be specified in an external file. + +The file should be a plain text file, containing only the private key. Synapse reads the private key from the given file once at startup. + +_Added in Synapse 1.134.0._ + +Defaults to `null`. + +Example configuration: +```yaml +recaptcha_private_key_path: /path/to/key/file +``` +--- ### `enable_registration_captcha` *(boolean)* Set to `true` to require users to complete a CAPTCHA test when registering an account. Requires a valid ReCaptcha public/private key. diff --git a/schema/synapse-config.schema.yaml b/schema/synapse-config.schema.yaml index 26f0e7f0bc..24fbf2419a 100644 --- a/schema/synapse-config.schema.yaml +++ b/schema/synapse-config.schema.yaml @@ -2692,6 +2692,21 @@ properties: default: null examples: - YOUR_PUBLIC_KEY + recaptcha_public_key_path: + type: ["string", "null"] + description: >- + An alternative to [`recaptcha_public_key`](#recaptcha_public_key): allows + the public key to be specified in an external file. + + + The file should be a plain text file, containing only the public key. + Synapse reads the public key from the given file once at startup. + + + _Added in Synapse 1.134.0._ + default: null + examples: + - /path/to/key/file recaptcha_private_key: type: ["string", "null"] description: >- @@ -2700,6 +2715,21 @@ properties: default: null examples: - YOUR_PRIVATE_KEY + recaptcha_private_key_path: + type: ["string", "null"] + description: >- + An alternative to [`recaptcha_private_key`](#recaptcha_private_key): + allows the private key to be specified in an external file. + + + The file should be a plain text file, containing only the private key. + Synapse reads the private key from the given file once at startup. + + + _Added in Synapse 1.134.0._ + default: null + examples: + - /path/to/key/file enable_registration_captcha: type: boolean description: >- diff --git a/synapse/config/captcha.py b/synapse/config/captcha.py index 57d67abbc3..ea1a5e0b32 100644 --- a/synapse/config/captcha.py +++ b/synapse/config/captcha.py @@ -23,7 +23,17 @@ from typing import Any from synapse.types import JsonDict -from ._base import Config, ConfigError +from ._base import Config, ConfigError, read_file + +CONFLICTING_RECAPTCHA_PRIVATE_KEY_OPTS_ERROR = """\ +You have configured both `recaptcha_private_key` and +`recaptcha_private_key_path`. These are mutually incompatible. +""" + +CONFLICTING_RECAPTCHA_PUBLIC_KEY_OPTS_ERROR = """\ +You have configured both `recaptcha_public_key` and `recaptcha_public_key_path`. +These are mutually incompatible. +""" class CaptchaConfig(Config): @@ -38,6 +48,13 @@ class CaptchaConfig(Config): "Config options that expect an in-line secret as value are disabled", ("recaptcha_private_key",), ) + recaptcha_private_key_path = config.get("recaptcha_private_key_path") + if recaptcha_private_key_path: + if recaptcha_private_key: + raise ConfigError(CONFLICTING_RECAPTCHA_PRIVATE_KEY_OPTS_ERROR) + recaptcha_private_key = read_file( + recaptcha_private_key_path, ("recaptcha_private_key_path",) + ).strip() if recaptcha_private_key is not None and not isinstance( recaptcha_private_key, str ): @@ -50,6 +67,13 @@ class CaptchaConfig(Config): "Config options that expect an in-line secret as value are disabled", ("recaptcha_public_key",), ) + recaptcha_public_key_path = config.get("recaptcha_public_key_path") + if recaptcha_public_key_path: + if recaptcha_public_key: + raise ConfigError(CONFLICTING_RECAPTCHA_PUBLIC_KEY_OPTS_ERROR) + recaptcha_public_key = read_file( + recaptcha_public_key_path, ("recaptcha_public_key_path",) + ).strip() if recaptcha_public_key is not None and not isinstance( recaptcha_public_key, str ): diff --git a/tests/config/test_load.py b/tests/config/test_load.py index a5456ac6f8..b72365b6e3 100644 --- a/tests/config/test_load.py +++ b/tests/config/test_load.py @@ -138,6 +138,8 @@ class ConfigLoadingFileTestCase(ConfigFileTestCase): "turn_shared_secret_path: /does/not/exist", "registration_shared_secret_path: /does/not/exist", "macaroon_secret_key_path: /does/not/exist", + "recaptcha_private_key_path: /does/not/exist", + "recaptcha_public_key_path: /does/not/exist", "form_secret_path: /does/not/exist", "worker_replication_secret_path: /does/not/exist", "experimental_features:\n msc3861:\n client_secret_path: /does/not/exist", @@ -167,6 +169,14 @@ class ConfigLoadingFileTestCase(ConfigFileTestCase): "macaroon_secret_key_path: {}", lambda c: c.key.macaroon_secret_key, ), + ( + "recaptcha_private_key_path: {}", + lambda c: c.captcha.recaptcha_private_key.encode("utf-8"), + ), + ( + "recaptcha_public_key_path: {}", + lambda c: c.captcha.recaptcha_public_key.encode("utf-8"), + ), ( "form_secret_path: {}", lambda c: c.key.form_secret.encode("utf-8"),