Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Commit

Permalink
Support registration_shared_secret in a file (#13614)
Browse files Browse the repository at this point in the history
A new `registration_shared_secret_path` option. This is kinda handy for k8s deployments and things.
  • Loading branch information
richvdh authored Aug 25, 2022
1 parent a2ce614 commit d092e6f
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 5 deletions.
1 change: 1 addition & 0 deletions changelog.d/13614.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Support setting the registration shared secret in a file, via a new `registration_shared_secret_path` configuration option.
18 changes: 18 additions & 0 deletions docs/usage/configuration/config_documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -2124,10 +2124,28 @@ registration_requires_token: true
If set, allows registration of standard or admin accounts by anyone who
has the shared secret, even if registration is otherwise disabled.

See also [`registration_shared_secret_path`](#registration_shared_secret_path).

Example configuration:
```yaml
registration_shared_secret: <PRIVATE STRING>
```

---
### `registration_shared_secret_path`

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

The file should be a plain text file, containing only the shared secret.

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

_Added in Synapse 1.67.0._

---
### `bcrypt_rounds`

Expand Down
45 changes: 42 additions & 3 deletions synapse/_scripts/register_new_matrix_user.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Copyright 2015, 2016 OpenMarket Ltd
# Copyright 2018 New Vector
# Copyright 2021 The Matrix.org Foundation C.I.C.
# Copyright 2021-22 The Matrix.org Foundation C.I.C.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand All @@ -25,6 +25,15 @@
import requests
import yaml

_CONFLICTING_SHARED_SECRET_OPTS_ERROR = """\
Conflicting options 'registration_shared_secret' and 'registration_shared_secret_path'
are both defined in config file.
"""

_NO_SHARED_SECRET_OPTS_ERROR = """\
No 'registration_shared_secret' or 'registration_shared_secret_path' defined in config.
"""

_DEFAULT_SERVER_URL = "http://localhost:8008"


Expand Down Expand Up @@ -222,9 +231,15 @@ def main() -> None:
# argparse should check that we have either config or shared secret
assert config

secret = config.get("registration_shared_secret", None)
secret = config.get("registration_shared_secret")
secret_file = config.get("registration_shared_secret_path")
if secret_file:
if secret:
print(_CONFLICTING_SHARED_SECRET_OPTS_ERROR, file=sys.stderr)
sys.exit(1)
secret = _read_file(secret_file, "registration_shared_secret_path").strip()
if not secret:
print("No 'registration_shared_secret' defined in config.")
print(_NO_SHARED_SECRET_OPTS_ERROR, file=sys.stderr)
sys.exit(1)

if args.server_url:
Expand Down Expand Up @@ -254,6 +269,30 @@ def main() -> None:
)


def _read_file(file_path: Any, config_path: str) -> str:
"""Check the given file exists, and read it into a string
If it does not, exit with an error indicating the problem
Args:
file_path: the file to be read
config_path: where in the configuration file_path came from, so that a useful
error can be emitted if it does not exist.
Returns:
content of the file.
"""
if not isinstance(file_path, str):
print(f"{config_path} setting is not a string", file=sys.stderr)
sys.exit(1)

try:
with open(file_path) as file_stream:
return file_stream.read()
except OSError as e:
print(f"Error accessing file {file_path}: {e}", file=sys.stderr)
sys.exit(1)


def _find_client_listener(config: Dict[str, Any]) -> Optional[str]:
# try to find a listener in the config. Returns a host:port pair
for listener in config.get("listeners", []):
Expand Down
33 changes: 31 additions & 2 deletions synapse/config/registration.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import argparse
from typing import Any, Optional
from typing import Any, Dict, Optional

from synapse.api.constants import RoomCreationPreset
from synapse.config._base import Config, ConfigError
from synapse.config._base import Config, ConfigError, read_file
from synapse.types import JsonDict, RoomAlias, UserID
from synapse.util.stringutils import random_string_with_symbols, strtobool

Expand All @@ -27,6 +27,11 @@
remove `account_threepid_delegates.email`.
"""

CONFLICTING_SHARED_SECRET_OPTS_ERROR = """\
You have configured both `registration_shared_secret` and
`registration_shared_secret_path`. These are mutually incompatible.
"""


class RegistrationConfig(Config):
section = "registration"
Expand All @@ -53,7 +58,16 @@ def read_config(self, config: JsonDict, **kwargs: Any) -> None:
self.enable_registration_token_3pid_bypass = config.get(
"enable_registration_token_3pid_bypass", False
)

# read the shared secret, either inline or from an external file
self.registration_shared_secret = config.get("registration_shared_secret")
registration_shared_secret_path = config.get("registration_shared_secret_path")
if registration_shared_secret_path:
if self.registration_shared_secret:
raise ConfigError(CONFLICTING_SHARED_SECRET_OPTS_ERROR)
self.registration_shared_secret = read_file(
registration_shared_secret_path, ("registration_shared_secret_path",)
).strip()

self.bcrypt_rounds = config.get("bcrypt_rounds", 12)

Expand Down Expand Up @@ -218,6 +232,21 @@ def generate_config_section(
else:
return ""

def generate_files(self, config: Dict[str, Any], config_dir_path: str) -> None:
# if 'registration_shared_secret_path' is specified, and the target file
# does not exist, generate it.
registration_shared_secret_path = config.get("registration_shared_secret_path")
if registration_shared_secret_path and not self.path_exists(
registration_shared_secret_path
):
print(
"Generating registration shared secret file "
+ registration_shared_secret_path
)
secret = random_string_with_symbols(50)
with open(registration_shared_secret_path, "w") as f:
f.write(f"{secret}\n")

@staticmethod
def add_arguments(parser: argparse.ArgumentParser) -> None:
reg_group = parser.add_argument_group("registration")
Expand Down

0 comments on commit d092e6f

Please sign in to comment.