diff --git a/doc/manual.rst b/doc/manual.rst index 3e205e6c1..6841deb21 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -405,11 +405,22 @@ established. Google Cloud Storage ~~~~~~~~~~~~~~~~~~~~ -Restic supports Google Cloud Storage as a backend. In order for this to work -you first need create a "service account" and download the JSON key file for -it. In addition, you need the Google Project ID that you can see in the Google +Restic supports Google Cloud Storage as a backend. + +Restic connects to Google Cloud Storage via a `service account`_. + +For normal restic operation, the service account must have the +``storage.objects.{create,delete,get,list}`` permissions for the bucket. These +are included in the "Storage Object Admin" role. For ``restic init``, the +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. + +In addition, you need 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 credentials file and the project ID as follows: +JSON key file and the project ID as follows: .. code-block:: console @@ -432,6 +443,9 @@ The number of concurrent connections to the GCS service can be set with the `-o gs.connections=10`. By default, at most five parallel connections are established. +.. _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 + Password prompt on Windows ~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/internal/backend/gs/config.go b/internal/backend/gs/config.go index cc1b7d805..eebd4fe23 100644 --- a/internal/backend/gs/config.go +++ b/internal/backend/gs/config.go @@ -8,8 +8,8 @@ import ( "github.com/restic/restic/internal/options" ) -// Config contains all configuration necessary to connect to an gcs compatible -// server. +// Config contains all configuration necessary to connect to a Google Cloud +// Storage bucket. type Config struct { ProjectID string JSONKeyPath string diff --git a/internal/backend/gs/gs.go b/internal/backend/gs/gs.go index 902726d1b..ea003d955 100644 --- a/internal/backend/gs/gs.go +++ b/internal/backend/gs/gs.go @@ -1,3 +1,4 @@ +// Package gs provides a restic backend for Google Cloud Storage. package gs import ( @@ -20,7 +21,13 @@ import ( storage "google.golang.org/api/storage/v1" ) -// Backend stores data on an gs endpoint. +// Backend stores data in a GCS bucket. +// +// The service account used to access the bucket must have these permissions: +// * storage.objects.create +// * storage.objects.delete +// * storage.objects.get +// * storage.objects.list type Backend struct { service *storage.Service projectID string @@ -31,7 +38,7 @@ type Backend struct { backend.Layout } -// make sure that *Backend implements backend.Backend +// Ensure that *Backend implements restic.Backend. var _ restic.Backend = &Backend{} func getStorageService(jsonKeyPath string) (*storage.Service, error) { @@ -87,21 +94,25 @@ func open(cfg Config) (*Backend, error) { return be, nil } -// Open opens the gs backend at bucket and region. +// Open opens the gs backend at the specified bucket. func Open(cfg Config) (restic.Backend, error) { return open(cfg) } -// Create opens the S3 backend at bucket and region and creates the bucket if -// it does not exist yet. +// Create opens the gs backend at the specified bucket and creates the bucket +// if it does not exist yet. +// +// In addition to the permissions required by Backend, Create requires these +// permissions: +// * storage.buckets.get +// * storage.buckets.create (if the bucket doesn't exist) func Create(cfg Config) (restic.Backend, error) { be, err := open(cfg) - if err != nil { return nil, errors.Wrap(err, "open") } - // Create bucket if not exists + // Create bucket if it doesn't exist. if _, err := be.service.Buckets.Get(be.bucketName).Do(); err != nil { bucket := &storage.Bucket{ Name: be.bucketName, @@ -371,5 +382,5 @@ func (be *Backend) Delete(ctx context.Context) error { return be.Remove(ctx, restic.Handle{Type: restic.ConfigFile}) } -// Close does nothing +// Close does nothing. func (be *Backend) Close() error { return nil }