Skip to content

Commit

Permalink
feat: adapt Dapr components to new metadata format and add tutorial f…
Browse files Browse the repository at this point in the history
…or referencing secrets

Signed-off-by: SoTrx <[email protected]>
  • Loading branch information
SoTrx committed Sep 15, 2024
1 parent cf3894a commit de84020
Show file tree
Hide file tree
Showing 11 changed files with 340 additions and 19 deletions.
143 changes: 143 additions & 0 deletions docs/content/guides/author-apps/dapr/how-to-dapr-secrets/index.md
Original file line number Diff line number Diff line change
@@ -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 how-to 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: Start with 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 detailled explanation on how to do so, refer to the [Add a Dapr building block]({{< ref how-to-dapr-building-block >}}) tutorial.

For this guide, we'll manually provision both a Redis instance and a Dapr State store component. This allows us to specify the Redis username in the Dapr state store, which we will later secure 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: <HOST>
- 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
Next, we'll secure the Redis username using a Dapr secret store. In your `app.bicep` file, add a Dapr secret store resource. We'll use the [`local-dev` recipe]({{< ref "guides/recipes/overview##use-lightweight-local-dev-recipes" >}}) to deploy the secret store, which will leverage 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: <HOST>
- 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 >}})

Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
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

//CONTAINER
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
}
}
}
}
//CONTAINER

// REDIS

resource redis 'Applications.Datastores/redisCaches@2023-10-01-preview' = {
name: 'demo-redis-manual'
properties: {
environment: environment
application: application
}
}

//STATESTORE
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'
}
}
}
}
}
//STATESTORE

//SECRETSTORE
resource secretstore 'Applications.Dapr/secretStores@2023-10-01-preview' = {
name: 'secretstore'
properties: {
environment: environment
application: application
}
}
//SECRETSTORE

Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
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'
}
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,14 @@ 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'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,14 @@ 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'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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` |

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,15 @@ resource pubsub 'Applications.Dapr/pubSubBrokers@2023-10-01-preview' = {
resourceProvisioning: 'manual'
type: 'pubsub.kafka'
metadata: {
brokers: '<KAFKA-URL>'
authRequired: false
consumeRetryInternal: 1024
brokers: {
value: '<KAFKA-URL>'
}
authRequired: {
value: false
}
consumeRetryInternal: {
value: 1024
}
}
version: 'v1'
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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` |

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,18 @@ resource secretstore 'Applications.Dapr/secretStores@2023-10-01-preview' = {
resourceProvisioning: 'manual'
type: 'secretstores.azure.keyvault'
metadata: {
vaultName: 'myvault'
azureTenantId: '<GUID>'
azureClientId: '<GUID>'
azureClientSecret: '*****'
vaultName: {
value: 'myvault'
}
azureTenantId: {
value: '<GUID>'
}
azureClientId: {
value: '<GUID>'
}
azureClientSecret: {
value: '*****'
}
}
version: 'v1'
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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` |

Expand Down
Loading

0 comments on commit de84020

Please sign in to comment.