什麼是Secrets
應用程序通常會通過使用專用的存儲來存儲敏感信息,如連接字符串、密鑰等。
通常這需要建立一個密鑰存儲,如Azure Key Vault、Hashicorp等,並在那裏存儲應用程序級別的密鑰。 要訪問這些密鑰存儲,應用程序需要導入密鑰存儲SDK,並使用它訪問這些密鑰。 這可能需要相當數量的模板代碼,這些代碼與應用的實際業務領域無關,因此在多雲場景中,可能會使用不同廠商特定的密鑰存儲,這就成爲一個更大的挑戰。
讓開發人員在任何地方更容易訪問應用程序密鑰, Dapr 提供一個專用的密鑰構建塊 ,允許開發人員從一個存儲獲得密鑰。
使用 Dapr 的密鑰存儲構建塊通常涉及以下內容:
- 設置一個特定的密鑰存儲解決方案的組件。
- 在應用程序代碼中使用 Dapr Secrets API 獲取密鑰。
- 在Dapr的Component文件中引用密鑰
工作原理
- 服務A調用 Dapr Secrets API,提供要檢索的Serects的名稱和要查詢的項名字。
- Dapr sidecar 從Secrets存儲中檢索指定的機密。
- 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:[email protected]: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成功
以下表格列出了所有可能的訪問權限配置
Scenarios | defaultAccess | allowedSecrets | deniedSecrets | permission |
---|---|---|---|---|
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 |