Dapr is an event-driven, portable runtime for building microservices on cloud and edge.

Dapr supports the fundamental features you’ll need such as: service invocation, state management, publish/subscribe messaging and since version 0.5.0 the ability to read from secret stores!

This post will show you to read kubernetes secrets using Dapr and .NET Core:

Prerequistes#

Create an .NET Core project and add dependencies#

Open the command line and type:

1dotnet new web -o dapr.k8s.secrets
2
3cd dapr.k8s.secrets
4
5dotnet add package Dapr.Client -v 0.5.0-preview02

Update Startup.cs#

Update Startup.cs with the following contents in order to expose a secret endpoint and use Dapr to fetch a kubernetes secret:

 1namespace dapr.k8s.secrets
 2{
 3    using System.Text.Json;
 4    using System.Threading.Tasks;
 5    using Microsoft.AspNetCore.Builder;
 6    using Microsoft.AspNetCore.Hosting;
 7    using Microsoft.AspNetCore.Http;
 8    using Microsoft.Extensions.Configuration;
 9    using Microsoft.Extensions.Hosting;
10    using Microsoft.Extensions.DependencyInjection;
11    using Dapr.Client;
12    using System;
13    using System.Collections.Generic;
14
15    public class Startup
16    {
17        // Dapr listens for requets on localhost
18        const string localhost = "127.0.0.1";
19
20        // Get the Dapr gRPC port
21        static string daprPort => Environment.GetEnvironmentVariable("DAPR_GRPC_PORT") ?? "50001";
22
23        public Startup(IConfiguration configuration)
24        {
25            Configuration = configuration;
26        }
27
28        public IConfiguration Configuration { get; }
29
30        public void ConfigureServices(IServiceCollection services)
31        {
32            // Create Dapr Client
33            var client = new DaprClientBuilder()
34                .UseEndpoint($"https://{localhost}:{daprPort}")
35                .Build();
36
37            // Add the DaprClient to DI.
38            services.AddSingleton<DaprClient>(client);
39        }
40
41        public void Configure(IApplicationBuilder app, IWebHostEnvironment env, DaprClient client)
42        {
43            if (env.IsDevelopment())
44            {
45                app.UseDeveloperExceptionPage();
46            }
47
48            app.UseRouting();
49
50            app.UseEndpoints(endpoints =>
51            {
52                // Add the secrets route
53                endpoints.MapGet("secret", Secret);
54            });
55
56            async Task Secret(HttpContext context)
57            {
58                // Get the secret from kubernetes
59                var secretValues = await client.GetSecretAsync(
60                    "kubernetes", // Name of the Dapr Secret Store
61                    "super-secret", // Name of the k8s secret
62                    new Dictionary<string, string>() { { "namespace", "default" } }); // Namespace where the k8s secret is deployed
63
64                // Get the secret value
65                var secretValue = secretValues["super-secret"];
66
67                context.Response.ContentType = "application/json";
68                await JsonSerializer.SerializeAsync(context.Response.Body, secretValue);
69            }
70        }
71    }
72}

Create a Dockerfile with the following contents:#

 1FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS builder
 2WORKDIR /app
 3
 4# caches restore result by copying csproj file separately
 5COPY *.csproj .
 6RUN dotnet restore
 7
 8COPY . .
 9RUN dotnet publish --output /app/ --configuration Release
10RUN sed -n 's:.*<AssemblyName>\(.*\)</AssemblyName>.*:\1:p' *.csproj > __assemblyname
11RUN if [ ! -s __assemblyname ]; then filename=$(ls *.csproj); echo ${filename%.*} > __assemblyname; fi
12
13# Stage 2
14FROM mcr.microsoft.com/dotnet/core/aspnet:3.1
15WORKDIR /app
16COPY --from=builder /app .
17
18ENV PORT 80
19EXPOSE 80
20
21ENTRYPOINT dotnet $(cat /app/__assemblyname).dll

Build the Docker image and push it to a container registry#

I’ll be pushing the container to Docker Hub:

1 docker build -t cmendibl3/dapr-k8s-secrets:1.0.0 .
2 docker push cmendibl3/dapr-k8s-secrets:1.0.0

Create manifest and deploy the application to kubernetes#

Create a deployment.yaml with the following contents (remember to replace the image with your own values):

 1---
 2# ASP.NET Core Application
 3apiVersion: apps/v1beta1
 4kind: Deployment
 5metadata:
 6  name: dapr-k8s-secrets
 7  namespace: default
 8spec:
 9  replicas: 1
10  template:
11    metadata:
12      labels:
13        app: dapr-k8s-secrets
14        aadpodidbinding: requires-vault
15      annotations:
16        dapr.io/enabled: "true"
17        dapr.io/id: "dapr-k8s-secrets"
18        dapr.io/port: "80"
19    spec:
20      containers:
21        - name: dapr-k8s-secrets
22          image: cmendibl3/dapr-k8s-secrets:1.0.0
23          ports:
24            - containerPort: 80
25          imagePullPolicy: Always
26---
27# Create a Kubernetes Secret
28apiVersion: v1
29kind: Secret
30metadata:
31  name: super-secret
32  namespace: default
33type: Opaque
34data:
35  super-secret: eW91ciBzdXBlciBzZWNyZXQK
36
37---
38# If RBAC is enabled in K8s, give the default service account access to read secrets in the default namespace
39apiVersion: rbac.authorization.k8s.io/v1
40kind: ClusterRoleBinding
41metadata:
42  name: dapr-secret-reader
43roleRef:
44  apiGroup: rbac.authorization.k8s.io
45  kind: ClusterRole
46  name: secret-reader
47subjects:
48  - kind: ServiceAccount
49    name: default
50    namespace: default

If you haven’t installed Dapr in your kuberntes cluster run:

1dapr init --kubernetes

Now deploy the application to kubernetes:

1kubectl apply -f ./deployment.yaml

Test the application#

Get the pod name and execute a port forward to test the API

1$pod = kubectl get po --selector=app=dapr-k8s-secrets -n default -o jsonpath='{.items[*].metadata.name}'
2kubectl port-forward $pod 80:80

Run the following command in other terminal:

1curl http://localhost/secret

If everything is working you should read:

1"your super secret"

Hope it helps!

Please find all code and files here, and learn more about Dapr and the Secret API here