Skip to content

feat(azure): migrate to modern azblob SDK and add SAS-token auth - #37

Merged
loreste merged 1 commit into
loreste:mainfrom
Ma91Wa:feat/azure-sdk-sas-auth-upstream
Jul 2, 2026
Merged

feat(azure): migrate to modern azblob SDK and add SAS-token auth#37
loreste merged 1 commit into
loreste:mainfrom
Ma91Wa:feat/azure-sdk-sas-auth-upstream

Conversation

@Ma91Wa

@Ma91Wa Ma91Wa commented Jul 2, 2026

Copy link
Copy Markdown

Summary

Migrates the Azure Blob Storage backend from the deprecated github.com/Azure/azure-storage-blob-go (v0.15.0, end-of-life, last release 2021) to the modern github.com/Azure/azure-sdk-for-go/sdk/storage/azblob (v1.8.0), and adds SAS-token authentication as a recommended, least-privilege alternative to the storage account key.

Motivation

  • The old SDK is deprecated and no longer maintained.
  • The only supported auth was the storage account key, which grants full read/write/delete access to the entire storage account. There was no way to use a scoped, expiring credential.

What changed

Authentication precedence in NewAzureStorage (first match wins):

  1. SAS token (RECORDING_STORAGE_AZURE_SAS_TOKEN) — recommended; can be scoped to a single container with just create/write permissions and an expiry.
  2. Account key (RECORDING_STORAGE_AZURE_ACCESS_KEY) — still supported; logs a least-privilege warning at startup.

Code:

  • pkg/backup/storage.go — Azure backend rewritten on the client-based API (UploadFile / DownloadFile / NewListBlobsFlatPager / DeleteBlob). Location URLs are built from the account/container/blob names only, never from client.URL(), so a SAS token can never leak into logs or stored .locations files. Metadata key stays backup_id (Azure rejects hyphens with HTTP 400).
  • pkg/config — new SASToken field + env loader; validateAzureStorageConfig enforces exactly one auth method when Azure is enabled and warns when the account key is used.
  • cmd/siprec/main.go — pass the SAS token through to the backup config.
  • pkg/http/config_redact.go — add sas to the secret-redaction patterns so tokens never appear in /api/config.

Tests: both auth paths, a SAS-token leak guard on location strings, blob-name round-trip, Azure validation cases, and redaction of the new fields.

Docs: README storage table, a new "Azure Blob Storage Authentication" section in docs/configuration.md (incl. an az storage container generate-sas example), and a CHANGELOG 1.3.0 entry.

Backwards compatibility

Existing deployments that use RECORDING_STORAGE_AZURE_ACCESS_KEY keep working unchanged — the only visible difference is a startup warning recommending a SAS token.

Out of scope

Managed Identity / azidentity was intentionally left out to keep the dependency footprint small; it can be added later.

Verification

  • go build ./... and go vet ./... pass.
  • go test ./pkg/backup/... ./pkg/config/... ./pkg/http/... pass.
  • Verified end-to-end against a real Azure Blob container on both auth paths (account key and a container-scoped SAS token with create+write): recordings upload as .mp3 blobs with backup_id metadata; no credentials appear in logs or location URLs.

Summary by CodeRabbit

  • New Features

    • Added support for Azure Blob Storage authentication using container-scoped SAS tokens, with clearer configuration options and guidance.
    • Recording uploads now support the newer Azure storage client implementation.
  • Bug Fixes

    • Improved startup checks to ensure Azure storage uses exactly one authentication method.
    • Prevented storage credentials and SAS details from appearing in config output or generated storage locations.
  • Documentation

    • Expanded setup and configuration docs for recording storage and Azure Blob Storage, including recommended secure authentication practices.

Replace the deprecated github.com/Azure/azure-storage-blob-go (v0.15.0, EOL)
with github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.8.0 and add
SAS-token authentication as the recommended, least-privilege method.

Auth precedence in NewAzureStorage (first match wins):
  1. SAS token  (RECORDING_STORAGE_AZURE_SAS_TOKEN) - recommended
  2. account key (RECORDING_STORAGE_AZURE_ACCESS_KEY) - logs a warning

Details:
- pkg/backup/storage.go: rewrite Azure backend on the client-based API
  (UploadFile/DownloadFile/NewListBlobsFlatPager/DeleteBlob). Location URLs
  are built from account/container/blob names only, never from client.URL(),
  so a SAS token can never leak into logs or stored locations. Metadata key
  stays "backup_id" (Azure rejects hyphens with HTTP 400).
- pkg/config: new SASToken field + env loader; validateAzureStorageConfig
  enforces exactly one auth method when Azure is enabled and warns on
  account-key use.
- cmd/siprec/main.go: pass SASToken through to the backup config.
- pkg/http/config_redact.go: add "sas" to the redaction patterns.
- Tests: both auth paths, SAS-token leak guard, blob-name round-trip,
  validation cases, and config redaction of the new fields.
- Docs: README storage table, configuration.md "Azure Blob Storage
  Authentication" section, CHANGELOG 1.3.0.

Backwards compatible: existing RECORDING_STORAGE_AZURE_ACCESS_KEY deployments
keep working unchanged (with an added least-privilege warning at startup).
Managed Identity is intentionally out of scope.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR migrates Azure Blob recording storage from the deprecated azure-storage-blob-go SDK to azure-sdk-for-go's azblob package, adds container-scoped SAS token authentication as an alternative to account-key auth, enforces exactly-one-auth-method validation at startup, extends secret redaction to SAS fields, and updates dependencies and documentation accordingly.

Changes

Azure Blob Storage SAS Authentication and SDK Migration

Layer / File(s) Summary
Config schema and SAS token wiring
pkg/config/config.go, cmd/siprec/main.go
Adds SASToken field to RecordingAzureConfig, loads it from RECORDING_STORAGE_AZURE_SAS_TOKEN, and passes it into backup.AzureConfig.
Azure auth validation
pkg/config/validation.go, pkg/config/validation_test.go
Adds validateAzureStorageConfig enforcing required account/container and exactly one auth method (SAS token or access key), with error/warning tests.
Azure Blob backend rewrite
pkg/backup/storage.go, pkg/backup/storage_test.go
Migrates AzureStorage to the azblob SDK client, adds SAS/SharedKey credential construction, blobLocation/blobNameFromLocation helpers, and rewrites Upload/Download/List/Delete/GetLocation, with new leak-prevention and blob-name-derivation tests.
Secret redaction for Azure fields
pkg/http/config_redact.go, pkg/http/config_handlers_test.go
Adds "sas" to secret field patterns and extends tests confirming Azure SAS token and access key redaction.
Dependency updates and documentation
go.mod, CHANGELOG.md, README.md, docs/configuration.md
Replaces azure-storage-blob-go with azure-sdk-for-go/sdk/storage/azblob, bumps several other dependencies, and documents the SAS auth setup, precedence, and logging safeguards.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Main as createRecordingStorage
  participant Storage as AzureStorage
  participant Client as azblob.Client
  participant Blob as Azure Blob Storage

  Main->>Storage: NewAzureStorage(AzureConfig with SASToken/AccessKey)
  alt SAS token configured
    Storage->>Client: NewClientWithNoCredential(SAS URL)
  else Access key configured
    Storage->>Client: NewClientWithSharedKeyCredential(account, key)
  end
  Storage-->>Main: AzureStorage instance

  Main->>Storage: Upload(backup)
  Storage->>Client: UploadFile(blobName, metadata)
  Client->>Blob: PUT blob data
  Storage->>Storage: blobLocation() (no SAS embedded)
  Storage-->>Main: azure://account/container/blob
Loading

Possibly related PRs

  • loreste/siprec#34: Both PRs touch pkg/backup/storage.go Azure metadata handling, with this PR's rewritten azblob backend reading/writing the backup_id metadata key aligning with the referenced PR's metadata fix.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: Azure Blob SDK migration plus SAS-token authentication support.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/backup/storage.go (1)

738-767: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

No timeout on Azure network calls; also applies to Download, List, and Delete.

Upload (and identically Download at line 782, List's pager at lines 790/804, and Delete at line 848) use context.Background() with no deadline. A stalled connection or hung Azure request will block the calling goroutine indefinitely, since none of these calls have a cancellation/timeout path.

🐛 Proposed fix (repeat for Download/List/Delete)
-	uploaded := time.Now().Format(time.RFC3339)
-	ctx := context.Background()
+	uploaded := time.Now().Format(time.RFC3339)
+	ctx, cancel := context.WithTimeout(context.Background(), uploadTimeout)
+	defer cancel()
 	_, err = a.client.UploadFile(ctx, a.container, blobName, file, &azblob.UploadFileOptions{
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/backup/storage.go` around lines 738 - 767, The Azure storage operations
are using uncancelable contexts, so stalled network calls can hang indefinitely.
Update AzureStorage.Upload and the matching Download, List pager, and Delete
paths to use a context with deadline or cancellation instead of
context.Background, and propagate that context through the Azure SDK calls. Keep
the fix consistent across the Upload, Download, List, and Delete methods so all
Azure requests can time out or be canceled.
🧹 Nitpick comments (1)
pkg/backup/storage.go (1)

769-787: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Partial file left on disk when Download fails.

If DownloadFile fails partway through, outFile may contain a truncated/corrupt file at localPath that isn't cleaned up, and callers may not realize the file is invalid.

♻️ Suggested cleanup on failure
 	ctx := context.Background()
 	if _, err := a.client.DownloadFile(ctx, a.container, blobName, outFile, nil); err != nil {
+		outFile.Close()
+		os.Remove(localPath)
 		return fmt.Errorf("failed to download from Azure: %w", err)
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/backup/storage.go` around lines 769 - 787, The AzureStorage.Download
method leaves a partial file behind if a failure occurs after os.Create succeeds
but before DownloadFile completes. Update Download to clean up localPath on any
download error by removing the created file when aDownloadFile returns an error,
while keeping the existing valid-file behavior on success; use the
AzureStorage.Download and blobNameFromLocation flow to place the cleanup in the
failure path.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/configuration.md`:
- Around line 428-438: The shell example has a broken line continuation because
the inline comment after the --permissions option interrupts the backslash, so
the command will not copy/paste as one statement. Update the generate-sas
snippet so the continuation is preserved in the az storage container
generate-sas example by moving the explanation for --permissions cw off the same
line (or otherwise removing the inline comment) and keeping the command lines
chained correctly.

In `@pkg/backup/storage.go`:
- Around line 694-698: The warning in the Azure Blob Storage access-key path is
written in German and should be converted to English. Update the logger.Warn
message in the AccessKey branch of storage.go to an English least-privilege
warning, keeping the same behavior and context around
azblob.NewSharedKeyCredential and the config.AccessKey handling.

---

Outside diff comments:
In `@pkg/backup/storage.go`:
- Around line 738-767: The Azure storage operations are using uncancelable
contexts, so stalled network calls can hang indefinitely. Update
AzureStorage.Upload and the matching Download, List pager, and Delete paths to
use a context with deadline or cancellation instead of context.Background, and
propagate that context through the Azure SDK calls. Keep the fix consistent
across the Upload, Download, List, and Delete methods so all Azure requests can
time out or be canceled.

---

Nitpick comments:
In `@pkg/backup/storage.go`:
- Around line 769-787: The AzureStorage.Download method leaves a partial file
behind if a failure occurs after os.Create succeeds but before DownloadFile
completes. Update Download to clean up localPath on any download error by
removing the created file when aDownloadFile returns an error, while keeping the
existing valid-file behavior on success; use the AzureStorage.Download and
blobNameFromLocation flow to place the cleanup in the failure path.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5e05a820-7f01-49ea-b3a3-a4ae064491bd

📥 Commits

Reviewing files that changed from the base of the PR and between b62a8f3 and 638bab0.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (12)
  • CHANGELOG.md
  • README.md
  • cmd/siprec/main.go
  • docs/configuration.md
  • go.mod
  • pkg/backup/storage.go
  • pkg/backup/storage_test.go
  • pkg/config/config.go
  • pkg/config/validation.go
  • pkg/config/validation_test.go
  • pkg/http/config_handlers_test.go
  • pkg/http/config_redact.go

Comment thread docs/configuration.md
Comment on lines +428 to +438
```bash
az storage container generate-sas \
--account-name mystorageaccount \
--name recordings \
--permissions cw \ # c = create, w = write (no read/delete)
--expiry 2027-01-01T00:00:00Z \
--https-only \
--auth-mode key \
--account-key "<account-key-used-only-to-mint-the-sas>" \
--output tsv
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Fix the shell example's line continuation.

The inline comment after --permissions cw \ breaks the continuation, so copy/paste will split the command into separate shell commands.

Proposed fix
-  --permissions cw \          # c = create, w = write (no read/delete)
+  --permissions cw \
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
```bash
az storage container generate-sas \
--account-name mystorageaccount \
--name recordings \
--permissions cw \ # c = create, w = write (no read/delete)
--expiry 2027-01-01T00:00:00Z \
--https-only \
--auth-mode key \
--account-key "<account-key-used-only-to-mint-the-sas>" \
--output tsv
```
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/configuration.md` around lines 428 - 438, The shell example has a broken
line continuation because the inline comment after the --permissions option
interrupts the backslash, so the command will not copy/paste as one statement.
Update the generate-sas snippet so the continuation is preserved in the az
storage container generate-sas example by moving the explanation for
--permissions cw off the same line (or otherwise removing the inline comment)
and keeping the command lines chained correctly.

Comment thread pkg/backup/storage.go
Comment on lines +694 to +698
case config.AccessKey != "":
if logger != nil {
logger.Warn("Azure Blob Storage nutzt Account Key Auth – SAS-Token wird empfohlen (least privilege).")
}
cred, credErr := azblob.NewSharedKeyCredential(config.Account, config.AccessKey)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Warning log message is written in German, not English.

Line 696 mixes German text into an otherwise all-English codebase: "Azure Blob Storage nutzt Account Key Auth – SAS-Token wird empfohlen (least privilege)." This is an important least-privilege security warning that most operators/log consumers won't understand if they don't read German.

🐛 Proposed fix
 	case config.AccessKey != "":
 		if logger != nil {
-			logger.Warn("Azure Blob Storage nutzt Account Key Auth – SAS-Token wird empfohlen (least privilege).")
+			logger.Warn("Azure Blob Storage is using account key auth; a SAS token is recommended (least privilege).")
 		}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
case config.AccessKey != "":
if logger != nil {
logger.Warn("Azure Blob Storage nutzt Account Key Auth – SAS-Token wird empfohlen (least privilege).")
}
cred, credErr := azblob.NewSharedKeyCredential(config.Account, config.AccessKey)
case config.AccessKey != "":
if logger != nil {
logger.Warn("Azure Blob Storage is using account key auth; a SAS token is recommended (least privilege).")
}
cred, credErr := azblob.NewSharedKeyCredential(config.Account, config.AccessKey)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/backup/storage.go` around lines 694 - 698, The warning in the Azure Blob
Storage access-key path is written in German and should be converted to English.
Update the logger.Warn message in the AccessKey branch of storage.go to an
English least-privilege warning, keeping the same behavior and context around
azblob.NewSharedKeyCredential and the config.AccessKey handling.

@loreste
loreste merged commit 782b1a9 into loreste:main Jul 2, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants