From 834f08fe2db4074d2f509b82a11d9e1b3047cd8d Mon Sep 17 00:00:00 2001 From: Maik Riechert Date: Wed, 15 May 2024 16:54:28 +0000 Subject: [PATCH 1/2] Azure: add option to force use of CLI credential --- changelog/unreleased/pull-4799 | 5 +++++ doc/030_preparing_a_new_repo.rst | 16 +++++++++++++++- doc/040_backup.rst | 1 + internal/backend/azure/azure.go | 12 ++++++++++++ internal/backend/azure/config.go | 19 +++++++++++++------ 5 files changed, 46 insertions(+), 7 deletions(-) create mode 100644 changelog/unreleased/pull-4799 diff --git a/changelog/unreleased/pull-4799 b/changelog/unreleased/pull-4799 new file mode 100644 index 000000000..0179bc51a --- /dev/null +++ b/changelog/unreleased/pull-4799 @@ -0,0 +1,5 @@ +Enhancement: Add option to force use of Azure CLI credential + +A new environment variable `AZURE_FORCE_CLI_CREDENTIAL=true` allows forcing the use of Azure CLI credential, ignoring other credentials like managed identity. + +https://github.com/restic/restic/pull/4799 diff --git a/doc/030_preparing_a_new_repo.rst b/doc/030_preparing_a_new_repo.rst index 5ff26934a..62499a1d6 100644 --- a/doc/030_preparing_a_new_repo.rst +++ b/doc/030_preparing_a_new_repo.rst @@ -548,9 +548,23 @@ For authentication export one of the following variables: # For SAS $ export AZURE_ACCOUNT_SAS= -Alternatively, if run on Azure, restic will automatically uses service accounts configured +For authentication using ``az login`` ensure the user has +the minimum permissions of the role assignment ``Storage Blob Data Contributor`` on Azure RBAC +for the storage account. + +.. code-block:: console + + $ az login + +Alternatively, if run on Azure, restic will automatically use service accounts configured via the standard environment variables or Workload / Managed Identities. +To enforce the use of the Azure CLI credential when other credentials are present, set the following environment variable: + +.. code-block:: console + + $ export AZURE_FORCE_CLI_CREDENTIAL=true + Restic will by default use Azure's global domain ``core.windows.net`` as endpoint suffix. You can specify other suffixes as follows: diff --git a/doc/040_backup.rst b/doc/040_backup.rst index c917c3c29..621b07e2e 100644 --- a/doc/040_backup.rst +++ b/doc/040_backup.rst @@ -597,6 +597,7 @@ environment variables. The following lists these environment variables: AZURE_ACCOUNT_KEY Account key for Azure AZURE_ACCOUNT_SAS Shared access signatures (SAS) for Azure AZURE_ENDPOINT_SUFFIX Endpoint suffix for Azure Storage (default: core.windows.net) + AZURE_FORCE_CLI_CREDENTIAL Force the use of Azure CLI credentials for authentication B2_ACCOUNT_ID Account ID or applicationKeyId for Backblaze B2 B2_ACCOUNT_KEY Account Key or applicationKey for Backblaze B2 diff --git a/internal/backend/azure/azure.go b/internal/backend/azure/azure.go index 50be63d5a..de58df374 100644 --- a/internal/backend/azure/azure.go +++ b/internal/backend/azure/azure.go @@ -101,6 +101,18 @@ func open(cfg Config, rt http.RoundTripper) (*Backend, error) { if err != nil { return nil, errors.Wrap(err, "NewAccountSASClientFromEndpointToken") } + } else if cfg.ForceCliCredential { + debug.Log(" - using AzureCLICredential") + + cred, err := azidentity.NewAzureCLICredential(nil) + if err != nil { + return nil, errors.Wrap(err, "NewAzureCLICredential") + } + + client, err = azContainer.NewClient(url, cred, opts) + if err != nil { + return nil, errors.Wrap(err, "NewClient") + } } else { debug.Log(" - using DefaultAzureCredential") cred, err := azidentity.NewDefaultAzureCredential(nil) diff --git a/internal/backend/azure/config.go b/internal/backend/azure/config.go index d819b35aa..61c413efa 100644 --- a/internal/backend/azure/config.go +++ b/internal/backend/azure/config.go @@ -3,6 +3,7 @@ package azure import ( "os" "path" + "strconv" "strings" "github.com/restic/restic/internal/errors" @@ -13,12 +14,13 @@ import ( // Config contains all configuration necessary to connect to an azure compatible // server. type Config struct { - AccountName string - AccountSAS options.SecretString - AccountKey options.SecretString - EndpointSuffix string - Container string - Prefix string + AccountName string + AccountSAS options.SecretString + AccountKey options.SecretString + ForceCliCredential bool + EndpointSuffix string + Container string + Prefix string Connections uint `option:"connections" help:"set a limit for the number of concurrent connections (default: 5)"` } @@ -73,6 +75,11 @@ func (cfg *Config) ApplyEnvironment(prefix string) { cfg.AccountSAS = options.NewSecretString(os.Getenv(prefix + "AZURE_ACCOUNT_SAS")) } + var forceCliCred, err = strconv.ParseBool(os.Getenv(prefix + "AZURE_FORCE_CLI_CREDENTIAL")) + if err == nil { + cfg.ForceCliCredential = forceCliCred + } + if cfg.EndpointSuffix == "" { cfg.EndpointSuffix = os.Getenv(prefix + "AZURE_ENDPOINT_SUFFIX") } From 12e858b7af2149e680d92ceac4aece9c38079b12 Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Sat, 18 May 2024 22:15:38 +0200 Subject: [PATCH 2/2] azure: deduplicate cli and default credentials case --- internal/backend/azure/azure.go | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/internal/backend/azure/azure.go b/internal/backend/azure/azure.go index de58df374..4ccfb9664 100644 --- a/internal/backend/azure/azure.go +++ b/internal/backend/azure/azure.go @@ -101,23 +101,21 @@ func open(cfg Config, rt http.RoundTripper) (*Backend, error) { if err != nil { return nil, errors.Wrap(err, "NewAccountSASClientFromEndpointToken") } - } else if cfg.ForceCliCredential { - debug.Log(" - using AzureCLICredential") - - cred, err := azidentity.NewAzureCLICredential(nil) - if err != nil { - return nil, errors.Wrap(err, "NewAzureCLICredential") - } - - client, err = azContainer.NewClient(url, cred, opts) - if err != nil { - return nil, errors.Wrap(err, "NewClient") - } } else { - debug.Log(" - using DefaultAzureCredential") - cred, err := azidentity.NewDefaultAzureCredential(nil) - if err != nil { - return nil, errors.Wrap(err, "NewDefaultAzureCredential") + var cred azcore.TokenCredential + + if cfg.ForceCliCredential { + debug.Log(" - using AzureCLICredential") + cred, err = azidentity.NewAzureCLICredential(nil) + if err != nil { + return nil, errors.Wrap(err, "NewAzureCLICredential") + } + } else { + debug.Log(" - using DefaultAzureCredential") + cred, err = azidentity.NewDefaultAzureCredential(nil) + if err != nil { + return nil, errors.Wrap(err, "NewDefaultAzureCredential") + } } client, err = azContainer.NewClient(url, cred, opts)