Dapr 入門教程之密鑰存儲
應用程序通常通過使用專用的 Secret 存儲來存儲敏感信息,如密鑰和 Token,用於與數據庫、服務和外部系統進行身份驗證的 Secret 等。通常這需要涉及到設置一個 Secret 存儲,如 Azure Key Vault
、Hashicorp Vault
等,並在那裏存儲應用程序級別的私密數據。爲了訪問這些 Secret 存儲,應用程序需要導入 Secret 存儲的 SDK,並使用它來訪問私密數據,這可能需要相當數量的代碼,這些代碼與應用程序的實際業務領域無關,因此在可能使用不同供應商特定的 Secret 存儲的多雲場景中,這將成爲更大的挑戰。
爲了使開發者更容易使用應用程序的私密數據,Dapr 有一個專門的 Secret 構建塊 API,允許開發者從 Secret 存儲中獲取私密數據。使用 Dapr 的 Secret 存儲構建塊通常涉及以下內容。
-
爲特定的 Secret 存儲解決方案設置一個組件。
-
在應用程序代碼中使用 Dapr 的 Secret API 來檢索私密數據。
-
(可選) 在 Dapr 組件文件中引用 Secret。
默認情況下,Dapr 在 Kubernetes 模式下通過 Helm 或 dapr init -k
部署的時候,啓用一個內置的 Kubernetes Secret 存儲,如果你使用另一個 Secret 存儲,你可以使用 disable-builtin-k8s-secret-store
設置禁用 Dapr Kubernetes Secret 存儲。
應用程序代碼可以調用 Secret 構建塊 API 從 Dapr 支持的 Secret 存儲中檢索私密數據,這些 Secret 存儲可以在你的代碼中使用。例如,下圖顯示了一個應用程序從配置的雲 Secret 存儲庫中的一個名爲 vault
的 Secret 存儲庫中請求名爲 mysecret
的私密數據。
應用程序可以使用 secrets API 來訪問來自 Kubernetes Secret 存儲的私密數據。在下面的例子中,應用程序從 Kubernetes Secret 存儲中檢索相同的 mysecret
。
本地環境使用 Secrets
同樣我們以 quickstarts
倉庫進行說明。
git clone [-b <dapr_version_tag>] https://github.com/dapr/quickstarts.git
cd quickstarts
然後定位到 secretstore
目錄下面的 node 文件夾:
$ cd tutorials/secretstore/node
在 app.js
中是一個簡單的 Express
應用,它暴露了一些路由和處理程序,我們可以先查看下該文件中的如下內容:
const daprPort = process.env.DAPR_HTTP_PORT || 3500;
const secretStoreName = process.env.SECRET_STORE;
const secretName = "mysecret";
其中 secretStoreName
從環境變量 SECRET_STORE
中讀取,,其爲 Kubernetes 部署注入了值 kubernetes
,對於本地開發,環境變量必須設置爲 localsecretstore
值。
然後我們看看 getsecret
處理程序代碼:
app.get("/getsecret", (_req, res) => {
const url = `${secretsUrl}/${secretStoreName}/${secretName}?metadata.namespace=default`;
console.log("Fetching URL: %s", url);
fetch(url)
.then((res) => res.json())
.then((json) => {
let secretBuffer = new Buffer(json["mysecret"]);
let encodedSecret = secretBuffer.toString("base64");
console.log("Base64 encoded secret is: %s", encodedSecret);
return res.send(encodedSecret);
});
});
該代碼從 secret store 中獲取名爲 mysecret
的數據,並顯示該數據的 Base64 編碼數據。
我們在 secrets.json
文件中添加一個 mysecret
的 Secret 數據:
{
"mysecret": "abcd"
}
同樣我們也需要添加一個 Secret 對應的 Component 組件,比如在本地自拓管模式,創建一個如下所示的配置文件:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: localsecretstore
namespace: default
spec:
type: secretstores.local.file
version: v1
metadata:
- name: secretsFile
value: secrets.json
- name: nestedSeparator
value: ":"
上面的組件定義了一個本地 Secret 存儲庫,其 Secret 文件路徑爲 secrets.json
文件。
其中 Secret 存儲 JSON 的路徑是與你調用
dapr run
的位置相關的。
然後我們需要將上面的 Secret Store 名稱設置爲環境變量:
# nux/Mac OS:
export SECRET_STORE="localsecretstore"
# Windows:
set SECRET_STORE=localsecretstore
接下來我們爲 Node 應用安裝依賴:
npm install # yarn
然後我們使用 Dapr 帶上本地的 secret store 組件運行 Node 應用:
$ dapr run --app-id nodeapp --components-path ./components --app-port 3000 --dapr-http-port 3500 node app.js
ℹ️ Starting Dapr with id nodeapp. HTTP Port: 3500. gRPC Port: 58744
INFO[0000] starting Dapr Runtime -- version 1.8.4 -- commit 18575823c74318c811d6cd6f57ffac76d5debe93 app_id=nodeapp instance=MBP2022.local scope=dapr.runtime type=log ver=1.8.4
# ......
INFO[0000] component loaded. name: localsecretstore, type: secretstores.local.file/v1 app_id=nodeapp instance=MBP2022.local scope=dapr.runtime type=log ver=1.8.4
INFO[0000] all outstanding components processed app_id=nodeapp instance=MBP2022.local scope=dapr.runtime type=log ver=1.8.4
# ......
== APP == Node App listening on port 3000!
INFO[0000] application discovered on port 3000 app_id=nodeapp instance=MBP2022.local scope=dapr.runtime type=log ver=1.8.4
WARN[0000] [DEPRECATION NOTICE] Adding a default content type to incoming service invocation requests is deprecated and will be removed in the future. See https://docs.dapr.io/operations/support/support-preview-features/ for more details. You can opt into the new behavior today by setting the configuration option `ServiceInvocation.NoDefaultContentType` to true. app_id=nodeapp instance=MBP2022.local scope=dapr.runtime type=log ver=1.8.4
INFO[0000] application configuration loaded app_id=nodeapp instance=MBP2022.local scope=dapr.runtime type=log ver=1.8.4
INFO[0000] actors: state store is not configured - this is okay for clients but services with hosted actors will fail to initialize! app_id=nodeapp instance=MBP2022.local scope=dapr.runtime type=log ver=1.8.4
INFO[0000] actor runtime started. actor idle timeout: 1h0m0s. actor scan interval: 30s app_id=nodeapp instance=MBP2022.local scope=dapr.runtime.actor type=log ver=1.8.4
INFO[0000] dapr initialized. Status: Running. Init Elapsed 326.57000000000005ms app_id=nodeapp instance=MBP2022.local scope=dapr.runtime type=log ver=1.8.4
INFO[0000] placement tables updated, version: 0 app_id=nodeapp instance=MBP2022.local scope=dapr.runtime.actor.internal.placement type=log ver=1.8.4
ℹ️ Updating metadata for app command: node app.js
✅ You're up and running! Both Dapr and your app logs will appear here.
啓動後我們可以使用 dapr list
來查看應用列表:
$ dapr list
APP ID HTTP PORT GRPC PORT APP PORT COMMAND AGE CREATED PID
nodeapp 3500 58744 3000 node app.js 11m 2022-09-27 15:13.46 5906
啓動完成後我們可以直接訪問應用的 getsecret
接口:
$ curl -k http://localhost:3000/getsecret
正常輸出結果是 YWJjZA==
,也就是上面的 abcd
做了 base64 編碼後的值。
然後觀察應用的日誌會出現類似於如下所示的內容:
== APP == Fetching URL: http://localhost:3500/v1.0/secrets/localsecretstore/mysecret?metadata.namespace=default
== APP == Base64 encoded secret is: YWJjZA==
測試完成後可以使用 dapr stop
命令來停止應用:
dapr stop --app-id nodeapp
Kubernetes 環境使用 Secrets
接下來我們來了解下在 Kubernetes 模式下 Dapr 是如何使用 Secrets store 的,當然首先需要在 Kubernetes 集羣中安裝 Dapr 控制平面。
Dapr 可以使用許多不同的 secret stores 來解析 secrets 數據,比如 AWS Secret Manager
、 Azure Key Vault
、 GCP Secret Manager
、 Kubernetes
等,我們這裏可以直接使用 Kubernetes 的 Secret 對象進行演示。
首先講 secrets 數據添加到 ./mysecret
文件,比如你的密碼是 abcd
,則 ./mysecret
文件內容應該就是 abcd
。
然後基於 ./mysecret
文件創建一個 Kubernetes Secret 對象:
$ kubectl create secret generic mysecret --from-file ./mysecret
注意創建的 Secret 對象的名稱 mysecret
,後面會使用到。
創建完成後我們可以查看下該對象中的數據是否符合預期:
$ kubectl get secret mysecret -o yaml
apiVersion: v1
data:
mysecret: YWJjZAo=
kind: Secret
metadata:
creationTimestamp: "2022-09-27T07:34:31Z"
name: mysecret
namespace: default
resourceVersion: "5133821"
uid: c9aa573c-5f71-439c-b482-748ac0fe3ae7
type: Opaque
接下來我們就可以部署 Node.js 應用到 Kubernetes 集羣中,對應的資源清單文件如下所示:
kind: Service
apiVersion: v1
metadata:
name: nodeapp
labels:
app: node
spec:
selector:
app: node
ports:
- protocol: TCP
port: 80
targetPort: 3000
type: LoadBalancer
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nodeapp
labels:
app: node
spec:
selector:
matchLabels:
app: node
template:
metadata:
labels:
app: node
annotations:
dapr.io/enabled: "true"
dapr.io/app-id: "nodeapp"
dapr.io/app-port: "3000"
spec:
containers:
- name: node
image: ghcr.io/dapr/samples/secretstorenode:latest
env:
- name: SECRET_STORE
value: "kubernetes"
ports:
- containerPort: 3000
imagePullPolicy: Always
這裏的核心重點是需要我們配置環境變量 SECRET_STORE
,將其值設置爲 kubernetes
,這樣我們的應用就知道應該通過 Kubernetes 獲取 Secret 數據了。直接部署該應用即可:
$ kubectl apply -f deploy/node.yaml
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nodeapp-6cb5b689cf-vtn74 2/2 Running 0 92
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nodeapp LoadBalancer 10.101.216.73 192.168.0.50 80:32719/TCP 13d
nodeapp-dapr ClusterIP None <none> 80/TCP,50001/TCP,50002/TCP,9090/TCP 13d
部署完成後我們這裏可以通過 192.168.0.50
這個 EXTERNAL-IP
訪問到應用:
curl -k http://192.168.0.50/getsecret
正常上面的請求輸出結果爲 YWJjZAo=
,也可以查看 Node 應用日誌:
$ kubectl logs --selector=app=node -c node
Node App listening on port 3000!
Fetching URL: http://localhost:3500/v1.0/secrets/kubernetes/mysecret?metadata.namespace=default
Base64 encoded secret is: YWJjZAo=
從上面日誌可以看出 Node 應用程序正在向 dapr 發出請求,以便從 secret store 獲取 secret 數據,注意其中的 mysecret
是上面創建的 Secret 對象名稱。
當然如果你使用的是其他 secret store,比如 HashiCorp Vault 則需要創建一個對應的 Component 組件了,類型爲secretstores.hashicorp.vault
,如下所示的資源清單:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: vault
spec:
type: secretstores.hashicorp.vault
version: v1
metadata:
- name: vaultAddr
value: [vault_address] # Optional. Default: "https://127.0.0.1:8200"
- name: caCert # Optional. This or caPath or caPem
value: "[ca_cert]"
- name: caPath # Optional. This or CaCert or caPem
value: "[path_to_ca_cert_file]"
- name: caPem # Optional. This or CaCert or CaPath
value : "[encoded_ca_cert_pem]"
- name: skipVerify # Optional. Default: false
value : "[skip_tls_verification]"
- name: tlsServerName # Optional.
value : "[tls_config_server_name]"
- name: vaultTokenMountPath # Required if vaultToken not provided. Path to token file.
value : "[path_to_file_containing_token]"
- name: vaultToken # Required if vaultTokenMountPath not provided. Token value.
value : "[path_to_file_containing_token]"
- name: vaultKVPrefix # Optional. Default: "dapr"
value : "[vault_prefix]"
- name: vaultKVUsePrefix # Optional. default: "true"
value: "[true/false]"
- name: enginePath # Optional. default: "secret"
value: "secret"
- name: vaultValueType # Optional. default: "map"
value: "map"
對於其他 Dapr 支持的 secret store 的配置屬性可以參考官方文檔 https://docs.dapr.io/reference/components-reference/supported-secret-stores/
瞭解相關信息。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/4nlI2qtN0khUUxe4Mg1u6w