Skip to content

Commit 2c134ea

Browse files
armruGabriFedi97mnencia
authored
feat: add support for DefaultAzureCredential authentication mechanism (#681)
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 1. Environment Variables (Service Principal) 2. Managed Identity 3. Azure CLI 4. Azure PowerShell This is particularly useful when running on Azure Kubernetes Service (AKS) with Workload Identity, eliminating the need to explicitly store credentials in Kubernetes Secrets. Signed-off-by: Armando Ruocco <[email protected]> Signed-off-by: Gabriele Fedi <[email protected]> Signed-off-by: Marco Nenciarini <[email protected]> Co-authored-by: Gabriele Fedi <[email protected]> Co-authored-by: Marco Nenciarini <[email protected]>
1 parent 0153abb commit 2c134ea

File tree

9 files changed

+327
-16
lines changed

9 files changed

+327
-16
lines changed

.wordlist.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
AKS
12
AccessDenied
23
AdditionalContainerArgs
34
Akamai
45
Azurite
56
BarmanObjectStore
67
BarmanObjectStoreConfiguration
78
BarmanObjectStores
9+
CLI
810
CNCF
911
CRD
1012
CloudNativePG
@@ -38,13 +40,15 @@ PITR
3840
PoR
3941
PostgreSQL
4042
Postgres
43+
PowerShell
4144
README
4245
RPO
4346
RTO
4447
RecoveryWindow
4548
ResourceRequirements
4649
RetentionPolicy
4750
SAS
51+
SDK
4852
SFO
4953
SPDX
5054
SPDX

config/crd/bases/barmancloud.cnpg.io_objectstores.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,11 @@ spec:
108108
- key
109109
- name
110110
type: object
111+
useDefaultAzureCredentials:
112+
description: |-
113+
Use the default Azure authentication flow, which includes DefaultAzureCredential.
114+
This allows authentication using environment variables and managed identities.
115+
type: boolean
111116
type: object
112117
data:
113118
description: |-

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ toolchain go1.25.5
77
require (
88
github.com/cert-manager/cert-manager v1.19.2
99
github.com/cloudnative-pg/api v1.28.0
10-
github.com/cloudnative-pg/barman-cloud v0.4.1-0.20251230211524-20b7e0e10b0f
10+
github.com/cloudnative-pg/barman-cloud v0.4.1-0.20260108104508-ced266c145f5
1111
github.com/cloudnative-pg/cloudnative-pg v1.28.0
1212
github.com/cloudnative-pg/cnpg-i v0.3.1
1313
github.com/cloudnative-pg/cnpg-i-machinery v0.4.2

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF
1818
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
1919
github.com/cloudnative-pg/api v1.28.0 h1:xElzHliO0eKkVQafkfMhDJo0aIRCmB1ItEt+SGh6B58=
2020
github.com/cloudnative-pg/api v1.28.0/go.mod h1:puXJBOsEaJd8JLgvCtxgl2TO/ZANap/z7bPepKRUgrk=
21-
github.com/cloudnative-pg/barman-cloud v0.4.1-0.20251230211524-20b7e0e10b0f h1:4/PwIQOwQSTIxuncGRn3pX2V9CRwl7zJNXOVWOMSCCU=
22-
github.com/cloudnative-pg/barman-cloud v0.4.1-0.20251230211524-20b7e0e10b0f/go.mod h1:qD0NtJOllNQbRB0MaleuHsZjFYaXtXfdg0HbFTbuHn0=
21+
github.com/cloudnative-pg/barman-cloud v0.4.1-0.20260108104508-ced266c145f5 h1:wPB7VTNgTv6t9sl4QYOBakmVTqHnOdKUht7Q3aL+uns=
22+
github.com/cloudnative-pg/barman-cloud v0.4.1-0.20260108104508-ced266c145f5/go.mod h1:qD0NtJOllNQbRB0MaleuHsZjFYaXtXfdg0HbFTbuHn0=
2323
github.com/cloudnative-pg/cloudnative-pg v1.28.0 h1:vkv0a0ewDSfJOPJrsyUr4uczsxheReAWf/k171V0Dm0=
2424
github.com/cloudnative-pg/cloudnative-pg v1.28.0/go.mod h1:209fkRR6m0vXUVQ9Q498eAPQqN2UlXECbXXtpGsZz3I=
2525
github.com/cloudnative-pg/cnpg-i v0.3.1 h1:fKj8NoToWI11HUL2UWYJBpkVzmaTvbs3kDMo7wQF8RU=

internal/cnpgi/operator/specs/secrets.go

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,17 @@ func CollectSecretNamesFromCredentials(barmanCredentials *barmanapi.BarmanCreden
3737
)
3838
}
3939
if barmanCredentials.Azure != nil {
40-
references = append(
41-
references,
42-
barmanCredentials.Azure.ConnectionString,
43-
barmanCredentials.Azure.StorageAccount,
44-
barmanCredentials.Azure.StorageKey,
45-
barmanCredentials.Azure.StorageSasToken,
46-
)
40+
// When using default Azure credentials or managed identity, no secrets are required
41+
if !barmanCredentials.Azure.UseDefaultAzureCredentials &&
42+
!barmanCredentials.Azure.InheritFromAzureAD {
43+
references = append(
44+
references,
45+
barmanCredentials.Azure.ConnectionString,
46+
barmanCredentials.Azure.StorageAccount,
47+
barmanCredentials.Azure.StorageKey,
48+
barmanCredentials.Azure.StorageSasToken,
49+
)
50+
}
4751
}
4852
if barmanCredentials.Google != nil {
4953
references = append(
Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
/*
2+
Copyright © contributors to CloudNativePG, established as
3+
CloudNativePG a Series of LF Projects, LLC.
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
17+
SPDX-License-Identifier: Apache-2.0
18+
*/
19+
20+
package specs
21+
22+
import (
23+
barmanapi "github.com/cloudnative-pg/barman-cloud/pkg/api"
24+
machineryapi "github.com/cloudnative-pg/machinery/pkg/api"
25+
26+
. "github.com/onsi/ginkgo/v2"
27+
. "github.com/onsi/gomega"
28+
)
29+
30+
var _ = Describe("CollectSecretNamesFromCredentials", func() {
31+
Context("when collecting secrets from AWS credentials", func() {
32+
It("should return secret names from S3 credentials", func() {
33+
credentials := &barmanapi.BarmanCredentials{
34+
AWS: &barmanapi.S3Credentials{
35+
AccessKeyIDReference: &machineryapi.SecretKeySelector{
36+
LocalObjectReference: machineryapi.LocalObjectReference{
37+
Name: "aws-secret",
38+
},
39+
Key: "access-key-id",
40+
},
41+
SecretAccessKeyReference: &machineryapi.SecretKeySelector{
42+
LocalObjectReference: machineryapi.LocalObjectReference{
43+
Name: "aws-secret",
44+
},
45+
Key: "secret-access-key",
46+
},
47+
},
48+
}
49+
50+
secrets := CollectSecretNamesFromCredentials(credentials)
51+
Expect(secrets).To(ContainElement("aws-secret"))
52+
})
53+
54+
It("should handle nil AWS credentials", func() {
55+
credentials := &barmanapi.BarmanCredentials{}
56+
57+
secrets := CollectSecretNamesFromCredentials(credentials)
58+
Expect(secrets).To(BeEmpty())
59+
})
60+
})
61+
62+
Context("when collecting secrets from Azure credentials", func() {
63+
It("should return secret names when using explicit credentials", func() {
64+
credentials := &barmanapi.BarmanCredentials{
65+
Azure: &barmanapi.AzureCredentials{
66+
ConnectionString: &machineryapi.SecretKeySelector{
67+
LocalObjectReference: machineryapi.LocalObjectReference{
68+
Name: "azure-secret",
69+
},
70+
Key: "connection-string",
71+
},
72+
},
73+
}
74+
75+
secrets := CollectSecretNamesFromCredentials(credentials)
76+
Expect(secrets).To(ContainElement("azure-secret"))
77+
})
78+
79+
It("should return empty list when using UseDefaultAzureCredentials", func() {
80+
credentials := &barmanapi.BarmanCredentials{
81+
Azure: &barmanapi.AzureCredentials{
82+
UseDefaultAzureCredentials: true,
83+
ConnectionString: &machineryapi.SecretKeySelector{
84+
LocalObjectReference: machineryapi.LocalObjectReference{
85+
Name: "azure-secret",
86+
},
87+
Key: "connection-string",
88+
},
89+
},
90+
}
91+
92+
secrets := CollectSecretNamesFromCredentials(credentials)
93+
Expect(secrets).To(BeEmpty())
94+
})
95+
96+
It("should return empty list when using InheritFromAzureAD", func() {
97+
credentials := &barmanapi.BarmanCredentials{
98+
Azure: &barmanapi.AzureCredentials{
99+
InheritFromAzureAD: true,
100+
},
101+
}
102+
103+
secrets := CollectSecretNamesFromCredentials(credentials)
104+
Expect(secrets).To(BeEmpty())
105+
})
106+
107+
It("should return secret names for storage account and key", func() {
108+
credentials := &barmanapi.BarmanCredentials{
109+
Azure: &barmanapi.AzureCredentials{
110+
StorageAccount: &machineryapi.SecretKeySelector{
111+
LocalObjectReference: machineryapi.LocalObjectReference{
112+
Name: "azure-storage",
113+
},
114+
Key: "account-name",
115+
},
116+
StorageKey: &machineryapi.SecretKeySelector{
117+
LocalObjectReference: machineryapi.LocalObjectReference{
118+
Name: "azure-storage",
119+
},
120+
Key: "account-key",
121+
},
122+
},
123+
}
124+
125+
secrets := CollectSecretNamesFromCredentials(credentials)
126+
Expect(secrets).To(ContainElement("azure-storage"))
127+
})
128+
})
129+
130+
Context("when collecting secrets from Google credentials", func() {
131+
It("should return secret names from Google credentials", func() {
132+
credentials := &barmanapi.BarmanCredentials{
133+
Google: &barmanapi.GoogleCredentials{
134+
ApplicationCredentials: &machineryapi.SecretKeySelector{
135+
LocalObjectReference: machineryapi.LocalObjectReference{
136+
Name: "google-secret",
137+
},
138+
Key: "credentials.json",
139+
},
140+
},
141+
}
142+
143+
secrets := CollectSecretNamesFromCredentials(credentials)
144+
Expect(secrets).To(ContainElement("google-secret"))
145+
})
146+
})
147+
148+
Context("when collecting secrets from multiple cloud providers", func() {
149+
It("should return secret names from all providers", func() {
150+
credentials := &barmanapi.BarmanCredentials{
151+
AWS: &barmanapi.S3Credentials{
152+
AccessKeyIDReference: &machineryapi.SecretKeySelector{
153+
LocalObjectReference: machineryapi.LocalObjectReference{
154+
Name: "aws-secret",
155+
},
156+
Key: "access-key-id",
157+
},
158+
},
159+
Azure: &barmanapi.AzureCredentials{
160+
ConnectionString: &machineryapi.SecretKeySelector{
161+
LocalObjectReference: machineryapi.LocalObjectReference{
162+
Name: "azure-secret",
163+
},
164+
Key: "connection-string",
165+
},
166+
},
167+
Google: &barmanapi.GoogleCredentials{
168+
ApplicationCredentials: &machineryapi.SecretKeySelector{
169+
LocalObjectReference: machineryapi.LocalObjectReference{
170+
Name: "google-secret",
171+
},
172+
Key: "credentials.json",
173+
},
174+
},
175+
}
176+
177+
secrets := CollectSecretNamesFromCredentials(credentials)
178+
Expect(secrets).To(ContainElements("aws-secret", "azure-secret", "google-secret"))
179+
})
180+
181+
It("should skip Azure secrets when using UseDefaultAzureCredentials with other providers", func() {
182+
credentials := &barmanapi.BarmanCredentials{
183+
AWS: &barmanapi.S3Credentials{
184+
AccessKeyIDReference: &machineryapi.SecretKeySelector{
185+
LocalObjectReference: machineryapi.LocalObjectReference{
186+
Name: "aws-secret",
187+
},
188+
Key: "access-key-id",
189+
},
190+
},
191+
Azure: &barmanapi.AzureCredentials{
192+
UseDefaultAzureCredentials: true,
193+
ConnectionString: &machineryapi.SecretKeySelector{
194+
LocalObjectReference: machineryapi.LocalObjectReference{
195+
Name: "azure-secret",
196+
},
197+
Key: "connection-string",
198+
},
199+
},
200+
}
201+
202+
secrets := CollectSecretNamesFromCredentials(credentials)
203+
Expect(secrets).To(ContainElement("aws-secret"))
204+
Expect(secrets).NotTo(ContainElement("azure-secret"))
205+
})
206+
})
207+
208+
Context("when handling nil references", func() {
209+
It("should skip nil secret references", func() {
210+
credentials := &barmanapi.BarmanCredentials{
211+
AWS: &barmanapi.S3Credentials{
212+
AccessKeyIDReference: &machineryapi.SecretKeySelector{
213+
LocalObjectReference: machineryapi.LocalObjectReference{
214+
Name: "aws-secret",
215+
},
216+
Key: "access-key-id",
217+
},
218+
SecretAccessKeyReference: nil,
219+
},
220+
}
221+
222+
secrets := CollectSecretNamesFromCredentials(credentials)
223+
Expect(secrets).To(ContainElement("aws-secret"))
224+
Expect(len(secrets)).To(Equal(1))
225+
})
226+
})
227+
})
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
Copyright © contributors to CloudNativePG, established as
3+
CloudNativePG a Series of LF Projects, LLC.
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
17+
SPDX-License-Identifier: Apache-2.0
18+
*/
19+
20+
package specs
21+
22+
import (
23+
"testing"
24+
25+
. "github.com/onsi/ginkgo/v2"
26+
. "github.com/onsi/gomega"
27+
)
28+
29+
func TestSpecs(t *testing.T) {
30+
RegisterFailHandler(Fail)
31+
RunSpecs(t, "Specs Suite")
32+
}

manifest.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,11 @@ spec:
107107
- key
108108
- name
109109
type: object
110+
useDefaultAzureCredentials:
111+
description: |-
112+
Use the default Azure authentication flow, which includes DefaultAzureCredential.
113+
This allows authentication using environment variables and managed identities.
114+
type: boolean
110115
type: object
111116
data:
112117
description: |-

0 commit comments

Comments
 (0)