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

Commit 4f6ee99

Browse files
committed
Config templating (#5900)
2 parents 35a521b + 6d97843 commit 4f6ee99

File tree

9 files changed

+366
-46
lines changed

9 files changed

+366
-46
lines changed

changelog.d/5900.feature

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add support for config templating.

docs/sample_config.yaml

+8-8
Original file line numberDiff line numberDiff line change
@@ -212,9 +212,9 @@ listeners:
212212
#
213213
- port: 8008
214214
tls: false
215-
bind_addresses: ['::1', '127.0.0.1']
216215
type: http
217216
x_forwarded: true
217+
bind_addresses: ['::1', '127.0.0.1']
218218

219219
resources:
220220
- names: [client, federation]
@@ -467,10 +467,10 @@ retention:
467467
# permission to listen on port 80.
468468
#
469469
acme:
470-
# ACME support is disabled by default. Uncomment the following line
471-
# (and tls_certificate_path and tls_private_key_path above) to enable it.
470+
# ACME support is disabled by default. Set this to `true` and uncomment
471+
# tls_certificate_path and tls_private_key_path above to enable it.
472472
#
473-
#enabled: true
473+
enabled: False
474474

475475
# Endpoint to use to request certificates. If you only want to test,
476476
# use Let's Encrypt's staging url:
@@ -481,17 +481,17 @@ acme:
481481
# Port number to listen on for the HTTP-01 challenge. Change this if
482482
# you are forwarding connections through Apache/Nginx/etc.
483483
#
484-
#port: 80
484+
port: 80
485485

486486
# Local addresses to listen on for incoming connections.
487487
# Again, you may want to change this if you are forwarding connections
488488
# through Apache/Nginx/etc.
489489
#
490-
#bind_addresses: ['::', '0.0.0.0']
490+
bind_addresses: ['::', '0.0.0.0']
491491

492492
# How many days remaining on a certificate before it is renewed.
493493
#
494-
#reprovision_threshold: 30
494+
reprovision_threshold: 30
495495

496496
# The domain that the certificate should be for. Normally this
497497
# should be the same as your Matrix domain (i.e., 'server_name'), but,
@@ -505,7 +505,7 @@ acme:
505505
#
506506
# If not set, defaults to your 'server_name'.
507507
#
508-
#domain: matrix.example.com
508+
domain: matrix.example.com
509509

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

synapse/config/_base.py

+37
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,11 @@ def generate_config(
182182
generate_secrets=False,
183183
report_stats=None,
184184
open_private_ports=False,
185+
listeners=None,
186+
database_conf=None,
187+
tls_certificate_path=None,
188+
tls_private_key_path=None,
189+
acme_domain=None,
185190
):
186191
"""Build a default configuration file
187192
@@ -208,6 +213,33 @@ def generate_config(
208213
open_private_ports (bool): True to leave private ports (such as the non-TLS
209214
HTTP listener) open to the internet.
210215
216+
listeners (list(dict)|None): A list of descriptions of the listeners
217+
synapse should start with each of which specifies a port (str), a list of
218+
resources (list(str)), tls (bool) and type (str). For example:
219+
[{
220+
"port": 8448,
221+
"resources": [{"names": ["federation"]}],
222+
"tls": True,
223+
"type": "http",
224+
},
225+
{
226+
"port": 443,
227+
"resources": [{"names": ["client"]}],
228+
"tls": False,
229+
"type": "http",
230+
}],
231+
232+
233+
database (str|None): The database type to configure, either `psycog2`
234+
or `sqlite3`.
235+
236+
tls_certificate_path (str|None): The path to the tls certificate.
237+
238+
tls_private_key_path (str|None): The path to the tls private key.
239+
240+
acme_domain (str|None): The domain acme will try to validate. If
241+
specified acme will be enabled.
242+
211243
Returns:
212244
str: the yaml config file
213245
"""
@@ -221,6 +253,11 @@ def generate_config(
221253
generate_secrets=generate_secrets,
222254
report_stats=report_stats,
223255
open_private_ports=open_private_ports,
256+
listeners=listeners,
257+
database_conf=database_conf,
258+
tls_certificate_path=tls_certificate_path,
259+
tls_private_key_path=tls_private_key_path,
260+
acme_domain=acme_domain,
224261
)
225262
)
226263

synapse/config/database.py

+19-8
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
# See the License for the specific language governing permissions and
1414
# limitations under the License.
1515
import os
16+
from textwrap import indent
17+
18+
import yaml
1619

1720
from ._base import Config
1821

@@ -38,20 +41,28 @@ def read_config(self, config, **kwargs):
3841

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

41-
def generate_config_section(self, data_dir_path, **kwargs):
42-
database_path = os.path.join(data_dir_path, "homeserver.db")
43-
return (
44-
"""\
45-
## Database ##
46-
47-
database:
48-
# The database engine name
44+
def generate_config_section(self, data_dir_path, database_conf, **kwargs):
45+
if not database_conf:
46+
database_path = os.path.join(data_dir_path, "homeserver.db")
47+
database_conf = (
48+
"""# The database engine name
4949
name: "sqlite3"
5050
# Arguments to pass to the engine
5151
args:
5252
# Path to the database
5353
database: "%(database_path)s"
54+
"""
55+
% locals()
56+
)
57+
else:
58+
database_conf = indent(yaml.dump(database_conf), " " * 10).lstrip()
59+
60+
return (
61+
"""\
62+
## Database ##
5463
64+
database:
65+
%(database_conf)s
5566
# Number of events to cache in memory.
5667
#
5768
#event_cache_size: 10K

synapse/config/server.py

+67-17
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@
1717

1818
import logging
1919
import os.path
20+
import re
21+
from textwrap import indent
2022

2123
import attr
24+
import yaml
2225
from netaddr import IPSet
2326

2427
from synapse.api.room_versions import KNOWN_ROOM_VERSIONS
@@ -482,7 +485,7 @@ def has_tls_listener(self):
482485
return any(l["tls"] for l in self.listeners)
483486

484487
def generate_config_section(
485-
self, server_name, data_dir_path, open_private_ports, **kwargs
488+
self, server_name, data_dir_path, open_private_ports, listeners, **kwargs
486489
):
487490
_, bind_port = parse_and_validate_server_name(server_name)
488491
if bind_port is not None:
@@ -496,11 +499,68 @@ def generate_config_section(
496499
# Bring DEFAULT_ROOM_VERSION into the local-scope for use in the
497500
# default config string
498501
default_room_version = DEFAULT_ROOM_VERSION
502+
secure_listeners = []
503+
unsecure_listeners = []
504+
private_addresses = ["::1", "127.0.0.1"]
505+
if listeners:
506+
for listener in listeners:
507+
if listener["tls"]:
508+
secure_listeners.append(listener)
509+
else:
510+
# If we don't want open ports we need to bind the listeners
511+
# to some address other than 0.0.0.0. Here we chose to use
512+
# localhost.
513+
# If the addresses are already bound we won't overwrite them
514+
# however.
515+
if not open_private_ports:
516+
listener.setdefault("bind_addresses", private_addresses)
517+
518+
unsecure_listeners.append(listener)
519+
520+
secure_http_bindings = indent(
521+
yaml.dump(secure_listeners), " " * 10
522+
).lstrip()
523+
524+
unsecure_http_bindings = indent(
525+
yaml.dump(unsecure_listeners), " " * 10
526+
).lstrip()
527+
528+
if not unsecure_listeners:
529+
unsecure_http_bindings = (
530+
"""- port: %(unsecure_port)s
531+
tls: false
532+
type: http
533+
x_forwarded: true"""
534+
% locals()
535+
)
536+
537+
if not open_private_ports:
538+
unsecure_http_bindings += (
539+
"\n bind_addresses: ['::1', '127.0.0.1']"
540+
)
541+
542+
unsecure_http_bindings += """
543+
544+
resources:
545+
- names: [client, federation]
546+
compress: false"""
547+
548+
if listeners:
549+
# comment out this block
550+
unsecure_http_bindings = "#" + re.sub(
551+
"\n {10}",
552+
lambda match: match.group(0) + "#",
553+
unsecure_http_bindings,
554+
)
499555

500-
unsecure_http_binding = "port: %i\n tls: false" % (unsecure_port,)
501-
if not open_private_ports:
502-
unsecure_http_binding += (
503-
"\n bind_addresses: ['::1', '127.0.0.1']"
556+
if not secure_listeners:
557+
secure_http_bindings = (
558+
"""#- port: %(bind_port)s
559+
# type: http
560+
# tls: true
561+
# resources:
562+
# - names: [client, federation]"""
563+
% locals()
504564
)
505565

506566
return (
@@ -693,25 +753,15 @@ def generate_config_section(
693753
# will also need to give Synapse a TLS key and certificate: see the TLS section
694754
# below.)
695755
#
696-
#- port: %(bind_port)s
697-
# type: http
698-
# tls: true
699-
# resources:
700-
# - names: [client, federation]
756+
%(secure_http_bindings)s
701757
702758
# Unsecure HTTP listener: for when matrix traffic passes through a reverse proxy
703759
# that unwraps TLS.
704760
#
705761
# If you plan to use a reverse proxy, please see
706762
# https://github.com/matrix-org/synapse/blob/master/docs/reverse_proxy.rst.
707763
#
708-
- %(unsecure_http_binding)s
709-
type: http
710-
x_forwarded: true
711-
712-
resources:
713-
- names: [client, federation]
714-
compress: false
764+
%(unsecure_http_bindings)s
715765
716766
# example additional_resources:
717767
#

synapse/config/tls.py

+38-12
Original file line numberDiff line numberDiff line change
@@ -239,12 +239,38 @@ def read_certificate_from_disk(self, require_cert_and_key):
239239
self.tls_fingerprints.append({"sha256": sha256_fingerprint})
240240

241241
def generate_config_section(
242-
self, config_dir_path, server_name, data_dir_path, **kwargs
242+
self,
243+
config_dir_path,
244+
server_name,
245+
data_dir_path,
246+
tls_certificate_path,
247+
tls_private_key_path,
248+
acme_domain,
249+
**kwargs
243250
):
251+
"""If the acme_domain is specified acme will be enabled.
252+
If the TLS paths are not specified the default will be certs in the
253+
config directory"""
254+
244255
base_key_name = os.path.join(config_dir_path, server_name)
245256

246-
tls_certificate_path = base_key_name + ".tls.crt"
247-
tls_private_key_path = base_key_name + ".tls.key"
257+
if bool(tls_certificate_path) != bool(tls_private_key_path):
258+
raise ConfigError(
259+
"Please specify both a cert path and a key path or neither."
260+
)
261+
262+
tls_enabled = (
263+
"" if tls_certificate_path and tls_private_key_path or acme_domain else "#"
264+
)
265+
266+
if not tls_certificate_path:
267+
tls_certificate_path = base_key_name + ".tls.crt"
268+
if not tls_private_key_path:
269+
tls_private_key_path = base_key_name + ".tls.key"
270+
271+
acme_enabled = bool(acme_domain)
272+
acme_domain = "matrix.example.com"
273+
248274
default_acme_account_file = os.path.join(data_dir_path, "acme_account.key")
249275

250276
# this is to avoid the max line length. Sorrynotsorry
@@ -269,11 +295,11 @@ def generate_config_section(
269295
# instance, if using certbot, use `fullchain.pem` as your certificate,
270296
# not `cert.pem`).
271297
#
272-
#tls_certificate_path: "%(tls_certificate_path)s"
298+
%(tls_enabled)stls_certificate_path: "%(tls_certificate_path)s"
273299
274300
# PEM-encoded private key for TLS
275301
#
276-
#tls_private_key_path: "%(tls_private_key_path)s"
302+
%(tls_enabled)stls_private_key_path: "%(tls_private_key_path)s"
277303
278304
# Whether to verify TLS server certificates for outbound federation requests.
279305
#
@@ -340,10 +366,10 @@ def generate_config_section(
340366
# permission to listen on port 80.
341367
#
342368
acme:
343-
# ACME support is disabled by default. Uncomment the following line
344-
# (and tls_certificate_path and tls_private_key_path above) to enable it.
369+
# ACME support is disabled by default. Set this to `true` and uncomment
370+
# tls_certificate_path and tls_private_key_path above to enable it.
345371
#
346-
#enabled: true
372+
enabled: %(acme_enabled)s
347373
348374
# Endpoint to use to request certificates. If you only want to test,
349375
# use Let's Encrypt's staging url:
@@ -354,17 +380,17 @@ def generate_config_section(
354380
# Port number to listen on for the HTTP-01 challenge. Change this if
355381
# you are forwarding connections through Apache/Nginx/etc.
356382
#
357-
#port: 80
383+
port: 80
358384
359385
# Local addresses to listen on for incoming connections.
360386
# Again, you may want to change this if you are forwarding connections
361387
# through Apache/Nginx/etc.
362388
#
363-
#bind_addresses: ['::', '0.0.0.0']
389+
bind_addresses: ['::', '0.0.0.0']
364390
365391
# How many days remaining on a certificate before it is renewed.
366392
#
367-
#reprovision_threshold: 30
393+
reprovision_threshold: 30
368394
369395
# The domain that the certificate should be for. Normally this
370396
# should be the same as your Matrix domain (i.e., 'server_name'), but,
@@ -378,7 +404,7 @@ def generate_config_section(
378404
#
379405
# If not set, defaults to your 'server_name'.
380406
#
381-
#domain: matrix.example.com
407+
domain: %(acme_domain)s
382408
383409
# file to use for the account key. This will be generated if it doesn't
384410
# exist.

0 commit comments

Comments
 (0)