This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

机密管理

从应用程序安全地访问机密

1 - 密钥管理概览

密钥管理 API 构建块概览

应用程序通常使用专用的密钥存储来存储敏感信息。例如,你使用存储在密钥存储(如 AWS Secrets Manager、Azure Key Vault、Hashicorp Vault 等)中的连接字符串、密钥、令牌和其他应用级密钥来对数据库、服务和外部系统进行身份验证。

要访问这些密钥存储,应用程序需要导入密钥存储 SDK,这通常需要大量无关的样板代码。在多云场景中,这会带来更大的挑战,因为可能会使用不同供应商特定的密钥存储。

密钥管理 API

Dapr 专用的密钥构建块 API 使开发者更容易从密钥存储中消费应用密钥。要使用 Dapr 的密钥存储构建块,你需要:

  1. 为特定的密钥存储解决方案设置一个组件。
  2. 在应用代码中使用 Dapr 密钥 API 检索密钥。
  3. (可选)在 Dapr 组件文件中引用密钥。

以下概览视频和演示展示了 Dapr 密钥管理的工作原理。

功能特性

密钥管理 API 构建块为你的应用程序带来了多项功能。

无需更改应用代码即可配置密钥

你可以在应用代码中调用密钥 API 来检索和使用来自 Dapr 支持的密钥存储的密钥。观看此视频,了解如何在你的应用程序中使用密钥管理 API 的示例。

例如,下图显示了一个应用程序从名为"vault"的密钥存储中请求名为"mysecret"的密钥,该密钥存储来自已配置的云密钥存储。

应用程序还可以使用密钥 API 访问 Kubernetes 密钥存储中的密钥。默认情况下,Dapr 在 Kubernetes 模式下启用内置的 Kubernetes 密钥存储,通过以下方式部署:

  • Helm 默认值,或
  • dapr init -k

如果你使用其他密钥存储,可以通过在 deployment.yaml 文件中添加注解 dapr.io/disable-builtin-k8s-secret-store: "true" 来禁用(不配置)Dapr Kubernetes 密钥存储。默认值为 false

在下面的示例中,应用程序从 Kubernetes 密钥存储中检索相同的密钥"mysecret"。

在 Azure 中,你可以配置 Dapr 使用托管身份通过 Azure Key Vault 进行身份验证来检索密钥。在下面的示例中:

  1. 配置 Azure Kubernetes Service (AKS) 群集以使用托管身份。
  2. Dapr 使用 Pod 标识代表应用程序从 Azure Key Vault 检索密钥。

在上面的示例中,应用程序代码无需更改即可获取相同的密钥。Dapr 通过密钥管理构建块 API 使用密钥管理组件。

使用我们的快速入门或教程之一试用密钥 API

在 Dapr 组件中引用密钥存储

在配置 Dapr 组件(如状态存储)时,通常需要在组件文件中包含凭证。或者,你可以将凭证放在 Dapr 支持的密钥存储中,并在 Dapr 组件中引用该密钥。这是首选方法,也是推荐的最佳实践,尤其是在生产环境中。

有关更多信息,请阅读在组件中引用密钥存储

限制对密钥的访问

为了对密钥访问提供更精细的控制,Dapr 提供了定义作用域和限制访问权限的功能。了解有关使用密钥作用域的更多信息。

试用密钥管理

快速入门和教程

想要测试 Dapr 密钥管理 API?通过以下快速入门和教程了解 Dapr 密钥的实际应用:

快速入门/教程描述
密钥管理快速入门使用密钥管理 API 从已配置的密钥存储在应用代码中检索密钥。
密钥存储教程演示如何使用 Dapr 密钥 API 访问密钥存储。

直接在你的应用中开始管理密钥

想跳过快速入门?没问题。你可以直接在你的应用程序中试用密钥管理构建块来检索和管理密钥。在安装 Dapr后,你可以从密钥操作指南开始使用密钥管理 API。

后续步骤

2 - 操作指南:获取密钥

使用密钥存储构建块安全地获取密钥

既然您已经了解了 Dapr 密钥构建块提供什么功能,了解它如何在您的服务中工作。本指南演示如何调用密钥 API 并从配置的密钥存储中检索应用代码中的密钥。

Diagram showing secrets management of example service.

设置密钥存储

在应用的代码中检索密钥之前,您必须配置一个密钥存储组件。本示例配置了一个使用本地 JSON 文件存储密钥的密钥存储。

在项目目录中,创建一个名为 secrets.json 的文件,包含以下内容:

{
   "secret": "Order Processing pass key"
}

创建一个名为 components 的新目录。导航到该目录并创建一个名为 local-secret-store.yaml 的组件文件,包含以下内容:

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: localsecretstore
spec:
  type: secretstores.local.file
  version: v1
  metadata:
  - name: secretsFile
    value: secrets.json  #path to secrets file
  - name: nestedSeparator
    value: ":"

更多信息:

获取密钥

通过使用密钥 API 调用 Dapr 边车来获取密钥:

curl http://localhost:3601/v1.0/secrets/localsecretstore/secret

查看完整 API 参考

从代码中调用密钥 API

现在您已经设置了本地密钥存储,调用 Dapr 从应用代码中获取密钥。以下是利用 Dapr SDK 获取密钥的代码示例。

using System;
using System.Threading.Tasks;
using Dapr.Client;

namespace EventService;

const string SECRET_STORE_NAME = "localsecretstore";

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDaprClient();
var app = builder.Build();

//Resolve a DaprClient from DI
var daprClient = app.Services.GetRequiredService<DaprClient>();

//Use the Dapr SDK to get a secret
var secret = await daprClient.GetSecretAsync(SECRET_STORE_NAME, "secret");

Console.WriteLine($"Result: {string.Join(", ", secret)}");
//dependencies
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.dapr.client.DaprClient;
import io.dapr.client.DaprClientBuilder;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;


//code
@SpringBootApplication
public class OrderProcessingServiceApplication {

    private static final Logger log = LoggerFactory.getLogger(OrderProcessingServiceApplication.class);
    private static final ObjectMapper JSON_SERIALIZER = new ObjectMapper();

    private static final String SECRET_STORE_NAME = "localsecretstore";

    public static void main(String[] args) throws InterruptedException, JsonProcessingException {
        DaprClient client = new DaprClientBuilder().build();
        //Using Dapr SDK to get a secret
        Map<String, String> secret = client.getSecret(SECRET_STORE_NAME, "secret").block();
        log.info("Result: " + JSON_SERIALIZER.writeValueAsString(secret));
    }
}
#dependencies 
import random
from time import sleep    
import requests
import logging
from dapr.clients import DaprClient
from dapr.clients.grpc._state import StateItem
from dapr.clients.grpc._request import TransactionalStateOperation, TransactionOperationType

#code
logging.basicConfig(level = logging.INFO)
DAPR_STORE_NAME = "localsecretstore"
key = 'secret'

with DaprClient() as client:
    #Using Dapr SDK to get a secret
    secret = client.get_secret(store_name=DAPR_STORE_NAME, key=key)
    logging.info('Result: ')
    logging.info(secret.secret)
    #Using Dapr SDK to get bulk secrets
    secret = client.get_bulk_secret(store_name=DAPR_STORE_NAME)
    logging.info('Result for bulk secret: ')
    logging.info(sorted(secret.secrets.items()))
//dependencies 
import (
	"context"
	"log"

	dapr "github.com/dapr/go-sdk/client"
)

//code
func main() {
	client, err := dapr.NewClient()
	SECRET_STORE_NAME := "localsecretstore"
	if err != nil {
		panic(err)
	}
	defer client.Close()
	ctx := context.Background()
     //Using Dapr SDK to get a secret
	secret, err := client.GetSecret(ctx, SECRET_STORE_NAME, "secret", nil)
	if secret != nil {
		log.Println("Result : ")
		log.Println(secret)
	}
    //Using Dapr SDK to get bulk secrets
	secretBulk, err := client.GetBulkSecret(ctx, SECRET_STORE_NAME, nil)

	if secret != nil {
		log.Println("Result for bulk: ")
		log.Println(secretBulk)
	}
}
//dependencies 
import { DaprClient, HttpMethod, CommunicationProtocolEnum } from '@dapr/dapr'; 

//code
const daprHost = "127.0.0.1"; 

async function main() {
    const client = new DaprClient({
        daprHost,
        daprPort: process.env.DAPR_HTTP_PORT,
        communicationProtocol: CommunicationProtocolEnum.HTTP,
    });
    const SECRET_STORE_NAME = "localsecretstore";
    //Using Dapr SDK to get a secret
    var secret = await client.secret.get(SECRET_STORE_NAME, "secret");
    console.log("Result: " + secret);
    //Using Dapr SDK to get bulk secrets
    secret = await client.secret.getBulk(SECRET_STORE_NAME);
    console.log("Result for bulk: " + secret);
}

main();

相关链接

3 - 如何使用:密钥作用域

使用作用域限制可从密钥存储中读取的密钥

一旦你为应用配置了密钥存储,该存储中定义的任何密钥默认都可从 Dapr 应用访问。

你可以通过定义密钥作用域来限制 Dapr 应用对特定密钥的访问。只需向应用配置添加具有限制性权限的密钥作用域策略。

密钥作用域策略适用于任何密钥存储,包括:

  • 本地密钥存储
  • Kubernetes 密钥存储
  • 公有云密钥存储

有关如何设置密钥存储的详细信息,请阅读如何获取密钥

观看此视频了解如何将密钥作用域与应用结合使用的演示。

场景1:拒绝对某个密钥存储的所有密钥的访问

在此示例中,将拒绝在 Kubernetes 集群上运行的应用访问所有密钥,该集群已配置名为 mycustomsecretstoreKubernetes 密钥存储。除了用户定义的自定义存储外,该示例还配置 Kubernetes 默认存储(名为 kubernetes)以确保拒绝所有密钥的访问。了解更多关于 Kubernetes 默认密钥存储的信息

定义以下 appconfig.yaml 配置并使用命令 kubectl apply -f appconfig.yaml 将其应用到 Kubernetes 集群。

apiVersion: dapr.io/v1alpha1
kind: Configuration
metadata:
  name: appconfig
spec:
  secrets:
    scopes:
      - storeName: kubernetes
        defaultAccess: deny
      - storeName: mycustomsecreststore
        defaultAccess: deny

对于需要被拒绝访问 Kubernetes 密钥存储的应用,遵循这些说明,并向应用 Pod 添加以下注解:

dapr.io/config: appconfig

定义后,应用将无法再访问 Kubernetes 密钥存储中的任何密钥。

场景2:仅允许访问某个密钥存储中的特定密钥

此示例使用名为 vault 的密钥存储。这可以是为应用设置的 Hashicorp 密钥存储组件。要允许 Dapr 应用仅访问 vault 密钥存储中的 secret1secret2,定义以下 appconfig.yaml

apiVersion: dapr.io/v1alpha1
kind: Configuration
metadata:
  name: appconfig
spec:
  secrets:
    scopes:
      - storeName: vault
        defaultAccess: deny
        allowedSecrets: ["secret1", "secret2"]

vault 密钥存储的默认访问权限为 deny,而基于 allowedSecrets 列表,应用可以访问某些密钥。了解如何将配置应用到边车

场景3:拒绝对某个密钥存储中某些敏感密钥的访问

定义以下 config.yaml

apiVersion: dapr.io/v1alpha1
kind: Configuration
metadata:
  name: appconfig
spec:
  secrets:
    scopes:
      - storeName: vault
        defaultAccess: allow # 此为默认值,可省略该行
        deniedSecrets: ["secret1", "secret2"]

此示例配置明确拒绝访问名为 vault 的密钥存储中的 secret1secret2,同时允许访问所有其他密钥。了解如何将配置应用到边车

权限优先级

allowedSecretsdeniedSecrets 列表值优先于 defaultAccess 策略。

场景defaultAccessallowedSecretsdeniedSecrets权限
1 - 仅默认访问deny/allowdeny/allow
2 - 默认拒绝,包含允许列表deny[“s1”]仅能访问 “s1”
3 - 默认允许,包含拒绝列表allow[“s1”]仅不能访问 “s1”
4 - 默认允许,包含允许列表allow[“s1”]仅能访问 “s1”
5 - 默认拒绝,包含拒绝列表deny[“s1”]deny
6 - 默认拒绝/允许,包含两个列表deny/allow[“s1”][“s2”]仅能访问 “s1”

相关链接