Append-only mode with S3 (Wasabi)

Hello,

I am using Wasabi to store my backups.

Here’s a basic IAM policy I use that gives the user full access to the bucket:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::angristan-test"
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:PutObject",
        "s3:GetObject",
        "s3:DeleteObject"
      ],
      "Resource": "arn:aws:s3:::angristan-test/*"
    }
  ]
}

I would like to make an append-only mode, so I remove "s3:DeleteObject".

Now I get

root@server:~# restic -r s3:s3.wasabisys.com/angristan-test backup /etc/apt/
repository e9ed581d opened successfully, password is correct

Files:           0 new,     0 changed,    19 unmodified
Dirs:            0 new,     0 changed,     1 unmodified
Added:      0 B

processed 19 files, 39.960 KiB in 0:04
snapshot 0e72ef76 saved
Remove(<lock/9491c2d1a7>) returned error, retrying after 472.796319ms: client.RemoveObject: Access Denied
Remove(<lock/9491c2d1a7>) returned error, retrying after 922.390202ms: client.RemoveObject: Access Denied
Remove(<lock/9491c2d1a7>) returned error, retrying after 665.789514ms: client.RemoveObject: Access Denied
Remove(<lock/9491c2d1a7>) returned error, retrying after 2.076801915s: client.RemoveObject: Access Denied
Remove(<lock/9491c2d1a7>) returned error, retrying after 1.890188244s: client.RemoveObject: Access Denied
Remove(<lock/9491c2d1a7>) returned error, retrying after 4.133933525s: client.RemoveObject: Access Denied
Remove(<lock/9491c2d1a7>) returned error, retrying after 5.553504421s: client.RemoveObject: Access Denied
Remove(<lock/9491c2d1a7>) returned error, retrying after 6.008811181s: client.RemoveObject: Access Denied
Remove(<lock/9491c2d1a7>) returned error, retrying after 10.161136116s: client.RemoveObject: Access Denied
Remove(<lock/9491c2d1a7>) returned error, retrying after 20.09185886s: client.RemoveObject: Access Denied
Remove(<lock/9491c2d1a7>) returned error, retrying after 290.650569ms: client.RemoveObject: Access Denied
Remove(<lock/9491c2d1a7>) returned error, retrying after 433.839292ms: client.RemoveObject: Access Denied
Remove(<lock/9491c2d1a7>) returned error, retrying after 1.687396317s: client.RemoveObject: Access Denied
Remove(<lock/9491c2d1a7>) returned error, retrying after 1.452850604s: client.RemoveObject: Access Denied
Remove(<lock/9491c2d1a7>) returned error, retrying after 3.769594116s: client.RemoveObject: Access Denied
Remove(<lock/9491c2d1a7>) returned error, retrying after 5.204395912s: client.RemoveObject: Access Denied
Remove(<lock/9491c2d1a7>) returned error, retrying after 6.57860458s: client.RemoveObject: Access Denied
Remove(<lock/9491c2d1a7>) returned error, retrying after 7.331616872s: client.RemoveObject: Access Denied
Remove(<lock/9491c2d1a7>) returned error, retrying after 16.957880538s: client.RemoveObject: Access Denied
Remove(<lock/9491c2d1a7>) returned error, retrying after 10.197069497s: client.RemoveObject: Access Denied
error in cleanup handler: client.RemoveObject: Access Denied

Is this intended? Is there a way to make an append-only policy?

I bet restic is trying to delete the lock file, but it can’t because that bucket policy is applied.

1 Like

That’s exactly it, the error is that restic cannot remove the lock.

@angristan make an additional policy allowing s3:DeleteObject only in the lock directory arn:aws:s3:::angristan-test/lock/*

There is an idea to ignore this error, but it is an ongoing effort:

1 Like

Thanks for the advice @lukastribus! The syntax is .../locks/*, though.

Using minio, I ended up using the following IAM policy, which is similar to the --append-only feature we know from the restic rest interface.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject"
            ],
            "Resource": "arn:aws:s3:::prefix.*/*"
        },
        {
            "Sid": "",
            "Effect": "Allow",
            "Action": [
                "s3:DeleteObject"
            ],
            "Resource": "arn:aws:s3:::prefix.*/locks/*"
        },
        {
            "Sid": "",
            "Effect": "Allow",
            "Action": [
                "s3:ListBucket",
                "s3:CreateBucket",
                "s3:GetBucketLocation"
            ],
            "Resource": "arn:aws:s3:::prefix.*"
        }
    ]
}

This allows restic to access all buckets starting with prefix.. Restic is also able to create a new bucket and initialize the repository starting with prefix..

1 Like

Note that you must enable bucket versioning for this to be effective. Otherwise, a PutObject can be used to irreversibly overwrite objects with nothing/garbage.