Skip to content

Commit

Permalink
Merge pull request #142 from kaovilai/file-based-authentication-with-wif
Browse files Browse the repository at this point in the history
Disable blob signing initialization for non SA file based credentials such as external_account type.
  • Loading branch information
blackpiglet authored Jul 26, 2023
2 parents 817c494 + 64c95c0 commit cfe24c2
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 5 deletions.
22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,27 @@ This involves creating a Google Service Account Key and using it as `--secret-fi

Note that Google Service Account keys are valid for decades (no clear expiry date) - so store it securely or rotate them as often as possible or both.

#### Option 2: Using Workload Identity
#### Option 2: Using File-sourced workforce identity federation short lived credentials

Keep in mind that [Workforce Identity Federation Users cannot generate signed URLs](https://cloud.google.com/iam/docs/federated-identity-supported-services#:~:text=workforce%20identity%20federation%20users%20cannot%20generate%20signed%20URLs.). This means, if you are using Workforce Identity Federation, you will not be able to run `velero backup logs`, `velero backup download`, `velero backup describe` and `velero restore describe`.

This involves creating an external credential file and using it as `--secret-file` during [installation](#Install-and-start-Velero).

1. Create a Workforce Identity Federation external credential file.

```bash
gcloud iam workforce-pools create-cred-config \
locations/global/workforcePools/WORKFORCE_POOL_ID/providers/PROVIDER_ID \
--subject-token-type=urn:ietf:params:oauth:token-type:id_token \
--credential-source-file=PATH_TO_OIDC_ID_TOKEN \
--workforce-pool-user-project=WORKFORCE_POOL_USER_PROJECT \
--output-file=config.json
```

#### Option 3: Using GKE Workload Identity

Keep in mind that [Workforce Identity Federation Users cannot generate signed URLs](https://cloud.google.com/iam/docs/federated-identity-supported-services#:~:text=workforce%20identity%20federation%20users%20cannot%20generate%20signed%20URLs.). This means, if you are using Workforce Identity Federation, you will not be able to run `velero backup logs`, `velero backup download`, `velero backup describe` and `velero restore describe`.

This requires a GKE cluster with workload identity enabled.

1. Create Velero Namespace
Expand Down
1 change: 1 addition & 0 deletions changelogs/unreleased/142-kaovilai
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Disable blob signing initialization for non service account file based credentials
35 changes: 33 additions & 2 deletions velero-plugin-for-gcp/object_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package main
import (
"context"
"encoding/base64"
"encoding/json"
"io"
"io/ioutil"
"time"
Expand Down Expand Up @@ -70,12 +71,30 @@ type ObjectStore struct {
privateKey []byte
bucketWriter bucketWriter
iamSvc *iamcredentials.Service
fileCredType credAccountKeys
}

func newObjectStore(logger logrus.FieldLogger) *ObjectStore {
return &ObjectStore{log: logger}
}

type credAccountKeys string

// From https://github.com/golang/oauth2/blob/d3ed0bb246c8d3c75b63937d9a5eecff9c74d7fe/google/google.go#L95
const (
serviceAccountKey credAccountKeys = "service_account"
externalAccountKey credAccountKeys = "external_account"
)

func getSecretAccountTypeKey(secretByte []byte) (credAccountKeys, error) {
var f map[string]interface{}
if err := json.Unmarshal(secretByte, &f); err != nil {
return "", err
}
// following will panic if cannot cast to credAccountKeys
return credAccountKeys(f["type"].(string)), nil
}

func (o *ObjectStore) Init(config map[string]string) error {
if err := veleroplugin.ValidateObjectStoreConfigKeys(config, kmsKeyNameConfigKey, serviceAccountConfig, credentialsFileConfigKey); err != nil {
return err
Expand Down Expand Up @@ -116,8 +135,14 @@ func (o *ObjectStore) Init(config map[string]string) error {
}

if creds.JSON != nil {
// Using Credentials File
err = o.initFromKeyFile(creds)
o.fileCredType, err = getSecretAccountTypeKey(creds.JSON)
if err != nil {
return errors.WithStack(err)
}
if o.fileCredType == serviceAccountKey {
// Using Credentials File
err = o.initFromKeyFile(creds)
}
} else {
// Using compute engine credentials. Use this if workload identity is enabled.
err = o.initFromComputeEngine(config)
Expand All @@ -140,6 +165,9 @@ func (o *ObjectStore) Init(config map[string]string) error {
return nil
}

// This function is used to populate the googleAccessID and privateKey fields when using a service account credentials file.
// it will error if credential file is not for a service account.
// Do not run this function if using non SA credentials such as external_account.
func (o *ObjectStore) initFromKeyFile(creds *google.Credentials) error {
jwtConfig, err := google.JWTConfigFromJSON(creds.JSON)
if err != nil {
Expand Down Expand Up @@ -273,6 +301,9 @@ func (o *ObjectStore) SignBytes(bytes []byte) ([]byte, error) {
}

func (o *ObjectStore) CreateSignedURL(bucket, key string, ttl time.Duration) (string, error) {
if o.fileCredType != serviceAccountKey {
return "", errors.New("cannot sign blob using non SA file credentials")
}
options := storage.SignedURLOptions{
GoogleAccessID: o.googleAccessID,
Method: "GET",
Expand Down
68 changes: 66 additions & 2 deletions velero-plugin-for-gcp/object_store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
Expand All @@ -24,7 +24,6 @@ import (
"cloud.google.com/go/storage"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

velerotest "github.com/vmware-tanzu/velero/pkg/test"
)

Expand Down Expand Up @@ -152,3 +151,68 @@ func TestObjectExists(t *testing.T) {
})
}
}

func Test_getSecretAccountKey(t *testing.T) {
type args struct {
secretByte []byte
}
tests := []struct {
name string
args args
want credAccountKeys
wantErr bool
}{
{
name: "get secret service account account key",
args: args{
// test data from https://cloud.google.com/iam/docs/keys-create-delete
secretByte: []byte(`{
"type": "service_account",
"project_id": "PROJECT_ID",
"private_key_id": "KEY_ID",
"private_key": "-----BEGIN PRIVATE KEY-----\nPRIVATE_KEY\n-----END PRIVATE KEY-----\n",
"client_email": "SERVICE_ACCOUNT_EMAIL",
"client_id": "CLIENT_ID",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://accounts.google.com/o/oauth2/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/SERVICE_ACCOUNT_EMAIL"
}
`),
},
want: serviceAccountKey,
wantErr: false,
},
{
name: "get secret external account key",
args: args{
// test data from https://cloud.google.com/iam/docs/workforce-obtaining-short-lived-credentials
secretByte: []byte(`{
"type": "external_account",
"audience": "//iam.googleapis.com/locations/global/workforcePools/WORKFORCE_POOL_ID/providers/PROVIDER_ID",
"subject_token_type": "urn:ietf:params:oauth:token-type:id_token",
"token_url": "https://sts.googleapis.com/v1/token",
"workforce_pool_user_project": "WORKFORCE_POOL_USER_PROJECT",
"credential_source": {
"file": "PATH_TO_OIDC_CREDENTIALS_FILE"
}
}
`),
},
want: externalAccountKey,
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := getSecretAccountTypeKey(tt.args.secretByte)
if (err != nil) != tt.wantErr {
t.Errorf("getSecretAccountKey() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("getSecretAccountKey() = %v, want %v", got, tt.want)
}
})
}
}

0 comments on commit cfe24c2

Please sign in to comment.