Browse Source

Allow overriding of outbound replication URI

Richard van der Hoff 5 years ago
parent
commit
ab69bd0b40
5 changed files with 58 additions and 20 deletions
  1. 7 0
      README.rst
  2. 22 0
      docs/replication.md
  3. 4 10
      sydent/db/peers.py
  4. 3 3
      sydent/http/httpsclient.py
  5. 22 7
      sydent/replication/peer.py

+ 7 - 0
README.rst

@@ -75,3 +75,10 @@ To use it::
     curl -XPOST 'http://localhost:8091/_matrix/identity/internal/bind' -H "Content-Type: application/json" -d '{"address": "matthew@arasphere.net", "medium": "email", "mxid": "@matthew:matrix.org"}'
 
 The response has the same format as ``/_matrix/identity/api/v1/3pid/bind``.
+
+
+Replication
+===========
+
+It is possible to configure a mesh of sydents which replicate identity bindings
+between each other. See `<docs/replication.md>`_.

+ 22 - 0
docs/replication.md

@@ -0,0 +1,22 @@
+Intra-sydent replication
+------------------------
+
+Replication takes place over HTTPs connections, using server and client TLS
+certificates (currently each sydent instance can only be configured with a
+single certificate which is used as both a server and a client certificate).
+
+Replication peers are (currently) configured in the sqlite database; you
+need to add a row to both the `peers` and `peer_pubkeys` tables.
+
+The `name` / `peername` in these tables must match the `server_name` in the
+configuration of the peer, which is the name that peer will use to sign
+associations.
+
+Inbound replication connections are authenticated according to the Common Name
+in the client certificate, so that must also match the `server_name`.
+
+By default, that name is also used for outbound connections, but it is possible
+to override this by adding a setting to the config file such as:
+
+    [peer.example.com]
+    base_replication_url = https://internal-address.example.com:4434

+ 4 - 10
sydent/db/peers.py

@@ -40,10 +40,8 @@ class PeerStore:
         if len(pubkeys) == 0:
             return None
 
-        p = RemotePeer(self.sydent, serverName, pubkeys)
+        p = RemotePeer(self.sydent, serverName, port, pubkeys)
         p.lastSentVersion = lastSentVer
-        if port:
-            p.port = port
 
         return p
 
@@ -62,10 +60,8 @@ class PeerStore:
         for row in res.fetchall():
             if row[0] != peername:
                 if len(pubkeys) > 0:
-                    p = RemotePeer(self.sydent, peername, pubkeys)
+                    p = RemotePeer(self.sydent, peername, port, pubkeys)
                     p.lastSentVersion = lastSentVer
-                    if port:
-                        p.port = port
                     peers.append(p)
                     pubkeys = {}
                 peername = row[0]
@@ -74,10 +70,8 @@ class PeerStore:
             pubkeys[row[3]] = row[4]
 
         if len(pubkeys) > 0:
-            p = RemotePeer(self.sydent, peername, pubkeys)
+            p = RemotePeer(self.sydent, peername, port, pubkeys)
             p.lastSentVersion = lastSentVer
-            if port:
-                p.port = port
             peers.append(p)
             pubkeys = {}
 
@@ -87,4 +81,4 @@ class PeerStore:
         cur = self.sydent.db.cursor()
         res = cur.execute("update peers set lastSentVersion = ?, lastPokeSucceededAt = ? "
                           "where name = ?", (lastSentVersion, lastPokeSucceeded, peerName))
-        self.sydent.db.commit()
+        self.sydent.db.commit()

+ 3 - 3
sydent/http/httpsclient.py

@@ -49,13 +49,13 @@ class ReplicationHttpsClient:
             #                                                      trustRoot=self.sydent.sslComponents.trustRoot)
             self.agent = Agent(twisted.internet.reactor, SydentPolicyForHTTPS(self.sydent))
 
-    def postJson(self, host, port, path, jsonObject):
+    def postJson(self, uri, jsonObject):
+        logger.debug("POSTing request to %s", uri)
         if not self.agent:
             logger.error("HTTPS post attempted but HTTPS is not configured")
             return
 
         headers = Headers({'Content-Type': ['application/json'], 'User-Agent': ['Sydent']})
-        uri = "https://%s:%s%s" % (host, port, path)
         reqDeferred = self.agent.request('POST', uri.encode('utf8'), headers,
                                          FileBodyProducer(StringIO(json.dumps(jsonObject))))
 
@@ -70,4 +70,4 @@ class SydentPolicyForHTTPS(object):
     def creatorForNetloc(self, hostname, port):
         return optionsForClientTLS(hostname.decode("ascii"),
                                    trustRoot=self.sydent.sslComponents.trustRoot,
-                                   clientCertificate=self.sydent.sslComponents.myPrivateCertificate)
+                                   clientCertificate=self.sydent.sslComponents.myPrivateCertificate)

+ 22 - 7
sydent/replication/peer.py

@@ -1,6 +1,7 @@
 # -*- coding: utf-8 -*-
 
 # Copyright 2014 OpenMarket Ltd
+# Copyright 2019 New Vector Ltd
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -13,6 +14,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 ConfigParser
 
 from sydent.db.threepid_associations import GlobalAssociationStore
 from sydent.threepid import threePidAssocFromDict
@@ -26,7 +28,6 @@ import logging
 import json
 import binascii
 
-import twisted.internet.reactor
 from twisted.internet import defer
 from twisted.web.client import readBody
 
@@ -79,10 +80,25 @@ class LocalPeer(Peer):
 
 
 class RemotePeer(Peer):
-    def __init__(self, sydent, server_name, pubkeys):
+    def __init__(self, sydent, server_name, port, pubkeys):
         super(RemotePeer, self).__init__(server_name, pubkeys)
         self.sydent = sydent
-        self.port = 1001
+
+        # look up or build the replication URL
+        try:
+            replication_url = sydent.cfg.get(
+                "peer.%s" % server_name, "base_replication_url",
+            )
+        except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
+            if not port:
+                port = 1001
+            replication_url = "https://%s:%i" % (server_name, port)
+
+        if replication_url[-1:] != '/':
+            replication_url += "/"
+
+        replication_url += "_matrix/identity/replicate/v1/push"
+        self.replication_url = replication_url
 
         # Get verify key for this peer
 
@@ -133,10 +149,9 @@ class RemotePeer(Peer):
     def pushUpdates(self, sgAssocs):
         body = {'sgAssocs': sgAssocs}
 
-        reqDeferred = self.sydent.replicationHttpsClient.postJson(self.servername,
-                                                                  self.port,
-                                                                  '/_matrix/identity/replicate/v1/push',
-                                                                  body)
+        reqDeferred = self.sydent.replicationHttpsClient.postJson(
+            self.replication_url, body
+        )
 
         # XXX: We'll also need to prune the deleted associations out of the
         # local associations table once they've been replicated to all peers