mirror of
https://git.anonymousland.org/anonymousland/synapse.git
synced 2024-12-25 09:59:27 -05:00
Support registration_shared_secret
in a file (#13614)
A new `registration_shared_secret_path` option. This is kinda handy for k8s deployments and things.
This commit is contained in:
parent
a2ce614447
commit
d092e6f32a
1
changelog.d/13614.feature
Normal file
1
changelog.d/13614.feature
Normal file
@ -0,0 +1 @@
|
|||||||
|
Support setting the registration shared secret in a file, via a new `registration_shared_secret_path` configuration option.
|
@ -2124,10 +2124,28 @@ registration_requires_token: true
|
|||||||
If set, allows registration of standard or admin accounts by anyone who
|
If set, allows registration of standard or admin accounts by anyone who
|
||||||
has the shared secret, even if registration is otherwise disabled.
|
has the shared secret, even if registration is otherwise disabled.
|
||||||
|
|
||||||
|
See also [`registration_shared_secret_path`](#registration_shared_secret_path).
|
||||||
|
|
||||||
Example configuration:
|
Example configuration:
|
||||||
```yaml
|
```yaml
|
||||||
registration_shared_secret: <PRIVATE STRING>
|
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`
|
### `bcrypt_rounds`
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# Copyright 2015, 2016 OpenMarket Ltd
|
# Copyright 2015, 2016 OpenMarket Ltd
|
||||||
# Copyright 2018 New Vector
|
# 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");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
@ -25,6 +25,15 @@ from typing import Any, Callable, Dict, Optional
|
|||||||
import requests
|
import requests
|
||||||
import yaml
|
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"
|
_DEFAULT_SERVER_URL = "http://localhost:8008"
|
||||||
|
|
||||||
|
|
||||||
@ -222,9 +231,15 @@ def main() -> None:
|
|||||||
# argparse should check that we have either config or shared secret
|
# argparse should check that we have either config or shared secret
|
||||||
assert config
|
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:
|
if not secret:
|
||||||
print("No 'registration_shared_secret' defined in config.")
|
print(_NO_SHARED_SECRET_OPTS_ERROR, file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
if args.server_url:
|
if args.server_url:
|
||||||
@ -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]:
|
def _find_client_listener(config: Dict[str, Any]) -> Optional[str]:
|
||||||
# try to find a listener in the config. Returns a host:port pair
|
# try to find a listener in the config. Returns a host:port pair
|
||||||
for listener in config.get("listeners", []):
|
for listener in config.get("listeners", []):
|
||||||
|
@ -13,10 +13,10 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
import argparse
|
import argparse
|
||||||
from typing import Any, Optional
|
from typing import Any, Dict, Optional
|
||||||
|
|
||||||
from synapse.api.constants import RoomCreationPreset
|
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.types import JsonDict, RoomAlias, UserID
|
||||||
from synapse.util.stringutils import random_string_with_symbols, strtobool
|
from synapse.util.stringutils import random_string_with_symbols, strtobool
|
||||||
|
|
||||||
@ -27,6 +27,11 @@ password resets, configure Synapse with an SMTP server via the `email` setting,
|
|||||||
remove `account_threepid_delegates.email`.
|
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):
|
class RegistrationConfig(Config):
|
||||||
section = "registration"
|
section = "registration"
|
||||||
@ -53,7 +58,16 @@ class RegistrationConfig(Config):
|
|||||||
self.enable_registration_token_3pid_bypass = config.get(
|
self.enable_registration_token_3pid_bypass = config.get(
|
||||||
"enable_registration_token_3pid_bypass", False
|
"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")
|
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)
|
self.bcrypt_rounds = config.get("bcrypt_rounds", 12)
|
||||||
|
|
||||||
@ -218,6 +232,21 @@ class RegistrationConfig(Config):
|
|||||||
else:
|
else:
|
||||||
return ""
|
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
|
@staticmethod
|
||||||
def add_arguments(parser: argparse.ArgumentParser) -> None:
|
def add_arguments(parser: argparse.ArgumentParser) -> None:
|
||||||
reg_group = parser.add_argument_group("registration")
|
reg_group = parser.add_argument_group("registration")
|
||||||
|
Loading…
Reference in New Issue
Block a user