Add recaptcha_{private,public}_key_path config option (#17984)

Another config option on my quest to a `*_path` variant for every
secret. Adds the config options `recaptcha_private_key_path` and
`recaptcha_public_key_path`. Tests and docs are included.

A public key is of course no secret, but it is closely related to the
private key, so it’s still useful to have a `*_path` variant for it.
This commit is contained in:
V02460
2025-07-14 18:37:36 +02:00
committed by GitHub
parent 5129668449
commit 481c4e2b55
5 changed files with 96 additions and 1 deletions

View File

@@ -0,0 +1 @@
Add `recaptcha_private_key_path` and `recaptcha_public_key_path` config option.

View File

@@ -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.

View File

@@ -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: >-

View File

@@ -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
):

View File

@@ -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"),