From 9c1d49e3123f2e1dd117f16a17af60a39dce868c Mon Sep 17 00:00:00 2001 From: Luc Gommans Date: Sat, 27 Nov 2021 02:55:18 +0100 Subject: [PATCH] Document "forget" security considerations and add references Removing data based on a policy when the attacker had the opportunity to add data to your repository comes with some considerations. This is added to the 060_forget.rst documentation. That document is also updated to reflect that restic now considers the current system time while running "forget". References to the security considerations section are added: - In `restic forget --help` - In the threat model (design.rst) - In the (030) setup section where an append-only setup is referenced A reference is also to be added to the `rest-server` readme's append-only paragraph (see my fork). This commit also resolves a typo (amount->number for countable noun), changes a password length recommendation into the metric that actually matters when creating passwords (entropy) since I was editing these doc files anyway, and updates the outdated copyright year in `conf.py`. Some wording in 060_forget (line 21..22) was changed to clarify what "forget" and "prune" do, to try and avoid the apparent misconception that "forget" does not remove any data. --- cmd/restic/cmd_forget.go | 2 ++ doc/030_preparing_a_new_repo.rst | 4 ++- doc/060_forget.rst | 48 ++++++++++++++++++++++++++++---- doc/conf.py | 2 +- doc/design.rst | 22 ++++++++++----- 5 files changed, 64 insertions(+), 14 deletions(-) diff --git a/cmd/restic/cmd_forget.go b/cmd/restic/cmd_forget.go index fd34f4038..01848745c 100644 --- a/cmd/restic/cmd_forget.go +++ b/cmd/restic/cmd_forget.go @@ -18,6 +18,8 @@ The "forget" command removes snapshots according to a policy. Please note that this command really only deletes the snapshot object in the repository, which is a reference to data stored there. In order to remove this (now unreferenced) data after 'forget' was run successfully, see the 'prune' command. +When using this command on special append-only repositories, please see the +documentation for security considerations. EXIT STATUS =========== diff --git a/doc/030_preparing_a_new_repo.rst b/doc/030_preparing_a_new_repo.rst index ebeec8800..47bd7439d 100644 --- a/doc/030_preparing_a_new_repo.rst +++ b/doc/030_preparing_a_new_repo.rst @@ -657,7 +657,9 @@ credentials) is encrypted/decrypted locally, then sent/received via A more advanced version of this setup forbids specific hosts from removing files in a repository. See the `blog post by Simon Ruderich `_ -for details. +for details. When using ``restic forget`` on such a repository, additional +security considerations apply: please review the documentation on removing +backup snapshots. The rclone command may also be hard-coded in the SSH configuration or the user's public key, in this case it may be sufficient to just start the SSH diff --git a/doc/060_forget.rst b/doc/060_forget.rst index 2c537f1f0..afa4f2c80 100644 --- a/doc/060_forget.rst +++ b/doc/060_forget.rst @@ -18,13 +18,13 @@ All backup space is finite, so restic allows removing old snapshots. This can be done either manually (by specifying a snapshot ID to remove) or by using a policy that describes which snapshots to forget. For all remove operations, two commands need to be called in sequence: -``forget`` to remove a snapshot and ``prune`` to actually remove the -data that was referenced by the snapshot from the repository. This can +``forget`` to remove a snapshot and ``prune`` to remove the remaining +data that was only referenced by this snapshot from the repository. This can be automated with the ``--prune`` option of the ``forget`` command, which runs ``prune`` automatically if snapshots have been removed. Pruning snapshots can be a time-consuming process, depending on the -amount of snapshots and data to process. During a prune operation, the +number of snapshots and data to process. During a prune operation, the repository is locked and backups cannot be completed. Please plan your pruning so that there's time to complete it and it doesn't interfere with regular backup runs. @@ -164,6 +164,9 @@ The most important command-line parameter here is ``--dry-run`` which instructs restic to not remove anything but print which snapshots would be removed. +.. note:: If you use append-only backups, some best practices apply. + Please refer to the security considerations below. + When ``forget`` is run with a policy, restic loads the list of all snapshots, then groups these by host name and list of directories. The grouping options can be set with ``--group-by``, to only group snapshots by paths and @@ -206,6 +209,8 @@ The ``forget`` command accepts the following parameters: .. note:: All calendar related ``--keep-*`` options work on the natural time boundaries and not relative to when you run the ``forget`` command. Weeks are Monday 00:00 -> Sunday 23:59, days 00:00 to 23:59, hours :00 to :59, etc. + Snapshots seemingly made in the future (relative to when you run the + ``forget`` command) will be ignored and never removed. .. note:: Specifying ``--keep-tag ''`` will match untagged snapshots only. @@ -312,8 +317,9 @@ four Sundays, but remove the rest: --------------------------------------------------------------- 8 snapshots -The result of the ``forget --keep-daily`` operation does not depend on when it -is run, it will only count the days for which a snapshot exists. This is a +The result of the ``forget --keep-daily`` operation only partially depends on when it +is run: it will only count the days for which a snapshot exists, although +with a `time` lying in the future are ignored and never removed. This is a safety feature: it prevents restic from removing snapshots when no new ones are created. Otherwise, running ``forget --keep-daily 4`` on a Friday (without any snapshot Monday to Thursday) would remove all snapshots! @@ -336,6 +342,38 @@ could specify: (Note that `1w` is not a recognized duration, so you will have to specify `7d` instead) +Security considerations in append-only mode +=========================================== + +To prevent data from being deleted by a compromised backup client (for example +due to a ransomware infection), a so-called append-only mode can be used. This +requires the server to deny delete and overwrite operations, which is not +possible on many standard back-ends. Software such as `rest-server`_ or +`rclone`_ can be used instead or in addition. + +.. _rest-server: https://github.com/restic/rest-server/ +.. _rclone: https://rclone.org/ + +To recover disk space from obsolete snapshots, ``forget`` and ``prune`` must be +run on a repository with full read-write access. If an attacker can do this, +the protection offered by append-only mode is void. However, even if only the +trusted client runs the ``forget`` command, it can be possible for the attacker +to add snapshots in such a pattern that all legitimate snapshots are removed. + +If the ``forget`` policy is to keep three weekly snapshots, the attacker can +add an empty backup for each of the last three weeks with a `time` slightly +newer than the existing snapshots (but still within the target week). The next +time the repository administrator (or cron job) runs the ``forget`` policy, the +legitimate snapshots will be removed. Even without pruning, recovering data +would be messy and some metadata will be lost. + +To avoid this, policies applied to append-only repositories should use the +``--keep-within`` option. If the system time is set correctly when ``forget`` +runs, this will allow you to notice problems with the backup or the compromised +host. This is, of course, limited to the specified duration: if +``restic forget --keep-within 7d`` is run 8 days after the last good snapshot, +then the attacker can still use that opportunity to remove all good snapshots. + Customize pruning ***************** diff --git a/doc/conf.py b/doc/conf.py index 3c0af927b..97c728ac6 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -35,7 +35,7 @@ master_doc = 'index' # General information about the project. project = 'restic' -copyright = '2018, restic authors' +copyright = '2021, restic authors' author = 'fd0' # The version info for the project you're documenting, acts as replacement for diff --git a/doc/design.rst b/doc/design.rst index c313c1a70..196ddaa66 100644 --- a/doc/design.rst +++ b/doc/design.rst @@ -607,7 +607,7 @@ examples of things an adversary could achieve in various circumstances. An adversary with read access to your backup storage location could: - Attempt a brute force password guessing attack against a copy of the - repository (even more reason to use long, 30+ character passwords). + repository (use strong passwords with sufficient entropy). - Infer which packs probably contain trees via file access patterns. - Infer the size of backups by using creation timestamps of repository objects. @@ -648,18 +648,26 @@ An adversary with write access to your files at the storage location could: An adversary who compromises a host system with append-only access to the backup repository could: +- Capture the password and decrypt backups from the past and in the future. + See the "leaked key" circumstance below. - Render new backups untrustworthy *after* the host has been compromised (due to having complete control over new backups). An attacker cannot delete or manipulate old backups. As such, restoring old snapshots created *before* a host compromise remains possible. - *Note: It is **not** recommended to ever run forget automatically for an - append-only backup to which a potentially compromised host has access - because an attacker using fake snapshots could cause forget to remove - correct snapshots.* +- Potentially manipulate the ``restic forget`` command into deleting all + legitimate snapshots, keeping only bogus snapshots added by the attacker. + Ransomware might try this in order to leave only one option to get your data + back: paying the ransom. For safe use of ``restic forget``, see the + documentation on removing backup snapshots. -An adversary who has a leaked key for a repository which has not been re-encrypted -could: +An adversary who has a leaked key for a repository could: - Decrypt existing and future backup data. If multiple hosts backup into the same repository, an attacker will get access to the backup data of every host. + Since the local encryption key gives access to the master key, a password + change will not prevent this. Changing the master key can currently be done + using ``restic copy`` which moves the data into a new repository with a new + master key, or by making a completely new repository and new backup. + Re-encrypting all data without creating a new repository is tracked in + :issue:`1602`.