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

Commit

Permalink
Merge pull request #5516 from matrix-org/rav/acme_key_path
Browse files Browse the repository at this point in the history
Allow configuration of the path used for ACME account keys.
  • Loading branch information
richvdh authored Jun 24, 2019
2 parents c3c6b00 + 5d9c101 commit cf7aef1
Show file tree
Hide file tree
Showing 10 changed files with 62 additions and 12 deletions.
1 change: 1 addition & 0 deletions changelog.d/5516.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Allow configuration of the path used for ACME account keys.
1 change: 1 addition & 0 deletions changelog.d/5521.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Allow configuration of the path used for ACME account keys.
1 change: 0 additions & 1 deletion changelog.d/5521.misc

This file was deleted.

1 change: 1 addition & 0 deletions changelog.d/5522.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Allow configuration of the path used for ACME account keys.
1 change: 0 additions & 1 deletion changelog.d/5522.misc

This file was deleted.

7 changes: 7 additions & 0 deletions docs/sample_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,13 @@ acme:
#
#domain: matrix.example.com

# file to use for the account key. This will be generated if it doesn't
# exist.
#
# If unspecified, we will use CONFDIR/client.key.
#
account_key_file: DATADIR/acme_account.key

# List of allowed TLS fingerprints for this server to publish along
# with the signing keys for this server. Other matrix servers that
# make HTTPS requests to this server will check that the TLS
Expand Down
3 changes: 0 additions & 3 deletions synapse/config/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -414,9 +414,6 @@ def read_config_files(self, config_files, config_dir_path, data_dir_path):
Returns: dict
"""
# FIXME: get rid of this
self.config_dir_path = config_dir_path

# first we read the config files into a dict
specified_config = {}
for config_file in config_files:
Expand Down
16 changes: 14 additions & 2 deletions synapse/config/tls.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@


class TlsConfig(Config):
def read_config(self, config, **kwargs):
def read_config(self, config, config_dir_path, **kwargs):

acme_config = config.get("acme", None)
if acme_config is None:
Expand All @@ -50,6 +50,10 @@ def read_config(self, config, **kwargs):
self.acme_reprovision_threshold = acme_config.get("reprovision_threshold", 30)
self.acme_domain = acme_config.get("domain", config.get("server_name"))

self.acme_account_key_file = self.abspath(
acme_config.get("account_key_file", config_dir_path + "/client.key")
)

self.tls_certificate_file = self.abspath(config.get("tls_certificate_path"))
self.tls_private_key_file = self.abspath(config.get("tls_private_key_path"))

Expand Down Expand Up @@ -213,11 +217,12 @@ def read_certificate_from_disk(self, require_cert_and_key):
if sha256_fingerprint not in sha256_fingerprints:
self.tls_fingerprints.append({"sha256": sha256_fingerprint})

def default_config(self, config_dir_path, server_name, **kwargs):
def default_config(self, config_dir_path, server_name, data_dir_path, **kwargs):
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"
default_acme_account_file = os.path.join(data_dir_path, "acme_account.key")

# this is to avoid the max line length. Sorrynotsorry
proxypassline = (
Expand Down Expand Up @@ -343,6 +348,13 @@ def default_config(self, config_dir_path, server_name, **kwargs):
#
#domain: matrix.example.com
# file to use for the account key. This will be generated if it doesn't
# exist.
#
# If unspecified, we will use CONFDIR/client.key.
#
account_key_file: %(default_acme_account_file)s
# List of allowed TLS fingerprints for this server to publish along
# with the signing keys for this server. Other matrix servers that
# make HTTPS requests to this server will check that the TLS
Expand Down
2 changes: 1 addition & 1 deletion synapse/handlers/acme.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def start_listening(self):
self._issuer = acme_issuing_service.create_issuing_service(
self.reactor,
acme_url=self.hs.config.acme_url,
pem_path=self.hs.config.config_dir_path,
account_key_file=self.hs.config.acme_account_key_file,
well_known_resource=well_known,
)

Expand Down
41 changes: 37 additions & 4 deletions synapse/handlers/acme_issuing_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,28 +21,34 @@
only need (and may only have available) if we are doing ACME, so is designed to be
imported conditionally.
"""
import logging

import attr
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from josepy import JWKRSA
from josepy.jwa import RS256
from txacme.challenges import HTTP01Responder
from txacme.client import Client
from txacme.endpoint import load_or_create_client_key
from txacme.interfaces import ICertificateStore
from txacme.service import AcmeIssuingService
from txacme.util import generate_private_key
from zope.interface import implementer

from twisted.internet import defer
from twisted.python.filepath import FilePath
from twisted.python.url import URL

logger = logging.getLogger(__name__)

def create_issuing_service(reactor, acme_url, pem_path, well_known_resource):

def create_issuing_service(reactor, acme_url, account_key_file, well_known_resource):
"""Create an ACME issuing service, and attach it to a web Resource
Args:
reactor: twisted reactor
acme_url (str): URL to use to request certificates
pem_path (str): where to store the client key
account_key_file (str): where to store the account key
well_known_resource (twisted.web.IResource): web resource for .well-known.
we will attach a child resource for "acme-challenge".
Expand All @@ -61,7 +67,7 @@ def create_issuing_service(reactor, acme_url, pem_path, well_known_resource):
lambda: Client.from_url(
reactor=reactor,
url=URL.from_text(acme_url),
key=load_or_create_client_key(FilePath(pem_path)),
key=load_or_create_client_key(account_key_file),
alg=RS256,
)
),
Expand All @@ -82,3 +88,30 @@ class ErsatzStore(object):
def store(self, server_name, pem_objects):
self.certs[server_name] = [o.as_bytes() for o in pem_objects]
return defer.succeed(None)


def load_or_create_client_key(key_file):
"""Load the ACME account key from a file, creating it if it does not exist.
Args:
key_file (str): name of the file to use as the account key
"""
# this is based on txacme.endpoint.load_or_create_client_key, but doesn't
# hardcode the 'client.key' filename
acme_key_file = FilePath(key_file)
if acme_key_file.exists():
logger.info("Loading ACME account key from '%s'", acme_key_file)
key = serialization.load_pem_private_key(
acme_key_file.getContent(), password=None, backend=default_backend()
)
else:
logger.info("Saving new ACME account key to '%s'", acme_key_file)
key = generate_private_key("rsa")
acme_key_file.setContent(
key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.TraditionalOpenSSL,
encryption_algorithm=serialization.NoEncryption(),
)
)
return JWKRSA(key=key)

0 comments on commit cf7aef1

Please sign in to comment.