mirror of
https://github.com/cloudnative-pg/plugin-barman-cloud.git
synced 2026-01-11 13:23:09 +01:00
feat: Add support for DefaultAzureCredential authentication mechanism
This commit adds support for the DefaultAzureCredential authentication mechanism in Azure Blob Storage. Users can now use the `useDefaultAzureCredentials` option to enable Azure's default credential chain, which automatically discovers and uses available credentials in the following order Signed-off-by: Armando Ruocco <armando.ruocco@enterprisedb.com>
This commit is contained in:
parent
0153abba82
commit
68e46024e2
2
go.mod
2
go.mod
@ -7,7 +7,7 @@ toolchain go1.25.5
|
||||
require (
|
||||
github.com/cert-manager/cert-manager v1.19.2
|
||||
github.com/cloudnative-pg/api v1.28.0
|
||||
github.com/cloudnative-pg/barman-cloud v0.4.1-0.20251230211524-20b7e0e10b0f
|
||||
github.com/cloudnative-pg/barman-cloud v0.4.1-0.20260108104508-ced266c145f5
|
||||
github.com/cloudnative-pg/cloudnative-pg v1.28.0
|
||||
github.com/cloudnative-pg/cnpg-i v0.3.1
|
||||
github.com/cloudnative-pg/cnpg-i-machinery v0.4.2
|
||||
|
||||
4
go.sum
4
go.sum
@ -18,8 +18,8 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cloudnative-pg/api v1.28.0 h1:xElzHliO0eKkVQafkfMhDJo0aIRCmB1ItEt+SGh6B58=
|
||||
github.com/cloudnative-pg/api v1.28.0/go.mod h1:puXJBOsEaJd8JLgvCtxgl2TO/ZANap/z7bPepKRUgrk=
|
||||
github.com/cloudnative-pg/barman-cloud v0.4.1-0.20251230211524-20b7e0e10b0f h1:4/PwIQOwQSTIxuncGRn3pX2V9CRwl7zJNXOVWOMSCCU=
|
||||
github.com/cloudnative-pg/barman-cloud v0.4.1-0.20251230211524-20b7e0e10b0f/go.mod h1:qD0NtJOllNQbRB0MaleuHsZjFYaXtXfdg0HbFTbuHn0=
|
||||
github.com/cloudnative-pg/barman-cloud v0.4.1-0.20260108104508-ced266c145f5 h1:wPB7VTNgTv6t9sl4QYOBakmVTqHnOdKUht7Q3aL+uns=
|
||||
github.com/cloudnative-pg/barman-cloud v0.4.1-0.20260108104508-ced266c145f5/go.mod h1:qD0NtJOllNQbRB0MaleuHsZjFYaXtXfdg0HbFTbuHn0=
|
||||
github.com/cloudnative-pg/cloudnative-pg v1.28.0 h1:vkv0a0ewDSfJOPJrsyUr4uczsxheReAWf/k171V0Dm0=
|
||||
github.com/cloudnative-pg/cloudnative-pg v1.28.0/go.mod h1:209fkRR6m0vXUVQ9Q498eAPQqN2UlXECbXXtpGsZz3I=
|
||||
github.com/cloudnative-pg/cnpg-i v0.3.1 h1:fKj8NoToWI11HUL2UWYJBpkVzmaTvbs3kDMo7wQF8RU=
|
||||
|
||||
@ -37,13 +37,16 @@ func CollectSecretNamesFromCredentials(barmanCredentials *barmanapi.BarmanCreden
|
||||
)
|
||||
}
|
||||
if barmanCredentials.Azure != nil {
|
||||
references = append(
|
||||
references,
|
||||
barmanCredentials.Azure.ConnectionString,
|
||||
barmanCredentials.Azure.StorageAccount,
|
||||
barmanCredentials.Azure.StorageKey,
|
||||
barmanCredentials.Azure.StorageSasToken,
|
||||
)
|
||||
// When using default Azure credentials, no secrets are required
|
||||
if !barmanCredentials.Azure.UseDefaultAzureCredentials {
|
||||
references = append(
|
||||
references,
|
||||
barmanCredentials.Azure.ConnectionString,
|
||||
barmanCredentials.Azure.StorageAccount,
|
||||
barmanCredentials.Azure.StorageKey,
|
||||
barmanCredentials.Azure.StorageSasToken,
|
||||
)
|
||||
}
|
||||
}
|
||||
if barmanCredentials.Google != nil {
|
||||
references = append(
|
||||
|
||||
227
internal/cnpgi/operator/specs/secrets_test.go
Normal file
227
internal/cnpgi/operator/specs/secrets_test.go
Normal file
@ -0,0 +1,227 @@
|
||||
/*
|
||||
Copyright © contributors to CloudNativePG, established as
|
||||
CloudNativePG a Series of LF Projects, LLC.
|
||||
|
||||
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
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package specs
|
||||
|
||||
import (
|
||||
barmanapi "github.com/cloudnative-pg/barman-cloud/pkg/api"
|
||||
machineryapi "github.com/cloudnative-pg/machinery/pkg/api"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("CollectSecretNamesFromCredentials", func() {
|
||||
Context("when collecting secrets from AWS credentials", func() {
|
||||
It("should return secret names from S3 credentials", func() {
|
||||
credentials := &barmanapi.BarmanCredentials{
|
||||
AWS: &barmanapi.S3Credentials{
|
||||
AccessKeyIDReference: &machineryapi.SecretKeySelector{
|
||||
LocalObjectReference: machineryapi.LocalObjectReference{
|
||||
Name: "aws-secret",
|
||||
},
|
||||
Key: "access-key-id",
|
||||
},
|
||||
SecretAccessKeyReference: &machineryapi.SecretKeySelector{
|
||||
LocalObjectReference: machineryapi.LocalObjectReference{
|
||||
Name: "aws-secret",
|
||||
},
|
||||
Key: "secret-access-key",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
secrets := CollectSecretNamesFromCredentials(credentials)
|
||||
Expect(secrets).To(ContainElement("aws-secret"))
|
||||
})
|
||||
|
||||
It("should handle nil AWS credentials", func() {
|
||||
credentials := &barmanapi.BarmanCredentials{}
|
||||
|
||||
secrets := CollectSecretNamesFromCredentials(credentials)
|
||||
Expect(secrets).To(BeEmpty())
|
||||
})
|
||||
})
|
||||
|
||||
Context("when collecting secrets from Azure credentials", func() {
|
||||
It("should return secret names when using explicit credentials", func() {
|
||||
credentials := &barmanapi.BarmanCredentials{
|
||||
Azure: &barmanapi.AzureCredentials{
|
||||
ConnectionString: &machineryapi.SecretKeySelector{
|
||||
LocalObjectReference: machineryapi.LocalObjectReference{
|
||||
Name: "azure-secret",
|
||||
},
|
||||
Key: "connection-string",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
secrets := CollectSecretNamesFromCredentials(credentials)
|
||||
Expect(secrets).To(ContainElement("azure-secret"))
|
||||
})
|
||||
|
||||
It("should return empty list when using UseDefaultAzureCredentials", func() {
|
||||
credentials := &barmanapi.BarmanCredentials{
|
||||
Azure: &barmanapi.AzureCredentials{
|
||||
UseDefaultAzureCredentials: true,
|
||||
ConnectionString: &machineryapi.SecretKeySelector{
|
||||
LocalObjectReference: machineryapi.LocalObjectReference{
|
||||
Name: "azure-secret",
|
||||
},
|
||||
Key: "connection-string",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
secrets := CollectSecretNamesFromCredentials(credentials)
|
||||
Expect(secrets).To(BeEmpty())
|
||||
})
|
||||
|
||||
It("should return empty list when using InheritFromAzureAD", func() {
|
||||
credentials := &barmanapi.BarmanCredentials{
|
||||
Azure: &barmanapi.AzureCredentials{
|
||||
InheritFromAzureAD: true,
|
||||
},
|
||||
}
|
||||
|
||||
secrets := CollectSecretNamesFromCredentials(credentials)
|
||||
Expect(secrets).To(BeEmpty())
|
||||
})
|
||||
|
||||
It("should return secret names for storage account and key", func() {
|
||||
credentials := &barmanapi.BarmanCredentials{
|
||||
Azure: &barmanapi.AzureCredentials{
|
||||
StorageAccount: &machineryapi.SecretKeySelector{
|
||||
LocalObjectReference: machineryapi.LocalObjectReference{
|
||||
Name: "azure-storage",
|
||||
},
|
||||
Key: "account-name",
|
||||
},
|
||||
StorageKey: &machineryapi.SecretKeySelector{
|
||||
LocalObjectReference: machineryapi.LocalObjectReference{
|
||||
Name: "azure-storage",
|
||||
},
|
||||
Key: "account-key",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
secrets := CollectSecretNamesFromCredentials(credentials)
|
||||
Expect(secrets).To(ContainElement("azure-storage"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("when collecting secrets from Google credentials", func() {
|
||||
It("should return secret names from Google credentials", func() {
|
||||
credentials := &barmanapi.BarmanCredentials{
|
||||
Google: &barmanapi.GoogleCredentials{
|
||||
ApplicationCredentials: &machineryapi.SecretKeySelector{
|
||||
LocalObjectReference: machineryapi.LocalObjectReference{
|
||||
Name: "google-secret",
|
||||
},
|
||||
Key: "credentials.json",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
secrets := CollectSecretNamesFromCredentials(credentials)
|
||||
Expect(secrets).To(ContainElement("google-secret"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("when collecting secrets from multiple cloud providers", func() {
|
||||
It("should return secret names from all providers", func() {
|
||||
credentials := &barmanapi.BarmanCredentials{
|
||||
AWS: &barmanapi.S3Credentials{
|
||||
AccessKeyIDReference: &machineryapi.SecretKeySelector{
|
||||
LocalObjectReference: machineryapi.LocalObjectReference{
|
||||
Name: "aws-secret",
|
||||
},
|
||||
Key: "access-key-id",
|
||||
},
|
||||
},
|
||||
Azure: &barmanapi.AzureCredentials{
|
||||
ConnectionString: &machineryapi.SecretKeySelector{
|
||||
LocalObjectReference: machineryapi.LocalObjectReference{
|
||||
Name: "azure-secret",
|
||||
},
|
||||
Key: "connection-string",
|
||||
},
|
||||
},
|
||||
Google: &barmanapi.GoogleCredentials{
|
||||
ApplicationCredentials: &machineryapi.SecretKeySelector{
|
||||
LocalObjectReference: machineryapi.LocalObjectReference{
|
||||
Name: "google-secret",
|
||||
},
|
||||
Key: "credentials.json",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
secrets := CollectSecretNamesFromCredentials(credentials)
|
||||
Expect(secrets).To(ContainElements("aws-secret", "azure-secret", "google-secret"))
|
||||
})
|
||||
|
||||
It("should skip Azure secrets when using UseDefaultAzureCredentials with other providers", func() {
|
||||
credentials := &barmanapi.BarmanCredentials{
|
||||
AWS: &barmanapi.S3Credentials{
|
||||
AccessKeyIDReference: &machineryapi.SecretKeySelector{
|
||||
LocalObjectReference: machineryapi.LocalObjectReference{
|
||||
Name: "aws-secret",
|
||||
},
|
||||
Key: "access-key-id",
|
||||
},
|
||||
},
|
||||
Azure: &barmanapi.AzureCredentials{
|
||||
UseDefaultAzureCredentials: true,
|
||||
ConnectionString: &machineryapi.SecretKeySelector{
|
||||
LocalObjectReference: machineryapi.LocalObjectReference{
|
||||
Name: "azure-secret",
|
||||
},
|
||||
Key: "connection-string",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
secrets := CollectSecretNamesFromCredentials(credentials)
|
||||
Expect(secrets).To(ContainElement("aws-secret"))
|
||||
Expect(secrets).NotTo(ContainElement("azure-secret"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("when handling nil references", func() {
|
||||
It("should skip nil secret references", func() {
|
||||
credentials := &barmanapi.BarmanCredentials{
|
||||
AWS: &barmanapi.S3Credentials{
|
||||
AccessKeyIDReference: &machineryapi.SecretKeySelector{
|
||||
LocalObjectReference: machineryapi.LocalObjectReference{
|
||||
Name: "aws-secret",
|
||||
},
|
||||
Key: "access-key-id",
|
||||
},
|
||||
SecretAccessKeyReference: nil,
|
||||
},
|
||||
}
|
||||
|
||||
secrets := CollectSecretNamesFromCredentials(credentials)
|
||||
Expect(secrets).To(ContainElement("aws-secret"))
|
||||
Expect(len(secrets)).To(Equal(1))
|
||||
})
|
||||
})
|
||||
})
|
||||
32
internal/cnpgi/operator/specs/suite_test.go
Normal file
32
internal/cnpgi/operator/specs/suite_test.go
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
Copyright © contributors to CloudNativePG, established as
|
||||
CloudNativePG a Series of LF Projects, LLC.
|
||||
|
||||
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
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package specs
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestSpecs(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Specs Suite")
|
||||
}
|
||||
@ -252,6 +252,33 @@ spec:
|
||||
[...]
|
||||
```
|
||||
|
||||
### Default Azure Credentials
|
||||
|
||||
The `useDefaultAzureCredentials` option enables the default Azure credentials
|
||||
flow, which uses [`DefaultAzureCredential`](https://learn.microsoft.com/en-us/python/api/azure-identity/azure.identity.defaultazurecredential)
|
||||
to automatically discover and use available credentials in the following order:
|
||||
|
||||
1. **Environment Variables** — `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, and `AZURE_TENANT_ID` for Service Principal authentication
|
||||
2. **Managed Identity** — Uses the managed identity assigned to the pod
|
||||
3. **Azure CLI** — Uses credentials from the Azure CLI if available
|
||||
4. **Azure PowerShell** — Uses credentials from Azure PowerShell if available
|
||||
|
||||
This is particularly useful when running on Azure Kubernetes Service (AKS) with
|
||||
[Workload Identity](https://learn.microsoft.com/en-us/azure/aks/workload-identity-overview):
|
||||
|
||||
```yaml
|
||||
apiVersion: barmancloud.cnpg.io/v1
|
||||
kind: ObjectStore
|
||||
metadata:
|
||||
name: azure-store
|
||||
spec:
|
||||
configuration:
|
||||
destinationPath: "<destination path here>"
|
||||
azureCredentials:
|
||||
useDefaultAzureCredentials: true
|
||||
[...]
|
||||
```
|
||||
|
||||
### Access Key, SAS Token, or Connection String
|
||||
|
||||
Store credentials in a Kubernetes secret:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user