Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add macaroon_secret_key_path config option #17983

Merged
merged 5 commits into from
Dec 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/17983.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add `macaroon_secret_key_path` config option.
16 changes: 16 additions & 0 deletions docs/usage/configuration/config_documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -3091,6 +3091,22 @@ Example configuration:
```yaml
macaroon_secret_key: <PRIVATE STRING>
```
---
### `macaroon_secret_key_path`

An alternative to [`macaroon_secret_key`](#macaroon_secret_key):
allows the secret key to be specified in an external file.

The file should be a plain text file, containing only the secret key.
Synapse reads the secret key from the given file once at startup.

Example configuration:
```yaml
macaroon_secret_key_path: /path/to/secrets/file
```

_Added in Synapse 1.121.0._

---
### `form_secret`

Expand Down
21 changes: 16 additions & 5 deletions synapse/config/key.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
from synapse.types import JsonDict
from synapse.util.stringutils import random_string, random_string_with_symbols

from ._base import Config, ConfigError
from ._base import Config, ConfigError, read_file

if TYPE_CHECKING:
from signedjson.key import VerifyKeyWithExpiry
Expand Down Expand Up @@ -91,6 +91,11 @@
'suppress_key_server_warning' to 'true' in homeserver.yaml.
--------------------------------------------------------------------------------"""

CONFLICTING_MACAROON_SECRET_KEY_OPTS_ERROR = """\
Conflicting options 'macaroon_secret_key' and 'macaroon_secret_key_path' are
both defined in config file.
"""

logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -166,10 +171,16 @@ def read_config(
)
)

macaroon_secret_key: Optional[str] = config.get(
"macaroon_secret_key", self.root.registration.registration_shared_secret
)

macaroon_secret_key = config.get("macaroon_secret_key")
macaroon_secret_key_path = config.get("macaroon_secret_key_path")
if macaroon_secret_key_path:
if macaroon_secret_key:
raise ConfigError(CONFLICTING_MACAROON_SECRET_KEY_OPTS_ERROR)
macaroon_secret_key = read_file(
macaroon_secret_key_path, "macaroon_secret_key_path"
).strip()
if not macaroon_secret_key:
macaroon_secret_key = self.root.registration.registration_shared_secret
if not macaroon_secret_key:
# Unfortunately, there are people out there that don't have this
# set. Lets just be "nice" and derive one from their secret key.
Expand Down
23 changes: 15 additions & 8 deletions tests/config/test_load.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@

class ConfigLoadingFileTestCase(ConfigFileTestCase):
def test_load_fails_if_server_name_missing(self) -> None:
self.generate_config_and_remove_lines_containing("server_name")
self.generate_config_and_remove_lines_containing(["server_name"])
with self.assertRaises(ConfigError):
HomeServerConfig.load_config("", ["-c", self.config_file])
with self.assertRaises(ConfigError):
Expand Down Expand Up @@ -76,7 +76,7 @@ def test_generates_and_loads_macaroon_secret_key(self) -> None:
)

def test_load_succeeds_if_macaroon_secret_key_missing(self) -> None:
self.generate_config_and_remove_lines_containing("macaroon")
self.generate_config_and_remove_lines_containing(["macaroon"])
config1 = HomeServerConfig.load_config("", ["-c", self.config_file])
config2 = HomeServerConfig.load_config("", ["-c", self.config_file])
config3 = HomeServerConfig.load_or_generate_config("", ["-c", self.config_file])
Expand Down Expand Up @@ -111,7 +111,7 @@ def test_disable_registration(self) -> None:
self.assertTrue(config3.registration.enable_registration)

def test_stats_enabled(self) -> None:
self.generate_config_and_remove_lines_containing("enable_metrics")
self.generate_config_and_remove_lines_containing(["enable_metrics"])
self.add_lines_to_config(["enable_metrics: true"])

# The default Metrics Flags are off by default.
Expand All @@ -131,6 +131,7 @@ def test_depreciated_identity_server_flag_throws_error(self) -> None:
[
"turn_shared_secret_path: /does/not/exist",
"registration_shared_secret_path: /does/not/exist",
"macaroon_secret_key_path: /does/not/exist",
*["redis:\n enabled: true\n password_path: /does/not/exist"]
* (hiredis is not None),
]
Expand All @@ -146,16 +147,20 @@ def test_secret_files_missing(self, config_str: str) -> None:
[
(
"turn_shared_secret_path: {}",
lambda c: c.voip.turn_shared_secret,
lambda c: c.voip.turn_shared_secret.encode("utf-8"),
),
(
"registration_shared_secret_path: {}",
lambda c: c.registration.registration_shared_secret,
lambda c: c.registration.registration_shared_secret.encode("utf-8"),
),
(
"macaroon_secret_key_path: {}",
lambda c: c.key.macaroon_secret_key,
),
*[
(
"redis:\n enabled: true\n password_path: {}",
lambda c: c.redis.redis_password,
lambda c: c.redis.redis_password.encode("utf-8"),
)
]
* (hiredis is not None),
Expand All @@ -164,11 +169,13 @@ def test_secret_files_missing(self, config_str: str) -> None:
def test_secret_files_existing(
self, config_line: str, get_secret: Callable[[RootConfig], str]
) -> None:
self.generate_config_and_remove_lines_containing("registration_shared_secret")
self.generate_config_and_remove_lines_containing(
["registration_shared_secret", "macaroon_secret_key"]
)
with tempfile.NamedTemporaryFile(buffering=0) as secret_file:
secret_file.write(b"53C237")

self.add_lines_to_config(["", config_line.format(secret_file.name)])
config = HomeServerConfig.load_config("", ["-c", self.config_file])

self.assertEqual(get_secret(config), "53C237")
self.assertEqual(get_secret(config), b"53C237")
5 changes: 3 additions & 2 deletions tests/config/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,13 @@ def generate_config(self) -> None:
],
)

def generate_config_and_remove_lines_containing(self, needle: str) -> None:
def generate_config_and_remove_lines_containing(self, needles: list[str]) -> None:
self.generate_config()

with open(self.config_file) as f:
contents = f.readlines()
contents = [line for line in contents if needle not in line]
for needle in needles:
contents = [line for line in contents if needle not in line]
with open(self.config_file, "w") as f:
f.write("".join(contents))

Expand Down
Loading