From 2eba9d174edc3ffcaed4e493e464b631cafd9c3a Mon Sep 17 00:00:00 2001 From: SoTrx <11771975+SoTrx@users.noreply.github.com> Date: Mon, 16 Sep 2024 19:58:07 +0200 Subject: [PATCH] New Dapr Components Metadata Format + Tutorial on Referencing Secrets in Dapr Components (#1234) * feat: adapt Dapr components to new metadata format and add tutorial for referencing secrets Signed-off-by: SoTrx <11771975+SoTrx@users.noreply.github.com> * Typo Signed-off-by: SoTrx <11771975+SoTrx@users.noreply.github.com> * Forgotten a closing brace in some bicep files Signed-off-by: SoTrx <11771975+SoTrx@users.noreply.github.com> * Using "you" instead of "we" in the guide Signed-off-by: SoTrx <11771975+SoTrx@users.noreply.github.com> * Update app-statestore-secret.bicep Signed-off-by: Yetkin Timocin * Update app-statestore.bicep Signed-off-by: Yetkin Timocin * Update app-statestore-secret.bicep Signed-off-by: Yetkin Timocin * Apply suggestions from code review Signed-off-by: Will <28876888+willtsai@users.noreply.github.com> --------- Signed-off-by: SoTrx <11771975+SoTrx@users.noreply.github.com> Signed-off-by: Yetkin Timocin Signed-off-by: Will <28876888+willtsai@users.noreply.github.com> Co-authored-by: Yetkin Timocin Co-authored-by: Will <28876888+willtsai@users.noreply.github.com> --- .../dapr/how-to-dapr-secrets/index.md | 143 ++++++++++++++++++ .../snippets/app-statestore-secret.bicep | 78 ++++++++++ .../snippets/app-statestore.bicep | 63 ++++++++ .../snippets/dapr-componentname.bicep | 12 +- .../dapr/overview/snippets/statestore.bicep | 12 +- .../dapr-schema/dapr-pubsub/index.md | 2 +- .../snippets/dapr-pubsub-manual.bicep | 12 +- .../dapr-schema/dapr-secretstore/index.md | 2 +- .../snippets/dapr-secretstore-manual.bicep | 16 +- .../dapr-schema/dapr-statestore/index.md | 2 +- .../snippets/dapr-statestore-manual.bicep | 12 +- 11 files changed, 335 insertions(+), 19 deletions(-) create mode 100644 docs/content/guides/author-apps/dapr/how-to-dapr-secrets/index.md create mode 100644 docs/content/guides/author-apps/dapr/how-to-dapr-secrets/snippets/app-statestore-secret.bicep create mode 100644 docs/content/guides/author-apps/dapr/how-to-dapr-secrets/snippets/app-statestore.bicep diff --git a/docs/content/guides/author-apps/dapr/how-to-dapr-secrets/index.md b/docs/content/guides/author-apps/dapr/how-to-dapr-secrets/index.md new file mode 100644 index 000000000..09c95c5dc --- /dev/null +++ b/docs/content/guides/author-apps/dapr/how-to-dapr-secrets/index.md @@ -0,0 +1,143 @@ +--- +type: docs +title: "How-To: Reference secrets in Dapr components" +linkTitle: "Reference secrets in components" +description: "Learn how to manage secrets in Dapr components" +weight: 300 +categories: "How-To" +tags: ["Dapr"] +--- + +This guide will provide an overview of how to: + +- Securely manage secrets in Dapr components using [Dapr secret stores](https://docs.dapr.io/operations/components/setup-secret-store/) + +## Prerequisites + +- [rad CLI]({{< ref "installation#step-1-install-the-rad-cli" >}}) +- [Bicep VSCode extension]({{< ref "installation#step-2-install-the-vs-code-extension" >}}) +- [Radius environment]({{< ref "installation#step-3-initialize-radius" >}}) +- [Radius local-dev Recipes]({{< ref howto-dev-recipes >}}) +- [Dapr installed on your Kubernetes cluster](https://docs.dapr.io/operations/hosting/kubernetes/kubernetes-deploy/) + +## Step 1: Create a container and a Dapr sidecar + +Begin by creating a file named `app.bicep`, which defines a container with a Dapr state store. If you need a detailed explanation on how to do so, refer to the [Add a Dapr building block]({{< ref how-to-dapr-building-block >}}) tutorial. + +In this guide, you manually provision both a Redis instance and a Dapr state store component. Specify the Redis username in the Dapr state store, which will later be secured using a Dapr Secret Store. + +{{< rad file="./snippets/app-statestore.bicep" embed=true >}} + +Deploy the application with the `rad` CLI: + +```bash +rad run ./app.bicep -a demo-secret +``` + +While the application is running, verify that the `redisUsername` is exposed in the Dapr state store component by running the following command **in another terminal**: +```sh +kubectl -n default-demo-secret get component demo-statestore -o yaml +``` + +The output should look similar to: +```yaml +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: demo-statestore + namespace: default-demo-secret +spec: + metadata: + - name: redisHost + value: + - name: redisUsername + # The Redis username is stored in plain text + value: default + type: state.redis + version: v1 +``` + +## Step 2: Add a Dapr secret store resource and create a secret + +Secure the Redis username using a Dapr Secret Store. In your `app.bicep` file, add a Dapr secret store resource. Use the [`local-dev` recipe]({{< ref "guides/recipes/overview##use-lightweight-local-dev-recipes" >}}) to deploy the secret store, leveraging Kubernetes secrets + +{{< rad file="./snippets/app-statestore-secret.bicep" embed=true marker="//SECRETSTORE" >}} + +> Visit the [Radius Recipe repo](https://github.com/radius-project/recipes/blob/main/local-dev/secretstores.bicep) to learn more about `local-dev` Recipes and view the Dapr Secret Store Recipe used in this guide. + + +Now, create a Kubernetes secret for the Redis username by creating a `redis-auth.yaml` file with the following content: +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: redis-auth + namespace: default-demo-secret +type: opaque +data: + # Result of "echo -n 'default' | base64" + username: ZGVmYXVsdA== +``` + +Apply the secret to your Kubernetes cluster by running: +```sh +kubectl apply -f redis-auth.yaml +``` + +## Step 3: Configure the Dapr state store to use the Dapr secret store + +Finally, update your Dapr state store to reference the created secret. + +{{< rad file="./snippets/app-statestore-secret.bicep" embed=true marker="//STATESTORE" >}} + + +## Step 4: Deploy the updated application + +Deploy the application with the updated configuration: +```bash +rad run ./app.bicep -a demo-secret +``` + +You can verify that the Redis username is no longer exposed by running the following command **in another terminal**: +```sh +kubectl -n default-demo-secret get component demo-statestore -o yaml +``` + +The output should show the Redis username stored in the secret store: +```yaml +apiVersion: dapr.io/v1alpha1 +auth: + secretStore: secretstore +kind: Component +metadata: + name: demo-statestore + namespace: default-demo-secret +spec: + metadata: + - name: redisHost + value: + - name: redisUsername + secretKeyRef: + key: username + name: redis-auth + type: state.redis + version: v1 +``` +Open [http://localhost:3000](http://localhost:3000) to view the Radius demo container. The TODO application should work as intended. + +> In a production environment, it's recommended to use a more secure secret store, such as Azure Key Vault or HashiCorp Vault, instead of relying on Kubernetes secrets. You can find the list of all Dapr secret store components [here](https://docs.dapr.io/reference/components-reference/supported-secret-stores/). + +## Cleanup + +To delete your app, run the [rad app delete]({{< ref rad_application_delete >}}) command to cleanup the app and its resources, including the Recipe resources: + +```bash +rad app delete -a demo-secret +kubectl delete secret redis-auth -n default-demo-secret +``` + +## Further reading + +- [Dapr building blocks](https://docs.dapr.io/concepts/building-blocks-concept/) +- [Dapr resource schemas]({{< ref dapr-schema >}}) + diff --git a/docs/content/guides/author-apps/dapr/how-to-dapr-secrets/snippets/app-statestore-secret.bicep b/docs/content/guides/author-apps/dapr/how-to-dapr-secrets/snippets/app-statestore-secret.bicep new file mode 100644 index 000000000..c86f2a2a5 --- /dev/null +++ b/docs/content/guides/author-apps/dapr/how-to-dapr-secrets/snippets/app-statestore-secret.bicep @@ -0,0 +1,78 @@ +extension radius + +@description('The ID of your Radius Application. Automatically injected by the rad CLI.') +param application string + +@description('The ID of your Radius environment. Automatically injected by the rad CLI.') +param environment string + +resource demo 'Applications.Core/containers@2023-10-01-preview' = { + name: 'demo' + properties: { + application: application + container: { + image: 'ghcr.io/radius-project/samples/demo:latest' + ports: { + web: { + containerPort: 3000 + } + } + } + extensions: [ + { + kind: 'daprSidecar' + appId: 'demo' + appPort: 3000 + } + ] + connections: { + redis: { + source: stateStore.id + } + } + } +} + +resource redis 'Applications.Datastores/redisCaches@2023-10-01-preview' = { + name: 'demo-redis-manual' + properties: { + environment: environment + application: application + } +} + +resource stateStore 'Applications.Dapr/stateStores@2023-10-01-preview' = { + name: 'demo-statestore' + properties: { + // The secret store to pull secret store + auth: { + secretStore: secretstore.name + } + application: application + environment: environment + resourceProvisioning: 'manual' + type: 'state.redis' + version: 'v1' + metadata: { + redisHost: { + value: '${redis.properties.host}:${redis.properties.port}' + } + redisUsername: { + secretKeyRef: { + // Secret object name + name: 'redis-auth' + // Secret key + key: 'username' + } + } + } + } +} + +resource secretstore 'Applications.Dapr/secretStores@2023-10-01-preview' = { + name: 'secretstore' + properties: { + environment: environment + application: application + } +} diff --git a/docs/content/guides/author-apps/dapr/how-to-dapr-secrets/snippets/app-statestore.bicep b/docs/content/guides/author-apps/dapr/how-to-dapr-secrets/snippets/app-statestore.bicep new file mode 100644 index 000000000..17544b748 --- /dev/null +++ b/docs/content/guides/author-apps/dapr/how-to-dapr-secrets/snippets/app-statestore.bicep @@ -0,0 +1,63 @@ +extension radius + +@description('The ID of your Radius Application. Automatically injected by the rad CLI.') +param application string + +@description('The ID of your Radius environment. Automatically injected by the rad CLI.') +param environment string + +resource demo 'Applications.Core/containers@2023-10-01-preview' = { + name: 'demo' + properties: { + application: application + container: { + image: 'ghcr.io/radius-project/samples/demo:latest' + ports: { + web: { + containerPort: 3000 + } + } + } + extensions: [ + { + kind: 'daprSidecar' + appId: 'demo' + appPort: 3000 + } + ] + connections: { + redis: { + source: stateStore.id + } + } + } +} + +resource redis 'Applications.Datastores/redisCaches@2023-10-01-preview' = { + name: 'demo-redis-manual' + properties: { + environment: environment + application: application + } +} + +resource stateStore 'Applications.Dapr/stateStores@2023-10-01-preview' = { + name: 'demo-statestore' + properties: { + application: application + environment: environment + resourceProvisioning: 'manual' + type: 'state.redis' + version: 'v1' + metadata: { + redisHost: { + value: '${redis.properties.host}:${redis.properties.port}' + } + // This value will be considered a secret later on + redisUsername: { + value: 'default' + } + } + } +} + diff --git a/docs/content/guides/author-apps/dapr/overview/snippets/dapr-componentname.bicep b/docs/content/guides/author-apps/dapr/overview/snippets/dapr-componentname.bicep index d9cbd7a6c..301a0aeaf 100644 --- a/docs/content/guides/author-apps/dapr/overview/snippets/dapr-componentname.bicep +++ b/docs/content/guides/author-apps/dapr/overview/snippets/dapr-componentname.bicep @@ -56,9 +56,15 @@ resource statestore 'Applications.Dapr/stateStores@2023-10-01-preview' = { { id: account::tableServices::table.id } ] metadata: { - accountName: account.name - accountKey: account.listKeys().keys[0].value - tableName: account::tableServices::table.name + accountName: { + value: account.name + } + accountKey: { + value: account.listKeys().keys[0].value + } + tableName: { + value: account::tableServices::table.name + } } type: 'state.azure.tablestorage' version: 'v1' diff --git a/docs/content/guides/author-apps/dapr/overview/snippets/statestore.bicep b/docs/content/guides/author-apps/dapr/overview/snippets/statestore.bicep index 38908a7e2..0ba97d53b 100644 --- a/docs/content/guides/author-apps/dapr/overview/snippets/statestore.bicep +++ b/docs/content/guides/author-apps/dapr/overview/snippets/statestore.bicep @@ -35,9 +35,15 @@ resource stateStore 'Applications.Dapr/stateStores@2023-10-01-preview' = { { id: account::tableServices::table.id } ] metadata: { - accountName: account.name - accountKey: account.listKeys().keys[0].value - tableName: account::tableServices::table.name + accountName: { + value: account.name + } + accountKey: { + value: account.listKeys().keys[0].value + } + tableName: { + value: account::tableServices::table.name + } } type: 'state.azure.tablestorage' version: 'v1' diff --git a/docs/content/reference/resource-schema/dapr-schema/dapr-pubsub/index.md b/docs/content/reference/resource-schema/dapr-schema/dapr-pubsub/index.md index 6dbc681e9..78b0c76c6 100644 --- a/docs/content/reference/resource-schema/dapr-schema/dapr-pubsub/index.md +++ b/docs/content/reference/resource-schema/dapr-schema/dapr-pubsub/index.md @@ -47,7 +47,7 @@ An `Applications.Dapr/pubSubBrokers` resource represents a [Dapr pub/sub](https: | [recipe](#recipe) | n | Configuration for the Recipe which will deploy the backing infrastructure. | [See below](#recipe) | [resources](#resources) | n | An array of resources which underlay this resource. For example, an Azure Service Bus namespace ID if the Dapr Pub/Sub resource is leveraging Service Bus. | [See below](#resources) | type | n | The Dapr component type. Set only when resourceProvisioning is 'manual'. | `pubsub.kafka` | -| metadata | n | Metadata object for the Dapr component. Schema must match [Dapr component](https://docs.dapr.io/reference/components-reference/supported-pubsub/). Set only when resourceProvisioning is 'manual'. | `{ brokers: kafkaRoute.properties.url }` | +| metadata | n | Metadata object for the Dapr component. Schema must match [Dapr component](https://docs.dapr.io/reference/components-reference/supported-pubsub/). Set only when resourceProvisioning is 'manual'. | `{ brokers: { value: kafkaRoute.properties.url } }` | | version | n | The version of the Dapr component. See [Dapr components](https://docs.dapr.io/reference/components-reference/supported-pubsub/) for available versions. Set only when resourceProvisioning is 'manual'. | `v1` | | componentName | n | _(read-only)_ The name of the Dapr component that is generated and applied to the underlying system. Used by the Dapr SDKs or APIs to access the Dapr component. | `mypubsub` | diff --git a/docs/content/reference/resource-schema/dapr-schema/dapr-pubsub/snippets/dapr-pubsub-manual.bicep b/docs/content/reference/resource-schema/dapr-schema/dapr-pubsub/snippets/dapr-pubsub-manual.bicep index 620f011d4..d2052ddc9 100644 --- a/docs/content/reference/resource-schema/dapr-schema/dapr-pubsub/snippets/dapr-pubsub-manual.bicep +++ b/docs/content/reference/resource-schema/dapr-schema/dapr-pubsub/snippets/dapr-pubsub-manual.bicep @@ -34,9 +34,15 @@ resource pubsub 'Applications.Dapr/pubSubBrokers@2023-10-01-preview' = { resourceProvisioning: 'manual' type: 'pubsub.kafka' metadata: { - brokers: '' - authRequired: false - consumeRetryInternal: 1024 + brokers: { + value: '' + } + authRequired: { + value: false + } + consumeRetryInternal: { + value: 1024 + } } version: 'v1' } diff --git a/docs/content/reference/resource-schema/dapr-schema/dapr-secretstore/index.md b/docs/content/reference/resource-schema/dapr-schema/dapr-secretstore/index.md index a9a15567b..f668bf6e7 100644 --- a/docs/content/reference/resource-schema/dapr-schema/dapr-secretstore/index.md +++ b/docs/content/reference/resource-schema/dapr-schema/dapr-secretstore/index.md @@ -48,7 +48,7 @@ This resource will automatically create and deploy the Dapr component spec for t | [recipe](#recipe) | n | Configuration for the Recipe which will deploy the backing infrastructure. | [See below](#recipe) | [resources](#resources) | n | An array of IDs of the underlying resources. | [See below](#resources) | type | n | The Dapr component type. Used when resourceProvisioning is `manual`. | `secretstores.azure.keyvault` -| metadata | n | Metadata for the Dapr component. Schema must match [Dapr component](https://docs.dapr.io/reference/components-reference/supported-secret-stores/) | `vaultName: 'test'` | +| metadata | n | Metadata for the Dapr component. Schema must match [Dapr component](https://docs.dapr.io/reference/components-reference/supported-secret-stores/) | `{ vaultName: {value: 'test'} }` | | version | n | The version of the Dapr component. See [Dapr components](https://docs.dapr.io/reference/components-reference/supported-secret-stores/) for available versions. | `v1` | | componentName | n | _(read-only)_ The name of the Dapr component that is generated and applied to the underlying system. Used by the Dapr SDKs or APIs to access the Dapr component. | `mysecretstore` | diff --git a/docs/content/reference/resource-schema/dapr-schema/dapr-secretstore/snippets/dapr-secretstore-manual.bicep b/docs/content/reference/resource-schema/dapr-schema/dapr-secretstore/snippets/dapr-secretstore-manual.bicep index 1786b4418..532886c56 100644 --- a/docs/content/reference/resource-schema/dapr-schema/dapr-secretstore/snippets/dapr-secretstore-manual.bicep +++ b/docs/content/reference/resource-schema/dapr-schema/dapr-secretstore/snippets/dapr-secretstore-manual.bicep @@ -19,10 +19,18 @@ resource secretstore 'Applications.Dapr/secretStores@2023-10-01-preview' = { resourceProvisioning: 'manual' type: 'secretstores.azure.keyvault' metadata: { - vaultName: 'myvault' - azureTenantId: '' - azureClientId: '' - azureClientSecret: '*****' + vaultName: { + value: 'myvault' + } + azureTenantId: { + value: '' + } + azureClientId: { + value: '' + } + azureClientSecret: { + value: '*****' + } } version: 'v1' } diff --git a/docs/content/reference/resource-schema/dapr-schema/dapr-statestore/index.md b/docs/content/reference/resource-schema/dapr-schema/dapr-statestore/index.md index 543efe033..fbec358ae 100644 --- a/docs/content/reference/resource-schema/dapr-schema/dapr-statestore/index.md +++ b/docs/content/reference/resource-schema/dapr-schema/dapr-statestore/index.md @@ -50,7 +50,7 @@ This resource will automatically create and deploy the Dapr component spec for t | [recipe](#recipe) | n | Configuration for the Recipe which will deploy the backing infrastructure. | [See below](#recipe) | [resources](#resources) | n | An array of IDs of the underlying resources. | [See below](#resources) | type | n | The Dapr component type. Used when `resourceProvisioning` is set to `manual`. | `state.couchbase` -| metadata | n | Metadata for the Dapr component. Schema must match [Dapr component](https://docs.dapr.io/reference/components-reference/supported-state-stores/). Used when `resourceProvisioning` is set to `manual`. | `couchbaseURL: https://*****` | +| metadata | n | Metadata for the Dapr component. Schema must match [Dapr component](https://docs.dapr.io/reference/components-reference/supported-state-stores/). Used when `resourceProvisioning` is set to `manual`. | `{ couchbaseURL: {value: 'https://*****' }` | | version | n | The version of the Dapr component. See [Dapr components](https://docs.dapr.io/reference/components-reference/supported-state-stores/) for available versions. Used when `resourceProvisioning` is set to `manual`. | `v1` | | componentName | n | _(read-only)_ The name of the Dapr component that is generated and applied to the underlying system. Used by the Dapr SDKs or APIs to access the Dapr component. | `mystatestore` | diff --git a/docs/content/reference/resource-schema/dapr-schema/dapr-statestore/snippets/dapr-statestore-manual.bicep b/docs/content/reference/resource-schema/dapr-schema/dapr-statestore/snippets/dapr-statestore-manual.bicep index 4cb6ae0c6..67eeae0cf 100644 --- a/docs/content/reference/resource-schema/dapr-schema/dapr-statestore/snippets/dapr-statestore-manual.bicep +++ b/docs/content/reference/resource-schema/dapr-schema/dapr-statestore/snippets/dapr-statestore-manual.bicep @@ -18,9 +18,15 @@ resource statestore 'Applications.Dapr/stateStores@2023-10-01-preview' = { { id: account::tableServices::table.id } ] metadata: { - accountName: account.name - accountKey: account.listKeys().keys[0].value - tableName: account::tableServices::table.name + accountName: { + value: account.name + } + accountKey: { + value: account.listKeys().keys[0].value + } + tableName: { + value: account::tableServices::table.name + } } type: 'state.azure.tablestorage' version: 'v1'