Browse Source

upgrade --archives-tam: make sure all archives are TAM authenticated

borg check (rebuild_manifest and rebuild_refcounts) drops archives without TAM,
so let's just always add the TAM.

for unencrypted repos (encryption=none) the TAM is insecure,
but without encryption and authentication, there is no expectation
of security anyway.
Thomas Waldmann 1 year ago
parent
commit
005662a8fe
1 changed files with 48 additions and 5 deletions
  1. 48 5
      src/borg/archiver.py

+ 48 - 5
src/borg/archiver.py

@@ -72,10 +72,11 @@ try:
     from .helpers import umount
     from .helpers import umount
     from .helpers import msgpack, msgpack_fallback
     from .helpers import msgpack, msgpack_fallback
     from .helpers import uid2user, gid2group
     from .helpers import uid2user, gid2group
+    from .helpers import safe_decode
     from .nanorst import rst_to_terminal
     from .nanorst import rst_to_terminal
     from .patterns import ArgparsePatternAction, ArgparseExcludeFileAction, ArgparsePatternFileAction, parse_exclude_pattern
     from .patterns import ArgparsePatternAction, ArgparseExcludeFileAction, ArgparsePatternFileAction, parse_exclude_pattern
     from .patterns import PatternMatcher
     from .patterns import PatternMatcher
-    from .item import Item
+    from .item import Item, ArchiveItem
     from .platform import get_flags, get_process_id, SyncFile
     from .platform import get_flags, get_process_id, SyncFile
     from .remote import RepositoryServer, RemoteRepository, cache_if_remote
     from .remote import RepositoryServer, RemoteRepository, cache_if_remote
     from .repository import Repository, LIST_SCAN_LIMIT, TAG_PUT, TAG_DELETE, TAG_COMMIT
     from .repository import Repository, LIST_SCAN_LIMIT, TAG_PUT, TAG_DELETE, TAG_COMMIT
@@ -1711,10 +1712,34 @@ class Archiver:
                           DASHES, logger=logging.getLogger('borg.output.stats'))
                           DASHES, logger=logging.getLogger('borg.output.stats'))
         return self.exit_code
         return self.exit_code
 
 
-    @with_repository(fake=('tam', 'disable_tam'), invert_fake=True, manifest=False, exclusive=True)
+    @with_repository(fake=('tam', 'disable_tam', 'archives_tam'), invert_fake=True, manifest=False, exclusive=True)
     def do_upgrade(self, args, repository, manifest=None, key=None):
     def do_upgrade(self, args, repository, manifest=None, key=None):
         """upgrade a repository from a previous version"""
         """upgrade a repository from a previous version"""
-        if args.tam:
+        if args.archives_tam:
+            manifest, key = Manifest.load(repository, (Manifest.Operation.CHECK,), force_tam_not_required=args.force)
+            with Cache(repository, key, manifest) as cache:
+                stats = Statistics()
+                for info in manifest.archives.list(sort_by=['ts']):
+                    archive_id = info.id
+                    archive_formatted = format_archive(info)
+                    cdata = repository.get(archive_id)
+                    data = key.decrypt(archive_id, cdata)
+                    archive, verified = key.unpack_and_verify_archive(data, force_tam_not_required=True)
+                    if not verified:  # we do not have an archive TAM yet -> add TAM now!
+                        archive = ArchiveItem(internal_dict=archive)
+                        archive.cmdline = [safe_decode(arg) for arg in archive.cmdline]
+                        data = key.pack_and_authenticate_metadata(archive.as_dict(), context=b'archive')
+                        new_archive_id = key.id_hash(data)
+                        cache.add_chunk(new_archive_id, data, stats)
+                        cache.chunk_decref(archive_id, stats)
+                        manifest.archives[info.name] = (new_archive_id, info.ts)
+                        print("Added archive TAM:   %s -> [%s]" % (archive_formatted, bin_to_hex(new_archive_id)))
+                    else:
+                        print("Archive TAM present: %s" % archive_formatted)
+                manifest.write()
+                repository.commit(compact=False)
+                cache.commit()
+        elif args.tam:
             manifest, key = Manifest.load(repository, (Manifest.Operation.CHECK,), force_tam_not_required=args.force)
             manifest, key = Manifest.load(repository, (Manifest.Operation.CHECK,), force_tam_not_required=args.force)
 
 
             if not hasattr(key, 'change_passphrase'):
             if not hasattr(key, 'change_passphrase'):
@@ -1722,10 +1747,9 @@ class Archiver:
                 return EXIT_ERROR
                 return EXIT_ERROR
 
 
             if not manifest.tam_verified or not manifest.config.get(b'tam_required', False):
             if not manifest.tam_verified or not manifest.config.get(b'tam_required', False):
-                # The standard archive listing doesn't include the archive ID like in borg 1.1.x
                 print('Manifest contents:')
                 print('Manifest contents:')
                 for archive_info in manifest.archives.list(sort_by=['ts']):
                 for archive_info in manifest.archives.list(sort_by=['ts']):
-                    print(format_archive(archive_info), '[%s]' % bin_to_hex(archive_info.id))
+                    print(format_archive(archive_info))
                 manifest.config[b'tam_required'] = True
                 manifest.config[b'tam_required'] = True
                 manifest.write()
                 manifest.write()
                 repository.commit()
                 repository.commit()
@@ -4103,6 +4127,23 @@ class Archiver:
         Borg 1.x.y upgrades
         Borg 1.x.y upgrades
         +++++++++++++++++++
         +++++++++++++++++++
 
 
+        Archive TAM authentication:
+
+        Use ``borg upgrade --archives-tam REPO`` to add archive TAMs to all
+        archives that are not TAM authenticated yet.
+        This is a convenient method to just trust all archives present - if
+        an archive does not have TAM authentication yet, a TAM will be added.
+        Archives created by old borg versions < 1.0.9 do not have TAMs.
+        Archives created by newer borg version should have TAMs already.
+        If you have a high risk environment, you should not just run this,
+        but first verify that the archives are authentic and not malicious
+        (== have good content, have a good timestamp).
+        Borg 1.2.5+ needs all archives to be TAM authenticated for safety reasons.
+
+        This upgrade needs to be done once per repository.
+
+        Manifest TAM authentication:
+
         Use ``borg upgrade --tam REPO`` to require manifest authentication
         Use ``borg upgrade --tam REPO`` to require manifest authentication
         introduced with Borg 1.0.9 to address security issues. This means
         introduced with Borg 1.0.9 to address security issues. This means
         that modifying the repository after doing this with a version prior
         that modifying the repository after doing this with a version prior
@@ -4183,6 +4224,8 @@ class Archiver:
                                help='Enable manifest authentication (in key and cache) (Borg 1.0.9 and later).')
                                help='Enable manifest authentication (in key and cache) (Borg 1.0.9 and later).')
         subparser.add_argument('--disable-tam', dest='disable_tam', action='store_true',
         subparser.add_argument('--disable-tam', dest='disable_tam', action='store_true',
                                help='Disable manifest authentication (in key and cache).')
                                help='Disable manifest authentication (in key and cache).')
+        subparser.add_argument('--archives-tam', dest='archives_tam', action='store_true',
+                               help='add TAM authentication for all archives.')
         subparser.add_argument('location', metavar='REPOSITORY', nargs='?', default='',
         subparser.add_argument('location', metavar='REPOSITORY', nargs='?', default='',
                                type=location_validator(archive=False),
                                type=location_validator(archive=False),
                                help='path to the repository to be upgraded')
                                help='path to the repository to be upgraded')