feat(backup): support identity-based auth methods for azure - #268
techknowlogick wants to merge 1 commit into
Conversation
81d20fc to
58574dd
Compare
|
@techknowlogick Thanks for your contribution. |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #268 +/- ##
=========================================
+ Coverage 4.53% 5.18% +0.64%
=========================================
Files 23 23
Lines 2051 2084 +33
=========================================
+ Hits 93 108 +15
- Misses 1950 1966 +16
- Partials 8 10 +2
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
ping @techknowlogick |
|
@derekbit thanks for ping. You can find the issue reported here: longhorn/longhorn#12600 |
Signed-off-by: techknowlogick <techknowlogick@gitea.com>
58574dd to
d6abca6
Compare
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds Azure identity-based authentication support for Azure Blob backup targets, allowing non-static credentials (managed identity, workload identity, service principals) while keeping shared key auth working.
Changes:
- Switch Azure Blob client creation from connection-string shared key only to SharedKeyCredential (if key present) or DefaultAzureCredential (otherwise).
- Extend credential env-var setup to optionally set Azure identity env vars (client/tenant/secret) without overwriting webhook-injected values.
- Add unit tests for parsing account name from URL and building the service URL.
Reviewed changes
Copilot reviewed 5 out of 218 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| util/credential.go | Adds env-var wiring for Azure identity credentials and relaxes account-key-only validation. |
| types/types.go | Introduces constants for AZURE_CLIENT_ID, AZURE_TENANT_ID, AZURE_CLIENT_SECRET. |
| azblob/azblob_service.go | Uses SharedKeyCredential vs DefaultAzureCredential and adds URL helpers for account/service endpoint construction. |
| azblob/azblob_service_test.go | Adds tests for the new URL helper functions. |
| go.mod | Adds azidentity but also changes several unrelated dependency versions. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| "github.com/Azure/azure-sdk-for-go/sdk/azcore" | ||
| "github.com/Azure/azure-sdk-for-go/sdk/azcore/streaming" | ||
| "github.com/Azure/azure-sdk-for-go/sdk/azidentity" | ||
| "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob" |
There was a problem hiding this comment.
azblobsvc is referenced but (in the shown import block) the azblob SDK package is imported as azblob. This will not compile unless there is an additional aliased import elsewhere in the file. Align the import name and usages (either import sdk/storage/azblob as azblobsvc, or update azblobsvc.* references to azblob.*) so the file compiles consistently.
| if err != nil { | ||
| return nil, err | ||
| } | ||
| opts := azblobsvc.ClientOptions{ClientOptions: azcore.ClientOptions{Transport: httpClient}} |
There was a problem hiding this comment.
azblobsvc is referenced but (in the shown import block) the azblob SDK package is imported as azblob. This will not compile unless there is an additional aliased import elsewhere in the file. Align the import name and usages (either import sdk/storage/azblob as azblobsvc, or update azblobsvc.* references to azblob.*) so the file compiles consistently.
| if err != nil { | ||
| return nil, fmt.Errorf("failed to create SharedKeyCredential: %w", err) | ||
| } | ||
| serviceClient, err = azblobsvc.NewClientWithSharedKeyCredential(serviceURL, cred, &opts) |
There was a problem hiding this comment.
azblobsvc is referenced but (in the shown import block) the azblob SDK package is imported as azblob. This will not compile unless there is an additional aliased import elsewhere in the file. Align the import name and usages (either import sdk/storage/azblob as azblobsvc, or update azblobsvc.* references to azblob.*) so the file compiles consistently.
| if err != nil { | ||
| return nil, fmt.Errorf("failed to create DefaultAzureCredential: %w", err) | ||
| } | ||
| serviceClient, err = azblobsvc.NewClient(serviceURL, cred, &opts) |
There was a problem hiding this comment.
azblobsvc is referenced but (in the shown import block) the azblob SDK package is imported as azblob. This will not compile unless there is an additional aliased import elsewhere in the file. Align the import name and usages (either import sdk/storage/azblob as azblobsvc, or update azblobsvc.* references to azblob.*) so the file compiles consistently.
| func extractAccountNameFromURL(u *url.URL) string { | ||
| if u.User != nil && u.Host != "" { | ||
| // Check if host contains ".blob." indicating full endpoint format | ||
| // e.g., "mystorageaccount.blob.core.windows.net" | ||
| if idx := strings.Index(u.Host, ".blob."); idx > 0 { | ||
| return u.Host[:idx] | ||
| } | ||
| } | ||
| return "" | ||
| } |
There was a problem hiding this comment.
The account-name extraction is gated on u.User != nil, but the logic actually parses the account name from u.Host. This unnecessarily prevents extraction for valid URLs that omit userinfo (e.g., URLs that embed container in the path rather than container@host). Remove the u.User != nil condition (keep the u.Host != "" check) and add a test case covering a URL with a host like mystorageaccount.blob.core.windows.net but no user part.
| github.com/longhorn/go-common-libs v0.0.0-20260103034008-119bdcf1b2d6 | ||
| github.com/pierrec/lz4/v4 v4.1.23 | ||
| github.com/sirupsen/logrus v1.9.3 | ||
| github.com/slok/goresilience v0.2.0 | ||
| github.com/spf13/afero v1.11.0 | ||
| github.com/stretchr/testify v1.11.1 | ||
| github.com/stretchr/testify v1.10.0 | ||
| github.com/urfave/cli v1.22.17 | ||
| golang.org/x/net v0.49.0 | ||
| golang.org/x/sys v0.40.0 | ||
| golang.org/x/net v0.47.0 | ||
| golang.org/x/sys v0.38.0 |
There was a problem hiding this comment.
This PR is focused on Azure identity auth, but go.mod also changes multiple unrelated dependencies (including apparent downgrades like logrus, testify, x/net, x/sys, x/text). That increases review surface and risk of regressions unrelated to the feature. Consider reverting the unrelated version changes (keeping only the new Azure identity deps), or splitting dependency churn into a separate PR with its own rationale/release notes.
| golang.org/x/crypto v0.44.0 // indirect | ||
| golang.org/x/text v0.31.0 // indirect |
There was a problem hiding this comment.
This PR is focused on Azure identity auth, but go.mod also changes multiple unrelated dependencies (including apparent downgrades like logrus, testify, x/net, x/sys, x/text). That increases review surface and risk of regressions unrelated to the feature. Consider reverting the unrelated version changes (keeping only the new Azure identity deps), or splitting dependency churn into a separate PR with its own rationale/release notes.
| accountName := os.Getenv("AZBLOB_ACCOUNT_NAME") | ||
| accountKey := os.Getenv("AZBLOB_ACCOUNT_KEY") | ||
| azureEndpoint := os.Getenv("AZBLOB_ENDPOINT") |
There was a problem hiding this comment.
These env-var keys are hard-coded strings, while the repo already has types.AZBlobAccountName, types.AZBlobAccountKey, and types.AZBlobEndpoint. Using the constants here would reduce duplication/typos and keep env-var naming consistent across packages.
|
@techknowlogick Could you address the comments from @copilot? Thank you. |
|
BTW @techknowlogick Could you create a PR to update https://github.com/longhorn/website/blob/master/content/docs/1.12.0/snapshots-and-backups/backup-and-restore/set-backup-target.md#set-up-azure-blob-storage-backupstore? Thanks. |
|
@mantissahz @COLDTURNIP Please review the PR. |
|
In general, LGTM. |
What this PR does / why we need it:
To support non-static tokens for Azure backup targets. As of right now, if you want to use Azure Blob storage as a backup target (unless you use a fragile workaround with k8s secrets), you need to use a static key.
This PR adds support for Azure managed identity, AKS workload identity and service principals.
If you're still keen on using static tokens but want to scope permissions, you can use service principals. Although this PR is backward compatible with the previous code/configuration, so you can keep using Account Key.
longhorn/longhorn#12600
Additional documentation or context
https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview
https://learn.microsoft.com/en-us/azure/aks/use-managed-identity
https://learn.microsoft.com/en-us/azure/azure-arc/kubernetes/conceptual-workload-identity