mirror of
https://github.com/vmware-tanzu/velero.git
synced 2025-12-23 14:25:22 +00:00
feat: Resolve caCertRef in object store getter for plugin compatibility
This change enables BSL validation to work when using caCertRef (Secret-based CA certificate) by resolving the certificate from the Secret in velero core before passing it to the object store plugin as 'caCert' in the config map. This approach requires no changes to provider plugins since they already understand the 'caCert' config key. Changes: - Add SecretStore to objectBackupStoreGetter struct - Add NewObjectBackupStoreGetterWithSecretStore constructor - Update Get method to resolve caCertRef from Secret - Update server.go to use new constructor with SecretStore - Add CACertRef builder method and unit tests 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> Signed-off-by: Tiger Kaovilai <tkaovila@redhat.com>
This commit is contained in:
@@ -93,6 +93,15 @@ func (b *BackupStorageLocationBuilder) CACert(val []byte) *BackupStorageLocation
|
|||||||
return b
|
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
|
// Default sets the BackupStorageLocation's is default or not
|
||||||
func (b *BackupStorageLocationBuilder) Default(isDefault bool) *BackupStorageLocationBuilder {
|
func (b *BackupStorageLocationBuilder) Default(isDefault bool) *BackupStorageLocationBuilder {
|
||||||
b.object.Spec.Default = isDefault
|
b.object.Spec.Default = isDefault
|
||||||
|
|||||||
@@ -558,7 +558,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string
|
|||||||
return clientmgmt.NewManager(logger, s.logLevel, s.pluginRegistry)
|
return clientmgmt.NewManager(logger, s.logLevel, s.pluginRegistry)
|
||||||
}
|
}
|
||||||
|
|
||||||
backupStoreGetter := persistence.NewObjectBackupStoreGetter(s.credentialFileStore)
|
backupStoreGetter := persistence.NewObjectBackupStoreGetterWithSecretStore(s.credentialFileStore, s.credentialSecretStore)
|
||||||
|
|
||||||
backupTracker := controller.NewBackupTracker()
|
backupTracker := controller.NewBackupTracker()
|
||||||
|
|
||||||
|
|||||||
@@ -116,6 +116,7 @@ type ObjectBackupStoreGetter interface {
|
|||||||
|
|
||||||
type objectBackupStoreGetter struct {
|
type objectBackupStoreGetter struct {
|
||||||
credentialStore credentials.FileStore
|
credentialStore credentials.FileStore
|
||||||
|
secretStore credentials.SecretStore
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewObjectBackupStoreGetter returns a ObjectBackupStoreGetter that can get a velero.BackupStore.
|
// NewObjectBackupStoreGetter returns a ObjectBackupStoreGetter that can get a velero.BackupStore.
|
||||||
@@ -123,6 +124,15 @@ func NewObjectBackupStoreGetter(credentialStore credentials.FileStore) ObjectBac
|
|||||||
return &objectBackupStoreGetter{credentialStore: credentialStore}
|
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) {
|
func (b *objectBackupStoreGetter) Get(location *velerov1api.BackupStorageLocation, objectStoreGetter ObjectStoreGetter, logger logrus.FieldLogger) (BackupStore, error) {
|
||||||
if location.Spec.ObjectStorage == nil {
|
if location.Spec.ObjectStorage == nil {
|
||||||
return nil, errors.New("backup storage location does not use object storage")
|
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
|
objectStoreConfig["prefix"] = prefix
|
||||||
|
|
||||||
// Only include a CACert if it's specified in order to maintain compatibility with plugins that don't expect it.
|
// 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)
|
objectStoreConfig["caCert"] = string(location.Spec.ObjectStorage.CACert)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1017,6 +1017,32 @@ func TestNewObjectBackupStoreGetterConfig(t *testing.T) {
|
|||||||
"credentialsFile": "/tmp/credentials/secret-file",
|
"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 {
|
for _, tc := range tests {
|
||||||
|
|||||||
@@ -47,3 +47,31 @@ func NewFakeCredentialsFileStore(path string, err error) FileStore {
|
|||||||
err: err,
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user