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

Add a script to generate a clean config file #4315

Merged
merged 1 commit into from
Dec 21, 2018
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/4315.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add a script to generate a clean config file
67 changes: 67 additions & 0 deletions scripts/generate_config
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#!/usr/bin/env python

import argparse
import sys

from synapse.config.homeserver import HomeServerConfig

if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument(
"--config-dir",
default="CONFDIR",

help="The path where the config files are kept. Used to create filenames for "
"things like the log config and the signing key. Default: %(default)s",
)

parser.add_argument(
"--data-dir",
default="DATADIR",
help="The path where the data files are kept. Used to create filenames for "
"things like the database and media store. Default: %(default)s",
)

parser.add_argument(
"--server-name",
default="SERVERNAME",
help="The server name. Used to initialise the server_name config param, but also "
"used in the names of some of the config files. Default: %(default)s",
)

parser.add_argument(
"--report-stats",
action="store",
help="Whether the generated config reports anonymized usage statistics",
choices=["yes", "no"],
)

parser.add_argument(
"--generate-secrets",
action="store_true",
help="Enable generation of new secrets for things like the macaroon_secret_key."
"By default, these parameters will be left unset."
)

parser.add_argument(
"-o", "--output-file",
type=argparse.FileType('w'),
default=sys.stdout,
help="File to write the configuration to. Default: stdout",
)

args = parser.parse_args()

report_stats = args.report_stats
if report_stats is not None:
report_stats = report_stats == "yes"

conf = HomeServerConfig().generate_config(
config_dir_path=args.config_dir,
data_dir_path=args.data_dir,
server_name=args.server_name,
generate_secrets=args.generate_secrets,
report_stats=report_stats,
)

args.output_file.write(conf)
56 changes: 43 additions & 13 deletions synapse/config/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,6 @@ def read_file(cls, file_path, config_name):
with open(file_path) as file_stream:
return file_stream.read()

@staticmethod
def default_path(name):
return os.path.abspath(os.path.join(os.path.curdir, name))

@staticmethod
def read_config_file(file_path):
with open(file_path) as file_stream:
Expand All @@ -151,24 +147,54 @@ def invoke_all(self, name, *args, **kargs):
return results

def generate_config(
self, config_dir_path, server_name, is_generating_file, report_stats=None
self,
config_dir_path,
data_dir_path,
server_name,
generate_secrets=False,
report_stats=None,
):
"""Build a default configuration file

This is used both when the user explicitly asks us to generate a config file
(eg with --generate_config), and before loading the config at runtime (to give
a base which the config files override)

Args:
config_dir_path (str): The path where the config files are kept. Used to
create filenames for things like the log config and the signing key.

data_dir_path (str): The path where the data files are kept. Used to create
filenames for things like the database and media store.

server_name (str): The server name. Used to initialise the server_name
config param, but also used in the names of some of the config files.

generate_secrets (bool): True if we should generate new secrets for things
like the macaroon_secret_key. If False, these parameters will be left
unset.

report_stats (bool|None): Initial setting for the report_stats setting.
If None, report_stats will be left unset.

Returns:
str: the yaml config file
"""
default_config = "# vim:ft=yaml\n"

default_config += "\n\n".join(
dedent(conf)
for conf in self.invoke_all(
"default_config",
config_dir_path=config_dir_path,
data_dir_path=data_dir_path,
server_name=server_name,
is_generating_file=is_generating_file,
generate_secrets=generate_secrets,
report_stats=report_stats,
)
)

config = yaml.load(default_config)

return default_config, config
return default_config

@classmethod
def load_config(cls, description, argv):
Expand Down Expand Up @@ -274,12 +300,14 @@ def load_or_generate_config(cls, description, argv):
if not cls.path_exists(config_dir_path):
os.makedirs(config_dir_path)
with open(config_path, "w") as config_file:
config_str, config = obj.generate_config(
config_str = obj.generate_config(
config_dir_path=config_dir_path,
data_dir_path=os.getcwd(),
server_name=server_name,
report_stats=(config_args.report_stats == "yes"),
is_generating_file=True,
generate_secrets=True,
)
config = yaml.load(config_str)
obj.invoke_all("generate_files", config)
config_file.write(config_str)
print(
Expand Down Expand Up @@ -350,11 +378,13 @@ def read_config_files(self, config_files, keys_directory=None, generate_keys=Fal
raise ConfigError(MISSING_SERVER_NAME)

server_name = specified_config["server_name"]
_, config = self.generate_config(
config_string = self.generate_config(
config_dir_path=config_dir_path,
data_dir_path=os.getcwd(),
server_name=server_name,
is_generating_file=False,
generate_secrets=False,
)
config = yaml.load(config_string)
config.pop("log_config")
config.update(specified_config)

Expand Down
5 changes: 3 additions & 2 deletions synapse/config/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os

from ._base import Config

Expand Down Expand Up @@ -45,8 +46,8 @@ def read_config(self, config):

self.set_databasepath(config.get("database_path"))

def default_config(self, **kwargs):
database_path = self.abspath("homeserver.db")
def default_config(self, data_dir_path, **kwargs):
database_path = os.path.join(data_dir_path, "homeserver.db")
return """\
# Database configuration
database:
Expand Down
7 changes: 0 additions & 7 deletions synapse/config/homeserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,3 @@ class HomeServerConfig(TlsConfig, ServerConfig, DatabaseConfig, LoggingConfig,
ServerNoticesConfig, RoomDirectoryConfig,
):
pass


if __name__ == '__main__':
import sys
sys.stdout.write(
HomeServerConfig().generate_config(sys.argv[1], sys.argv[2], True)[0]
)
27 changes: 18 additions & 9 deletions synapse/config/key.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,26 +66,35 @@ def read_config(self, config):
# falsification of values
self.form_secret = config.get("form_secret", None)

def default_config(self, config_dir_path, server_name, is_generating_file=False,
def default_config(self, config_dir_path, server_name, generate_secrets=False,
**kwargs):
base_key_name = os.path.join(config_dir_path, server_name)

if is_generating_file:
macaroon_secret_key = random_string_with_symbols(50)
form_secret = '"%s"' % random_string_with_symbols(50)
if generate_secrets:
macaroon_secret_key = 'macaroon_secret_key: "%s"' % (
random_string_with_symbols(50),
)
form_secret = 'form_secret: "%s"' % random_string_with_symbols(50)
else:
macaroon_secret_key = None
form_secret = 'null'
macaroon_secret_key = "# macaroon_secret_key: <PRIVATE STRING>"
form_secret = "# form_secret: <PRIVATE STRING>"

return """\
macaroon_secret_key: "%(macaroon_secret_key)s"
# a secret which is used to sign access tokens. If none is specified,
# the registration_shared_secret is used, if one is given; otherwise,
# a secret key is derived from the signing key.
#
# Note that changing this will invalidate any active access tokens, so
# all clients will have to log back in.
%(macaroon_secret_key)s

# Used to enable access token expiration.
expire_access_token: False

# a secret which is used to calculate HMACs for form values, to stop
# falsification of values
form_secret: %(form_secret)s
# falsification of values. Must be specified for the User Consent
# forms to work.
%(form_secret)s

## Signing Keys ##

Expand Down
4 changes: 1 addition & 3 deletions synapse/config/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,7 @@ def read_config(self, config):
self.log_file = self.abspath(config.get("log_file"))

def default_config(self, config_dir_path, server_name, **kwargs):
log_config = self.abspath(
os.path.join(config_dir_path, server_name + ".log.config")
)
log_config = os.path.join(config_dir_path, server_name + ".log.config")
return """
# A yaml python logging config file
log_config: "%(log_config)s"
Expand Down
12 changes: 9 additions & 3 deletions synapse/config/metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,16 @@ def read_config(self, config):
self.metrics_bind_host = config.get("metrics_bind_host", "127.0.0.1")

def default_config(self, report_stats=None, **kwargs):
suffix = "" if report_stats is None else "report_stats: %(report_stats)s\n"
return ("""\
res = """\
## Metrics ###

# Enable collection and rendering of performance metrics
enable_metrics: False
""" + suffix) % locals()
"""

if report_stats is None:
res += "# report_stats: true|false\n"
else:
res += "report_stats: %s\n" % ('true' if report_stats else 'false')

return res
11 changes: 8 additions & 3 deletions synapse/config/registration.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,13 @@ def read_config(self, config):
raise ConfigError('Invalid auto_join_rooms entry %s' % (room_alias,))
self.autocreate_auto_join_rooms = config.get("autocreate_auto_join_rooms", True)

def default_config(self, **kwargs):
registration_shared_secret = random_string_with_symbols(50)
def default_config(self, generate_secrets=False, **kwargs):
if generate_secrets:
registration_shared_secret = 'registration_shared_secret: "%s"' % (
random_string_with_symbols(50),
)
else:
registration_shared_secret = '# registration_shared_secret: <PRIVATE STRING>'

return """\
## Registration ##
Expand All @@ -78,7 +83,7 @@ def default_config(self, **kwargs):

# 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"
%(registration_shared_secret)s

# Set the number of bcrypt rounds used to generate password hash.
# Larger numbers increase the work factor needed to generate the hash.
Expand Down
8 changes: 4 additions & 4 deletions synapse/config/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import os
from collections import namedtuple

from synapse.util.module_loader import load_module
Expand Down Expand Up @@ -175,9 +175,9 @@ def read_config(self, config):
"url_preview_url_blacklist", ()
)

def default_config(self, **kwargs):
media_store = self.default_path("media_store")
uploads_path = self.default_path("uploads")
def default_config(self, data_dir_path, **kwargs):
media_store = os.path.join(data_dir_path, "media_store")
uploads_path = os.path.join(data_dir_path, "uploads")
return r"""
# Directory where uploaded images and attachments are stored.
media_store_path: "%(media_store)s"
Expand Down
5 changes: 3 additions & 2 deletions synapse/config/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
# limitations under the License.

import logging
import os.path

from synapse.http.endpoint import parse_and_validate_server_name

Expand Down Expand Up @@ -203,15 +204,15 @@ def read_config(self, config):
]
})

def default_config(self, server_name, **kwargs):
def default_config(self, server_name, data_dir_path, **kwargs):
_, bind_port = parse_and_validate_server_name(server_name)
if bind_port is not None:
unsecure_port = bind_port - 400
else:
bind_port = 8448
unsecure_port = 8008

pid_file = self.abspath("homeserver.pid")
pid_file = os.path.join(data_dir_path, "homeserver.pid")
return """\
## Server ##

Expand Down