From 249661a9480b17a5c605cba2072cff0add6a1e70 Mon Sep 17 00:00:00 2001 From: Tomas Hozza Date: Mon, 7 Mar 2022 14:29:38 +0100 Subject: [PATCH] worker: rework GCP credentials handling Refactor the handling of GCP credentials in the worker to be equivalent to what is done for AWS. The main idea is that the code decides which credentials to use when processing each job. This change will allow preferring credentials passed via upload `TargetOptions` with the job, over the credentials configured in worker's configuration or the default way of authenticating implemented by the Google library. Move loading of GCP credentials to the internal `gcp` library into `NewFromFile()` function accepting path to the file with credentials. Signed-off-by: Tomas Hozza --- cmd/osbuild-worker/jobimpl-osbuild.go | 35 +++++++++++++++++++++++++-- cmd/osbuild-worker/main.go | 13 +++------- internal/cloud/gcp/gcp.go | 10 ++++++++ 3 files changed, 47 insertions(+), 11 deletions(-) diff --git a/cmd/osbuild-worker/jobimpl-osbuild.go b/cmd/osbuild-worker/jobimpl-osbuild.go index 5397d65ab..a14903d35 100644 --- a/cmd/osbuild-worker/jobimpl-osbuild.go +++ b/cmd/osbuild-worker/jobimpl-osbuild.go @@ -33,7 +33,7 @@ type OSBuildJobImpl struct { Store string Output string KojiServers map[string]koji.GSSAPICredentials - GCPCreds []byte + GCPCreds string AzureCreds *azure.Credentials AWSCreds string AWSBucket string @@ -63,6 +63,37 @@ func (impl *OSBuildJobImpl) getAWSForEndpoint(endpoint, region, accessId, secret return nil, fmt.Errorf("no credentials found") } +// getGCP returns an *gcp.GCP object using credentials based on the following +// predefined preference: +// +// 1. If the provided `credentials` parameter is not `nil`, it is used to +// authenticate with GCP. +// +// 2. If a path to GCP credentials file was provided in the worker's +// configuration, it is used to authenticate with GCP. +// +// 3. Use Application Default Credentials from the Google library, which tries +// to automatically find a way to authenticate using the following options: +// +// 3a. If `GOOGLE_APPLICATION_CREDENTIALS` environment variable is set, it +// tries to load and use credentials form the file pointed to by the +// variable. +// +// 3b. It tries to authenticate using the service account attached to the +// resource which is running the code (e.g. Google Compute Engine VM). +func (impl *OSBuildJobImpl) getGCP(credentials []byte) (*gcp.GCP, error) { + if credentials != nil { + logrus.Info("[GCP] 🔑 using credentials provided with the job request") + return gcp.New(credentials) + } else if impl.GCPCreds != "" { + logrus.Info("[GCP] 🔑 using credentials from the worker configuration") + return gcp.NewFromFile(impl.GCPCreds) + } else { + logrus.Info("[GCP] 🔑 using Application Default Credentials via Google library") + return gcp.New(nil) + } +} + func validateResult(result *worker.OSBuildJobResult, jobID string) { logWithId := logrus.WithField("jobId", jobID) if result.JobError != nil { @@ -434,7 +465,7 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error { case *target.GCPTargetOptions: ctx := context.Background() - g, err := gcp.New(impl.GCPCreds) + g, err := impl.getGCP(nil) if err != nil { osbuildJobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidConfig, err.Error()) return nil diff --git a/cmd/osbuild-worker/main.go b/cmd/osbuild-worker/main.go index 96b5fae60..f593621e2 100644 --- a/cmd/osbuild-worker/main.go +++ b/cmd/osbuild-worker/main.go @@ -287,16 +287,11 @@ func main() { } } - // Check if the credentials file was provided in the worker configuration, - // and load it early to prevent potential failure due to issues with the file. - // Note that the content validity of the provided file is not checked and - // can not be reasonable checked with GCP other than by making real API calls. - var gcpCredentials []byte + // If the credentials are not provided in the configuration, then the + // worker will rely on the GCP library to authenticate using default means. + var gcpCredentials string if config.GCP != nil { - gcpCredentials, err = ioutil.ReadFile(config.GCP.Credentials) - if err != nil { - logrus.Fatalf("cannot load GCP credentials: %v", err) - } + gcpCredentials = config.GCP.Credentials } // If the credentials are not provided in the configuration, then the diff --git a/internal/cloud/gcp/gcp.go b/internal/cloud/gcp/gcp.go index 7196b18af..046f6dc92 100644 --- a/internal/cloud/gcp/gcp.go +++ b/internal/cloud/gcp/gcp.go @@ -55,6 +55,16 @@ func New(credentials []byte) (*GCP, error) { return &GCP{creds}, nil } +// NewFromFile loads the credentials from a file and returns an authenticated +// *GCP object instance. +func NewFromFile(path string) (*GCP, error) { + gcpCredentials, err := ioutil.ReadFile(path) + if err != nil { + return nil, fmt.Errorf("cannot load GCP credentials from file %q: %v", path, err) + } + return New(gcpCredentials) +} + // GetCredentialsFromEnv reads the service account credentials JSON file from // the path pointed to by the environment variable name stored in // 'GCPCredentialsEnvName'. If the content of the JSON file was read successfully,