Automatically load Google auth

This change removes the hardcoded Google auth mechanism for the GCS
backend, instead using Google's provided client library to discover and
generate credential material.

Google recommend that client libraries use their common auth mechanism
in order to authorise requests against Google services. Doing so means
you automatically support various types of authentication, from the
standard GOOGLE_APPLICATION_CREDENTIALS environment variable to making
use of Google's metadata API if running within Google Container Engine.
This commit is contained in:
Lawrence Jones 2018-01-12 17:25:02 +00:00
parent 54c6837ec4
commit 0dfdc11ed9
No known key found for this signature in database
GPG key ID: 74179F6972FC3A21
5 changed files with 12 additions and 46 deletions

View file

@ -440,18 +440,6 @@ func parseConfig(loc location.Location, opts options.Options) (interface{}, erro
cfg.ProjectID = os.Getenv("GOOGLE_PROJECT_ID")
}
if cfg.JSONKeyPath == "" {
if path := os.Getenv("GOOGLE_APPLICATION_CREDENTIALS"); path != "" {
// Check read access
if _, err := ioutil.ReadFile(path); err != nil {
return nil, errors.Fatalf("Failed to read google credential from file %v: %v", path, err)
}
cfg.JSONKeyPath = path
} else {
return nil, errors.Fatal("No credential file path is set")
}
}
if err := opts.Apply(loc.Scheme, &cfg); err != nil {
return nil, err
}

View file

@ -8,13 +8,13 @@ import (
"github.com/restic/restic/internal/options"
)
// Config contains all configuration necessary to connect to a Google Cloud
// Storage bucket.
// Config contains all configuration necessary to connect to a Google Cloud Storage
// bucket. We use Google's default application credentials to acquire an access token, so
// we don't require that calling code supply any authentication material here.
type Config struct {
ProjectID string
JSONKeyPath string
Bucket string
Prefix string
ProjectID string
Bucket string
Prefix string
Connections uint `option:"connections" help:"set a limit for the number of concurrent connections (default: 20)"`
}

View file

@ -15,9 +15,6 @@ import (
"github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/restic"
"io/ioutil"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
"google.golang.org/api/googleapi"
storage "google.golang.org/api/storage/v1"
@ -43,30 +40,12 @@ type Backend struct {
// Ensure that *Backend implements restic.Backend.
var _ restic.Backend = &Backend{}
func getStorageService(jsonKeyPath string, rt http.RoundTripper) (*storage.Service, error) {
raw, err := ioutil.ReadFile(jsonKeyPath)
if err != nil {
return nil, errors.Wrap(err, "ReadFile")
}
conf, err := google.JWTConfigFromJSON(raw, storage.DevstorageReadWriteScope)
func getStorageService() (*storage.Service, error) {
client, err := google.DefaultClient(context.TODO(), storage.DevstorageReadWriteScope)
if err != nil {
return nil, err
}
// create a new HTTP client
httpClient := &http.Client{
Transport: rt,
}
// create a now context with the HTTP client stored at the oauth2.HTTPClient key
ctx := context.WithValue(context.Background(), oauth2.HTTPClient, httpClient)
// then pass this context to Client(), which returns a new HTTP client
client := conf.Client(ctx)
// that we can then pass to New()
service, err := storage.New(client)
if err != nil {
return nil, err
@ -80,7 +59,7 @@ const defaultListMaxItems = 1000
func open(cfg Config, rt http.RoundTripper) (*Backend, error) {
debug.Log("open, config %#v", cfg)
service, err := getStorageService(cfg.JSONKeyPath, rt)
service, err := getStorageService()
if err != nil {
return nil, errors.Wrap(err, "getStorageService")
}

View file

@ -34,7 +34,6 @@ func newGSTestSuite(t testing.TB) *test.Suite {
cfg := gscfg.(gs.Config)
cfg.ProjectID = os.Getenv("RESTIC_TEST_GS_PROJECT_ID")
cfg.JSONKeyPath = os.Getenv("RESTIC_TEST_GS_APPLICATION_CREDENTIALS")
cfg.Prefix = fmt.Sprintf("test-%d", time.Now().UnixNano())
return cfg, nil
},
@ -88,8 +87,8 @@ func TestBackendGS(t *testing.T) {
}()
vars := []string{
"GOOGLE_APPLICATION_CREDENTIALS",
"RESTIC_TEST_GS_PROJECT_ID",
"RESTIC_TEST_GS_APPLICATION_CREDENTIALS",
"RESTIC_TEST_GS_REPOSITORY",
}
@ -106,8 +105,8 @@ func TestBackendGS(t *testing.T) {
func BenchmarkBackendGS(t *testing.B) {
vars := []string{
"GOOGLE_APPLICATION_CREDENTIALS",
"RESTIC_TEST_GS_PROJECT_ID",
"RESTIC_TEST_GS_APPLICATION_CREDENTIALS",
"RESTIC_TEST_GS_REPOSITORY",
}

View file

@ -183,7 +183,7 @@ func (env *TravisEnvironment) RunTests() error {
env.env["GOPATH"] = os.Getenv("GOPATH")
if env.gcsCredentialsFile != "" {
env.env["RESTIC_TEST_GS_APPLICATION_CREDENTIALS"] = env.gcsCredentialsFile
env.env["GOOGLE_APPLICATION_CREDENTIALS"] = env.gcsCredentialsFile
}
// ensure that the following tests cannot be silently skipped on Travis