diff --git a/pkg/builder/backup_storage_location_builder.go b/pkg/builder/backup_storage_location_builder.go index 1c25d5a60..33becd2a7 100644 --- a/pkg/builder/backup_storage_location_builder.go +++ b/pkg/builder/backup_storage_location_builder.go @@ -93,6 +93,15 @@ func (b *BackupStorageLocationBuilder) CACert(val []byte) *BackupStorageLocation return b } +// CACertRef sets the BackupStorageLocation's object storage CACertRef (Secret reference). +func (b *BackupStorageLocationBuilder) CACertRef(selector *corev1api.SecretKeySelector) *BackupStorageLocationBuilder { + if b.object.Spec.StorageType.ObjectStorage == nil { + b.object.Spec.StorageType.ObjectStorage = new(velerov1api.ObjectStorageLocation) + } + b.object.Spec.ObjectStorage.CACertRef = selector + return b +} + // Default sets the BackupStorageLocation's is default or not func (b *BackupStorageLocationBuilder) Default(isDefault bool) *BackupStorageLocationBuilder { b.object.Spec.Default = isDefault diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 053cee077..e744013e8 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -558,7 +558,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string return clientmgmt.NewManager(logger, s.logLevel, s.pluginRegistry) } - backupStoreGetter := persistence.NewObjectBackupStoreGetter(s.credentialFileStore) + backupStoreGetter := persistence.NewObjectBackupStoreGetterWithSecretStore(s.credentialFileStore, s.credentialSecretStore) backupTracker := controller.NewBackupTracker() diff --git a/pkg/persistence/object_store.go b/pkg/persistence/object_store.go index 44ad96118..e3da1f362 100644 --- a/pkg/persistence/object_store.go +++ b/pkg/persistence/object_store.go @@ -116,6 +116,7 @@ type ObjectBackupStoreGetter interface { type objectBackupStoreGetter struct { credentialStore credentials.FileStore + secretStore credentials.SecretStore } // NewObjectBackupStoreGetter returns a ObjectBackupStoreGetter that can get a velero.BackupStore. @@ -123,6 +124,15 @@ func NewObjectBackupStoreGetter(credentialStore credentials.FileStore) ObjectBac return &objectBackupStoreGetter{credentialStore: credentialStore} } +// NewObjectBackupStoreGetterWithSecretStore returns an ObjectBackupStoreGetter with SecretStore +// support for resolving caCertRef from Kubernetes Secrets. +func NewObjectBackupStoreGetterWithSecretStore(credentialStore credentials.FileStore, secretStore credentials.SecretStore) ObjectBackupStoreGetter { + return &objectBackupStoreGetter{ + credentialStore: credentialStore, + secretStore: secretStore, + } +} + func (b *objectBackupStoreGetter) Get(location *velerov1api.BackupStorageLocation, objectStoreGetter ObjectStoreGetter, logger logrus.FieldLogger) (BackupStore, error) { if location.Spec.ObjectStorage == nil { return nil, errors.New("backup storage location does not use object storage") @@ -160,7 +170,16 @@ func (b *objectBackupStoreGetter) Get(location *velerov1api.BackupStorageLocatio objectStoreConfig["prefix"] = prefix // Only include a CACert if it's specified in order to maintain compatibility with plugins that don't expect it. - if location.Spec.ObjectStorage.CACert != nil { + // Prefer caCertRef (from Secret) over inline caCert (deprecated). + if location.Spec.ObjectStorage.CACertRef != nil { + if b.secretStore != nil { + caCertString, err := b.secretStore.Get(location.Spec.ObjectStorage.CACertRef) + if err != nil { + return nil, errors.Wrap(err, "error getting CA certificate from secret") + } + objectStoreConfig["caCert"] = caCertString + } + } else if location.Spec.ObjectStorage.CACert != nil { objectStoreConfig["caCert"] = string(location.Spec.ObjectStorage.CACert) } diff --git a/pkg/persistence/object_store_test.go b/pkg/persistence/object_store_test.go index 0314f78c3..fac2f8d97 100644 --- a/pkg/persistence/object_store_test.go +++ b/pkg/persistence/object_store_test.go @@ -1017,6 +1017,32 @@ func TestNewObjectBackupStoreGetterConfig(t *testing.T) { "credentialsFile": "/tmp/credentials/secret-file", }, }, + { + name: "location with CACertRef is initialized with caCert from secret", + location: builder.ForBackupStorageLocation("", "").Provider(provider).Bucket(bucket).CACertRef( + builder.ForSecretKeySelector("cacert-secret", "ca.crt").Result(), + ).Result(), + getter: NewObjectBackupStoreGetterWithSecretStore( + velerotest.NewFakeCredentialsFileStore("", nil), + velerotest.NewFakeCredentialsSecretStore("cacert-from-secret", nil), + ), + wantConfig: map[string]string{ + "bucket": "bucket", + "prefix": "", + "caCert": "cacert-from-secret", + }, + }, + { + name: "location with CACertRef and no SecretStore uses no caCert", + location: builder.ForBackupStorageLocation("", "").Provider(provider).Bucket(bucket).CACertRef( + builder.ForSecretKeySelector("cacert-secret", "ca.crt").Result(), + ).Result(), + getter: NewObjectBackupStoreGetter(velerotest.NewFakeCredentialsFileStore("", nil)), + wantConfig: map[string]string{ + "bucket": "bucket", + "prefix": "", + }, + }, } for _, tc := range tests { diff --git a/pkg/test/fake_credential_file_store.go b/pkg/test/fake_credential_file_store.go index af6618521..d1580441e 100644 --- a/pkg/test/fake_credential_file_store.go +++ b/pkg/test/fake_credential_file_store.go @@ -47,3 +47,31 @@ func NewFakeCredentialsFileStore(path string, err error) FileStore { err: err, } } + +// SecretStore defines operations for interacting with credentials +// that are stored in Secret. +type SecretStore interface { + // Get returns the secret key defined by the given selector + Get(selector *corev1api.SecretKeySelector) (string, error) +} + +type fakeCredentialsSecretStore struct { + data string + err error +} + +// Get returns the secret data. +func (f *fakeCredentialsSecretStore) Get(*corev1api.SecretKeySelector) (string, error) { + return f.data, f.err +} + +// NewFakeCredentialsSecretStore creates a SecretStore which will return the given data +// and error when Get is called. +// data is the secret value to return (e.g., certificate content). +// err is the error to return, if any. +func NewFakeCredentialsSecretStore(data string, err error) SecretStore { + return &fakeCredentialsSecretStore{ + data: data, + err: err, + } +}