From c360da0f8b642d28601a06f2b5c3e072a5dbfca7 Mon Sep 17 00:00:00 2001
From: V02460 <git@kaialexhiller.de>
Date: Wed, 26 Feb 2025 16:55:10 +0100
Subject: [PATCH] Add worker_replication_secret_path config option (#18191)

Workers now get their secrets from files, too! There are not many config
options left to pathify :) Includes documentation and unit tests.

### Pull Request Checklist

<!-- Please read
https://element-hq.github.io/synapse/latest/development/contributing_guide.html
before submitting your pull request -->

* [x] Pull request is based on the develop branch
* [x] Pull request includes a [changelog
file](https://element-hq.github.io/synapse/latest/development/contributing_guide.html#changelog).
The entry should:
- Be a short description of your change which makes sense to users.
"Fixed a bug that prevented receiving messages from other servers."
instead of "Moved X method from `EventStore` to `EventWorkerStore`.".
  - Use markdown where necessary, mostly for `code blocks`.
  - End with either a period (.) or an exclamation mark (!).
  - Start with a capital letter.
- Feel free to credit yourself, by adding a sentence "Contributed by
@github_username." or "Contributed by [Your Name]." to the end of the
entry.
* [x] [Code
style](https://element-hq.github.io/synapse/latest/code_style.html) is
correct
(run the
[linters](https://element-hq.github.io/synapse/latest/development/contributing_guide.html#run-the-linters))

---------

Co-authored-by: Devon Hudson <devon.dmytro@gmail.com>
---
 changelog.d/18191.feature                     |  1 +
 .../configuration/config_documentation.md     | 18 +++++++++++++++-
 synapse/config/workers.py                     | 21 +++++++++++++++++--
 tests/config/test_load.py                     |  5 +++++
 4 files changed, 42 insertions(+), 3 deletions(-)
 create mode 100644 changelog.d/18191.feature

diff --git a/changelog.d/18191.feature b/changelog.d/18191.feature
new file mode 100644
index 0000000000..f47c9e2275
--- /dev/null
+++ b/changelog.d/18191.feature
@@ -0,0 +1 @@
+Add `worker_replication_secret_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 f5a5aa2eb4..ffee089304 100644
--- a/docs/usage/configuration/config_documentation.md
+++ b/docs/usage/configuration/config_documentation.md
@@ -3252,7 +3252,7 @@ Example configuration:
 form_secret_path: /path/to/secrets/file
 ```
 
-_Added in Synapse 1.125.0._
+_Added in Synapse 1.126.0._
 
 ---
 ## Signing Keys
@@ -4527,6 +4527,22 @@ Example configuration:
 ```yaml
 worker_replication_secret: "secret_secret"
 ```
+---
+### `worker_replication_secret_path`
+
+An alternative to [`worker_replication_secret`](#worker_replication_secret):
+allows the secret to be specified in an external file.
+
+The file should be a plain text file, containing only the secret.
+Synapse reads the secret from the given file once at startup.
+
+Example configuration:
+```yaml
+worker_replication_secret_path: /path/to/secrets/file
+```
+
+_Added in Synapse 1.126.0._
+
 ---
 ### `start_pushers`
 
diff --git a/synapse/config/workers.py b/synapse/config/workers.py
index 632cef46ce..5af50ee952 100644
--- a/synapse/config/workers.py
+++ b/synapse/config/workers.py
@@ -38,6 +38,7 @@ from synapse.config._base import (
     ConfigError,
     RoutableShardedWorkerHandlingConfig,
     ShardedWorkerHandlingConfig,
+    read_file,
 )
 from synapse.config._util import parse_and_validate_mapping
 from synapse.config.server import (
@@ -65,6 +66,11 @@ configuration under `main` inside the `instance_map`. See workers documentation
 `https://element-hq.github.io/synapse/latest/workers.html#worker-configuration`
 """
 
+CONFLICTING_WORKER_REPLICATION_SECRET_OPTS_ERROR = """\
+Conflicting options 'worker_replication_secret' and
+'worker_replication_secret_path' are both defined in config file.
+"""
+
 # This allows for a handy knob when it's time to change from 'master' to
 # something with less 'history'
 MAIN_PROCESS_INSTANCE_NAME = "master"
@@ -244,12 +250,23 @@ class WorkerConfig(Config):
             raise ConfigError(DIRECT_TCP_ERROR, ("worker_replication_port",))
 
         # The shared secret used for authentication when connecting to the main synapse.
-        self.worker_replication_secret = config.get("worker_replication_secret", None)
-        if self.worker_replication_secret and not allow_secrets_in_config:
+        worker_replication_secret = config.get("worker_replication_secret", None)
+        if worker_replication_secret and not allow_secrets_in_config:
             raise ConfigError(
                 "Config options that expect an in-line secret as value are disabled",
                 ("worker_replication_secret",),
             )
+        worker_replication_secret_path = config.get(
+            "worker_replication_secret_path", None
+        )
+        if worker_replication_secret_path:
+            if worker_replication_secret:
+                raise ConfigError(CONFLICTING_WORKER_REPLICATION_SECRET_OPTS_ERROR)
+            self.worker_replication_secret = read_file(
+                worker_replication_secret_path, "worker_replication_secret_path"
+            ).strip()
+        else:
+            self.worker_replication_secret = worker_replication_secret
 
         self.worker_name = config.get("worker_name", self.worker_app)
         self.instance_name = self.worker_name or MAIN_PROCESS_INSTANCE_NAME
diff --git a/tests/config/test_load.py b/tests/config/test_load.py
index 14dfa6e59d..a5456ac6f8 100644
--- a/tests/config/test_load.py
+++ b/tests/config/test_load.py
@@ -139,6 +139,7 @@ class ConfigLoadingFileTestCase(ConfigFileTestCase):
             "registration_shared_secret_path: /does/not/exist",
             "macaroon_secret_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",
             "experimental_features:\n  msc3861:\n    admin_token_path: /does/not/exist",
             *["redis:\n  enabled: true\n  password_path: /does/not/exist"]
@@ -170,6 +171,10 @@ class ConfigLoadingFileTestCase(ConfigFileTestCase):
                 "form_secret_path: {}",
                 lambda c: c.key.form_secret.encode("utf-8"),
             ),
+            (
+                "worker_replication_secret_path: {}",
+                lambda c: c.worker.worker_replication_secret.encode("utf-8"),
+            ),
             (
                 "experimental_features:\n  msc3861:\n    client_secret_path: {}",
                 lambda c: c.experimental.msc3861.client_secret().encode("utf-8"),
-- 
GitLab