mirror of
https://github.com/element-hq/synapse
synced 2024-08-21 10:10:20 +00:00
Merge pull request #136 from matrix-org/markjh/config_cleanup
Config restructuring.
This commit is contained in:
commit
7b50769eb9
19 changed files with 572 additions and 496 deletions
|
@ -318,7 +318,7 @@ ArchLinux
|
||||||
If running `$ synctl start` fails with 'returned non-zero exit status 1',
|
If running `$ synctl start` fails with 'returned non-zero exit status 1',
|
||||||
you will need to explicitly call Python2.7 - either running as::
|
you will need to explicitly call Python2.7 - either running as::
|
||||||
|
|
||||||
$ python2.7 -m synapse.app.homeserver --daemonize -c homeserver.yaml --pid-file homeserver.pid
|
$ python2.7 -m synapse.app.homeserver --daemonize -c homeserver.yaml
|
||||||
|
|
||||||
...or by editing synctl with the correct python executable.
|
...or by editing synctl with the correct python executable.
|
||||||
|
|
||||||
|
@ -409,7 +409,6 @@ SRV record, as that is the name other machines will expect it to have::
|
||||||
|
|
||||||
$ python -m synapse.app.homeserver \
|
$ python -m synapse.app.homeserver \
|
||||||
--server-name YOURDOMAIN \
|
--server-name YOURDOMAIN \
|
||||||
--bind-port 8448 \
|
|
||||||
--config-path homeserver.yaml \
|
--config-path homeserver.yaml \
|
||||||
--generate-config
|
--generate-config
|
||||||
$ python -m synapse.app.homeserver --config-path homeserver.yaml
|
$ python -m synapse.app.homeserver --config-path homeserver.yaml
|
||||||
|
|
|
@ -16,30 +16,30 @@ if [ $# -eq 1 ]; then
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
export PYTHONPATH=$(readlink -f $(pwd))
|
||||||
|
|
||||||
|
|
||||||
|
echo $PYTHONPATH
|
||||||
|
|
||||||
for port in 8080 8081 8082; do
|
for port in 8080 8081 8082; do
|
||||||
echo "Starting server on port $port... "
|
echo "Starting server on port $port... "
|
||||||
|
|
||||||
https_port=$((port + 400))
|
https_port=$((port + 400))
|
||||||
|
mkdir -p demo/$port
|
||||||
|
pushd demo/$port
|
||||||
|
|
||||||
|
#rm $DIR/etc/$port.config
|
||||||
python -m synapse.app.homeserver \
|
python -m synapse.app.homeserver \
|
||||||
--generate-config \
|
--generate-config \
|
||||||
--config-path "demo/etc/$port.config" \
|
|
||||||
-p "$https_port" \
|
|
||||||
--unsecure-port "$port" \
|
|
||||||
-H "localhost:$https_port" \
|
-H "localhost:$https_port" \
|
||||||
-f "$DIR/$port.log" \
|
--config-path "$DIR/etc/$port.config" \
|
||||||
-d "$DIR/$port.db" \
|
|
||||||
-D --pid-file "$DIR/$port.pid" \
|
|
||||||
--manhole $((port + 1000)) \
|
|
||||||
--tls-dh-params-path "demo/demo.tls.dh" \
|
|
||||||
--media-store-path "demo/media_store.$port" \
|
|
||||||
$PARAMS $SYNAPSE_PARAMS \
|
|
||||||
--enable-registration
|
|
||||||
|
|
||||||
python -m synapse.app.homeserver \
|
python -m synapse.app.homeserver \
|
||||||
--config-path "demo/etc/$port.config" \
|
--config-path "$DIR/etc/$port.config" \
|
||||||
|
-D \
|
||||||
-vv \
|
-vv \
|
||||||
|
|
||||||
|
popd
|
||||||
done
|
done
|
||||||
|
|
||||||
cd "$CWD"
|
cd "$CWD"
|
||||||
|
|
|
@ -409,7 +409,6 @@ def setup(config_options):
|
||||||
config.server_name,
|
config.server_name,
|
||||||
domain_with_port=domain_with_port,
|
domain_with_port=domain_with_port,
|
||||||
upload_dir=os.path.abspath("uploads"),
|
upload_dir=os.path.abspath("uploads"),
|
||||||
db_name=config.database_path,
|
|
||||||
db_config=config.database_config,
|
db_config=config.database_config,
|
||||||
tls_context_factory=tls_context_factory,
|
tls_context_factory=tls_context_factory,
|
||||||
config=config,
|
config=config,
|
||||||
|
@ -422,9 +421,7 @@ def setup(config_options):
|
||||||
redirect_root_to_web_client=True,
|
redirect_root_to_web_client=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
db_name = hs.get_db_name()
|
logger.info("Preparing database: %r...", config.database_config)
|
||||||
|
|
||||||
logger.info("Preparing database: %s...", db_name)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
db_conn = database_engine.module.connect(
|
db_conn = database_engine.module.connect(
|
||||||
|
@ -446,7 +443,7 @@ def setup(config_options):
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
logger.info("Database prepared in %s.", db_name)
|
logger.info("Database prepared in %r.", config.database_config)
|
||||||
|
|
||||||
if config.manhole:
|
if config.manhole:
|
||||||
f = twisted.manhole.telnet.ShellFactory()
|
f = twisted.manhole.telnet.ShellFactory()
|
||||||
|
|
|
@ -18,15 +18,18 @@ import sys
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import signal
|
import signal
|
||||||
|
import yaml
|
||||||
|
|
||||||
SYNAPSE = ["python", "-B", "-m", "synapse.app.homeserver"]
|
SYNAPSE = ["python", "-B", "-m", "synapse.app.homeserver"]
|
||||||
|
|
||||||
CONFIGFILE = "homeserver.yaml"
|
CONFIGFILE = "homeserver.yaml"
|
||||||
PIDFILE = "homeserver.pid"
|
|
||||||
|
|
||||||
GREEN = "\x1b[1;32m"
|
GREEN = "\x1b[1;32m"
|
||||||
NORMAL = "\x1b[m"
|
NORMAL = "\x1b[m"
|
||||||
|
|
||||||
|
CONFIG = yaml.load(open(CONFIGFILE))
|
||||||
|
PIDFILE = CONFIG["pid_file"]
|
||||||
|
|
||||||
|
|
||||||
def start():
|
def start():
|
||||||
if not os.path.exists(CONFIGFILE):
|
if not os.path.exists(CONFIGFILE):
|
||||||
|
@ -40,7 +43,7 @@ def start():
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
print "Starting ...",
|
print "Starting ...",
|
||||||
args = SYNAPSE
|
args = SYNAPSE
|
||||||
args.extend(["--daemonize", "-c", CONFIGFILE, "--pid-file", PIDFILE])
|
args.extend(["--daemonize", "-c", CONFIGFILE])
|
||||||
subprocess.check_call(args)
|
subprocess.check_call(args)
|
||||||
print GREEN + "started" + NORMAL
|
print GREEN + "started" + NORMAL
|
||||||
|
|
||||||
|
|
|
@ -14,9 +14,10 @@
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import sys
|
|
||||||
import os
|
import os
|
||||||
import yaml
|
import yaml
|
||||||
|
import sys
|
||||||
|
from textwrap import dedent
|
||||||
|
|
||||||
|
|
||||||
class ConfigError(Exception):
|
class ConfigError(Exception):
|
||||||
|
@ -24,18 +25,35 @@ class ConfigError(Exception):
|
||||||
|
|
||||||
|
|
||||||
class Config(object):
|
class Config(object):
|
||||||
def __init__(self, args):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def parse_size(string):
|
def parse_size(value):
|
||||||
|
if isinstance(value, int) or isinstance(value, long):
|
||||||
|
return value
|
||||||
sizes = {"K": 1024, "M": 1024 * 1024}
|
sizes = {"K": 1024, "M": 1024 * 1024}
|
||||||
size = 1
|
size = 1
|
||||||
suffix = string[-1]
|
suffix = value[-1]
|
||||||
if suffix in sizes:
|
if suffix in sizes:
|
||||||
string = string[:-1]
|
value = value[:-1]
|
||||||
size = sizes[suffix]
|
size = sizes[suffix]
|
||||||
return int(string) * size
|
return int(value) * size
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse_duration(value):
|
||||||
|
if isinstance(value, int) or isinstance(value, long):
|
||||||
|
return value
|
||||||
|
second = 1000
|
||||||
|
hour = 60 * 60 * second
|
||||||
|
day = 24 * hour
|
||||||
|
week = 7 * day
|
||||||
|
year = 365 * day
|
||||||
|
sizes = {"s": second, "h": hour, "d": day, "w": week, "y": year}
|
||||||
|
size = 1
|
||||||
|
suffix = value[-1]
|
||||||
|
if suffix in sizes:
|
||||||
|
value = value[:-1]
|
||||||
|
size = sizes[suffix]
|
||||||
|
return int(value) * size
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def abspath(file_path):
|
def abspath(file_path):
|
||||||
|
@ -77,17 +95,6 @@ class Config(object):
|
||||||
with open(file_path) as file_stream:
|
with open(file_path) as file_stream:
|
||||||
return file_stream.read()
|
return file_stream.read()
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def read_yaml_file(cls, file_path, config_name):
|
|
||||||
cls.check_file(file_path, config_name)
|
|
||||||
with open(file_path) as file_stream:
|
|
||||||
try:
|
|
||||||
return yaml.load(file_stream)
|
|
||||||
except:
|
|
||||||
raise ConfigError(
|
|
||||||
"Error parsing yaml in file %r" % (file_path,)
|
|
||||||
)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def default_path(name):
|
def default_path(name):
|
||||||
return os.path.abspath(os.path.join(os.path.curdir, name))
|
return os.path.abspath(os.path.join(os.path.curdir, name))
|
||||||
|
@ -97,84 +104,119 @@ class Config(object):
|
||||||
with open(file_path) as file_stream:
|
with open(file_path) as file_stream:
|
||||||
return yaml.load(file_stream)
|
return yaml.load(file_stream)
|
||||||
|
|
||||||
@classmethod
|
def invoke_all(self, name, *args, **kargs):
|
||||||
def add_arguments(cls, parser):
|
results = []
|
||||||
pass
|
for cls in type(self).mro():
|
||||||
|
if name in cls.__dict__:
|
||||||
|
results.append(getattr(cls, name)(self, *args, **kargs))
|
||||||
|
return results
|
||||||
|
|
||||||
@classmethod
|
def generate_config(self, config_dir_path, server_name):
|
||||||
def generate_config(cls, args, config_dir_path):
|
default_config = "# vim:ft=yaml\n"
|
||||||
pass
|
|
||||||
|
default_config += "\n\n".join(dedent(conf) for conf in self.invoke_all(
|
||||||
|
"default_config", config_dir_path, server_name
|
||||||
|
))
|
||||||
|
|
||||||
|
config = yaml.load(default_config)
|
||||||
|
|
||||||
|
return default_config, config
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def load_config(cls, description, argv, generate_section=None):
|
def load_config(cls, description, argv, generate_section=None):
|
||||||
|
obj = cls()
|
||||||
|
|
||||||
config_parser = argparse.ArgumentParser(add_help=False)
|
config_parser = argparse.ArgumentParser(add_help=False)
|
||||||
config_parser.add_argument(
|
config_parser.add_argument(
|
||||||
"-c", "--config-path",
|
"-c", "--config-path",
|
||||||
|
action="append",
|
||||||
metavar="CONFIG_FILE",
|
metavar="CONFIG_FILE",
|
||||||
help="Specify config file"
|
help="Specify config file"
|
||||||
)
|
)
|
||||||
config_parser.add_argument(
|
config_parser.add_argument(
|
||||||
"--generate-config",
|
"--generate-config",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
help="Generate config file"
|
help="Generate a config file for the server name"
|
||||||
|
)
|
||||||
|
config_parser.add_argument(
|
||||||
|
"-H", "--server-name",
|
||||||
|
help="The server name to generate a config file for"
|
||||||
)
|
)
|
||||||
config_args, remaining_args = config_parser.parse_known_args(argv)
|
config_args, remaining_args = config_parser.parse_known_args(argv)
|
||||||
|
|
||||||
|
if not config_args.config_path:
|
||||||
|
config_parser.error(
|
||||||
|
"Must supply a config file.\nA config file can be automatically"
|
||||||
|
" generated using \"--generate-config -h SERVER_NAME"
|
||||||
|
" -c CONFIG-FILE\""
|
||||||
|
)
|
||||||
|
|
||||||
|
config_dir_path = os.path.dirname(config_args.config_path[0])
|
||||||
|
config_dir_path = os.path.abspath(config_dir_path)
|
||||||
if config_args.generate_config:
|
if config_args.generate_config:
|
||||||
if not config_args.config_path:
|
server_name = config_args.server_name
|
||||||
config_parser.error(
|
if not server_name:
|
||||||
"Must specify where to generate the config file"
|
print "Most specify a server_name to a generate config for."
|
||||||
)
|
sys.exit(1)
|
||||||
config_dir_path = os.path.dirname(config_args.config_path)
|
(config_path,) = config_args.config_path
|
||||||
if os.path.exists(config_args.config_path):
|
|
||||||
defaults = cls.read_config_file(config_args.config_path)
|
|
||||||
else:
|
|
||||||
defaults = {}
|
|
||||||
else:
|
|
||||||
if config_args.config_path:
|
|
||||||
defaults = cls.read_config_file(config_args.config_path)
|
|
||||||
else:
|
|
||||||
defaults = {}
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(
|
|
||||||
parents=[config_parser],
|
|
||||||
description=description,
|
|
||||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
||||||
)
|
|
||||||
cls.add_arguments(parser)
|
|
||||||
parser.set_defaults(**defaults)
|
|
||||||
|
|
||||||
args = parser.parse_args(remaining_args)
|
|
||||||
|
|
||||||
if config_args.generate_config:
|
|
||||||
config_dir_path = os.path.dirname(config_args.config_path)
|
|
||||||
config_dir_path = os.path.abspath(config_dir_path)
|
|
||||||
if not os.path.exists(config_dir_path):
|
if not os.path.exists(config_dir_path):
|
||||||
os.makedirs(config_dir_path)
|
os.makedirs(config_dir_path)
|
||||||
cls.generate_config(args, config_dir_path)
|
if os.path.exists(config_path):
|
||||||
config = {}
|
print "Config file %r already exists" % (config_path,)
|
||||||
for key, value in vars(args).items():
|
yaml_config = cls.read_config_file(config_path)
|
||||||
if (key not in set(["config_path", "generate_config"])
|
yaml_name = yaml_config["server_name"]
|
||||||
and value is not None):
|
if server_name != yaml_name:
|
||||||
config[key] = value
|
print (
|
||||||
with open(config_args.config_path, "w") as config_file:
|
"Config file %r has a different server_name: "
|
||||||
# TODO(mark/paul) We might want to output emacs-style mode
|
" %r != %r" % (config_path, server_name, yaml_name)
|
||||||
# markers as well as vim-style mode markers into the file,
|
)
|
||||||
# to further hint to people this is a YAML file.
|
sys.exit(1)
|
||||||
config_file.write("# vim:ft=yaml\n")
|
config_bytes, config = obj.generate_config(
|
||||||
yaml.dump(config, config_file, default_flow_style=False)
|
config_dir_path, server_name
|
||||||
print (
|
)
|
||||||
"A config file has been generated in %s for server name"
|
config.update(yaml_config)
|
||||||
" '%s' with corresponding SSL keys and self-signed"
|
print "Generating any missing keys for %r" % (server_name,)
|
||||||
" certificates. Please review this file and customise it to"
|
obj.invoke_all("generate_files", config)
|
||||||
" your needs."
|
sys.exit(0)
|
||||||
) % (
|
with open(config_path, "wb") as config_file:
|
||||||
config_args.config_path, config['server_name']
|
config_bytes, config = obj.generate_config(
|
||||||
)
|
config_dir_path, server_name
|
||||||
|
)
|
||||||
|
obj.invoke_all("generate_files", config)
|
||||||
|
config_file.write(config_bytes)
|
||||||
|
print (
|
||||||
|
"A config file has been generated in %s for server name"
|
||||||
|
" '%s' with corresponding SSL keys and self-signed"
|
||||||
|
" certificates. Please review this file and customise it to"
|
||||||
|
" your needs."
|
||||||
|
) % (config_path, server_name)
|
||||||
print (
|
print (
|
||||||
"If this server name is incorrect, you will need to regenerate"
|
"If this server name is incorrect, you will need to regenerate"
|
||||||
" the SSL certificates"
|
" the SSL certificates"
|
||||||
)
|
)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
return cls(args)
|
specified_config = {}
|
||||||
|
for config_path in config_args.config_path:
|
||||||
|
yaml_config = cls.read_config_file(config_path)
|
||||||
|
specified_config.update(yaml_config)
|
||||||
|
|
||||||
|
server_name = specified_config["server_name"]
|
||||||
|
_, config = obj.generate_config(config_dir_path, server_name)
|
||||||
|
config.pop("log_config")
|
||||||
|
config.update(specified_config)
|
||||||
|
|
||||||
|
obj.invoke_all("read_config", config)
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
parents=[config_parser],
|
||||||
|
description=description,
|
||||||
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||||
|
)
|
||||||
|
|
||||||
|
obj.invoke_all("add_arguments", parser)
|
||||||
|
args = parser.parse_args(remaining_args)
|
||||||
|
|
||||||
|
obj.invoke_all("read_arguments", args)
|
||||||
|
|
||||||
|
return obj
|
||||||
|
|
|
@ -17,15 +17,11 @@ from ._base import Config
|
||||||
|
|
||||||
class AppServiceConfig(Config):
|
class AppServiceConfig(Config):
|
||||||
|
|
||||||
def __init__(self, args):
|
def read_config(self, config):
|
||||||
super(AppServiceConfig, self).__init__(args)
|
self.app_service_config_files = config.get("app_service_config_files", [])
|
||||||
self.app_service_config_files = args.app_service_config_files
|
|
||||||
|
|
||||||
@classmethod
|
def default_config(cls, config_dir_path, server_name):
|
||||||
def add_arguments(cls, parser):
|
return """\
|
||||||
super(AppServiceConfig, cls).add_arguments(parser)
|
# A list of application service config file to use
|
||||||
group = parser.add_argument_group("appservice")
|
app_service_config_files: []
|
||||||
group.add_argument(
|
"""
|
||||||
"--app-service-config-files", type=str, nargs='+',
|
|
||||||
help="A list of application service config files to use."
|
|
||||||
)
|
|
||||||
|
|
|
@ -17,42 +17,35 @@ from ._base import Config
|
||||||
|
|
||||||
class CaptchaConfig(Config):
|
class CaptchaConfig(Config):
|
||||||
|
|
||||||
def __init__(self, args):
|
def read_config(self, config):
|
||||||
super(CaptchaConfig, self).__init__(args)
|
self.recaptcha_private_key = config["recaptcha_private_key"]
|
||||||
self.recaptcha_private_key = args.recaptcha_private_key
|
self.recaptcha_public_key = config["recaptcha_public_key"]
|
||||||
self.recaptcha_public_key = args.recaptcha_public_key
|
self.enable_registration_captcha = config["enable_registration_captcha"]
|
||||||
self.enable_registration_captcha = args.enable_registration_captcha
|
|
||||||
|
|
||||||
# XXX: This is used for more than just captcha
|
# XXX: This is used for more than just captcha
|
||||||
self.captcha_ip_origin_is_x_forwarded = (
|
self.captcha_ip_origin_is_x_forwarded = (
|
||||||
args.captcha_ip_origin_is_x_forwarded
|
config["captcha_ip_origin_is_x_forwarded"]
|
||||||
)
|
)
|
||||||
self.captcha_bypass_secret = args.captcha_bypass_secret
|
self.captcha_bypass_secret = config.get("captcha_bypass_secret")
|
||||||
|
|
||||||
@classmethod
|
def default_config(self, config_dir_path, server_name):
|
||||||
def add_arguments(cls, parser):
|
return """\
|
||||||
super(CaptchaConfig, cls).add_arguments(parser)
|
## Captcha ##
|
||||||
group = parser.add_argument_group("recaptcha")
|
|
||||||
group.add_argument(
|
# This Home Server's ReCAPTCHA public key.
|
||||||
"--recaptcha-public-key", type=str, default="YOUR_PUBLIC_KEY",
|
recaptcha_private_key: "YOUR_PUBLIC_KEY"
|
||||||
help="This Home Server's ReCAPTCHA public key."
|
|
||||||
)
|
# This Home Server's ReCAPTCHA private key.
|
||||||
group.add_argument(
|
recaptcha_public_key: "YOUR_PRIVATE_KEY"
|
||||||
"--recaptcha-private-key", type=str, default="YOUR_PRIVATE_KEY",
|
|
||||||
help="This Home Server's ReCAPTCHA private key."
|
# Enables ReCaptcha checks when registering, preventing signup
|
||||||
)
|
# unless a captcha is answered. Requires a valid ReCaptcha
|
||||||
group.add_argument(
|
# public/private key.
|
||||||
"--enable-registration-captcha", type=bool, default=False,
|
enable_registration_captcha: False
|
||||||
help="Enables ReCaptcha checks when registering, preventing signup"
|
|
||||||
+ " unless a captcha is answered. Requires a valid ReCaptcha "
|
# When checking captchas, use the X-Forwarded-For (XFF) header
|
||||||
+ "public/private key."
|
# as the client IP and not the actual client IP.
|
||||||
)
|
captcha_ip_origin_is_x_forwarded: False
|
||||||
group.add_argument(
|
|
||||||
"--captcha_ip_origin_is_x_forwarded", type=bool, default=False,
|
# A secret key used to bypass the captcha test entirely.
|
||||||
help="When checking captchas, use the X-Forwarded-For (XFF) header"
|
#captcha_bypass_secret: "YOUR_SECRET_HERE"
|
||||||
+ " as the client IP and not the actual client IP."
|
"""
|
||||||
)
|
|
||||||
group.add_argument(
|
|
||||||
"--captcha_bypass_secret", type=str,
|
|
||||||
help="A secret key used to bypass the captcha test entirely."
|
|
||||||
)
|
|
||||||
|
|
|
@ -14,28 +14,21 @@
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from ._base import Config
|
from ._base import Config
|
||||||
import os
|
|
||||||
import yaml
|
|
||||||
|
|
||||||
|
|
||||||
class DatabaseConfig(Config):
|
class DatabaseConfig(Config):
|
||||||
def __init__(self, args):
|
|
||||||
super(DatabaseConfig, self).__init__(args)
|
|
||||||
if args.database_path == ":memory:":
|
|
||||||
self.database_path = ":memory:"
|
|
||||||
else:
|
|
||||||
self.database_path = self.abspath(args.database_path)
|
|
||||||
self.event_cache_size = self.parse_size(args.event_cache_size)
|
|
||||||
|
|
||||||
if args.database_config:
|
def read_config(self, config):
|
||||||
with open(args.database_config) as f:
|
self.event_cache_size = self.parse_size(
|
||||||
self.database_config = yaml.safe_load(f)
|
config.get("event_cache_size", "10K")
|
||||||
else:
|
)
|
||||||
|
|
||||||
|
self.database_config = config.get("database")
|
||||||
|
|
||||||
|
if self.database_config is None:
|
||||||
self.database_config = {
|
self.database_config = {
|
||||||
"name": "sqlite3",
|
"name": "sqlite3",
|
||||||
"args": {
|
"args": {},
|
||||||
"database": self.database_path,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
name = self.database_config.get("name", None)
|
name = self.database_config.get("name", None)
|
||||||
|
@ -50,24 +43,37 @@ class DatabaseConfig(Config):
|
||||||
else:
|
else:
|
||||||
raise RuntimeError("Unsupported database type '%s'" % (name,))
|
raise RuntimeError("Unsupported database type '%s'" % (name,))
|
||||||
|
|
||||||
@classmethod
|
self.set_databasepath(config.get("database_path"))
|
||||||
def add_arguments(cls, parser):
|
|
||||||
super(DatabaseConfig, cls).add_arguments(parser)
|
def default_config(self, config, config_dir_path):
|
||||||
|
database_path = self.abspath("homeserver.db")
|
||||||
|
return """\
|
||||||
|
# Database configuration
|
||||||
|
database:
|
||||||
|
# The database engine name
|
||||||
|
name: "sqlite3"
|
||||||
|
# Arguments to pass to the engine
|
||||||
|
args:
|
||||||
|
# Path to the database
|
||||||
|
database: "%(database_path)s"
|
||||||
|
|
||||||
|
# Number of events to cache in memory.
|
||||||
|
event_cache_size: "10K"
|
||||||
|
""" % locals()
|
||||||
|
|
||||||
|
def read_arguments(self, args):
|
||||||
|
self.set_databasepath(args.database_path)
|
||||||
|
|
||||||
|
def set_databasepath(self, database_path):
|
||||||
|
if database_path != ":memory:":
|
||||||
|
database_path = self.abspath(database_path)
|
||||||
|
if self.database_config.get("name", None) == "sqlite3":
|
||||||
|
if database_path is not None:
|
||||||
|
self.database_config["args"]["database"] = database_path
|
||||||
|
|
||||||
|
def add_arguments(self, parser):
|
||||||
db_group = parser.add_argument_group("database")
|
db_group = parser.add_argument_group("database")
|
||||||
db_group.add_argument(
|
db_group.add_argument(
|
||||||
"-d", "--database-path", default="homeserver.db",
|
"-d", "--database-path", metavar="SQLITE_DATABASE_PATH",
|
||||||
metavar="SQLITE_DATABASE_PATH", help="The database name."
|
help="The path to a sqlite database to use."
|
||||||
)
|
)
|
||||||
db_group.add_argument(
|
|
||||||
"--event-cache-size", default="100K",
|
|
||||||
help="Number of events to cache in memory."
|
|
||||||
)
|
|
||||||
db_group.add_argument(
|
|
||||||
"--database-config", default=None,
|
|
||||||
help="Location of the database configuration file."
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def generate_config(cls, args, config_dir_path):
|
|
||||||
super(DatabaseConfig, cls).generate_config(args, config_dir_path)
|
|
||||||
args.database_path = os.path.abspath(args.database_path)
|
|
||||||
|
|
|
@ -36,4 +36,6 @@ class HomeServerConfig(TlsConfig, ServerConfig, DatabaseConfig, LoggingConfig,
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
import sys
|
import sys
|
||||||
HomeServerConfig.load_config("Generate config", sys.argv[1:], "HomeServer")
|
sys.stdout.write(
|
||||||
|
HomeServerConfig().generate_config(sys.argv[1], sys.argv[2])[0]
|
||||||
|
)
|
||||||
|
|
|
@ -20,48 +20,58 @@ from syutil.crypto.signing_key import (
|
||||||
is_signing_algorithm_supported, decode_verify_key_bytes
|
is_signing_algorithm_supported, decode_verify_key_bytes
|
||||||
)
|
)
|
||||||
from syutil.base64util import decode_base64
|
from syutil.base64util import decode_base64
|
||||||
|
from synapse.util.stringutils import random_string
|
||||||
|
|
||||||
|
|
||||||
class KeyConfig(Config):
|
class KeyConfig(Config):
|
||||||
|
|
||||||
def __init__(self, args):
|
def read_config(self, config):
|
||||||
super(KeyConfig, self).__init__(args)
|
self.signing_key = self.read_signing_key(config["signing_key_path"])
|
||||||
self.signing_key = self.read_signing_key(args.signing_key_path)
|
|
||||||
self.old_signing_keys = self.read_old_signing_keys(
|
self.old_signing_keys = self.read_old_signing_keys(
|
||||||
args.old_signing_key_path
|
config["old_signing_keys"]
|
||||||
|
)
|
||||||
|
self.key_refresh_interval = self.parse_duration(
|
||||||
|
config["key_refresh_interval"]
|
||||||
)
|
)
|
||||||
self.key_refresh_interval = args.key_refresh_interval
|
|
||||||
self.perspectives = self.read_perspectives(
|
self.perspectives = self.read_perspectives(
|
||||||
args.perspectives_config_path
|
config["perspectives"]
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
def default_config(self, config_dir_path, server_name):
|
||||||
def add_arguments(cls, parser):
|
base_key_name = os.path.join(config_dir_path, server_name)
|
||||||
super(KeyConfig, cls).add_arguments(parser)
|
return """\
|
||||||
key_group = parser.add_argument_group("keys")
|
## Signing Keys ##
|
||||||
key_group.add_argument("--signing-key-path",
|
|
||||||
help="The signing key to sign messages with")
|
|
||||||
key_group.add_argument("--old-signing-key-path",
|
|
||||||
help="The keys that the server used to sign"
|
|
||||||
" sign messages with but won't use"
|
|
||||||
" to sign new messages. E.g. it has"
|
|
||||||
" lost its private key")
|
|
||||||
key_group.add_argument("--key-refresh-interval",
|
|
||||||
default=24 * 60 * 60 * 1000, # 1 Day
|
|
||||||
help="How long a key response is valid for."
|
|
||||||
" Used to set the exipiry in /key/v2/."
|
|
||||||
" Controls how frequently servers will"
|
|
||||||
" query what keys are still valid")
|
|
||||||
key_group.add_argument("--perspectives-config-path",
|
|
||||||
help="The trusted servers to download signing"
|
|
||||||
" keys from")
|
|
||||||
|
|
||||||
def read_perspectives(self, perspectives_config_path):
|
# Path to the signing key to sign messages with
|
||||||
config = self.read_yaml_file(
|
signing_key_path: "%(base_key_name)s.signing.key"
|
||||||
perspectives_config_path, "perspectives_config_path"
|
|
||||||
)
|
# The keys that the server used to sign messages with but won't use
|
||||||
|
# to sign new messages. E.g. it has lost its private key
|
||||||
|
old_signing_keys: {}
|
||||||
|
# "ed25519:auto":
|
||||||
|
# # Base64 encoded public key
|
||||||
|
# key: "The public part of your old signing key."
|
||||||
|
# # Millisecond POSIX timestamp when the key expired.
|
||||||
|
# expired_ts: 123456789123
|
||||||
|
|
||||||
|
# How long key response published by this server is valid for.
|
||||||
|
# Used to set the valid_until_ts in /key/v2 APIs.
|
||||||
|
# Determines how quickly servers will query to check which keys
|
||||||
|
# are still valid.
|
||||||
|
key_refresh_interval: "1d" # 1 Day.
|
||||||
|
|
||||||
|
# The trusted servers to download signing keys from.
|
||||||
|
perspectives:
|
||||||
|
servers:
|
||||||
|
"matrix.org":
|
||||||
|
verify_keys:
|
||||||
|
"ed25519:auto":
|
||||||
|
key: "Noi6WqcDj0QmPxCNQqgezwTlBKrfqehY1u2FyWP9uYw"
|
||||||
|
""" % locals()
|
||||||
|
|
||||||
|
def read_perspectives(self, perspectives_config):
|
||||||
servers = {}
|
servers = {}
|
||||||
for server_name, server_config in config["servers"].items():
|
for server_name, server_config in perspectives_config["servers"].items():
|
||||||
for key_id, key_data in server_config["verify_keys"].items():
|
for key_id, key_data in server_config["verify_keys"].items():
|
||||||
if is_signing_algorithm_supported(key_id):
|
if is_signing_algorithm_supported(key_id):
|
||||||
key_base64 = key_data["key"]
|
key_base64 = key_data["key"]
|
||||||
|
@ -82,66 +92,42 @@ class KeyConfig(Config):
|
||||||
" Try running again with --generate-config"
|
" Try running again with --generate-config"
|
||||||
)
|
)
|
||||||
|
|
||||||
def read_old_signing_keys(self, old_signing_key_path):
|
def read_old_signing_keys(self, old_signing_keys):
|
||||||
old_signing_keys = self.read_file(
|
keys = {}
|
||||||
old_signing_key_path, "old_signing_key"
|
for key_id, key_data in old_signing_keys.items():
|
||||||
)
|
if is_signing_algorithm_supported(key_id):
|
||||||
try:
|
key_base64 = key_data["key"]
|
||||||
return syutil.crypto.signing_key.read_old_signing_keys(
|
key_bytes = decode_base64(key_base64)
|
||||||
old_signing_keys.splitlines(True)
|
verify_key = decode_verify_key_bytes(key_id, key_bytes)
|
||||||
)
|
verify_key.expired_ts = key_data["expired_ts"]
|
||||||
except Exception:
|
keys[key_id] = verify_key
|
||||||
raise ConfigError(
|
else:
|
||||||
"Error reading old signing keys."
|
raise ConfigError(
|
||||||
)
|
"Unsupported signing algorithm for old key: %r" % (key_id,)
|
||||||
|
)
|
||||||
|
return keys
|
||||||
|
|
||||||
@classmethod
|
def generate_files(self, config):
|
||||||
def generate_config(cls, args, config_dir_path):
|
signing_key_path = config["signing_key_path"]
|
||||||
super(KeyConfig, cls).generate_config(args, config_dir_path)
|
if not os.path.exists(signing_key_path):
|
||||||
base_key_name = os.path.join(config_dir_path, args.server_name)
|
with open(signing_key_path, "w") as signing_key_file:
|
||||||
|
key_id = "a_" + random_string(4)
|
||||||
args.pid_file = os.path.abspath(args.pid_file)
|
|
||||||
|
|
||||||
if not args.signing_key_path:
|
|
||||||
args.signing_key_path = base_key_name + ".signing.key"
|
|
||||||
|
|
||||||
if not os.path.exists(args.signing_key_path):
|
|
||||||
with open(args.signing_key_path, "w") as signing_key_file:
|
|
||||||
syutil.crypto.signing_key.write_signing_keys(
|
syutil.crypto.signing_key.write_signing_keys(
|
||||||
signing_key_file,
|
signing_key_file,
|
||||||
(syutil.crypto.signing_key.generate_signing_key("auto"),),
|
(syutil.crypto.signing_key.generate_signing_key(key_id),),
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
signing_keys = cls.read_file(args.signing_key_path, "signing_key")
|
signing_keys = self.read_file(signing_key_path, "signing_key")
|
||||||
if len(signing_keys.split("\n")[0].split()) == 1:
|
if len(signing_keys.split("\n")[0].split()) == 1:
|
||||||
# handle keys in the old format.
|
# handle keys in the old format.
|
||||||
|
key_id = "a_" + random_string(4)
|
||||||
key = syutil.crypto.signing_key.decode_signing_key_base64(
|
key = syutil.crypto.signing_key.decode_signing_key_base64(
|
||||||
syutil.crypto.signing_key.NACL_ED25519,
|
syutil.crypto.signing_key.NACL_ED25519,
|
||||||
"auto",
|
key_id,
|
||||||
signing_keys.split("\n")[0]
|
signing_keys.split("\n")[0]
|
||||||
)
|
)
|
||||||
with open(args.signing_key_path, "w") as signing_key_file:
|
with open(signing_key_path, "w") as signing_key_file:
|
||||||
syutil.crypto.signing_key.write_signing_keys(
|
syutil.crypto.signing_key.write_signing_keys(
|
||||||
signing_key_file,
|
signing_key_file,
|
||||||
(key,),
|
(key,),
|
||||||
)
|
)
|
||||||
|
|
||||||
if not args.old_signing_key_path:
|
|
||||||
args.old_signing_key_path = base_key_name + ".old.signing.keys"
|
|
||||||
|
|
||||||
if not os.path.exists(args.old_signing_key_path):
|
|
||||||
with open(args.old_signing_key_path, "w"):
|
|
||||||
pass
|
|
||||||
|
|
||||||
if not args.perspectives_config_path:
|
|
||||||
args.perspectives_config_path = base_key_name + ".perspectives"
|
|
||||||
|
|
||||||
if not os.path.exists(args.perspectives_config_path):
|
|
||||||
with open(args.perspectives_config_path, "w") as perspectives_file:
|
|
||||||
perspectives_file.write(
|
|
||||||
'servers:\n'
|
|
||||||
' matrix.org:\n'
|
|
||||||
' verify_keys:\n'
|
|
||||||
' "ed25519:auto":\n'
|
|
||||||
' key: "Noi6WqcDj0QmPxCNQqgezwTlBKrfqehY1u2FyWP9uYw"\n'
|
|
||||||
)
|
|
||||||
|
|
|
@ -19,25 +19,88 @@ from twisted.python.log import PythonLoggingObserver
|
||||||
import logging
|
import logging
|
||||||
import logging.config
|
import logging.config
|
||||||
import yaml
|
import yaml
|
||||||
|
from string import Template
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
DEFAULT_LOG_CONFIG = Template("""
|
||||||
|
version: 1
|
||||||
|
|
||||||
|
formatters:
|
||||||
|
precise:
|
||||||
|
format: '%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s\
|
||||||
|
- %(message)s'
|
||||||
|
|
||||||
|
filters:
|
||||||
|
context:
|
||||||
|
(): synapse.util.logcontext.LoggingContextFilter
|
||||||
|
request: ""
|
||||||
|
|
||||||
|
handlers:
|
||||||
|
file:
|
||||||
|
class: logging.handlers.RotatingFileHandler
|
||||||
|
formatter: precise
|
||||||
|
filename: ${log_file}
|
||||||
|
maxBytes: 104857600
|
||||||
|
backupCount: 10
|
||||||
|
filters: [context]
|
||||||
|
level: INFO
|
||||||
|
console:
|
||||||
|
class: logging.StreamHandler
|
||||||
|
formatter: precise
|
||||||
|
|
||||||
|
loggers:
|
||||||
|
synapse:
|
||||||
|
level: INFO
|
||||||
|
|
||||||
|
synapse.storage.SQL:
|
||||||
|
level: INFO
|
||||||
|
|
||||||
|
root:
|
||||||
|
level: INFO
|
||||||
|
handlers: [file, console]
|
||||||
|
""")
|
||||||
|
|
||||||
|
|
||||||
class LoggingConfig(Config):
|
class LoggingConfig(Config):
|
||||||
def __init__(self, args):
|
|
||||||
super(LoggingConfig, self).__init__(args)
|
|
||||||
self.verbosity = int(args.verbose) if args.verbose else None
|
|
||||||
self.log_config = self.abspath(args.log_config)
|
|
||||||
self.log_file = self.abspath(args.log_file)
|
|
||||||
|
|
||||||
@classmethod
|
def read_config(self, config):
|
||||||
|
self.verbosity = config.get("verbose", 0)
|
||||||
|
self.log_config = self.abspath(config.get("log_config"))
|
||||||
|
self.log_file = self.abspath(config.get("log_file"))
|
||||||
|
|
||||||
|
def default_config(self, config_dir_path, server_name):
|
||||||
|
log_file = self.abspath("homeserver.log")
|
||||||
|
log_config = self.abspath(
|
||||||
|
os.path.join(config_dir_path, server_name + ".log.config")
|
||||||
|
)
|
||||||
|
return """
|
||||||
|
# Logging verbosity level.
|
||||||
|
verbose: 0
|
||||||
|
|
||||||
|
# File to write logging to
|
||||||
|
log_file: "%(log_file)s"
|
||||||
|
|
||||||
|
# A yaml python logging config file
|
||||||
|
log_config: "%(log_config)s"
|
||||||
|
""" % locals()
|
||||||
|
|
||||||
|
def read_arguments(self, args):
|
||||||
|
if args.verbose is not None:
|
||||||
|
self.verbosity = args.verbose
|
||||||
|
if args.log_config is not None:
|
||||||
|
self.log_config = args.log_config
|
||||||
|
if args.log_file is not None:
|
||||||
|
self.log_file = args.log_file
|
||||||
|
|
||||||
def add_arguments(cls, parser):
|
def add_arguments(cls, parser):
|
||||||
super(LoggingConfig, cls).add_arguments(parser)
|
|
||||||
logging_group = parser.add_argument_group("logging")
|
logging_group = parser.add_argument_group("logging")
|
||||||
logging_group.add_argument(
|
logging_group.add_argument(
|
||||||
'-v', '--verbose', dest="verbose", action='count',
|
'-v', '--verbose', dest="verbose", action='count',
|
||||||
help="The verbosity level."
|
help="The verbosity level."
|
||||||
)
|
)
|
||||||
logging_group.add_argument(
|
logging_group.add_argument(
|
||||||
'-f', '--log-file', dest="log_file", default="homeserver.log",
|
'-f', '--log-file', dest="log_file",
|
||||||
help="File to log to."
|
help="File to log to."
|
||||||
)
|
)
|
||||||
logging_group.add_argument(
|
logging_group.add_argument(
|
||||||
|
@ -45,6 +108,14 @@ class LoggingConfig(Config):
|
||||||
help="Python logging config file"
|
help="Python logging config file"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def generate_files(self, config):
|
||||||
|
log_config = config.get("log_config")
|
||||||
|
if log_config and not os.path.exists(log_config):
|
||||||
|
with open(log_config, "wb") as log_config_file:
|
||||||
|
log_config_file.write(
|
||||||
|
DEFAULT_LOG_CONFIG.substitute(log_file=config["log_file"])
|
||||||
|
)
|
||||||
|
|
||||||
def setup_logging(self):
|
def setup_logging(self):
|
||||||
log_format = (
|
log_format = (
|
||||||
"%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s"
|
"%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s"
|
||||||
|
|
|
@ -17,20 +17,17 @@ from ._base import Config
|
||||||
|
|
||||||
|
|
||||||
class MetricsConfig(Config):
|
class MetricsConfig(Config):
|
||||||
def __init__(self, args):
|
def read_config(self, config):
|
||||||
super(MetricsConfig, self).__init__(args)
|
self.enable_metrics = config["enable_metrics"]
|
||||||
self.enable_metrics = args.enable_metrics
|
self.metrics_port = config.get("metrics_port")
|
||||||
self.metrics_port = args.metrics_port
|
|
||||||
|
|
||||||
@classmethod
|
def default_config(self, config_dir_path, server_name):
|
||||||
def add_arguments(cls, parser):
|
return """\
|
||||||
super(MetricsConfig, cls).add_arguments(parser)
|
## Metrics ###
|
||||||
metrics_group = parser.add_argument_group("metrics")
|
|
||||||
metrics_group.add_argument(
|
# Enable collection and rendering of performance metrics
|
||||||
'--enable-metrics', dest="enable_metrics", action="store_true",
|
enable_metrics: False
|
||||||
help="Enable collection and rendering of performance metrics"
|
|
||||||
)
|
# Separate port to accept metrics requests on (on localhost)
|
||||||
metrics_group.add_argument(
|
# metrics_port: 8081
|
||||||
'--metrics-port', metavar="PORT", type=int,
|
"""
|
||||||
help="Separate port to accept metrics requests on (on localhost)"
|
|
||||||
)
|
|
||||||
|
|
|
@ -17,56 +17,42 @@ from ._base import Config
|
||||||
|
|
||||||
class RatelimitConfig(Config):
|
class RatelimitConfig(Config):
|
||||||
|
|
||||||
def __init__(self, args):
|
def read_config(self, config):
|
||||||
super(RatelimitConfig, self).__init__(args)
|
self.rc_messages_per_second = config["rc_messages_per_second"]
|
||||||
self.rc_messages_per_second = args.rc_messages_per_second
|
self.rc_message_burst_count = config["rc_message_burst_count"]
|
||||||
self.rc_message_burst_count = args.rc_message_burst_count
|
|
||||||
|
|
||||||
self.federation_rc_window_size = args.federation_rc_window_size
|
self.federation_rc_window_size = config["federation_rc_window_size"]
|
||||||
self.federation_rc_sleep_limit = args.federation_rc_sleep_limit
|
self.federation_rc_sleep_limit = config["federation_rc_sleep_limit"]
|
||||||
self.federation_rc_sleep_delay = args.federation_rc_sleep_delay
|
self.federation_rc_sleep_delay = config["federation_rc_sleep_delay"]
|
||||||
self.federation_rc_reject_limit = args.federation_rc_reject_limit
|
self.federation_rc_reject_limit = config["federation_rc_reject_limit"]
|
||||||
self.federation_rc_concurrent = args.federation_rc_concurrent
|
self.federation_rc_concurrent = config["federation_rc_concurrent"]
|
||||||
|
|
||||||
@classmethod
|
def default_config(self, config_dir_path, server_name):
|
||||||
def add_arguments(cls, parser):
|
return """\
|
||||||
super(RatelimitConfig, cls).add_arguments(parser)
|
## Ratelimiting ##
|
||||||
rc_group = parser.add_argument_group("ratelimiting")
|
|
||||||
rc_group.add_argument(
|
|
||||||
"--rc-messages-per-second", type=float, default=0.2,
|
|
||||||
help="number of messages a client can send per second"
|
|
||||||
)
|
|
||||||
rc_group.add_argument(
|
|
||||||
"--rc-message-burst-count", type=float, default=10,
|
|
||||||
help="number of message a client can send before being throttled"
|
|
||||||
)
|
|
||||||
|
|
||||||
rc_group.add_argument(
|
# Number of messages a client can send per second
|
||||||
"--federation-rc-window-size", type=int, default=10000,
|
rc_messages_per_second: 0.2
|
||||||
help="The federation window size in milliseconds",
|
|
||||||
)
|
|
||||||
|
|
||||||
rc_group.add_argument(
|
# Number of message a client can send before being throttled
|
||||||
"--federation-rc-sleep-limit", type=int, default=10,
|
rc_message_burst_count: 10.0
|
||||||
help="The number of federation requests from a single server"
|
|
||||||
" in a window before the server will delay processing the"
|
|
||||||
" request.",
|
|
||||||
)
|
|
||||||
|
|
||||||
rc_group.add_argument(
|
# The federation window size in milliseconds
|
||||||
"--federation-rc-sleep-delay", type=int, default=500,
|
federation_rc_window_size: 1000
|
||||||
help="The duration in milliseconds to delay processing events from"
|
|
||||||
" remote servers by if they go over the sleep limit.",
|
|
||||||
)
|
|
||||||
|
|
||||||
rc_group.add_argument(
|
# The number of federation requests from a single server in a window
|
||||||
"--federation-rc-reject-limit", type=int, default=50,
|
# before the server will delay processing the request.
|
||||||
help="The maximum number of concurrent federation requests allowed"
|
federation_rc_sleep_limit: 10
|
||||||
" from a single server",
|
|
||||||
)
|
|
||||||
|
|
||||||
rc_group.add_argument(
|
# The duration in milliseconds to delay processing events from
|
||||||
"--federation-rc-concurrent", type=int, default=3,
|
# remote servers by if they go over the sleep limit.
|
||||||
help="The number of federation requests to concurrently process"
|
federation_rc_sleep_delay: 500
|
||||||
" from a single server",
|
|
||||||
)
|
# The maximum number of concurrent federation requests allowed
|
||||||
|
# from a single server
|
||||||
|
federation_rc_reject_limit: 50
|
||||||
|
|
||||||
|
# The number of federation requests to concurrently process from a
|
||||||
|
# single server
|
||||||
|
federation_rc_concurrent: 3
|
||||||
|
"""
|
||||||
|
|
|
@ -17,45 +17,44 @@ from ._base import Config
|
||||||
|
|
||||||
from synapse.util.stringutils import random_string_with_symbols
|
from synapse.util.stringutils import random_string_with_symbols
|
||||||
|
|
||||||
import distutils.util
|
from distutils.util import strtobool
|
||||||
|
|
||||||
|
|
||||||
class RegistrationConfig(Config):
|
class RegistrationConfig(Config):
|
||||||
|
|
||||||
def __init__(self, args):
|
def read_config(self, config):
|
||||||
super(RegistrationConfig, self).__init__(args)
|
|
||||||
|
|
||||||
# `args.enable_registration` may either be a bool or a string depending
|
|
||||||
# on if the option was given a value (e.g. --enable-registration=true
|
|
||||||
# would set `args.enable_registration` to "true" not True.)
|
|
||||||
self.disable_registration = not bool(
|
self.disable_registration = not bool(
|
||||||
distutils.util.strtobool(str(args.enable_registration))
|
strtobool(str(config["enable_registration"]))
|
||||||
)
|
)
|
||||||
self.registration_shared_secret = args.registration_shared_secret
|
if "disable_registration" in config:
|
||||||
|
self.disable_registration = bool(
|
||||||
|
strtobool(str(config["disable_registration"]))
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
self.registration_shared_secret = config.get("registration_shared_secret")
|
||||||
def add_arguments(cls, parser):
|
|
||||||
super(RegistrationConfig, cls).add_arguments(parser)
|
def default_config(self, config_dir, server_name):
|
||||||
|
registration_shared_secret = random_string_with_symbols(50)
|
||||||
|
return """\
|
||||||
|
## Registration ##
|
||||||
|
|
||||||
|
# Enable registration for new users.
|
||||||
|
enable_registration: True
|
||||||
|
|
||||||
|
# If set, allows registration by anyone who also has the shared
|
||||||
|
# secret, even if registration is otherwise disabled.
|
||||||
|
registration_shared_secret: "%(registration_shared_secret)s"
|
||||||
|
""" % locals()
|
||||||
|
|
||||||
|
def add_arguments(self, parser):
|
||||||
reg_group = parser.add_argument_group("registration")
|
reg_group = parser.add_argument_group("registration")
|
||||||
|
|
||||||
reg_group.add_argument(
|
reg_group.add_argument(
|
||||||
"--enable-registration",
|
"--enable-registration", action="store_true", default=None,
|
||||||
const=True,
|
help="Enable registration for new users."
|
||||||
default=False,
|
|
||||||
nargs='?',
|
|
||||||
help="Enable registration for new users.",
|
|
||||||
)
|
|
||||||
reg_group.add_argument(
|
|
||||||
"--registration-shared-secret", type=str,
|
|
||||||
help="If set, allows registration by anyone who also has the shared"
|
|
||||||
" secret, even if registration is otherwise disabled.",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
def read_arguments(self, args):
|
||||||
def generate_config(cls, args, config_dir_path):
|
if args.enable_registration is not None:
|
||||||
super(RegistrationConfig, cls).generate_config(args, config_dir_path)
|
self.disable_registration = not bool(
|
||||||
if args.enable_registration is None:
|
strtobool(str(args.enable_registration))
|
||||||
args.enable_registration = False
|
)
|
||||||
|
|
||||||
if args.registration_shared_secret is None:
|
|
||||||
args.registration_shared_secret = random_string_with_symbols(50)
|
|
||||||
|
|
|
@ -17,32 +17,20 @@ from ._base import Config
|
||||||
|
|
||||||
|
|
||||||
class ContentRepositoryConfig(Config):
|
class ContentRepositoryConfig(Config):
|
||||||
def __init__(self, args):
|
def read_config(self, config):
|
||||||
super(ContentRepositoryConfig, self).__init__(args)
|
self.max_upload_size = self.parse_size(config["max_upload_size"])
|
||||||
self.max_upload_size = self.parse_size(args.max_upload_size)
|
self.max_image_pixels = self.parse_size(config["max_image_pixels"])
|
||||||
self.max_image_pixels = self.parse_size(args.max_image_pixels)
|
self.media_store_path = self.ensure_directory(config["media_store_path"])
|
||||||
self.media_store_path = self.ensure_directory(args.media_store_path)
|
|
||||||
|
|
||||||
def parse_size(self, string):
|
def default_config(self, config_dir_path, server_name):
|
||||||
sizes = {"K": 1024, "M": 1024 * 1024}
|
media_store = self.default_path("media_store")
|
||||||
size = 1
|
return """
|
||||||
suffix = string[-1]
|
# Directory where uploaded images and attachments are stored.
|
||||||
if suffix in sizes:
|
media_store_path: "%(media_store)s"
|
||||||
string = string[:-1]
|
|
||||||
size = sizes[suffix]
|
|
||||||
return int(string) * size
|
|
||||||
|
|
||||||
@classmethod
|
# The largest allowed upload size in bytes
|
||||||
def add_arguments(cls, parser):
|
max_upload_size: "10M"
|
||||||
super(ContentRepositoryConfig, cls).add_arguments(parser)
|
|
||||||
db_group = parser.add_argument_group("content_repository")
|
# Maximum number of pixels that will be thumbnailed
|
||||||
db_group.add_argument(
|
max_image_pixels: "32M"
|
||||||
"--max-upload-size", default="10M"
|
""" % locals()
|
||||||
)
|
|
||||||
db_group.add_argument(
|
|
||||||
"--media-store-path", default=cls.default_path("media_store")
|
|
||||||
)
|
|
||||||
db_group.add_argument(
|
|
||||||
"--max-image-pixels", default="32M",
|
|
||||||
help="Maximum number of pixels that will be thumbnailed"
|
|
||||||
)
|
|
||||||
|
|
|
@ -17,64 +17,85 @@ from ._base import Config
|
||||||
|
|
||||||
|
|
||||||
class ServerConfig(Config):
|
class ServerConfig(Config):
|
||||||
def __init__(self, args):
|
|
||||||
super(ServerConfig, self).__init__(args)
|
|
||||||
self.server_name = args.server_name
|
|
||||||
self.bind_port = args.bind_port
|
|
||||||
self.bind_host = args.bind_host
|
|
||||||
self.unsecure_port = args.unsecure_port
|
|
||||||
self.daemonize = args.daemonize
|
|
||||||
self.pid_file = self.abspath(args.pid_file)
|
|
||||||
self.web_client = args.web_client
|
|
||||||
self.manhole = args.manhole
|
|
||||||
self.soft_file_limit = args.soft_file_limit
|
|
||||||
|
|
||||||
if not args.content_addr:
|
def read_config(self, config):
|
||||||
host = args.server_name
|
self.server_name = config["server_name"]
|
||||||
|
self.bind_port = config["bind_port"]
|
||||||
|
self.bind_host = config["bind_host"]
|
||||||
|
self.unsecure_port = config["unsecure_port"]
|
||||||
|
self.manhole = config.get("manhole")
|
||||||
|
self.pid_file = self.abspath(config.get("pid_file"))
|
||||||
|
self.web_client = config["web_client"]
|
||||||
|
self.soft_file_limit = config["soft_file_limit"]
|
||||||
|
|
||||||
|
# Attempt to guess the content_addr for the v0 content repostitory
|
||||||
|
content_addr = config.get("content_addr")
|
||||||
|
if not content_addr:
|
||||||
|
host = self.server_name
|
||||||
if ':' not in host:
|
if ':' not in host:
|
||||||
host = "%s:%d" % (host, args.unsecure_port)
|
host = "%s:%d" % (host, self.unsecure_port)
|
||||||
else:
|
else:
|
||||||
host = host.split(':')[0]
|
host = host.split(':')[0]
|
||||||
host = "%s:%d" % (host, args.unsecure_port)
|
host = "%s:%d" % (host, self.unsecure_port)
|
||||||
args.content_addr = "http://%s" % (host,)
|
content_addr = "http://%s" % (host,)
|
||||||
|
|
||||||
self.content_addr = args.content_addr
|
self.content_addr = content_addr
|
||||||
|
|
||||||
@classmethod
|
def default_config(self, config_dir_path, server_name):
|
||||||
def add_arguments(cls, parser):
|
if ":" in server_name:
|
||||||
super(ServerConfig, cls).add_arguments(parser)
|
bind_port = int(server_name.split(":")[1])
|
||||||
|
unsecure_port = bind_port - 400
|
||||||
|
else:
|
||||||
|
bind_port = 8448
|
||||||
|
unsecure_port = 8008
|
||||||
|
|
||||||
|
pid_file = self.abspath("homeserver.pid")
|
||||||
|
return """\
|
||||||
|
## Server ##
|
||||||
|
|
||||||
|
# The domain name of the server, with optional explicit port.
|
||||||
|
# This is used by remote servers to connect to this server,
|
||||||
|
# e.g. matrix.org, localhost:8080, etc.
|
||||||
|
server_name: "%(server_name)s"
|
||||||
|
|
||||||
|
# The port to listen for HTTPS requests on.
|
||||||
|
# For when matrix traffic is sent directly to synapse.
|
||||||
|
bind_port: %(bind_port)s
|
||||||
|
|
||||||
|
# The port to listen for HTTP requests on.
|
||||||
|
# For when matrix traffic passes through loadbalancer that unwraps TLS.
|
||||||
|
unsecure_port: %(unsecure_port)s
|
||||||
|
|
||||||
|
# Local interface to listen on.
|
||||||
|
# The empty string will cause synapse to listen on all interfaces.
|
||||||
|
bind_host: ""
|
||||||
|
|
||||||
|
# When running as a daemon, the file to store the pid in
|
||||||
|
pid_file: %(pid_file)s
|
||||||
|
|
||||||
|
# Whether to serve a web client from the HTTP/HTTPS root resource.
|
||||||
|
web_client: True
|
||||||
|
|
||||||
|
# Set the soft limit on the number of file descriptors synapse can use
|
||||||
|
# Zero is used to indicate synapse should set the soft limit to the
|
||||||
|
# hard limit.
|
||||||
|
soft_file_limit: 0
|
||||||
|
|
||||||
|
# Turn on the twisted telnet manhole service on localhost on the given
|
||||||
|
# port.
|
||||||
|
#manhole: 9000
|
||||||
|
""" % locals()
|
||||||
|
|
||||||
|
def read_arguments(self, args):
|
||||||
|
if args.manhole is not None:
|
||||||
|
self.manhole = args.manhole
|
||||||
|
self.daemonize = args.daemonize
|
||||||
|
|
||||||
|
def add_arguments(self, parser):
|
||||||
server_group = parser.add_argument_group("server")
|
server_group = parser.add_argument_group("server")
|
||||||
server_group.add_argument(
|
|
||||||
"-H", "--server-name", default="localhost",
|
|
||||||
help="The domain name of the server, with optional explicit port. "
|
|
||||||
"This is used by remote servers to connect to this server, "
|
|
||||||
"e.g. matrix.org, localhost:8080, etc."
|
|
||||||
)
|
|
||||||
server_group.add_argument("-p", "--bind-port", metavar="PORT",
|
|
||||||
type=int, help="https port to listen on",
|
|
||||||
default=8448)
|
|
||||||
server_group.add_argument("--unsecure-port", metavar="PORT",
|
|
||||||
type=int, help="http port to listen on",
|
|
||||||
default=8008)
|
|
||||||
server_group.add_argument("--bind-host", default="",
|
|
||||||
help="Local interface to listen on")
|
|
||||||
server_group.add_argument("-D", "--daemonize", action='store_true',
|
server_group.add_argument("-D", "--daemonize", action='store_true',
|
||||||
help="Daemonize the home server")
|
help="Daemonize the home server")
|
||||||
server_group.add_argument('--pid-file', default="homeserver.pid",
|
|
||||||
help="When running as a daemon, the file to"
|
|
||||||
" store the pid in")
|
|
||||||
server_group.add_argument('--web_client', default=True, type=bool,
|
|
||||||
help="Whether or not to serve a web client")
|
|
||||||
server_group.add_argument("--manhole", metavar="PORT", dest="manhole",
|
server_group.add_argument("--manhole", metavar="PORT", dest="manhole",
|
||||||
type=int,
|
type=int,
|
||||||
help="Turn on the twisted telnet manhole"
|
help="Turn on the twisted telnet manhole"
|
||||||
" service on the given port.")
|
" service on the given port.")
|
||||||
server_group.add_argument("--content-addr", default=None,
|
|
||||||
help="The host and scheme to use for the "
|
|
||||||
"content repository")
|
|
||||||
server_group.add_argument("--soft-file-limit", type=int, default=0,
|
|
||||||
help="Set the soft limit on the number of "
|
|
||||||
"file descriptors synapse can use. "
|
|
||||||
"Zero is used to indicate synapse "
|
|
||||||
"should set the soft limit to the hard"
|
|
||||||
"limit.")
|
|
||||||
|
|
|
@ -23,37 +23,44 @@ GENERATE_DH_PARAMS = False
|
||||||
|
|
||||||
|
|
||||||
class TlsConfig(Config):
|
class TlsConfig(Config):
|
||||||
def __init__(self, args):
|
def read_config(self, config):
|
||||||
super(TlsConfig, self).__init__(args)
|
|
||||||
self.tls_certificate = self.read_tls_certificate(
|
self.tls_certificate = self.read_tls_certificate(
|
||||||
args.tls_certificate_path
|
config.get("tls_certificate_path")
|
||||||
)
|
)
|
||||||
|
|
||||||
self.no_tls = args.no_tls
|
self.no_tls = config.get("no_tls", False)
|
||||||
|
|
||||||
if self.no_tls:
|
if self.no_tls:
|
||||||
self.tls_private_key = None
|
self.tls_private_key = None
|
||||||
else:
|
else:
|
||||||
self.tls_private_key = self.read_tls_private_key(
|
self.tls_private_key = self.read_tls_private_key(
|
||||||
args.tls_private_key_path
|
config.get("tls_private_key_path")
|
||||||
)
|
)
|
||||||
|
|
||||||
self.tls_dh_params_path = self.check_file(
|
self.tls_dh_params_path = self.check_file(
|
||||||
args.tls_dh_params_path, "tls_dh_params"
|
config.get("tls_dh_params_path"), "tls_dh_params"
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
def default_config(self, config_dir_path, server_name):
|
||||||
def add_arguments(cls, parser):
|
base_key_name = os.path.join(config_dir_path, server_name)
|
||||||
super(TlsConfig, cls).add_arguments(parser)
|
|
||||||
tls_group = parser.add_argument_group("tls")
|
tls_certificate_path = base_key_name + ".tls.crt"
|
||||||
tls_group.add_argument("--tls-certificate-path",
|
tls_private_key_path = base_key_name + ".tls.key"
|
||||||
help="PEM encoded X509 certificate for TLS")
|
tls_dh_params_path = base_key_name + ".tls.dh"
|
||||||
tls_group.add_argument("--tls-private-key-path",
|
|
||||||
help="PEM encoded private key for TLS")
|
return """\
|
||||||
tls_group.add_argument("--tls-dh-params-path",
|
# PEM encoded X509 certificate for TLS
|
||||||
help="PEM dh parameters for ephemeral keys")
|
tls_certificate_path: "%(tls_certificate_path)s"
|
||||||
tls_group.add_argument("--no-tls", action='store_true',
|
|
||||||
help="Don't bind to the https port.")
|
# PEM encoded private key for TLS
|
||||||
|
tls_private_key_path: "%(tls_private_key_path)s"
|
||||||
|
|
||||||
|
# PEM dh parameters for ephemeral keys
|
||||||
|
tls_dh_params_path: "%(tls_dh_params_path)s"
|
||||||
|
|
||||||
|
# Don't bind to the https port
|
||||||
|
no_tls: False
|
||||||
|
""" % locals()
|
||||||
|
|
||||||
def read_tls_certificate(self, cert_path):
|
def read_tls_certificate(self, cert_path):
|
||||||
cert_pem = self.read_file(cert_path, "tls_certificate")
|
cert_pem = self.read_file(cert_path, "tls_certificate")
|
||||||
|
@ -63,22 +70,13 @@ class TlsConfig(Config):
|
||||||
private_key_pem = self.read_file(private_key_path, "tls_private_key")
|
private_key_pem = self.read_file(private_key_path, "tls_private_key")
|
||||||
return crypto.load_privatekey(crypto.FILETYPE_PEM, private_key_pem)
|
return crypto.load_privatekey(crypto.FILETYPE_PEM, private_key_pem)
|
||||||
|
|
||||||
@classmethod
|
def generate_files(self, config):
|
||||||
def generate_config(cls, args, config_dir_path):
|
tls_certificate_path = config["tls_certificate_path"]
|
||||||
super(TlsConfig, cls).generate_config(args, config_dir_path)
|
tls_private_key_path = config["tls_private_key_path"]
|
||||||
base_key_name = os.path.join(config_dir_path, args.server_name)
|
tls_dh_params_path = config["tls_dh_params_path"]
|
||||||
|
|
||||||
if args.tls_certificate_path is None:
|
if not os.path.exists(tls_private_key_path):
|
||||||
args.tls_certificate_path = base_key_name + ".tls.crt"
|
with open(tls_private_key_path, "w") as private_key_file:
|
||||||
|
|
||||||
if args.tls_private_key_path is None:
|
|
||||||
args.tls_private_key_path = base_key_name + ".tls.key"
|
|
||||||
|
|
||||||
if args.tls_dh_params_path is None:
|
|
||||||
args.tls_dh_params_path = base_key_name + ".tls.dh"
|
|
||||||
|
|
||||||
if not os.path.exists(args.tls_private_key_path):
|
|
||||||
with open(args.tls_private_key_path, "w") as private_key_file:
|
|
||||||
tls_private_key = crypto.PKey()
|
tls_private_key = crypto.PKey()
|
||||||
tls_private_key.generate_key(crypto.TYPE_RSA, 2048)
|
tls_private_key.generate_key(crypto.TYPE_RSA, 2048)
|
||||||
private_key_pem = crypto.dump_privatekey(
|
private_key_pem = crypto.dump_privatekey(
|
||||||
|
@ -86,17 +84,17 @@ class TlsConfig(Config):
|
||||||
)
|
)
|
||||||
private_key_file.write(private_key_pem)
|
private_key_file.write(private_key_pem)
|
||||||
else:
|
else:
|
||||||
with open(args.tls_private_key_path) as private_key_file:
|
with open(tls_private_key_path) as private_key_file:
|
||||||
private_key_pem = private_key_file.read()
|
private_key_pem = private_key_file.read()
|
||||||
tls_private_key = crypto.load_privatekey(
|
tls_private_key = crypto.load_privatekey(
|
||||||
crypto.FILETYPE_PEM, private_key_pem
|
crypto.FILETYPE_PEM, private_key_pem
|
||||||
)
|
)
|
||||||
|
|
||||||
if not os.path.exists(args.tls_certificate_path):
|
if not os.path.exists(tls_certificate_path):
|
||||||
with open(args.tls_certificate_path, "w") as certifcate_file:
|
with open(tls_certificate_path, "w") as certifcate_file:
|
||||||
cert = crypto.X509()
|
cert = crypto.X509()
|
||||||
subject = cert.get_subject()
|
subject = cert.get_subject()
|
||||||
subject.CN = args.server_name
|
subject.CN = config["server_name"]
|
||||||
|
|
||||||
cert.set_serial_number(1000)
|
cert.set_serial_number(1000)
|
||||||
cert.gmtime_adj_notBefore(0)
|
cert.gmtime_adj_notBefore(0)
|
||||||
|
@ -110,16 +108,16 @@ class TlsConfig(Config):
|
||||||
|
|
||||||
certifcate_file.write(cert_pem)
|
certifcate_file.write(cert_pem)
|
||||||
|
|
||||||
if not os.path.exists(args.tls_dh_params_path):
|
if not os.path.exists(tls_dh_params_path):
|
||||||
if GENERATE_DH_PARAMS:
|
if GENERATE_DH_PARAMS:
|
||||||
subprocess.check_call([
|
subprocess.check_call([
|
||||||
"openssl", "dhparam",
|
"openssl", "dhparam",
|
||||||
"-outform", "PEM",
|
"-outform", "PEM",
|
||||||
"-out", args.tls_dh_params_path,
|
"-out", tls_dh_params_path,
|
||||||
"2048"
|
"2048"
|
||||||
])
|
])
|
||||||
else:
|
else:
|
||||||
with open(args.tls_dh_params_path, "w") as dh_params_file:
|
with open(tls_dh_params_path, "w") as dh_params_file:
|
||||||
dh_params_file.write(
|
dh_params_file.write(
|
||||||
"2048-bit DH parameters taken from rfc3526\n"
|
"2048-bit DH parameters taken from rfc3526\n"
|
||||||
"-----BEGIN DH PARAMETERS-----\n"
|
"-----BEGIN DH PARAMETERS-----\n"
|
||||||
|
|
|
@ -17,28 +17,21 @@ from ._base import Config
|
||||||
|
|
||||||
class VoipConfig(Config):
|
class VoipConfig(Config):
|
||||||
|
|
||||||
def __init__(self, args):
|
def read_config(self, config):
|
||||||
super(VoipConfig, self).__init__(args)
|
self.turn_uris = config.get("turn_uris", [])
|
||||||
self.turn_uris = args.turn_uris
|
self.turn_shared_secret = config["turn_shared_secret"]
|
||||||
self.turn_shared_secret = args.turn_shared_secret
|
self.turn_user_lifetime = self.parse_duration(config["turn_user_lifetime"])
|
||||||
self.turn_user_lifetime = args.turn_user_lifetime
|
|
||||||
|
|
||||||
@classmethod
|
def default_config(self, config_dir_path, server_name):
|
||||||
def add_arguments(cls, parser):
|
return """\
|
||||||
super(VoipConfig, cls).add_arguments(parser)
|
## Turn ##
|
||||||
group = parser.add_argument_group("voip")
|
|
||||||
group.add_argument(
|
# The public URIs of the TURN server to give to clients
|
||||||
"--turn-uris", type=str, default=None, action='append',
|
turn_uris: []
|
||||||
help="The public URIs of the TURN server to give to clients"
|
|
||||||
)
|
# The shared secret used to compute passwords for the TURN server
|
||||||
group.add_argument(
|
turn_shared_secret: "YOUR_SHARED_SECRET"
|
||||||
"--turn-shared-secret", type=str, default=None,
|
|
||||||
help=(
|
# How long generated TURN credentials last
|
||||||
"The shared secret used to compute passwords for the TURN"
|
turn_user_lifetime: "1h"
|
||||||
" server"
|
"""
|
||||||
)
|
|
||||||
)
|
|
||||||
group.add_argument(
|
|
||||||
"--turn-user-lifetime", type=int, default=(1000 * 60 * 60),
|
|
||||||
help="How long generated TURN credentials last, in ms"
|
|
||||||
)
|
|
||||||
|
|
|
@ -59,7 +59,6 @@ class BaseHomeServer(object):
|
||||||
'config',
|
'config',
|
||||||
'clock',
|
'clock',
|
||||||
'http_client',
|
'http_client',
|
||||||
'db_name',
|
|
||||||
'db_pool',
|
'db_pool',
|
||||||
'persistence_service',
|
'persistence_service',
|
||||||
'replication_layer',
|
'replication_layer',
|
||||||
|
|
Loading…
Reference in a new issue