From 2e704c69ac1c05baceb368aa3d701fab480e991b Mon Sep 17 00:00:00 2001 From: Connor Findlay Date: Thu, 17 Oct 2024 20:08:11 +1300 Subject: [PATCH 1/3] backend/azure: Handle Container SAS/SAT Ignore AuthorizationFailure caused by using a container level SAS/SAT token when calling GetProperties during the Create() call. This is because the GetProperties call expects an Account Level token, and the container level token simply lacks the appropriate permissions. Supressing the Authorization Failure is OK, because if the token is actually invalid, this is caught elsewhere when we try to actually use the token to do work. --- internal/backend/azure/azure.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/internal/backend/azure/azure.go b/internal/backend/azure/azure.go index e09593fd6..8f5ee9f00 100644 --- a/internal/backend/azure/azure.go +++ b/internal/backend/azure/azure.go @@ -157,6 +157,12 @@ func Create(ctx context.Context, cfg Config, rt http.RoundTripper) (*Backend, er if err != nil { return nil, errors.Wrap(err, "container.Create") } + } else if err != nil && bloberror.HasCode(err, bloberror.AuthorizationFailure) { + // We ignore this Auth. Failure, as the failure is related to the type + // of SAS/SAT, not an actual real failure. If the token is invalid, we + // fail later on anyway. + // For details see Issue #4004. + debug.Log("Ignoring AuthorizationFailure when calling GetProperties") } else if err != nil { return be, errors.Wrap(err, "container.GetProperties") } From 7bdfcf13fb5a605775521ebafb944da41644cc1a Mon Sep 17 00:00:00 2001 From: Connor Findlay Date: Thu, 17 Oct 2024 20:23:32 +1300 Subject: [PATCH 2/3] changelog: Add changes in issue-4004 Add changelog entry in the 'unreleased' sub-folder for changes introduced when fixing issue #4004. --- changelog/unreleased/issue-4004 | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 changelog/unreleased/issue-4004 diff --git a/changelog/unreleased/issue-4004 b/changelog/unreleased/issue-4004 new file mode 100644 index 000000000..ca23af26f --- /dev/null +++ b/changelog/unreleased/issue-4004 @@ -0,0 +1,12 @@ +Bugfix: Allow use of container level SAS/SAT tokens with Azure backend + +When using a SAS/SAT token for authentication with Azure, restic was expecting +the provided token to be generated at the account level, granting permissions +to the storage account and all its containers. This caused an error that did +not allow tokens that were generated at the container level to be used to +initalize a repository. +Restic now allows SAS/SAT tokens that were generated at the account or +container level to be used to initalize a repository. + +https://github.com/restic/restic/issues/4004 +https://github.com/restic/restic/pull/5093 From b434f560cc53b074c23f4aad1619014f0f9be465 Mon Sep 17 00:00:00 2001 From: Connor Findlay Date: Thu, 17 Oct 2024 21:26:52 +1300 Subject: [PATCH 3/3] backend/azure: Add tests for both token types Add two new test cases, TestBackendAzureAccountToken and TestBackendAzureContainerToken, that ensure that the authorization using both types of token works. This introduces two new environment variables, RESTIC_TEST_AZURE_ACCOUNT_SAS and RESTIC_TEST_AZURE_CONTAINER_SAS, that contain the tokens to use when testing restic. If an environment variable is missing, the related test is skipped. --- internal/backend/azure/azure_test.go | 85 ++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/internal/backend/azure/azure_test.go b/internal/backend/azure/azure_test.go index 7df27d325..adafb6b03 100644 --- a/internal/backend/azure/azure_test.go +++ b/internal/backend/azure/azure_test.go @@ -80,6 +80,91 @@ func BenchmarkBackendAzure(t *testing.B) { newAzureTestSuite().RunBenchmarks(t) } +// TestBackendAzureAccountToken tests that a Storage Account SAS/SAT token can authorize. +// This test ensures that restic can use a token that was generated using the storage +// account keys can be used to authorize the azure connection. +// Requires the RESTIC_TEST_AZURE_ACCOUNT_NAME, RESTIC_TEST_AZURE_REPOSITORY, and the +// RESTIC_TEST_AZURE_ACCOUNT_SAS environment variables to be set, otherwise this test +// will be skipped. +func TestBackendAzureAccountToken(t *testing.T) { + vars := []string{ + "RESTIC_TEST_AZURE_ACCOUNT_NAME", + "RESTIC_TEST_AZURE_REPOSITORY", + "RESTIC_TEST_AZURE_ACCOUNT_SAS", + } + + for _, v := range vars { + if os.Getenv(v) == "" { + t.Skipf("set %v to test SAS/SAT Token Authentication", v) + return + } + } + + ctx, cancel := context.WithCancel(context.TODO()) + defer cancel() + + cfg, err := azure.ParseConfig(os.Getenv("RESTIC_TEST_AZURE_REPOSITORY")) + if err != nil { + t.Fatal(err) + } + + cfg.AccountName = os.Getenv("RESTIC_TEST_AZURE_ACCOUNT_NAME") + cfg.AccountSAS = options.NewSecretString(os.Getenv("RESTIC_TEST_AZURE_ACCOUNT_SAS")) + + tr, err := backend.Transport(backend.TransportOptions{}) + if err != nil { + t.Fatal(err) + } + + _, err = azure.Create(ctx, *cfg, tr) + if err != nil { + t.Fatal(err) + } +} + +// TestBackendAzureContainerToken tests that a container SAS/SAT token can authorize. +// This test ensures that restic can use a token that was generated using a user +// delegation key against the container we are storing data in can be used to +// authorize the azure connection. +// Requires the RESTIC_TEST_AZURE_ACCOUNT_NAME, RESTIC_TEST_AZURE_REPOSITORY, and the +// RESTIC_TEST_AZURE_CONTAINER_SAS environment variables to be set, otherwise this test +// will be skipped. +func TestBackendAzureContainerToken(t *testing.T) { + vars := []string{ + "RESTIC_TEST_AZURE_ACCOUNT_NAME", + "RESTIC_TEST_AZURE_REPOSITORY", + "RESTIC_TEST_AZURE_CONTAINER_SAS", + } + + for _, v := range vars { + if os.Getenv(v) == "" { + t.Skipf("set %v to test SAS/SAT Token Authentication", v) + return + } + } + + ctx, cancel := context.WithCancel(context.TODO()) + defer cancel() + + cfg, err := azure.ParseConfig(os.Getenv("RESTIC_TEST_AZURE_REPOSITORY")) + if err != nil { + t.Fatal(err) + } + + cfg.AccountName = os.Getenv("RESTIC_TEST_AZURE_ACCOUNT_NAME") + cfg.AccountSAS = options.NewSecretString(os.Getenv("RESTIC_TEST_AZURE_CONTAINER_SAS")) + + tr, err := backend.Transport(backend.TransportOptions{}) + if err != nil { + t.Fatal(err) + } + + _, err = azure.Create(ctx, *cfg, tr) + if err != nil { + t.Fatal(err) + } +} + func TestUploadLargeFile(t *testing.T) { if os.Getenv("RESTIC_AZURE_TEST_LARGE_UPLOAD") == "" { t.Skip("set RESTIC_AZURE_TEST_LARGE_UPLOAD=1 to test large uploads")