gs: support authentication with access token

In the Google Cloud Storage backend, support specifying access tokens
directly, as an alternative to a credentials file. This is useful when
restic is used non-interactively by some other program that is already
authenticated and eliminates the need to store long lived credentials.

The access token is specified in the GOOGLE_ACCESS_TOKEN environment
variable and takes precedence over GOOGLE_APPLICATION_CREDENTIALS.
This commit is contained in:
Peter Schultz 2020-07-21 19:24:30 +02:00
parent 82c908871d
commit 758b44b9c0
4 changed files with 42 additions and 7 deletions

View file

@ -0,0 +1,7 @@
Enhancement: Authenticate to Google Cloud Storage with access token
When using the GCS backend, it is now possible to authenticate with OAuth2
access tokens instead of a credentials file by setting the GOOGLE_ACCESS_TOKEN
environment variable.
https://github.com/restic/restic/pull/2849

View file

@ -458,6 +458,18 @@ which means if you're running in Google Container Engine or are otherwise
located on an instance with default service accounts then these should work out of
the box.
Alternatively, you can specify an existing access token directly:
.. code-block:: console
$ export GOOGLE_ACCESS_TOKEN=ya29.a0AfH6SMC78...
If ``GOOGLE_ACCESS_TOKEN`` is set all other authentication mechanisms are
disabled. The access token must have at least the
``https://www.googleapis.com/auth/devstorage.read_write`` scope. Keep in mind
that access tokens are short-lived (usually one hour), so they are not suitable
if creating a backup takes longer than that, for instance.
Once authenticated, you can use the ``gs:`` backend type to create a new
repository in the bucket ``foo`` at the root path:

View file

@ -47,15 +47,25 @@ func getStorageService(rt http.RoundTripper) (*storage.Service, error) {
Transport: rt,
}
// create a now context with the HTTP client stored at the oauth2.HTTPClient key
// create a new context with the HTTP client stored at the oauth2.HTTPClient key
ctx := context.WithValue(context.Background(), oauth2.HTTPClient, httpClient)
// use this context
client, err := google.DefaultClient(ctx, storage.DevstorageReadWriteScope)
if err != nil {
return nil, err
var ts oauth2.TokenSource
if token := os.Getenv("GOOGLE_ACCESS_TOKEN"); token != "" {
ts = oauth2.StaticTokenSource(&oauth2.Token{
AccessToken: token,
TokenType: "Bearer",
})
} else {
var err error
ts, err = google.DefaultTokenSource(ctx, storage.DevstorageReadWriteScope)
if err != nil {
return nil, err
}
}
client := oauth2.NewClient(ctx, ts)
service, err := storage.New(client)
if err != nil {
return nil, err

View file

@ -87,7 +87,6 @@ func TestBackendGS(t *testing.T) {
}()
vars := []string{
"GOOGLE_APPLICATION_CREDENTIALS",
"RESTIC_TEST_GS_PROJECT_ID",
"RESTIC_TEST_GS_REPOSITORY",
}
@ -98,6 +97,10 @@ func TestBackendGS(t *testing.T) {
return
}
}
if os.Getenv("GOOGLE_APPLICATION_CREDENTIALS")+os.Getenv("GOOGLE_ACCESS_TOKEN") == "" {
t.Skipf("environment variable GOOGLE_APPLICATION_CREDENTIALS not set, nor GOOGLE_ACCESS_TOKEN")
return
}
t.Logf("run tests")
newGSTestSuite(t).RunTests(t)
@ -105,7 +108,6 @@ func TestBackendGS(t *testing.T) {
func BenchmarkBackendGS(t *testing.B) {
vars := []string{
"GOOGLE_APPLICATION_CREDENTIALS",
"RESTIC_TEST_GS_PROJECT_ID",
"RESTIC_TEST_GS_REPOSITORY",
}
@ -116,6 +118,10 @@ func BenchmarkBackendGS(t *testing.B) {
return
}
}
if os.Getenv("GOOGLE_APPLICATION_CREDENTIALS")+os.Getenv("GOOGLE_ACCESS_TOKEN") == "" {
t.Skipf("environment variable GOOGLE_APPLICATION_CREDENTIALS not set, nor GOOGLE_ACCESS_TOKEN")
return
}
t.Logf("run tests")
newGSTestSuite(t).RunBenchmarks(t)