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

Config templating #5900

Merged
merged 23 commits into from Aug 28, 2019
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
001ee16
Started templating the output
JorikSchellekens Aug 21, 2019
ce5e5b2
Tls config
JorikSchellekens Aug 21, 2019
2b15b7b
Pass configu options into sections
JorikSchellekens Aug 22, 2019
0000e90
Move args to generate_config
JorikSchellekens Aug 22, 2019
4ec743f
Erant debug print
JorikSchellekens Aug 22, 2019
84c89a7
Do something sensible when opening private ports
JorikSchellekens Aug 22, 2019
48e2e2e
Comment explaining tls config defaults
JorikSchellekens Aug 22, 2019
54d8db1
Not relevent to this pr
JorikSchellekens Aug 22, 2019
2139138
newsfile
JorikSchellekens Aug 22, 2019
0627c9f
isort
JorikSchellekens Aug 22, 2019
bd2a1f4
Surround paths in quotes.
JorikSchellekens Aug 22, 2019
fa1f322
Nicer listener formatting
JorikSchellekens Aug 22, 2019
67e7c63
docstrings
JorikSchellekens Aug 23, 2019
e7450cd
Formatting
JorikSchellekens Aug 23, 2019
4a41acd
Imagine a system composed entirely of x, y, z etc and the basic opera…
JorikSchellekens Aug 23, 2019
ec656ce
Cleaner acme enable/disable
JorikSchellekens Aug 23, 2019
3a9ab48
Update comment
JorikSchellekens Aug 23, 2019
4f74502
Ports should be bound if they are *not* open.
JorikSchellekens Aug 23, 2019
4d31a54
Do something similar to what read_config does with openports.
JorikSchellekens Aug 23, 2019
da080f8
Template tests
JorikSchellekens Aug 23, 2019
1c05589
Pass database args opaquely.
JorikSchellekens Aug 27, 2019
861019d
Properly integrate
JorikSchellekens Aug 27, 2019
e495111
Don't change the sample config
JorikSchellekens Aug 27, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/5900.feature
@@ -0,0 +1 @@
Add support for config templating.
2 changes: 1 addition & 1 deletion docs/sample_config.yaml
Expand Up @@ -205,9 +205,9 @@ listeners:
#
- port: 8008
tls: false
bind_addresses: ['::1', '127.0.0.1']
type: http
x_forwarded: true
bind_addresses: ['::1', '127.0.0.1']

resources:
- names: [client, federation]
Expand Down
10 changes: 10 additions & 0 deletions synapse/config/_base.py
Expand Up @@ -181,6 +181,11 @@ def generate_config(
generate_secrets=False,
report_stats=None,
open_private_ports=False,
listeners=None,
database=None,
tls_certificate_path=None,
tls_private_key_path=None,
acme_domain=None,
JorikSchellekens marked this conversation as resolved.
Show resolved Hide resolved
):
"""Build a default configuration file

Expand Down Expand Up @@ -220,6 +225,11 @@ def generate_config(
generate_secrets=generate_secrets,
report_stats=report_stats,
open_private_ports=open_private_ports,
listeners=listeners,
database=database,
tls_certificate_path=tls_certificate_path,
tls_private_key_path=tls_private_key_path,
acme_domain=acme_domain,
)
)

Expand Down
6 changes: 4 additions & 2 deletions synapse/config/database.py
Expand Up @@ -38,15 +38,17 @@ def read_config(self, config, **kwargs):

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

def generate_config_section(self, data_dir_path, **kwargs):
def generate_config_section(self, data_dir_path, database, **kwargs):
if not database:
database = "sqlite3"
JorikSchellekens marked this conversation as resolved.
Show resolved Hide resolved
database_path = os.path.join(data_dir_path, "homeserver.db")
return (
"""\
## Database ##

database:
# The database engine name
name: "sqlite3"
name: "%(database)s"
# Arguments to pass to the engine
args:
# Path to the database
Expand Down
86 changes: 69 additions & 17 deletions synapse/config/server.py
Expand Up @@ -17,8 +17,11 @@

import logging
import os.path
import re
from textwrap import indent

import attr
import yaml
from netaddr import IPSet

from synapse.api.room_versions import KNOWN_ROOM_VERSIONS
Expand Down Expand Up @@ -352,7 +355,7 @@ def has_tls_listener(self):
return any(l["tls"] for l in self.listeners)

def generate_config_section(
self, server_name, data_dir_path, open_private_ports, **kwargs
self, server_name, data_dir_path, open_private_ports, listeners, **kwargs
):
_, bind_port = parse_and_validate_server_name(server_name)
if bind_port is not None:
Expand All @@ -366,11 +369,70 @@ def generate_config_section(
# Bring DEFAULT_ROOM_VERSION into the local-scope for use in the
# default config string
default_room_version = DEFAULT_ROOM_VERSION
secure_listeners = []
unsecure_listeners = []
private_addresses = ["::1", "127.0.0.1"]
if listeners:
for listener in listeners:
if listener["tls"]:
secure_listeners.append(listener)
else:
# The open_private_ports option kind of conflicts with the
# idea of passing in your own listener config however
# the local addresses need to be bound if open_private_ports is
# specified.
if open_private_ports:
bind_addresses = listener.setdefault("bind_addresses", [])
for address in private_addresses:
if address not in bind_addresses:
bind_addresses.append(address)

unsecure_listeners.append(listener)

secure_http_bindings = indent(
yaml.dump(secure_listeners), " " * 10
).lstrip()
JorikSchellekens marked this conversation as resolved.
Show resolved Hide resolved

unsecure_http_bindings = indent(
yaml.dump(unsecure_listeners), " " * 10
).lstrip()

if not unsecure_listeners:
unsecure_http_bindings = (
"""- port: %(unsecure_port)s
tls: false
type: http
x_forwarded: true"""
% locals()
)

if not open_private_ports:
unsecure_http_bindings += (
"\n bind_addresses: ['::1', '127.0.0.1']"
)

unsecure_http_bindings += """

resources:
- names: [client, federation]
compress: false"""

if listeners:
# comment out this block
unsecure_http_bindings = "#" + re.sub(
"\n {10}",
lambda match: match.group(0) + "#",
unsecure_http_bindings,
)

unsecure_http_binding = "port: %i\n tls: false" % (unsecure_port,)
if not open_private_ports:
unsecure_http_binding += (
"\n bind_addresses: ['::1', '127.0.0.1']"
if not secure_listeners:
secure_http_bindings = (
"""#- port: %(bind_port)s
# type: http
# tls: true
# resources:
# - names: [client, federation]"""
% locals()
)

return (
Expand Down Expand Up @@ -556,25 +618,15 @@ def generate_config_section(
# will also need to give Synapse a TLS key and certificate: see the TLS section
# below.)
#
#- port: %(bind_port)s
# type: http
# tls: true
# resources:
# - names: [client, federation]
%(secure_http_bindings)s

# Unsecure HTTP listener: for when matrix traffic passes through a reverse proxy
# that unwraps TLS.
#
# If you plan to use a reverse proxy, please see
# https://github.com/matrix-org/synapse/blob/master/docs/reverse_proxy.rst.
#
- %(unsecure_http_binding)s
type: http
x_forwarded: true

resources:
- names: [client, federation]
compress: false
%(unsecure_http_bindings)s

# example additional_resources:
#
Expand Down
46 changes: 36 additions & 10 deletions synapse/config/tls.py
Expand Up @@ -239,12 +239,38 @@ def read_certificate_from_disk(self, require_cert_and_key):
self.tls_fingerprints.append({"sha256": sha256_fingerprint})

def generate_config_section(
self, config_dir_path, server_name, data_dir_path, **kwargs
self,
config_dir_path,
server_name,
data_dir_path,
tls_certificate_path,
tls_private_key_path,
acme_domain,
**kwargs
):
"""If the acme_domain is specified acme will be enabled.
If the TLS paths are not specified the default will be certs in the
config directory"""

base_key_name = os.path.join(config_dir_path, server_name)

tls_certificate_path = base_key_name + ".tls.crt"
tls_private_key_path = base_key_name + ".tls.key"
if bool(tls_certificate_path) ^ bool(tls_private_key_path):
JorikSchellekens marked this conversation as resolved.
Show resolved Hide resolved
raise ConfigError(
"Please specify both a cert path and a key path or neither."
)

tls_enabled = (
"" if tls_certificate_path and tls_private_key_path or acme_domain else "#"
)

if not tls_certificate_path:
tls_certificate_path = base_key_name + ".tls.crt"
if not tls_private_key_path:
tls_private_key_path = base_key_name + ".tls.key"

acme_enabled = "" if acme_domain else "#"
acme_domain = "matrix.example.com"

default_acme_account_file = os.path.join(data_dir_path, "acme_account.key")

# this is to avoid the max line length. Sorrynotsorry
Expand All @@ -269,11 +295,11 @@ def generate_config_section(
# instance, if using certbot, use `fullchain.pem` as your certificate,
# not `cert.pem`).
#
#tls_certificate_path: "%(tls_certificate_path)s"
%(tls_enabled)stls_certificate_path: "%(tls_certificate_path)s"

# PEM-encoded private key for TLS
#
#tls_private_key_path: "%(tls_private_key_path)s"
%(tls_enabled)stls_private_key_path: "%(tls_private_key_path)s"

# Whether to verify TLS server certificates for outbound federation requests.
#
Expand Down Expand Up @@ -343,7 +369,7 @@ def generate_config_section(
# ACME support is disabled by default. Uncomment the following line
# (and tls_certificate_path and tls_private_key_path above) to enable it.
#
#enabled: true
%(acme_enabled)senabled: true
JorikSchellekens marked this conversation as resolved.
Show resolved Hide resolved

# Endpoint to use to request certificates. If you only want to test,
# use Let's Encrypt's staging url:
Expand All @@ -354,17 +380,17 @@ def generate_config_section(
# Port number to listen on for the HTTP-01 challenge. Change this if
# you are forwarding connections through Apache/Nginx/etc.
#
#port: 80
%(acme_enabled)sport: 80

# Local addresses to listen on for incoming connections.
# Again, you may want to change this if you are forwarding connections
# through Apache/Nginx/etc.
#
#bind_addresses: ['::', '0.0.0.0']
%(acme_enabled)sbind_addresses: ['::', '0.0.0.0']

# How many days remaining on a certificate before it is renewed.
#
#reprovision_threshold: 30
%(acme_enabled)sreprovision_threshold: 30

# The domain that the certificate should be for. Normally this
# should be the same as your Matrix domain (i.e., 'server_name'), but,
Expand All @@ -378,7 +404,7 @@ def generate_config_section(
#
# If not set, defaults to your 'server_name'.
#
#domain: matrix.example.com
%(acme_enabled)sdomain: %(acme_domain)s

# file to use for the account key. This will be generated if it doesn't
# exist.
Expand Down