netcmd: Flush replUpToDateVector when restoring offline backup
authorTim Beale <timbeale@catalyst.net.nz>
Thu, 8 Nov 2018 04:34:26 +0000 (17:34 +1300)
committerAndrew Bartlett <abartlet@samba.org>
Tue, 20 Nov 2018 00:33:33 +0000 (01:33 +0100)
The replUpToDateVector could be incorrect after an offline backup was
restored. This means replication propagation dampening doesn't work
properly. In the worst case, a singleton DC would have no
replUpToDateVector at all, and so *all* objects created on that DC get
replicated every time a new DRS connection is established between 2 DCs.
This becomes a real problem if you used that singleton DC to create 100K
objects...

This patch flushes the replUpToDateVector when an offline backup gets
restored. We need to do this before we add in the new DC and remove the
old DCs.

Note that this is only a problem for offline backups. The online/rename
backups are received over DRS, and as part of the replication they
receive the latest replUpToDateVector from the DC being backed up.

Signed-off-by: Tim Beale <timbeale@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
python/samba/netcmd/domain_backup.py
selftest/knownfail.d/domain_backup [deleted file]

index afa70e3b34e840422fd721d3cf31307bb8d5d358..c7602a8be261c64d491549c87cefa77c68903a64 100644 (file)
@@ -33,7 +33,7 @@ from samba.auth import system_session
 from samba.join import DCJoinContext, join_clone, DCCloneAndRenameContext
 from samba.dcerpc.security import dom_sid
 from samba.netcmd import Option, CommandError
-from samba.dcerpc import misc, security
+from samba.dcerpc import misc, security, drsblobs
 from samba import Ldb
 from . fsmo import cmd_fsmo_seize
 from samba.provision import make_smbconf, DEFAULTSITE
@@ -51,6 +51,8 @@ from samba.mdb_util import mdb_copy
 import errno
 from subprocess import CalledProcessError
 from samba import sites
+from samba.dsdb import _dsdb_load_udv_v2
+from samba.ndr import ndr_pack
 
 
 # work out a SID (based on a free RID) to use when the domain gets restored.
@@ -417,6 +419,26 @@ class cmd_domain_backup_restore(cmd_fsmo_seize):
 
         return backup_type
 
+    def save_uptodate_vectors(self, samdb, partitions):
+        """Ensures the UTDV used by DRS is correct after an offline backup"""
+        for nc in partitions:
+            # load the replUpToDateVector we *should* have
+            utdv = _dsdb_load_udv_v2(samdb, nc)
+
+            # convert it to NDR format and write it into the DB
+            utdv_blob = drsblobs.replUpToDateVectorBlob()
+            utdv_blob.version = 2
+            utdv_blob.ctr.cursors = utdv
+            utdv_blob.ctr.count = len(utdv)
+            new_value = ndr_pack(utdv_blob)
+
+            m = ldb.Message()
+            m.dn = ldb.Dn(samdb, nc)
+            m["replUpToDateVector"] = ldb.MessageElement(new_value,
+                                                         ldb.FLAG_MOD_REPLACE,
+                                                         "replUpToDateVector")
+            samdb.modify(m)
+
     def run(self, sambaopts=None, credopts=None, backup_file=None,
             targetdir=None, newservername=None, host_ip=None, host_ip6=None,
             site=None):
@@ -471,13 +493,21 @@ class cmd_domain_backup_restore(cmd_fsmo_seize):
             site = self.create_default_site(samdb, logger)
             logger.info("Adding new DC to site '{0}'".format(site))
 
-        # Create account using the join_add_objects function in the join object
-        # We need namingContexts, account control flags, and the sid saved by
-        # the backup process.
+        # read the naming contexts out of the DB
         res = samdb.search(base="", scope=ldb.SCOPE_BASE,
                            attrs=['namingContexts'])
         ncs = [str(r) for r in res[0].get('namingContexts')]
 
+        # for offline backups we need to make sure the upToDateness info
+        # contains the invocation-ID and highest-USN of the DC we backed up.
+        # Otherwise replication propagation dampening won't correctly filter
+        # objects created by that DC
+        if backup_type == "offline":
+            self.save_uptodate_vectors(samdb, ncs)
+
+        # Create account using the join_add_objects function in the join object
+        # We need namingContexts, account control flags, and the sid saved by
+        # the backup process.
         creds = credopts.get_credentials(lp)
         ctx = DCJoinContext(logger, creds=creds, lp=lp, site=site,
                             forced_local_samdb=samdb,
diff --git a/selftest/knownfail.d/domain_backup b/selftest/knownfail.d/domain_backup
deleted file mode 100644 (file)
index 0484a92..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-samba.tests.domain_backup.samba.tests.domain_backup.DomainBackupOffline.test_backup_restore\(ad_dc:local\)
-samba.tests.domain_backup.samba.tests.domain_backup.DomainBackupOffline.test_backup_restore_into_site\(ad_dc:local\)
-samba.tests.domain_backup.samba.tests.domain_backup.DomainBackupOffline.test_backup_restore_with_conf\(ad_dc:local\)