Reading Kubernetes Secrets with Dapr and .NET Core
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
- .Net Core SDK 3.1
- Dapr CLI
- Dapr DotNet SDK
- Docker
- A working kubernetes cluster
Create an .NET Core project and add dependencies
Open the command line and type:
dotnet new web -o dapr.k8s.secretscd dapr.k8s.secretsdotnet 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:
namespace dapr.k8s.secrets
{
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Dapr.Client;
using System;
using System.Collections.Generic; public class Startup
{
// Dapr listens for requets on localhost
const string localhost = "127.0.0.1"; // Get the Dapr gRPC port
static string daprPort => Environment.GetEnvironmentVariable("DAPR_GRPC_PORT") ?? "50001"; public Startup(IConfiguration configuration)
{
Configuration = configuration;
} public IConfiguration Configuration { get; } public void ConfigureServices(IServiceCollection services)
{
// Create Dapr Client
var client = new DaprClientBuilder()
.UseEndpoint($"https://{localhost}:{daprPort}")
.Build(); // Add the DaprClient to DI.
services.AddSingleton<DaprClient>(client);
} public void Configure(IApplicationBuilder app, IWebHostEnvironment env, DaprClient client)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
} app.UseRouting(); app.UseEndpoints(endpoints =>
{
// Add the secrets route
endpoints.MapGet("secret", Secret);
}); async Task Secret(HttpContext context)
{
// Get the secret from kubernetes
var secretValues = await client.GetSecretAsync(
"kubernetes", // Name of the Dapr Secret Store
"super-secret", // Name of the k8s secret
new Dictionary<string, string>() { { "namespace", "default" } }); // Namespace where the k8s secret is deployed // Get the secret value
var secretValue = secretValues["super-secret"]; context.Response.ContentType = "application/json";
await JsonSerializer.SerializeAsync(context.Response.Body, secretValue);
}
}
}
}
Create a Dockerfile with the following contents:
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS builder
WORKDIR /app# caches restore result by copying csproj file separately
COPY *.csproj .
RUN dotnet restoreCOPY . .
RUN dotnet publish --output /app/ --configuration Release
RUN sed -n 's:.*<AssemblyName>\(.*\)</AssemblyName>.*:\1:p' *.csproj > __assemblyname
RUN if [ ! -s __assemblyname ]; then filename=$(ls *.csproj); echo ${filename%.*} > __assemblyname; fi# Stage 2
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1
WORKDIR /app
COPY --from=builder /app .ENV PORT 80
EXPOSE 80ENTRYPOINT 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:
docker build -t cmendibl3/dapr-k8s-secrets:1.0.0 .
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):
---
# ASP.NET Core Application
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: dapr-k8s-secrets
namespace: default
spec:
replicas: 1
template:
metadata:
labels:
app: dapr-k8s-secrets
aadpodidbinding: requires-vault
annotations:
dapr.io/enabled: "true"
dapr.io/id: "dapr-k8s-secrets"
dapr.io/port: "80"
spec:
containers:
- name: dapr-k8s-secrets
image: cmendibl3/dapr-k8s-secrets:1.0.0
ports:
- containerPort: 80
imagePullPolicy: Always
---
# Create a Kubernetes Secret
apiVersion: v1
kind: Secret
metadata:
name: super-secret
namespace: default
type: Opaque
data:
super-secret: eW91ciBzdXBlciBzZWNyZXQK---
# If RBAC is enabled in K8s, give the default service account access to read secrets in the default namespace
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: dapr-secret-reader
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: secret-reader
subjects:
- kind: ServiceAccount
name: default
namespace: default
If you haven’t installed Dapr in your kuberntes cluster run:
dapr init --kubernetes
Now deploy the application to kubernetes:
kubectl apply -f ./deployment.yaml
Test the application
Get the pod name and execute a port forward to test the API
$pod = kubectl get po --selector=app=dapr-k8s-secrets -n default -o jsonpath='{.items[*].metadata.name}'
kubectl port-forward $pod 80:80
Run the following command in other terminal:
curl http://localhost/secret
If everything is working you should read:
"your super secret"
Hope it helps!
Please find all code and files here, and learn more about Dapr and the Secret API here
This article was originally posted on my own site.