Azure / Azurite

A lightweight server clone of Azure Storage that simulates most of the commands supported by it with minimal dependencies
MIT License
1.83k stars 324 forks source link

InvalidResourceName Error when Accessing Blobs Using SAS Tokens Generated with Azurite #2460

Closed root-11 closed 1 month ago

root-11 commented 2 months ago

Hello, I am encountering an InvalidResourceName error when trying to access Azure Blob Storage using Shared Access Signature (SAS) tokens generated by my Azure Function app in a local development environment with Azurite.

Which service(blob, file, queue, table) does this issue concern?

generate_blob_sas from azure.storage.blob

Which version of the Azurite was used?

3.32.0

Where do you get Azurite? (npm, DockerHub, NuGet, Visual Studio Code Extension)

npm

What's the Node.js version?

v20.17.0

What problem was encountered?

Steps to Reproduce:

  1. Start Azurite locally using the default configuration.
  2. Set up an Azure Function with Python 3.11 to generate SAS tokens for blobs stored in Azurite.
  3. Use the SAS token to try to access a blob via a browser or a curl command.

Expected Behavior: The blob should be accessible using the SAS token without any errors.

Actual Behavior: The system returns an InvalidResourceName error. Here is the error detail:

<Error> <Code>InvalidResourceName</Code> <Message>The specified resource name contains invalid characters. RequestId:b0e84284-7a20-4092-aa66-e4f8e8eb6aa6 Time:2024-09-02T14:56:11.268Z</Message> </Error> 

Steps to reproduce the issue?

start azurite, upload hello world! as filetest.txt, attempt to create a sas-token for it.

import os
import logging
from datetime import datetime, timedelta
import azure.functions as func
from azure.storage.blob import BlobServiceClient
from azure.core.exceptions import ResourceExistsError

from azure.storage.blob import BlobSasPermissions, generate_blob_sas

app = func.FunctionApp(http_auth_level=func.AuthLevel.ANONYMOUS)

@app.route(route="sastoken")
def httpSasToken(req: func.HttpRequest) -> func.HttpResponse:
    logging.info('Python HTTP request sas-token.')

    blob_name = req.params.get('filename')
    container_name = req.params.get('container')
    if not blob_name or not container_name:
        return func.HttpResponse("Please pass 'filename' and 'container' as query parameters.", status_code=400)

    account_name = os.getenv('STORAGE_ACCOUNT_NAME')  # Default to Azurite
    account_key = os.getenv('STORAGE_ACCOUNT_KEY')

    sas_token = generate_blob_sas(
        account_name=os.getenv('STORAGE_ACCOUNT_NAME'),  # Real account name for the token
        container_name=container_name,
        blob_name=blob_name,
        account_key=account_key,
        permission=BlobSasPermissions(read=True),
        expiry=datetime.now() + timedelta(hours=1),  # Token valid for 1 hour
        protocol="http",
        start=datetime.now() - timedelta(minutes=1),
        version="2020-02-10"  # Ensure this is a supported version
    )

    sas_url = f"http://{account_name}:10000/{container_name}/{blob_name}?{sas_token}"
    return func.HttpResponse(f"SAS URL: {sas_url}", status_code=200)

Logs created using: azurite --silent --location ./azurite --debug azurite-debug.log: azurite-debug.log

Q: Please be sure to remove any PII or sensitive information before sharing!

A: All information is demo information.

The debug log will log raw request headers and bodies, so that we can replay these against Azurite using REST and create tests to validate resolution.

Have you found a mitigation/solution?

No I'm stuck.

Attempts to Resolve:

What i see:

image

Could you help me understand what might be causing this issue or if there's a configuration with Azurite that I might be missing? Thank you for your assistance!

Additional details:

azure-core == 1.30
azure-functions == 1.20Attempts to Resolve:
azure-storage-blob== 12.22

I've verified that the blob and container names do not contain special characters. I've checked and rechecked the SAS token parameters and encoding. Could you help me understand what might be causing this issue or if there's a configuration with Azurite that I might be missing?

Thank you for your assistance!

blueww commented 1 month ago

@EmmaZhu Would you please help to look?

@root-11 Would you please share Azurite debug log (start Azurite with parameter like "--debug c:\logfile.log"), then we can do more investigation.

root-11 commented 1 month ago

This log ends with the error. Action performed:

  1. attempt to upload file (file already existed in the container, so ignore this error)
  2. create the sas-token
  3. replace devstore with localhost in the url
  4. open incognito window to try the sas-token (fails)

logfile.log

blueww commented 1 month ago

@root-11

I see in the 2 failed (400) request, the resource Uri are incorrect, they both missed the storage account name.

"RequestURL=http://localhost/testcontainer/testfile.txt?st=2024-09-09T11%3A03%3A37Z&se=..." "RequestURL=http://localhost/favicon.ico"

The correct Uri should look like:

"RequestURL=http://localhost/devstoreaccount1/testcontainer/testfile.txt?st=2024-09-09T11%3A03%3A37Z&se=..." "RequestURL=http://localhost/devstoreaccount1/favicon.ico"

bjornmadsen commented 1 month ago

A fully formed sas-token was craeted by including the blob service url with port for azurite and adding the storage account name as advised.

functioning example:

http://127.0.0.1:10000/devstoreaccount1/testcontainer/ok_rpi.csv?st=2024-09-10T10%3A45%3A42Z&se=2024-09-10T11%3A55%3A42Z&sp=r&spr=http&sv=2024-08-04&sr=b&sig=tYQCmhkWE3tXbFrlA15DhLwYitHKkSuj6OJmBdk3ikE%3D

For effect see:

   blob_service = "127.0.0.1:10000"
    sas_url = f"http://{blob_service}/{STORAGE_ACCOUNT_NAME}/{container_name}/{blob_name}?{sas_token}"

in the function below:


@app.route(route="make_sas_token")
def make_sas_token(req: func.HttpRequest) -> func.HttpResponse:
    blob_name = req.params.get('filename')

    #< ... cut for brevity ...>

    sas_token = generate_blob_sas(
        account_name=STORAGE_ACCOUNT_NAME,  # Real account name for the token
        container_name=container_name,
        blob_name=blob_name,
        account_key=STORAGE_ACCOUNT_KEY,
        permission=BlobSasPermissions(read=True),
        expiry=datetime.now(UTC) + timedelta(hours=1),  # Token valid for 1 hour
        protocol="http",
        start=datetime.now(UTC) - timedelta(minutes=10),
        version="2020-02-10"  # Ensure this is a supported version
    )
    blob_service = "127.0.0.1:10000"
    sas_url = f"http://{blob_service}/{STORAGE_ACCOUNT_NAME}/{container_name}/{blob_name}?{sas_token}"
    return func.HttpResponse(sas_url, status_code=200)

Thank you for helping out with the missing manual!

blueww commented 1 month ago

@bjornmadsen

I will close this issue as the problem is resolved. Feel free to contact us again if need any further assistance on Azurite.