Securing Credentials in .NET Applications with Docker and Kubernetes
This guide covers best practices for securing credentials in .NET applications, particularly when deployed to Docker containers and Kubernetes environments.
Table of Contents
- Local Development: Removing Credentials from Settings Files
- Loading Credentials Securely in .NET
- Using Kubernetes Secrets
- Setting Up Kubernetes Deployments
- Development Workflow with KinD
- Advanced Options for Production
- Best Practices
Local Development: Removing Credentials from Settings Files
Remove sensitive credentials from your configuration files to keep them out of source control:
appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"SMTP": {
"Host": "",
"Port": 25,
"EnableSsl": true
// Credentials removed and will be loaded from secure sources
}
}appsettings.Development.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"SMTP": {
"Host": "sandbox.smtp.mailtrap.io",
"Port": 2525,
"EnableSsl": true
// Credentials removed and will be loaded from secure sources
}
}Loading Credentials Securely in .NET
Update your .NET application to load credentials from environment variables or user secrets:
// From Program.cs
var mailConfig = new Config();
builder.Configuration.GetSection("SMTP").Bind(mailConfig);
// Load credentials from environment variables (production) or user secrets (development)
mailConfig.User = Environment.GetEnvironmentVariable("SMTP__User") ??
builder.Configuration["SMTP:User"] ??
mailConfig.User;
mailConfig.Pass = Environment.GetEnvironmentVariable("SMTP__Pass") ??
builder.Configuration["SMTP:Pass"] ??
mailConfig.Pass;
builder.Services.AddSingleton(mailConfig);Setting Up .NET User Secrets
For local development, use .NET's user secrets:
# Initialize user secrets for your project
cd src/HandOver/WebApi
dotnet user-secrets init --project HandOverApi.csproj
# Add your SMTP credentials to user secrets
dotnet user-secrets set "SMTP:User" "XXX" --project HandOverApi.csproj
dotnet user-secrets set "SMTP:Pass" "XXX" --project HandOverApi.csproj
dotnet user-secrets set "SMTP:User" "00f0fa8e9fb44e" --project HandOverApi.csproj
dotnet user-secrets set "SMTP:Pass" "eae4f504920a28" --project HandOverApi.csprojUsing Kubernetes Secrets
Create a Kubernetes Secret to store your credentials:
# kubernetes/smtp-credentials.yaml
apiVersion: v1
kind: Secret
metadata:
name: smtp-credentials
type: Opaque
data:
# Base64 encoded credentials - DO NOT COMMIT THIS FILE
smtp-user: 00f0fa8e9fb44e # Replace with actual base64 encoded username
smtp-pass: eae4f504920a28 # Replace with actual base64 encoded passwordGenerate base64 encoded values for your secrets:
# Convert credentials to base64
echo -n "your-smtp-username" | base64
echo -n "your-smtp-password" | base64
# Create the secret in your Kubernetes cluster
kubectl apply -f kubernetes/smtp-credentials.yamlSetting Up Kubernetes Deployments
Configure your Kubernetes deployment to use the secrets:
# kubernetes/handover-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: handover-api
labels:
app: handover-api
spec:
replicas: 1
selector:
matchLabels:
app: handover-api
template:
metadata:
labels:
app: handover-api
spec:
containers:
- name: handover-api
image: handover-api:latest
ports:
- containerPort: 80
env:
- name: SMTP__User
valueFrom:
secretKeyRef:
name: smtp-credentials
key: smtp-user
- name: SMTP__Pass
valueFrom:
secretKeyRef:
name: smtp-credentials
key: smtp-passDevelopment Workflow with KinD
For local Kubernetes development, use KinD (Kubernetes in Docker):
# Create a KinD cluster
kind create cluster --name handover-dev
# Apply your secrets
kubectl apply -f kubernetes/smtp-credentials.yaml
# Build and load your Docker image into KinD
docker build -t handover-api:latest .
kind load docker-image handover-api:latest --name handover-dev
# Deploy your application
kubectl apply -f kubernetes/handover-deployment.yamlAdvanced Options for Production
Sealed Secrets for GitOps
For GitOps workflows, use Sealed Secrets to securely commit encrypted secrets to Git:
# Install the Sealed Secrets controller
kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.19.5/controller.yaml
# Install kubeseal CLI
wget https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.19.5/kubeseal-0.19.5-linux-amd64.tar.gz
tar -xvzf kubeseal-0.19.5-linux-amd64.tar.gz
sudo install -m 755 kubeseal /usr/local/bin/kubeseal
# Create a sealed secret
kubectl create secret generic smtp-credentials \
--from-literal=smtp-user=your-username \
--from-literal=smtp-pass=your-password \
--dry-run=client -o yaml | \
kubeseal --format yaml > kubernetes/sealed-smtp-credentials.yaml
# Apply the sealed secret (this is safe to commit to git)
kubectl apply -f kubernetes/sealed-smtp-credentials.yamlHashiCorp Vault Integration
For enterprise environments, use HashiCorp Vault:
# Install Vault using Helm
helm repo add hashicorp https://helm.releases.hashicorp.com
helm install vault hashicorp/vault
# Example Pod using Vault Agent Injector
kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: handover-api
labels:
app: handover-api
spec:
replicas: 1
selector:
matchLabels:
app: handover-api
template:
metadata:
labels:
app: handover-api
annotations:
vault.hashicorp.com/agent-inject: 'true'
vault.hashicorp.com/role: 'handover-api'
vault.hashicorp.com/agent-inject-secret-smtp.env: 'secret/data/smtp'
vault.hashicorp.com/agent-inject-template-smtp.env: |
{{- with secret "secret/data/smtp" -}}
export SMTP__User="{{ .Data.data.user }}"
export SMTP__Pass="{{ .Data.data.pass }}"
{{- end -}}
spec:
containers:
- name: handover-api
image: handover-api:latest
EOFBest Practices
Never store secrets in code or images
- Keep credentials out of source control
- Don't bake secrets into Docker images
Use dedicated secret management tools
- Development: .NET User Secrets
- Production: Kubernetes Secrets, Vault, or cloud providers' secret management services
Limit access to secrets
- Use Kubernetes RBAC to control who can view and manage secrets
- Implement the principle of least privilege
Rotate credentials regularly
- Set up a process for credential rotation
- Automate rotation where possible
Encrypt secrets at rest
- Ensure your Kubernetes etcd is encrypted
- Use additional encryption tools when needed
Audit secret access
- Enable audit logging for secret access
- Monitor for unauthorized access attempts
Consider managed services
- For production, consider AWS Secrets Manager, Azure Key Vault, or Google Secret Manager
Separate development and production secrets
- Use different secrets for different environments
- Never use production credentials in development