什麼是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 |