mirror of
https://github.com/octoleo/restic.git
synced 2024-11-30 00:33:57 +00:00
Merge pull request #1281 from prattmic/gcs_perms
gs: allow backend creation without storage.buckets.get
This commit is contained in:
commit
801dbb6d03
@ -22,6 +22,11 @@ Important Changes in 0.X.Y
|
|||||||
untangle mixed files automatically.
|
untangle mixed files automatically.
|
||||||
https://github.com/restic/restic/pull/1265
|
https://github.com/restic/restic/pull/1265
|
||||||
|
|
||||||
|
* The Google Cloud Storage backend no longer requires the service account to
|
||||||
|
have the `storage.buckets.get` permission ("Storage Admin" role) in `restic
|
||||||
|
init` if the bucket already exists.
|
||||||
|
https://github.com/restic/restic/pull/1281
|
||||||
|
|
||||||
Small changes
|
Small changes
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
|
@ -411,16 +411,18 @@ Restic connects to Google Cloud Storage via a `service account`_.
|
|||||||
|
|
||||||
For normal restic operation, the service account must have the
|
For normal restic operation, the service account must have the
|
||||||
``storage.objects.{create,delete,get,list}`` permissions for the bucket. These
|
``storage.objects.{create,delete,get,list}`` permissions for the bucket. These
|
||||||
are included in the "Storage Object Admin" role. For ``restic init``, the
|
are included in the "Storage Object Admin" role.
|
||||||
service account must also have the ``storage.buckets.get`` and
|
|
||||||
``storage.buckets.create`` (if the bucket does not exist) permissions. These
|
|
||||||
are included in the "Storage Admin" role.
|
|
||||||
|
|
||||||
`Create a service account key`_ and download the JSON credentials file.
|
``restic init`` can create the repository bucket. Doing so requires the
|
||||||
|
``storage.buckets.create`` permission ("Storage Admin" role). If the bucket
|
||||||
|
already exists that permission is unnecessary.
|
||||||
|
|
||||||
In addition, you need the Google Project ID that you can see in the Google
|
To use the Google Cloud Storage backend, first `create a service account key`_
|
||||||
Cloud Platform console at the "Storage/Settings" menu. Export the path to the
|
and download the JSON credentials file.
|
||||||
JSON key file and the project ID as follows:
|
|
||||||
|
Second, find the Google Project ID that you can see in the Google Cloud
|
||||||
|
Platform console at the "Storage/Settings" menu. Export the path to the JSON
|
||||||
|
key file and the project ID as follows:
|
||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
|
||||||
@ -436,7 +438,7 @@ bucket `foo` at the root path:
|
|||||||
enter password for new backend:
|
enter password for new backend:
|
||||||
enter password again:
|
enter password again:
|
||||||
|
|
||||||
created restic backend bde47d6254 at gs:restic-dev-an:foo2
|
created restic backend bde47d6254 at gs:foo:/
|
||||||
[...]
|
[...]
|
||||||
|
|
||||||
The number of concurrent connections to the GCS service can be set with the
|
The number of concurrent connections to the GCS service can be set with the
|
||||||
@ -444,7 +446,7 @@ The number of concurrent connections to the GCS service can be set with the
|
|||||||
established.
|
established.
|
||||||
|
|
||||||
.. _service account: https://cloud.google.com/storage/docs/authentication#service_accounts
|
.. _service account: https://cloud.google.com/storage/docs/authentication#service_accounts
|
||||||
.. _Create a service account key: https://cloud.google.com/storage/docs/authentication#generating-a-private-key
|
.. _create a service account key: https://cloud.google.com/storage/docs/authentication#generating-a-private-key
|
||||||
|
|
||||||
|
|
||||||
Password prompt on Windows
|
Password prompt on Windows
|
||||||
|
@ -99,28 +99,59 @@ func Open(cfg Config) (restic.Backend, error) {
|
|||||||
return open(cfg)
|
return open(cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create opens the gs backend at the specified bucket and creates the bucket
|
// Create opens the gs backend at the specified bucket and attempts to creates
|
||||||
// if it does not exist yet.
|
// the bucket if it does not exist yet.
|
||||||
//
|
//
|
||||||
// In addition to the permissions required by Backend, Create requires these
|
// The service account must have the "storage.buckets.create" permission to
|
||||||
// permissions:
|
// create a bucket the does not yet exist.
|
||||||
// * storage.buckets.get
|
|
||||||
// * storage.buckets.create (if the bucket doesn't exist)
|
|
||||||
func Create(cfg Config) (restic.Backend, error) {
|
func Create(cfg Config) (restic.Backend, error) {
|
||||||
be, err := open(cfg)
|
be, err := open(cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "open")
|
return nil, errors.Wrap(err, "open")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create bucket if it doesn't exist.
|
// Try to determine if the bucket exists. If it does not, try to create it.
|
||||||
|
//
|
||||||
|
// A Get call has three typical error cases:
|
||||||
|
//
|
||||||
|
// * nil: Bucket exists and we have access to the metadata (returned).
|
||||||
|
//
|
||||||
|
// * 403: Bucket exists and we do not have access to the metadata. We
|
||||||
|
// don't have storage.buckets.get permission to the bucket, but we may
|
||||||
|
// still be able to access objects in the bucket.
|
||||||
|
//
|
||||||
|
// * 404: Bucket doesn't exist.
|
||||||
|
//
|
||||||
|
// Determining if the bucket is accessible is best-effort because the
|
||||||
|
// 403 case is ambiguous.
|
||||||
if _, err := be.service.Buckets.Get(be.bucketName).Do(); err != nil {
|
if _, err := be.service.Buckets.Get(be.bucketName).Do(); err != nil {
|
||||||
|
gerr, ok := err.(*googleapi.Error)
|
||||||
|
if !ok {
|
||||||
|
// Don't know what to do with this error.
|
||||||
|
return nil, errors.Wrap(err, "service.Buckets.Get")
|
||||||
|
}
|
||||||
|
|
||||||
|
switch gerr.Code {
|
||||||
|
case 403:
|
||||||
|
// Bucket exists, but we don't know if it is
|
||||||
|
// accessible. Optimistically assume it is; if not,
|
||||||
|
// future Backend calls will fail.
|
||||||
|
debug.Log("Unable to determine if bucket %s is accessible (err %v). Continuing as if it is.", be.bucketName, err)
|
||||||
|
case 404:
|
||||||
|
// Bucket doesn't exist, try to create it.
|
||||||
bucket := &storage.Bucket{
|
bucket := &storage.Bucket{
|
||||||
Name: be.bucketName,
|
Name: be.bucketName,
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := be.service.Buckets.Insert(be.projectID, bucket).Do(); err != nil {
|
if _, err := be.service.Buckets.Insert(be.projectID, bucket).Do(); err != nil {
|
||||||
|
// Always an error, as the bucket definitely
|
||||||
|
// doesn't exist.
|
||||||
return nil, errors.Wrap(err, "service.Buckets.Insert")
|
return nil, errors.Wrap(err, "service.Buckets.Insert")
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
// Don't know what to do with this error.
|
||||||
|
return nil, errors.Wrap(err, "service.Buckets.Get")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return be, nil
|
return be, nil
|
||||||
|
Loading…
Reference in New Issue
Block a user