فهرست منبع

Add --append-only to borg serve

Fixes #1168
Lee Bousfield 9 سال پیش
والد
کامیت
c515d6018d
5فایلهای تغییر یافته به همراه24 افزوده شده و 7 حذف شده
  1. 5 2
      borg/archiver.py
  2. 3 2
      borg/remote.py
  3. 5 2
      borg/repository.py
  4. 4 1
      borg/testsuite/repository.py
  5. 7 0
      docs/usage.rst

+ 5 - 2
borg/archiver.py

@@ -115,7 +115,7 @@ class Archiver:
     def do_serve(self, args):
     def do_serve(self, args):
         """Start in server mode. This command is usually not used manually.
         """Start in server mode. This command is usually not used manually.
         """
         """
-        return RepositoryServer(restrict_to_paths=args.restrict_to_paths).serve()
+        return RepositoryServer(restrict_to_paths=args.restrict_to_paths, append_only=args.append_only).serve()
 
 
     @with_repository(create=True, exclusive=True, manifest=False)
     @with_repository(create=True, exclusive=True, manifest=False)
     def do_init(self, args, repository):
     def do_init(self, args, repository):
@@ -916,6 +916,8 @@ class Archiver:
         subparser.set_defaults(func=self.do_serve)
         subparser.set_defaults(func=self.do_serve)
         subparser.add_argument('--restrict-to-path', dest='restrict_to_paths', action='append',
         subparser.add_argument('--restrict-to-path', dest='restrict_to_paths', action='append',
                                metavar='PATH', help='restrict repository access to PATH')
                                metavar='PATH', help='restrict repository access to PATH')
+        subparser.add_argument('--append-only', dest='append_only', action='store_true',
+                               help='only allow appending to repository segment files')
         init_epilog = textwrap.dedent("""
         init_epilog = textwrap.dedent("""
         This command initializes an empty repository. A repository is a filesystem
         This command initializes an empty repository. A repository is a filesystem
         directory containing the deduplicated data from zero or more archives.
         directory containing the deduplicated data from zero or more archives.
@@ -1491,8 +1493,9 @@ class Archiver:
             if result.func != forced_result.func:
             if result.func != forced_result.func:
                 # someone is trying to execute a different borg subcommand, don't do that!
                 # someone is trying to execute a different borg subcommand, don't do that!
                 return forced_result
                 return forced_result
-            # the only thing we take from the forced "borg serve" ssh command is --restrict-to-path
+            # we only take specific options from the forced "borg serve" command:
             result.restrict_to_paths = forced_result.restrict_to_paths
             result.restrict_to_paths = forced_result.restrict_to_paths
+            result.append_only = forced_result.append_only
         return result
         return result
 
 
     def parse_args(self, args=None):
     def parse_args(self, args=None):

+ 3 - 2
borg/remote.py

@@ -54,9 +54,10 @@ class RepositoryServer:  # pragma: no cover
         'break_lock',
         'break_lock',
     )
     )
 
 
-    def __init__(self, restrict_to_paths):
+    def __init__(self, restrict_to_paths, append_only):
         self.repository = None
         self.repository = None
         self.restrict_to_paths = restrict_to_paths
         self.restrict_to_paths = restrict_to_paths
+        self.append_only = append_only
 
 
     def serve(self):
     def serve(self):
         stdin_fd = sys.stdin.fileno()
         stdin_fd = sys.stdin.fileno()
@@ -123,7 +124,7 @@ class RepositoryServer:  # pragma: no cover
                     break
                     break
             else:
             else:
                 raise PathNotAllowed(path)
                 raise PathNotAllowed(path)
-        self.repository = Repository(path, create, lock_wait=lock_wait, lock=lock)
+        self.repository = Repository(path, create, lock_wait=lock_wait, lock=lock, append_only=self.append_only)
         self.repository.__enter__()  # clean exit handled by serve() method
         self.repository.__enter__()  # clean exit handled by serve() method
         return self.repository.id
         return self.repository.id
 
 

+ 5 - 2
borg/repository.py

@@ -53,7 +53,7 @@ class Repository:
     class ObjectNotFound(ErrorWithTraceback):
     class ObjectNotFound(ErrorWithTraceback):
         """Object with key {} not found in repository {}."""
         """Object with key {} not found in repository {}."""
 
 
-    def __init__(self, path, create=False, exclusive=False, lock_wait=None, lock=True):
+    def __init__(self, path, create=False, exclusive=False, lock_wait=None, lock=True, append_only=False):
         self.path = os.path.abspath(path)
         self.path = os.path.abspath(path)
         self._location = Location('file://%s' % self.path)
         self._location = Location('file://%s' % self.path)
         self.io = None
         self.io = None
@@ -64,6 +64,7 @@ class Repository:
         self.do_lock = lock
         self.do_lock = lock
         self.do_create = create
         self.do_create = create
         self.exclusive = exclusive
         self.exclusive = exclusive
+        self.append_only = append_only
 
 
     def __del__(self):
     def __del__(self):
         if self.lock:
         if self.lock:
@@ -169,7 +170,9 @@ class Repository:
             raise self.InvalidRepository(path)
             raise self.InvalidRepository(path)
         self.max_segment_size = self.config.getint('repository', 'max_segment_size')
         self.max_segment_size = self.config.getint('repository', 'max_segment_size')
         self.segments_per_dir = self.config.getint('repository', 'segments_per_dir')
         self.segments_per_dir = self.config.getint('repository', 'segments_per_dir')
-        self.append_only = self.config.getboolean('repository', 'append_only', fallback=False)
+        # append_only can be set in the constructor
+        # it shouldn't be overridden (True -> False) here
+        self.append_only = self.append_only or self.config.getboolean('repository', 'append_only', fallback=False)
         self.id = unhexlify(self.config.get('repository', 'id').strip())
         self.id = unhexlify(self.config.get('repository', 'id').strip())
         self.io = LoggedIO(self.path, self.max_segment_size, self.segments_per_dir)
         self.io = LoggedIO(self.path, self.max_segment_size, self.segments_per_dir)
 
 

+ 4 - 1
borg/testsuite/repository.py

@@ -201,11 +201,14 @@ class RepositoryCommitTestCase(RepositoryTestCaseBase):
 
 
 
 
 class RepositoryAppendOnlyTestCase(RepositoryTestCaseBase):
 class RepositoryAppendOnlyTestCase(RepositoryTestCaseBase):
+    def open(self, create=False):
+        return Repository(os.path.join(self.tmppath, 'repository'), create=create, append_only=True)
+
     def test_destroy_append_only(self):
     def test_destroy_append_only(self):
         # Can't destroy append only repo (via the API)
         # Can't destroy append only repo (via the API)
-        self.repository.append_only = True
         with self.assert_raises(ValueError):
         with self.assert_raises(ValueError):
             self.repository.destroy()
             self.repository.destroy()
+        assert self.repository.append_only
 
 
     def test_append_only(self):
     def test_append_only(self):
         def segments_in_repository():
         def segments_in_repository():

+ 7 - 0
docs/usage.rst

@@ -727,6 +727,13 @@ To activate append-only mode, edit the repository ``config`` file and add a line
 In append-only mode Borg will create a transaction log in the ``transactions`` file,
 In append-only mode Borg will create a transaction log in the ``transactions`` file,
 where each line is a transaction and a UTC timestamp.
 where each line is a transaction and a UTC timestamp.
 
 
+In addition, ``borg serve`` can act as if a repository is in append-only mode with
+its option ``--append-only``. This can be very useful for fine-tuning access control
+in ``.ssh/authorized_keys`` ::
+
+    command="borg serve --append-only ..." ssh-rsa <key used for not-always-trustable backup clients>
+    command="borg serve ..." ssh-rsa <key used for backup management>
+
 Example
 Example
 +++++++
 +++++++