Docker化的Azure Functions经常因为容器无法访问基于文件的密钥存储而失败,出现401错误。解决方法是切换到基于blob的密钥存储,保持容器无状态,并让Azure处理密钥解密。这样可以恢复身份验证而不破坏你的构建。Docker化的Azure Functions经常因为容器无法访问基于文件的密钥存储而失败,出现401错误。解决方法是切换到基于blob的密钥存储,保持容器无状态,并让Azure处理密钥解密。这样可以恢复身份验证而不破坏你的构建。

如何修复 Docker 化 Azure Functions 中的 401 未授权错误

问题

你已经使用Docker将Azure Function部署到生产环境,一切看起来都很好,但当你尝试调用你的函数端点时,你会收到一个令人沮丧的401 Unauthorized错误:

curl -X POST \ "https://my-prod-functions.azurewebsites.net/api/MyFunction?code=my-function-key" \ -H "Content-Type: application/json" # Response: 401 Unauthorized

同时,你的非Docker化开发环境使用完全相同的代码和密钥却能完美运行。这是怎么回事?

调查

步骤1:排除明显原因

首先,我检查了所有常见嫌疑:

  • 函数密钥正确(从Portal → Functions → Function Keys复制)
  • 未启用App Service身份验证
  • 没有IP限制
  • CORS配置正确
  • 在代码中授权级别设置为AuthorizationLevel.Function

步骤2:检查日志

Application Insights揭示了一些有趣的信息:

Request successfully matched the route with name 'MyFunction' and template 'api/MyFunction' Executing StatusCodeResult, setting HTTP status code 401

请求已到达函数并匹配路由,但Azure在代码执行之前就返回了401。这意味着问题出在Azure Functions运行时身份验证层,而不是我的代码中。

步骤3:关键发现

我通过Kudu控制台(https://my-functions.scm.azurewebsites.net)检查了环境变量,发现:

AzureWebJobsSecretStorageType = files WEBSITES_ENABLE_APP_SERVICE_STORAGE = false # I THOUGHT THIS IS THE PROBLEM!

根本原因

这是发生的情况:

Azure Functions如何存储密钥

Azure Functions可以通过两种方式存储身份验证密钥:

  1. 基于文件的存储AzureWebJobsSecretStorageType = files
  • 密钥作为JSON文件存储在/home/data/Functions/secrets/
  • 需要持久文件系统访问
  • 非容器化应用的默认设置
  1. 基于Blob的存储AzureWebJobsSecretStorageType = blob
  • 密钥存储在Azure Blob Storage中
  • 无文件系统依赖
  • 推荐用于Docker容器

Docker冲突

当你设置WEBSITES_ENABLE_APP_SERVICE_STORAGE = false(无状态Docker容器的常见设置)时,Azure不会挂载持久存储到你的容器。

这意味着:

  • 你的函数密钥存在于Azure的存储中
  • 但你的容器无法访问它们
  • 身份验证总是以401失败

让我在容器中验证这一点:

# SSH into container or via Kudu ls -la /home/data/ # Result: No such file or directory ls -la /azure-functions-host/Secrets/ #Result: No such file or directory

secrets目录不存在,因为存储没有被挂载!

次要问题

当我最初尝试通过设置WEBSITES_ENABLE_APP_SERVICE_STORAGE = true来修复这个问题时,身份验证工作了,但我却得到了404 Not Found

\ 为什么?因为在/home/site/wwwroot/挂载Azure的持久存储覆盖了我的Docker容器的应用程序文件。挂载的目录只有host.json但没有编译的DLL(在docker容器中,而不是主机容器,记住这一点很重要,这里有两个容器:主机容器和应用docker容器),所以Azure Functions运行时找到0个函数加载,因此当请求进来时,身份验证有效(blob中的密钥),但找不到函数,所以我们得到404。简而言之,这个选项使主机能找到函数密钥文件(所以身份验证有效),但丢失了函数本身。我们不能使用它,WEBSITES_ENABLE_APP_SERVICE_STORAGE需要设为false。

解决方案

Docker化Azure Functions的正确修复方法是使用基于blob的密钥存储

步骤1:更改密钥存储类型

在Azure Portal中:

  1. 转到你的Function App
  2. 导航至ConfigurationApplication Settings
  3. 查找或添加:AzureWebJobsSecretStorageType
  4. 设置值为:blob
  5. 确保:WEBSITES_ENABLE_APP_SERVICE_STORAGEfalse

步骤2:保存并重启

点击Save,然后重启function app。Azure将自动:

  • 在你的存储账户中创建一个azure-webjobs-secrets容器
  • 将现有密钥迁移到blob存储
  • 配置运行时从blob读取

步骤3:获取你的密钥

转到Portal → Function App → Functions → [你的函数] → Function Keys

从Portal复制密钥(这是解密版本)。

步骤4:测试

curl -X POST \ "https://my-prod-functions.azurewebsites.net/api/MyFunction?code=<KEY_FROM_PORTAL>" \ -H "Content-Type: application/json" \ -d '{"test": "data"}' # Response: 200 OK

理解密钥加密

当你检查blob存储时,你会看到密钥存储如下:

{ "keys": [ { "name": "default", "value": "CfDJ8AAAAAAA...encrypted-value...", "encrypted": true } ] }

重要:你不能直接使用这个加密值!Azure自动:

  1. 将加密的密钥存储在blob存储中(为了安全)
  2. 在运行时使用机器密钥解密它们
  3. 根据解密值验证传入请求

始终从Azure Portal UI获取你的密钥,它显示解密版本。

完整配置参考

Dockerfile(示例)

FROM mcr.microsoft.com/azure-functions/dotnet:4 AS base WORKDIR /home/site/wwwroot EXPOSE 80 FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build WORKDIR /src COPY ["MyFunction/MyFunction.csproj", "MyFunction/"] RUN dotnet restore "MyFunction/MyFunction.csproj" COPY . . WORKDIR "/src/MyFunction" RUN dotnet build "MyFunction.csproj" -c Release -o /app/build FROM build AS publish RUN dotnet publish "MyFunction.csproj" -c Release -o /app/publish FROM base AS final WORKDIR /home/site/wwwroot COPY --from=publish /app/publish . ENV AzureWebJobsScriptRoot=/home/site/wwwroot \ AzureFunctionsJobHost__Logging__Console__IsEnabled=true

\

必需的应用设置

# Secret storage configuration AzureWebJobsSecretStorageType = blob AzureWebJobsStorage = <your-storage-connection-string> # Docker configuration WEBSITES_ENABLE_APP_SERVICE_STORAGE = false WEBSITE_RUN_FROM_PACKAGE = 0 # Functions runtime FUNCTIONS_WORKER_RUNTIME = dotnet FUNCTIONS_EXTENSION_VERSION = ~4

为什么会发生这种情况

这个问题特定于Docker + Azure Functions,因为:

  1. Docker容器应该是无状态和不可变的
  2. 基于文件的密钥需要挂载持久存储
  3. 挂载存储可能会覆盖容器化的应用程序文件
  4. 解决方案是使用blob存储,它对无状态友好

非Docker化的function apps没有这个问题,因为它们自然可以访问App Service文件系统。

总结

问题:在没有挂载卷的情况下使用基于文件的密钥存储时,Docker化的Azure Functions返回401。

解决方案:使用基于blob的密钥存储,它是无状态的且对Docker友好。

关键设置:

AzureWebJobsSecretStorageType = blob WEBSITES_ENABLE_APP_SERVICE_STORAGE = false

这种配置允许你的Docker容器保持无状态,同时仍然能够从Azure Blob Storage安全地访问身份验证密钥。


遇到类似问题?欢迎在下方评论中联系我!

免责声明: 本网站转载的文章均来源于公开平台,仅供参考。这些文章不代表 MEXC 的观点或意见。所有版权归原作者所有。如果您认为任何转载文章侵犯了第三方权利,请联系 [email protected] 以便将其删除。MEXC 不对转载文章的及时性、准确性或完整性作出任何陈述或保证,并且不对基于此类内容所采取的任何行动或决定承担责任。转载材料仅供参考,不构成任何商业、金融、法律和/或税务决策的建议、认可或依据。