Skip to content

Commit ced266c

Browse files
armrumnencia
andauthored
feat: Add DefaultAzureCredential authentication support (#174)
Support the Azure SDK DefaultAzureCredential authentication flow, which allows credential authentication through environment variables (AZURE_CLIENT_ID, AZURE_TENANT_ID, AZURE_CLIENT_SECRET) and managed identities in AKS environments. Signed-off-by: Armando Ruocco <armando.ruocco@enterprisedb.com> Signed-off-by: Marco Nenciarini <marco.nenciarini@enterprisedb.com> Co-authored-by: Marco Nenciarini <marco.nenciarini@enterprisedb.com>
1 parent 20b7e0e commit ced266c

File tree

5 files changed

+171
-13
lines changed

5 files changed

+171
-13
lines changed

pkg/api/config.go

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ type S3Credentials struct {
119119
// - storageKey (requires storageAccount)
120120
// - storageSasToken (requires storageAccount)
121121
// - inheritFromAzureAD (inheriting credentials from the pod environment)
122+
// - useDefaultAzureCredentials (using the default Azure authentication flow)
122123
type AzureCredentials struct {
123124
// The connection string to be used
124125
// +optional
@@ -141,6 +142,11 @@ type AzureCredentials struct {
141142
// Use the Azure AD based authentication without providing explicitly the keys.
142143
// +optional
143144
InheritFromAzureAD bool `json:"inheritFromAzureAD,omitempty"`
145+
146+
// Use the default Azure authentication flow, which includes DefaultAzureCredential.
147+
// This allows authentication using environment variables and managed identities.
148+
// +optional
149+
UseDefaultAzureCredentials bool `json:"useDefaultAzureCredentials,omitempty"`
144150
}
145151

146152
// GoogleCredentials is the type for the Google Cloud Storage credentials.
@@ -328,28 +334,31 @@ func (crendentials BarmanCredentials) ArePopulated() bool {
328334
func (azure *AzureCredentials) ValidateAzureCredentials(path *field.Path) field.ErrorList {
329335
allErrors := field.ErrorList{}
330336

331-
secrets := 0
337+
authMethods := 0
332338
if azure.InheritFromAzureAD {
333-
secrets++
339+
authMethods++
340+
}
341+
if azure.UseDefaultAzureCredentials {
342+
authMethods++
334343
}
335344
if azure.StorageKey != nil {
336-
secrets++
345+
authMethods++
337346
}
338347
if azure.StorageSasToken != nil {
339-
secrets++
348+
authMethods++
340349
}
341350

342-
if secrets != 1 && azure.ConnectionString == nil {
351+
if authMethods != 1 && azure.ConnectionString == nil {
343352
allErrors = append(
344353
allErrors,
345354
field.Invalid(
346355
path,
347356
azure,
348357
"when connection string is not specified, one and only one of "+
349-
"storage key, storage SAS token, or Azure AD authentication is required"))
358+
"storage key, storage SAS token, Azure AD authentication, or default Azure credentials is required"))
350359
}
351360

352-
if (secrets != 0 || azure.StorageAccount != nil) && azure.ConnectionString != nil {
361+
if (authMethods != 0 || azure.StorageAccount != nil) && azure.ConnectionString != nil {
353362
allErrors = append(
354363
allErrors,
355364
field.Invalid(

pkg/api/config_test.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,4 +295,45 @@ var _ = Describe("azure credentials", func() {
295295
}
296296
Expect(azureCredentials.ValidateAzureCredentials(path)).ToNot(BeEmpty())
297297
})
298+
299+
It("is correct when using default azure credentials", func() {
300+
azureCredentials := AzureCredentials{
301+
UseDefaultAzureCredentials: true,
302+
}
303+
Expect(azureCredentials.ValidateAzureCredentials(path)).To(BeEmpty())
304+
})
305+
306+
It("is not correct when multiple credential types are specified including default", func() {
307+
azureCredentials := AzureCredentials{
308+
UseDefaultAzureCredentials: true,
309+
InheritFromAzureAD: true,
310+
}
311+
Expect(azureCredentials.ValidateAzureCredentials(path)).ToNot(BeEmpty())
312+
})
313+
314+
It("is not correct when default credentials and storage key are specified", func() {
315+
azureCredentials := AzureCredentials{
316+
UseDefaultAzureCredentials: true,
317+
StorageKey: &machineryapi.SecretKeySelector{
318+
LocalObjectReference: machineryapi.LocalObjectReference{
319+
Name: "azure-config",
320+
},
321+
Key: "storageKey",
322+
},
323+
}
324+
Expect(azureCredentials.ValidateAzureCredentials(path)).ToNot(BeEmpty())
325+
})
326+
327+
It("is not correct when default credentials and connection string are specified", func() {
328+
azureCredentials := AzureCredentials{
329+
UseDefaultAzureCredentials: true,
330+
ConnectionString: &machineryapi.SecretKeySelector{
331+
LocalObjectReference: machineryapi.LocalObjectReference{
332+
Name: "azure-config",
333+
},
334+
Key: "connectionString",
335+
},
336+
}
337+
Expect(azureCredentials.ValidateAzureCredentials(path)).ToNot(BeEmpty())
338+
})
298339
})

pkg/command/commandbuilder.go

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,18 +90,30 @@ func appendCloudProviderOptions(
9090
"--cloud-provider",
9191
"azure-blob-storage")
9292

93+
// deprecated, to be removed in future versions
9394
if useDefaultAzureCredentials(ctx) {
95+
options = append(
96+
options,
97+
"--credential",
98+
"default")
9499
break
95100
}
96101

97-
if !credentials.Azure.InheritFromAzureAD {
102+
if credentials.Azure.UseDefaultAzureCredentials {
103+
options = append(
104+
options,
105+
"--credential",
106+
"default")
98107
break
99108
}
100109

101-
options = append(
102-
options,
103-
"--credential",
104-
"managed-identity")
110+
if credentials.Azure.InheritFromAzureAD {
111+
options = append(
112+
options,
113+
"--credential",
114+
"managed-identity")
115+
break
116+
}
105117
case credentials.Google != nil:
106118
options = append(
107119
options,
@@ -129,8 +141,10 @@ func useDefaultAzureCredentials(ctx context.Context) bool {
129141
return result
130142
}
131143

132-
// ContextWithDefaultAzureCredentials create a context that contains the contextKeyUseDefaultAzureCredentials flag.
144+
// ContextWithDefaultAzureCredentials creates a context that contains the contextKeyUseDefaultAzureCredentials flag.
133145
// When set to true barman-cloud will use the default Azure credentials.
146+
//
147+
// Deprecated: Use AzureCredentials.UseDefaultAzureCredentials instead.
134148
func ContextWithDefaultAzureCredentials(ctx context.Context, enabled bool) context.Context {
135149
return context.WithValue(ctx, contextKeyUseDefaultAzureCredentials, enabled)
136150
}

pkg/command/commandbuilder_test.go

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import (
2020
"context"
2121
"strings"
2222

23+
machineryapi "github.com/cloudnative-pg/machinery/pkg/api"
24+
2325
barmanApi "github.com/cloudnative-pg/barman-cloud/pkg/api"
2426

2527
. "github.com/onsi/ginkgo/v2"
@@ -79,3 +81,91 @@ var _ = Describe("useDefaultAzureCredentials", func() {
7981
Expect(useDefaultAzureCredentials(newCtx)).To(BeTrue())
8082
})
8183
})
84+
85+
var _ = Describe("AppendCloudProviderOptions with Azure credentials", func() {
86+
var options []string
87+
88+
BeforeEach(func() {
89+
options = []string{}
90+
})
91+
92+
It("should use default credential when UseDefaultAzureCredentials is set", func(ctx SpecContext) {
93+
credentials := barmanApi.BarmanCredentials{
94+
Azure: &barmanApi.AzureCredentials{
95+
UseDefaultAzureCredentials: true,
96+
},
97+
}
98+
result, err := appendCloudProviderOptions(ctx, options, credentials)
99+
Expect(err).ToNot(HaveOccurred())
100+
Expect(result).To(ContainElements(
101+
"--cloud-provider", "azure-blob-storage",
102+
"--credential", "default",
103+
))
104+
})
105+
106+
It("should use managed-identity credential when InheritFromAzureAD is set", func(ctx SpecContext) {
107+
credentials := barmanApi.BarmanCredentials{
108+
Azure: &barmanApi.AzureCredentials{
109+
InheritFromAzureAD: true,
110+
},
111+
}
112+
result, err := appendCloudProviderOptions(ctx, options, credentials)
113+
Expect(err).ToNot(HaveOccurred())
114+
Expect(result).To(ContainElements(
115+
"--cloud-provider", "azure-blob-storage",
116+
"--credential", "managed-identity",
117+
))
118+
})
119+
120+
It("should not use any credential flag for explicit credentials", func(ctx SpecContext) {
121+
credentials := barmanApi.BarmanCredentials{
122+
Azure: &barmanApi.AzureCredentials{
123+
StorageAccount: &machineryapi.SecretKeySelector{
124+
LocalObjectReference: machineryapi.LocalObjectReference{
125+
Name: "test",
126+
},
127+
Key: "account",
128+
},
129+
StorageKey: &machineryapi.SecretKeySelector{
130+
LocalObjectReference: machineryapi.LocalObjectReference{
131+
Name: "test",
132+
},
133+
Key: "key",
134+
},
135+
},
136+
}
137+
result, err := appendCloudProviderOptions(ctx, options, credentials)
138+
Expect(err).ToNot(HaveOccurred())
139+
Expect(result).To(Equal([]string{
140+
"--cloud-provider", "azure-blob-storage",
141+
}))
142+
})
143+
144+
It("should use default credential from context when context flag is set", func(ctx SpecContext) {
145+
credentials := barmanApi.BarmanCredentials{
146+
Azure: &barmanApi.AzureCredentials{},
147+
}
148+
newCtx := context.WithValue(ctx, contextKeyUseDefaultAzureCredentials, true)
149+
result, err := appendCloudProviderOptions(newCtx, options, credentials)
150+
Expect(err).ToNot(HaveOccurred())
151+
Expect(result).To(ContainElements(
152+
"--cloud-provider", "azure-blob-storage",
153+
"--credential", "default",
154+
))
155+
})
156+
157+
It("should prioritize UseDefaultAzureCredentials over InheritFromAzureAD", func(ctx SpecContext) {
158+
credentials := barmanApi.BarmanCredentials{
159+
Azure: &barmanApi.AzureCredentials{
160+
UseDefaultAzureCredentials: true,
161+
InheritFromAzureAD: true,
162+
},
163+
}
164+
result, err := appendCloudProviderOptions(ctx, options, credentials)
165+
Expect(err).ToNot(HaveOccurred())
166+
Expect(result).To(ContainElements(
167+
"--cloud-provider", "azure-blob-storage",
168+
"--credential", "default",
169+
))
170+
})
171+
})

pkg/credentials/env.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,10 @@ func envSetAzureCredentials(
218218
return env, nil
219219
}
220220

221+
if configuration.Azure.UseDefaultAzureCredentials {
222+
return env, nil
223+
}
224+
221225
// Get storage account name
222226
if configuration.Azure.StorageAccount != nil {
223227
storageAccount, err := extractValueFromSecret(

0 commit comments

Comments
 (0)