Reputation: 740
I want to get a reference to the blob and generate a SAS URL for it.
How? Without exposing my storage account key?
What all have I tried? Getting the reference to blob by using SAS (of blob container or storage account). My references: https://learn.microsoft.com/en-us/azure/storage/common/storage-dotnet-shared-access-signature-part-1?toc=%2fazure%2fstorage%2fblobs%2ftoc.json
The exception that I see: "Can not create Shared Access Signature unless Account Key credentials are used"
But I do not (obviously) want to expose my account key! Is this even possible? If not, is there any other way of doing it?
Upvotes: 0
Views: 2151
Reputation: 725
This is an interpretation of this example
First we have this to get the accountKey:
public static async Task<StorageAccountKey> GetAccountKeys(string KeyName)
{
IAzure storageAccounts;
if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable(@"AZURE_TENANT_ID"))
&& !string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable(@"AZURE_SUBSCRIPTION_ID")))
{
storageAccounts = GetStorageAccountWithTenantAndSubscription();
}
else
{
AzureCredentials credentials = SdkContext.AzureCredentialsFactory.FromSystemAssignedManagedServiceIdentity(MSIResourceType.AppService, AzureEnvironment.AzureGlobalCloud);
storageAccounts = Microsoft.Azure.Management.Fluent.Azure
.Authenticate(credentials)
.WithDefaultSubscription();
}
IStorageAccount storageAccount = await storageAccounts.StorageAccounts.GetByResourceGroupAsync(
Environment.GetEnvironmentVariable("STORAGE_ACCOUNT_GROUP"),
Environment.GetEnvironmentVariable("STORAGE_ACCOUNT_NAME")
);
IReadOnlyList<StorageAccountKey> accountKeys = storageAccount.GetKeys();
return accountKeys.FirstOrDefault(k => k.KeyName == KeyName);
}
private static IAzure GetStorageAccountWithTenantAndSubscription()
{
DefaultAzureCredential tokenCred = new DefaultAzureCredential(includeInteractiveCredentials: true);
string armToken = tokenCred.GetToken(new TokenRequestContext(scopes: new[] { "https://management.azure.com/.default" }, parentRequestId: null), default).Token;
TokenCredentials armCreds = new TokenCredentials(armToken);
string graphToken = tokenCred.GetToken(new TokenRequestContext(scopes: new[] { "https://graph.windows.net/.default" }, parentRequestId: null), default).Token;
TokenCredentials graphCreds = new TokenCredentials(graphToken);
AzureCredentials credentials = new AzureCredentials(armCreds, graphCreds, Environment.GetEnvironmentVariable(@"AZURE_TENANT_ID"), AzureEnvironment.AzureGlobalCloud);
return Microsoft.Azure.Management.Fluent.Azure
.Authenticate(credentials)
.WithSubscription(Environment.GetEnvironmentVariable(@"AZURE_SUBSCRIPTION_ID"));
}
Where you need to define the next environment variables:
AZURE_TENANT_ID
AZURE_SUBSCRIPTION_ID
STORAGE_ACCOUNT_GROUP
STORAGE_ACCOUNT_NAME
All of them can be found on the https://portal.azure.com/ and if you run az login
then you can do this to generate the connection string:
private static async Task<string> GetAccountSASToken()
{
StorageAccountKey accountKeyObj = await GetAccountKeys(Environment.GetEnvironmentVariable("STORAGE_ACCOUNT_KEY"));
string accountKey = accountKeyObj.Value;
string accountName = Environment.GetEnvironmentVariable("STORAGE_ACCOUNT_NAME");
StorageSharedKeyCredential key = new StorageSharedKeyCredential(accountName, accountKey);
AccountSasBuilder sasBuilder = new AccountSasBuilder()
{
Services = AccountSasServices.Blobs | AccountSasServices.Files,
ResourceTypes = AccountSasResourceTypes.Container | AccountSasResourceTypes.Object,
ExpiresOn = DateTimeOffset.UtcNow.AddHours(1),
Protocol = SasProtocol.Https
};
sasBuilder.SetPermissions(AccountSasPermissions.List | AccountSasPermissions.Read);
string sasToken = sasBuilder.ToSasQueryParameters(key).ToString();
return sasToken;
}
And that's all you need.
Upvotes: 0
Reputation: 15561
In short: no, there's no other way to do that besides using one of the keys. You need one of the Access Keys to be able to create a SAS token. Here's why you cannot do that with an existing SAS token:
The signature is an HMAC computed over the string-to-sign and key using the SHA256 algorithm, and then encoded using Base64 encoding.
This means the signature that is part of your SAS token is a calculated value. Part of that calculation is based on (one of the) key(s), since that is used to calculate the non-reversible hash. The fact that this hash is non-reversible means you cannot retrieve the Access Key used to calculate the hash. And therefor, you cannot use a SAS token to create another SAS token: you don't have an Access Key available to calculate the signature.
When you create a storage account, you get two storage access keys, which provide full control over the storage account contents. These keys are admin credentials.
More information: Constructing a Service SAS
Upvotes: 6