mirror of
https://github.com/restic/restic.git
synced 2025-12-03 23:11:47 +00:00
b2: Support file hiding instead of deleting them permanently
Automatically fall back to hiding files if not authorized to permanently
delete files. This allows using restic with an append-only application
key with B2. Thus, an attacker cannot directly delete backups with the
API key used by restic.
To use this feature create an application key without the deleteFiles
capability. It is recommended to restrict the key to just one bucket.
For example using the b2 command line tool:
b2 create-key --bucket <bucketName> <keyName> listBuckets,readFiles,writeFiles,listFiles
Suggested-by: Daniel Gröber <dxld@darkboxed.org>
This commit is contained in:
@@ -18,6 +18,7 @@ import (
|
||||
|
||||
"github.com/cenkalti/backoff/v4"
|
||||
"github.com/kurin/blazer/b2"
|
||||
"github.com/kurin/blazer/base"
|
||||
)
|
||||
|
||||
// b2Backend is a backend which stores its data on Backblaze B2.
|
||||
@@ -28,6 +29,8 @@ type b2Backend struct {
|
||||
listMaxItems int
|
||||
layout.Layout
|
||||
sem sema.Semaphore
|
||||
|
||||
canDelete bool
|
||||
}
|
||||
|
||||
// Billing happens in 1000 item granlarity, but we are more interested in reducing the number of network round trips
|
||||
@@ -104,6 +107,7 @@ func Open(ctx context.Context, cfg Config, rt http.RoundTripper) (restic.Backend
|
||||
},
|
||||
listMaxItems: defaultListMaxItems,
|
||||
sem: sem,
|
||||
canDelete: true,
|
||||
}
|
||||
|
||||
return be, nil
|
||||
@@ -314,11 +318,27 @@ func (be *b2Backend) Remove(ctx context.Context, h restic.Handle) error {
|
||||
// the retry backend will also repeat the remove method up to 10 times
|
||||
for i := 0; i < 3; i++ {
|
||||
obj := be.bucket.Object(be.Filename(h))
|
||||
err := obj.Delete(ctx)
|
||||
if err == nil {
|
||||
// keep deleting until we are sure that no leftover file versions exist
|
||||
continue
|
||||
|
||||
var err error
|
||||
if be.canDelete {
|
||||
err = obj.Delete(ctx)
|
||||
if err == nil {
|
||||
// keep deleting until we are sure that no leftover file versions exist
|
||||
continue
|
||||
}
|
||||
|
||||
code, _ := base.Code(err)
|
||||
if code == 401 { // unauthorized
|
||||
// fallback to hide if not allowed to delete files
|
||||
be.canDelete = false
|
||||
debug.Log("Removing %v failed, falling back to b2_hide_file.", h)
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
// hide adds a new file version hiding all older ones, thus retries are not necessary
|
||||
err = obj.Hide(ctx)
|
||||
}
|
||||
|
||||
// consider a file as removed if b2 informs us that it does not exist
|
||||
if b2.IsNotExist(err) {
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user