Dapr + .NET Core实战Secrets

Posted chester.chen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Dapr + .NET Core实战Secrets相关的知识,希望对你有一定的参考价值。

什么是Secrets

应用程序通常会通过使用专用的存储来存储敏感信息,如连接字符串、密钥等。

通常这需要建立一个密钥存储,如Azure Key Vault、Hashicorp等,并在那里存储应用程序级别的密钥。 要访问这些密钥存储,应用程序需要导入密钥存储SDK,并使用它访问这些密钥。 这可能需要相当数量的模板代码,这些代码与应用的实际业务领域无关,因此在多云场景中,可能会使用不同厂商特定的密钥存储,这就成为一个更大的挑战。

让开发人员在任何地方更容易访问应用程序密钥, Dapr 提供一个专用的密钥构建块 ,允许开发人员从一个存储获得密钥。

使用 Dapr 的密钥存储构建块通常涉及以下内容:

  1. 设置一个特定的密钥存储解决方案的组件。
  2. 在应用程序代码中使用 Dapr Secrets API 获取密钥。
  3. 在Dapr的Component文件中引用密钥

工作原理

 

 

  1. 服务A调用 Dapr Secrets API,提供要检索的Serects的名称和要查询的项名字。
  2. Dapr sidecar 从Secrets存储中检索指定的机密。
  3. Dapr sidecar 将Secrets信息返回给服务。

Dapr目前支持的Secrets存储请见存储

使用Secrets时,应用程序与 Dapr sidecar 交互。 sidecar 公开Secrets API。 可以使用 HTTP 或 gRPC 调用 API。 使用以下 URL 调用 HTTP API:

http://localhost:<dapr-port>/v1.0/secrets/<store-name>/<name>?<metadata>

URL 包含以下字段:

  • <dapr-port> 指定 Dapr sidecar 侦听的端口号。
  • <store-name> 指定 Dapr Secrets存储的名称。
  • <name> 指定要检索的密钥的名称。
  • <metadata> 提供Secrets的其他信息。 此段是可选的,每个Secrets存储的元数据属性不同。 有关元数据属性详细信息

项目实战

通过Dapr SDK获取secrets

仍然使用FrontEnd项目,并使用本地文件存储Secrets,首先在默认component目录C:\\Users\\<username>\\.dapr\\components中新建文件secrets01.json,声明密钥内容

{
    "RabbitMQConnectStr": "amqp://admin:123456@192.168.43.101:5672"
}

在此目录新建secrets01.yaml定义store

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: secrets01
spec:
  type: secretstores.local.file
  version: v1
  metadata:
  - name: secretsFile
    value: C:\\Users\\username\\.dapr\\components\\secrets01.json
  - name: nestedSeparator
    value: ":"

定义接口获取Secrets01的内容,新建SecretsController

using Dapr.Client;

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;

using System.Collections.Generic;
using System.Threading.Tasks;

namespace FrontEnd.Controllers
{
    [Route("[controller]")]
    [ApiController]
    public class SecretsController : ControllerBase
    {
        private readonly ILogger<SecretsController> _logger;
        private readonly DaprClient _daprClient;
        public SecretsController(ILogger<SecretsController> logger, DaprClient daprClient)
        {
            _logger = logger;
            _daprClient = daprClient;
        }

        [HttpGet]
        public async Task<ActionResult> GetAsync()
        {
            Dictionary<string, string> secrets = await _daprClient.GetSecretAsync("secrets01", "RabbitMQConnectStr");
            return Ok(secrets);
        }
    }
}

 

运行Frontend

dapr run --dapr-http-port 3501 --app-port 5001  --app-id frontend dotnet  .\\FrontEnd\\bin\\Debug\\net5.0\\FrontEnd.dll

验证此api,获取成功

 

通过IConfiguration访问Secrets

Dapr还提供了从IConfiguration中访问Secrets的方法,首先引入nuget包Dapr.Extensions.Config

 

在Program.cs中修改注册

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureAppConfiguration(config =>
                {
                    var daprClient = new DaprClientBuilder().Build();
                    var secretDescriptors = new List<DaprSecretDescriptor> { new DaprSecretDescriptor("RabbitMQConnectStr") };
                    config.AddDaprSecretStore("secrets01", secretDescriptors, daprClient);
                })
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>().UseUrls("http://*:5001");
                });

在SecretsController注入IConfiguration

        private readonly ILogger<SecretsController> _logger;
        private readonly DaprClient _daprClient;
        private readonly IConfiguration _configuration;
        public SecretsController(ILogger<SecretsController> logger, DaprClient daprClient, IConfiguration configuration)
        {
            _logger = logger;
            _daprClient = daprClient;
            _configuration = configuration;
        }

在SecretsController中新增接口

        [HttpGet("get01")]
        public async Task<ActionResult> Get01Async()
        {
            return Ok(_configuration["RabbitMQConnectStr"]);
        }

调用接口,获取数据成功

 

其他组件引用Secrets

Dapr的其他组件,同样可以引用Secrets,我们以上节RabbitMQBinding为例,修改rabbitbinding.yaml

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: RabbitBinding
spec:
  type: bindings.rabbitmq
  version: v1
  metadata:
  - name: queueName
    value: queue1
  - name: host
    secretKeyRef:
      name: RabbitMQConnectStr
      key: RabbitMQConnectStr
  - name: durable
    value: true
  - name: deleteWhenUnused
    value: false
  - name: ttlInSeconds
    value: 60
  - name: prefetchCount
    value: 0
  - name: exclusive
    value: false
  - name: maxPriority
    value: 5
auth:
  secretStore: secrets01

secretKeyRef元素引用指定的密钥。 它将替换以前的 明文 值。  在 auth 中找到对应的secretStore。

现在运行Frontend

dapr run --dapr-http-port 3501 --app-port 5001  --app-id frontend dotnet  .\\FrontEnd\\bin\\Debug\\net5.0\\FrontEnd.dll

在RabbitMQ Management中发送消息,消费成功

== APP == info: FrontEnd.Controllers.RabbitBindingController[0]
== APP ==       .............binding.............11122444

限制Secrets访问权限

我们可以在Dapr的默认配置文件C:\\Users\\username\\.dapr\\config.yaml中设置Secrets的访问权限,现在我们尝试禁止secrets01的权限

apiVersion: dapr.io/v1alpha1
kind: Configuration
metadata:
  name: daprConfig
spec:
  tracing:
    samplingRate: "1"
    zipkin:
      endpointAddress: http://localhost:9411/api/v2/spans
  secrets:
    scopes:
      - storeName: secrets01
        defaultAccess: deny

设置之后,Frontend会启动失败,因为我们在Program.cs中设置了读取secrets01。

== APP == Unhandled exception. Dapr.DaprException: Secret operation failed: the Dapr endpoint indicated a failure. See InnerException for details.
== APP ==  ---> Grpc.Core.RpcException: Status(StatusCode="PermissionDenied", Detail="access denied by policy to get "RabbitMQConnectStr" from "secrets01"")
== APP ==    at Dapr.Client.DaprClientGrpc.GetSecretAsync(String storeName, String key, IReadOnlyDictionary`2 metadata, CancellationToken cancellationToken)
== APP ==    --- End of inner exception stack trace ---
== APP ==    at Dapr.Client.DaprClientGrpc.GetSecretAsync(String storeName, String key, IReadOnlyDictionary`2 metadata, CancellationToken cancellationToken)
== APP ==    at Dapr.Extensions.Configuration.DaprSecretStore.DaprSecretStoreConfigurationProvider.LoadAsync()
== APP ==    at Dapr.Extensions.Configuration.DaprSecretStore.DaprSecretStoreConfigurationProvider.Load()
== APP ==    at Microsoft.Extensions.Configuration.ConfigurationRoot..ctor(IList`1 providers)
== APP ==    at Microsoft.Extensions.Configuration.ConfigurationBuilder.Build()
== APP ==    at Microsoft.Extensions.Hosting.HostBuilder.BuildAppConfiguration()
== APP ==    at Microsoft.Extensions.Hosting.HostBuilder.Build()
== APP ==    at FrontEnd.Program.Main(String[] args) in C:\\demo\\test\\DaprBackEnd\\FrontEnd\\Program.cs:line 20

我们可以修改配置让其允许

apiVersion: dapr.io/v1alpha1
kind: Configuration
metadata:
  name: daprConfig
spec:
  tracing:
    samplingRate: "1"
    zipkin:
      endpointAddress: http://localhost:9411/api/v2/spans
  secrets:
    scopes:
      - storeName: secrets01
        defaultAccess: deny
        allowedSecrets: ["RabbitMQConnectStr"]

重启Frontend成功

以下表格列出了所有可能的访问权限配置

ScenariosdefaultAccessallowedSecretsdeniedSecretspermission
1 - Only default access deny/allow empty empty deny/allow
2 - Default deny with allowed list deny [“s1”] empty only “s1” can be accessed
3 - Default allow with deneied list allow empty [“s1”] only “s1” cannot be accessed
4 - Default allow with allowed list allow [“s1”] empty only “s1” can be accessed
5 - Default deny with denied list deny empty [“s1”] deny
6 - Default deny/allow with both lists deny/allow [“s1”] [“s2”] only “s1” can be accessed

以上是关于Dapr + .NET Core实战Secrets的主要内容,如果未能解决你的问题,请参考以下文章

Dapr + .NET Core实战 服务调用

Dapr + .NET Core实战状态管理

Dapr + .NET 实战(十-终篇)K8S运行Dapr

Dapr + .NET 实战本地调试

Dapr + .NET 实战(十三)跨语言开发

Dapr + .NET 实战(十四)虚拟机集群部署 mDNS + Consul