Fix resources isues 5

This commit is contained in:
2026-01-22 11:15:11 +01:00
parent 6505044f24
commit 0183f3ab72
20 changed files with 399 additions and 1193 deletions

View File

@@ -2,7 +2,7 @@
## Executive Summary
This document outlines the recommended architecture for deploying Mailu email services across development and production environments for the Bakery-IA project. The solution addresses DNSSEC validation requirements while maintaining consistency across different Kubernetes platforms.
This document outlines the recommended architecture for deploying Mailu email services across development and production environments for the Bakery-IA project. The solution addresses DNSSEC validation requirements using CoreDNS with DNS-over-TLS while maintaining consistency across different Kubernetes platforms.
## Environment Overview
@@ -25,124 +25,76 @@ This document outlines the recommended architecture for deploying Mailu email se
## Architectural Solution
### Unified DNS Resolution Strategy
### DNS Resolution Strategy
**Recommended Approach**: Deploy Unbound as a dedicated DNSSEC-validating resolver pod in both environments
**Approach**: Use CoreDNS with DNS-over-TLS to Cloudflare (1.1.1.1) for DNSSEC validation
#### Benefits:
- ✅ Leverages existing Kubernetes DNS infrastructure
- ✅ No additional pods required (uses CoreDNS already in cluster)
- ✅ DNSSEC validation provided by Cloudflare's DNS-over-TLS
- ✅ Consistent behavior across dev and prod
- ✅ Meets Mailu's DNSSEC requirements
-Privacy-preserving (no external DNS queries)
- ✅ Avoids rate-limiting from public DNS providers
- ✅ Full control over DNS resolution
-Simple and reliable
### Implementation Components
#### 1. Unbound Deployment Manifest
#### 1. CoreDNS Configuration with DNS-over-TLS
```yaml
# unbound.yaml - Cross-environment compatible
apiVersion: apps/v1
kind: Deployment
metadata:
name: unbound-resolver
namespace: mailu
labels:
app: unbound
component: dns
spec:
replicas: 1 # Scale to 2+ in production with anti-affinity
selector:
matchLabels:
app: unbound
template:
metadata:
labels:
app: unbound
component: dns
spec:
containers:
- name: unbound
image: mvance/unbound:latest
ports:
- containerPort: 53
name: dns-udp
protocol: UDP
- containerPort: 53
name: dns-tcp
protocol: TCP
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "300m"
memory: "384Mi"
readinessProbe:
exec:
command: ["drill", "@127.0.0.1", "-p", "53", "+dnssec", "example.org"]
initialDelaySeconds: 10
periodSeconds: 30
securityContext:
capabilities:
add: ["NET_BIND_SERVICE"]
---
apiVersion: v1
kind: Service
metadata:
name: unbound-dns
namespace: mailu
spec:
selector:
app: unbound
ports:
- name: dns-udp
port: 53
targetPort: 53
protocol: UDP
- name: dns-tcp
port: 53
targetPort: 53
protocol: TCP
# CoreDNS Corefile configuration for DNSSEC via DNS-over-TLS
.:53 {
errors
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
prometheus :9153
forward . tls://1.1.1.1 tls://1.0.0.1 {
tls_servername cloudflare-dns.com
health_check 5s
}
cache 30 {
disable success cluster.local
disable denial cluster.local
}
loop
reload
loadbalance
}
```
#### 2. Mailu Configuration (values.yaml)
```yaml
# Production-tuned Mailu configuration
dnsPolicy: None
dnsConfig:
nameservers:
- "10.152.183.x" # Replace with actual unbound service IP
global:
# Using Kubernetes CoreDNS for DNS resolution
# CoreDNS is configured with DNS-over-TLS (Cloudflare) for DNSSEC validation
custom_dns_servers: "10.152.183.10" # MicroK8s CoreDNS IP (adjust for your cluster)
# Component-specific DNS configuration
# DNS configuration - use Kubernetes DNS (ClusterFirst)
# CoreDNS provides DNSSEC validation via DNS-over-TLS to Cloudflare
admin:
dnsPolicy: None
dnsConfig:
nameservers:
- "10.152.183.x"
dnsPolicy: "ClusterFirst"
rspamd:
dnsPolicy: None
dnsConfig:
nameservers:
- "10.152.183.x"
dnsPolicy: "ClusterFirst"
# Environment-specific configurations
persistence:
enabled: true
# Development: use default storage class
# Production: use microk8s-hostpath or longhorn
storageClass: "standard"
storageClass: "" # Use cluster default
replicas: 1 # Increase in production as needed
# Security settings
secretKey: "generate-strong-key-here"
# Ingress configuration
# Use existing Bakery-IA ingress controller
```
### Environment-Specific Adaptations
@@ -157,23 +109,21 @@ secretKey: "generate-strong-key-here"
**Deployment:**
```bash
# Apply unbound
kubectl apply -f unbound.yaml
# Get unbound service IP
UNBOUND_IP=$(kubectl get svc unbound-dns -n mailu -o jsonpath='{.spec.clusterIP}')
# Get CoreDNS service IP
COREDNS_IP=$(kubectl get svc kube-dns -n kube-system -o jsonpath='{.spec.clusterIP}')
# Deploy Mailu with dev-specific values
helm upgrade --install mailu mailu/mailu \
--namespace mailu \
-f values-dev.yaml \
--set dnsConfig.nameservers[0]=$UNBOUND_IP
--namespace bakery-ia \
-f infrastructure/platform/mail/mailu-helm/values.yaml \
-f infrastructure/platform/mail/mailu-helm/dev/values.yaml \
--set global.custom_dns_servers=$COREDNS_IP
```
#### Production (MicroK8s/Ubuntu)
**Enhancements:**
- Use Longhorn or OpenEBS for storage
- Use microk8s-hostpath for storage
- Enable monitoring and logging
- Configure proper ingress with TLS
- Set up backup solutions
@@ -181,19 +131,17 @@ helm upgrade --install mailu mailu/mailu \
**Deployment:**
```bash
# Enable required MicroK8s addons
microk8s enable dns storage ingress metallb
microk8s enable dns storage ingress
# Apply unbound
kubectl apply -f unbound.yaml
# Get unbound service IP
UNBOUND_IP=$(kubectl get svc unbound-dns -n mailu -o jsonpath='{.spec.clusterIP}')
# Get CoreDNS service IP
COREDNS_IP=$(kubectl get svc kube-dns -n kube-system -o jsonpath='{.spec.clusterIP}')
# Deploy Mailu with production values
helm upgrade --install mailu mailu/mailu \
--namespace mailu \
-f values-prod.yaml \
--set dnsConfig.nameservers[0]=$UNBOUND_IP
--namespace bakery-ia \
-f infrastructure/platform/mail/mailu-helm/values.yaml \
-f infrastructure/platform/mail/mailu-helm/prod/values.yaml \
--set global.custom_dns_servers=$COREDNS_IP
```
## Verification Procedures
@@ -201,52 +149,39 @@ helm upgrade --install mailu mailu/mailu \
### DNSSEC Validation Test
```bash
# From within a Mailu pod
kubectl exec -it -n mailu deploy/mailu-admin -- bash
# Test DNS resolution from within a Mailu pod
kubectl exec -it -n bakery-ia deploy/mailu-admin -- bash
# Test DNSSEC validation
dig @unbound-dns +short +dnssec +adflag example.org A
# Test DNSSEC validation (via CoreDNS -> Cloudflare DNS-over-TLS)
dig +short +dnssec +adflag example.org A
# Should show AD flag in response
# Should show AD flag in response indicating DNSSEC validation
```
### Service Health Checks
```bash
# Check unbound service
kubectl get pods -n mailu -l app=unbound
kubectl logs -n mailu -l app=unbound
# Check CoreDNS is running
kubectl get pods -n kube-system -l k8s-app=kube-dns
# Check Mailu components
kubectl get pods -n mailu
kubectl logs -n mailu -l app.kubernetes.io/name=mailu
kubectl get pods -n bakery-ia | grep mailu
kubectl logs -n bakery-ia -l app.kubernetes.io/name=mailu
```
## Monitoring and Maintenance
### Production Monitoring Setup
```yaml
# Example monitoring configuration for production
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: unbound-monitor
namespace: mailu
spec:
selector:
matchLabels:
app: unbound
endpoints:
- port: dns-tcp
interval: 30s
path: /metrics
```
CoreDNS exposes Prometheus metrics on port 9153 by default. Monitor:
- DNS query latency
- DNS query success/failure rates
- DNS cache hit ratio
### Backup Strategy
**Production:**
- Daily Velero backups of Mailu namespace
- Daily Velero backups of bakery-ia namespace
- Weekly database dumps
- Monthly full cluster snapshots
@@ -258,16 +193,21 @@ spec:
### Common Issues and Solutions
**Issue: DNSSEC validation failures**
- Verify unbound pod logs
- Check network policies
- Test DNS resolution from within pods
**Issue: DNS resolution failures**
- Verify CoreDNS pods are running
- Check CoreDNS logs: `kubectl logs -n kube-system -l k8s-app=kube-dns`
- Test DNS resolution: `kubectl run -it --rm dns-test --image=busybox -- nslookup google.com`
**Issue: Mailu pods failing to start**
- Confirm DNS configuration in values.yaml
- Verify unbound service is reachable
- Verify CoreDNS service is reachable
- Check resource availability
**Issue: DNSSEC validation errors**
- Ensure CoreDNS is configured with DNS-over-TLS
- Test with: `dig +dnssec example.org`
- Verify Cloudflare DNS is reachable
**Issue: Performance problems**
- Monitor CPU/memory usage
- Adjust resource limits
@@ -327,12 +267,12 @@ spec:
## Conclusion
This architecture provides a robust, consistent solution for deploying Mailu across development and production environments. By using Unbound as a dedicated DNSSEC-validating resolver, we ensure compliance with Mailu's requirements while maintaining flexibility and reliability across different Kubernetes platforms.
This architecture provides a robust, consistent solution for deploying Mailu across development and production environments. By using CoreDNS with DNS-over-TLS to Cloudflare, we ensure compliance with Mailu's DNSSEC requirements while maintaining simplicity and reliability.
The solution is designed to be:
- **Simple**: Uses existing Kubernetes DNS infrastructure
- **Consistent**: Same core architecture across environments
- **Reliable**: Production-grade availability and monitoring
- **Efficient**: Optimized resource usage
- **Maintainable**: Clear documentation and troubleshooting guides
- **Reliable**: Production-grade availability
- **Efficient**: No additional pods required for DNS
This approach aligns with the Bakery-IA project's requirements for a secure, reliable email infrastructure that can be consistently deployed across different environments.

View File

@@ -71,7 +71,7 @@ A complete multi-tenant SaaS platform consisting of:
│ PostgreSQL (18 DBs) │ Redis │ RabbitMQ │ MinIO │
├─────────────────────────────────────────────────────────────────────────────┤
│ LAYER 2: NETWORK & SECURITY │
Unbound DNS │ CoreDNS │ Ingress Controller │ Cert-Manager │ TLS
CoreDNS (DNS-over-TLS) │ Ingress Controller │ Cert-Manager │ TLS │
├─────────────────────────────────────────────────────────────────────────────┤
│ LAYER 1: FOUNDATION │
│ Namespaces │ Storage Classes │ RBAC │ ConfigMaps │ Secrets │
@@ -1045,107 +1045,84 @@ kubectl exec -n bakery-ia deployment/gateway -- curl -s http://localhost:8000/he
## Phase 7: Deploy Optional Services
### Step 7.1: Deploy Unbound DNS (Required for Mailu)
### Step 7.1: Configure CoreDNS with DNS-over-TLS for DNSSEC
> **Why Unbound?** Mailu requires DNSSEC validation for email security (DKIM/SPF/DMARC via rspamd).
> CoreDNS does NOT support DNSSEC natively, so Unbound provides this capability.
> **DNS Architecture:** CoreDNS is configured to use DNS-over-TLS with Cloudflare (1.1.1.1) for DNSSEC validation.
> This provides DNSSEC support for Mailu without requiring additional DNS pods.
```bash
# Clean up any stuck Unbound deployments from previous attempts
kubectl delete deployment -n bakery-ia -l app.kubernetes.io/name=unbound --ignore-not-found
# Check if CoreDNS is already configured with DNS-over-TLS
kubectl get configmap coredns -n kube-system -o jsonpath='{.data.Corefile}' | grep -o 'tls://1.1.1.1' || echo "Not configured"
# Deploy Unbound DNS resolver with minimal resources
# Note: prod/values.yaml uses 50m CPU, 64Mi memory - very lightweight
helm upgrade --install unbound infrastructure/platform/networking/dns/unbound-helm \
-n bakery-ia \
-f infrastructure/platform/networking/dns/unbound-helm/values.yaml \
-f infrastructure/platform/networking/dns/unbound-helm/prod/values.yaml \
--timeout 5m \
--wait
# If not configured, update CoreDNS to use DNS-over-TLS with Cloudflare
cat > /tmp/coredns-corefile.yaml << 'EOF'
apiVersion: v1
kind: ConfigMap
metadata:
name: coredns
namespace: kube-system
data:
Corefile: |
.:53 {
errors
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
prometheus :9153
forward . tls://1.1.1.1 tls://1.0.0.1 {
tls_servername cloudflare-dns.com
health_check 5s
}
cache 30 {
disable success cluster.local
disable denial cluster.local
}
loop
reload
loadbalance
}
EOF
# Verify Unbound pod is running
kubectl get pods -n bakery-ia -l app.kubernetes.io/name=unbound
kubectl apply -f /tmp/coredns-corefile.yaml
# Restart CoreDNS to apply changes
kubectl rollout restart deployment coredns -n kube-system
kubectl rollout status deployment coredns -n kube-system --timeout=60s
# Verify CoreDNS is running
kubectl get pods -n kube-system -l k8s-app=kube-dns
# Expected: 1/1 Running
# Get Unbound service IP (will be used in subsequent steps)
UNBOUND_IP=$(kubectl get svc unbound-dns -n bakery-ia -o jsonpath='{.spec.clusterIP}')
echo "Unbound DNS IP: $UNBOUND_IP"
# Save this IP - you'll need it for Step 7.2 and 7.3
# Get CoreDNS service IP (will be used for Mailu)
COREDNS_IP=$(kubectl get svc kube-dns -n kube-system -o jsonpath='{.spec.clusterIP}')
echo "CoreDNS IP: $COREDNS_IP"
# Save this IP - you'll need it for Step 7.2
# Test Unbound is working (from inside the cluster)
kubectl run -it --rm dns-test --image=busybox --restart=Never -- \
nslookup google.com $UNBOUND_IP
# Test DNS resolution is working
kubectl run -it --rm dns-test --image=busybox --restart=Never -- nslookup google.com
# Expected: Should resolve google.com successfully
```
**Troubleshooting Unbound:**
**Troubleshooting CoreDNS:**
```bash
# If pod is Pending, check resources
kubectl describe pod -n bakery-ia -l app.kubernetes.io/name=unbound | grep -A 5 Events
# Check CoreDNS logs
kubectl logs -n kube-system -l k8s-app=kube-dns
# Check node resource availability
kubectl describe node | grep -A 10 "Allocated resources"
# Check CoreDNS configuration
kubectl get configmap coredns -n kube-system -o yaml
# If resources are exhausted, scale down non-critical services temporarily
kubectl scale deployment signoz-frontend -n bakery-ia --replicas=0 --ignore-not-found
# Verify DNS-over-TLS is working
kubectl run -it --rm dns-test --image=busybox --restart=Never -- nslookup cloudflare.com
```
### Step 7.2: Configure CoreDNS (Choose ONE Option)
> **Architecture Decision:** You have two options for DNS configuration.
> Choose based on your cluster size and requirements.
#### Option A: Mailu-Only DNSSEC (Recommended for Single-Node)
Only Mailu pods use Unbound for DNSSEC. CoreDNS uses public DNS for everything else.
This is simpler and avoids making Unbound a single point of failure for the entire cluster.
```bash
# Ensure CoreDNS uses public DNS (8.8.8.8, 1.1.1.1)
# This is likely already the default, but verify:
kubectl get configmap coredns -n kube-system -o yaml | grep forward
# If it shows forwarding to Unbound IP, restore to public DNS:
kubectl patch configmap coredns -n kube-system --type merge -p '{
"data": {
"Corefile": ".:53 {\n errors\n health {\n lameduck 5s\n }\n ready\n kubernetes cluster.local in-addr.arpa ip6.arpa {\n pods insecure\n fallthrough in-addr.arpa ip6.arpa\n ttl 30\n }\n prometheus :9153\n forward . 8.8.8.8 1.1.1.1 {\n max_concurrent 1000\n }\n cache 30\n loop\n reload\n loadbalance\n}\n"
}
}'
kubectl rollout restart deployment coredns -n kube-system
kubectl rollout status deployment coredns -n kube-system --timeout=60s
```
#### Option B: Cluster-Wide DNSSEC (For Multi-Node HA)
All cluster DNS queries go through Unbound. Provides DNSSEC for all pods.
Only use this if you have multiple Unbound replicas for high availability.
```bash
# Get Unbound IP
UNBOUND_IP=$(kubectl get svc unbound-dns -n bakery-ia -o jsonpath='{.spec.clusterIP}')
# Patch CoreDNS to forward ALL external queries to Unbound
kubectl patch configmap coredns -n kube-system --type merge -p "{
\"data\": {
\"Corefile\": \".:53 {\\n errors\\n health {\\n lameduck 5s\\n }\\n ready\\n kubernetes cluster.local in-addr.arpa ip6.arpa {\\n pods insecure\\n fallthrough in-addr.arpa ip6.arpa\\n ttl 30\\n }\\n prometheus :9153\\n forward . $UNBOUND_IP {\\n max_concurrent 1000\\n }\\n cache 30\\n loop\\n reload\\n loadbalance\\n}\\n\"
}
}"
kubectl rollout restart deployment coredns -n kube-system
kubectl rollout status deployment coredns -n kube-system --timeout=60s
```
**Verify DNS is working:**
```bash
# Test DNS resolution from a pod
kubectl run -it --rm dns-test --image=busybox --restart=Never -- nslookup google.com
# Expected: Should resolve successfully
```
### Step 7.3: Deploy Mailu Email Server
### Step 7.2: Deploy Mailu Email Server
```bash
# Add Mailu Helm repository
@@ -1156,18 +1133,18 @@ helm repo update
kubectl apply -f infrastructure/platform/mail/mailu-helm/configs/mailu-admin-credentials-secret.yaml -n bakery-ia
kubectl apply -f infrastructure/platform/mail/mailu-helm/configs/mailu-certificates-secret.yaml -n bakery-ia
# Get Unbound DNS IP dynamically
UNBOUND_IP=$(kubectl get svc unbound-dns -n bakery-ia -o jsonpath='{.spec.clusterIP}')
echo "Using Unbound DNS IP: $UNBOUND_IP"
# Get CoreDNS service IP dynamically
COREDNS_IP=$(kubectl get svc kube-dns -n kube-system -o jsonpath='{.spec.clusterIP}')
echo "Using CoreDNS IP: $COREDNS_IP"
# Install Mailu with production configuration
# The --set flag dynamically passes the Unbound IP for DNSSEC validation
# The --set flag dynamically passes the CoreDNS IP for DNS resolution
# DNSSEC validation is provided by CoreDNS via DNS-over-TLS to Cloudflare
helm upgrade --install mailu mailu/mailu \
-n bakery-ia \
-f infrastructure/platform/mail/mailu-helm/values.yaml \
-f infrastructure/platform/mail/mailu-helm/prod/values.yaml \
--set global.custom_dns_servers="$UNBOUND_IP" \
--set admin.dnsConfig.nameservers[0]="$UNBOUND_IP" \
--set global.custom_dns_servers="$COREDNS_IP" \
--timeout 10m
# Wait for Mailu to be ready (may take 5-10 minutes)

154
Tiltfile
View File

@@ -728,100 +728,20 @@ k8s_resource('rabbitmq', resource_deps=['security-setup'], labels=['01-infrastru
k8s_resource('minio', resource_deps=['security-setup'], labels=['01-infrastructure'])
k8s_resource('minio-bucket-init', resource_deps=['minio'], labels=['01-infrastructure'])
# Unbound DNSSEC Resolver - Infrastructure component for Mailu DNS validation
# CoreDNS DNSSEC Configuration - Infrastructure component for Mailu DNS validation
local_resource(
'unbound-helm',
'coredns-dnssec',
cmd='''
echo "Deploying Unbound DNS resolver via Helm..."
echo "Configuring CoreDNS with DNS-over-TLS for DNSSEC validation..."
echo ""
# Check if Unbound is already deployed
if helm list -n bakery-ia | grep -q unbound; then
echo "Unbound already deployed, checking status..."
helm status unbound -n bakery-ia
else
echo "Installing Unbound..."
# Check if CoreDNS is already configured with DNS-over-TLS
CURRENT_FORWARD=$(kubectl get configmap coredns -n kube-system -o jsonpath='{.data.Corefile}' 2>/dev/null | grep -o 'tls://1.1.1.1' || echo "")
# Determine environment (dev or prod) based on context
ENVIRONMENT="dev"
if [[ "$(kubectl config current-context)" == *"prod"* ]]; then
ENVIRONMENT="prod"
fi
if [ -z "$CURRENT_FORWARD" ]; then
echo "Updating CoreDNS to use DNS-over-TLS with Cloudflare..."
echo "Environment detected: $ENVIRONMENT"
# Install Unbound with appropriate values
if [ "$ENVIRONMENT" = "dev" ]; then
helm upgrade --install unbound infrastructure/platform/networking/dns/unbound-helm \
-n bakery-ia \
--create-namespace \
-f infrastructure/platform/networking/dns/unbound-helm/values.yaml \
-f infrastructure/platform/networking/dns/unbound-helm/dev/values.yaml \
--timeout 5m \
--wait
else
helm upgrade --install unbound infrastructure/platform/networking/dns/unbound-helm \
-n bakery-ia \
--create-namespace \
-f infrastructure/platform/networking/dns/unbound-helm/values.yaml \
-f infrastructure/platform/networking/dns/unbound-helm/prod/values.yaml \
--timeout 5m \
--wait
fi
echo ""
echo "Unbound deployment completed"
fi
echo ""
echo "Unbound DNS Service Information:"
echo " Service Name: unbound-dns.bakery-ia.svc.cluster.local"
echo " Ports: UDP/TCP 53"
echo " Used by: Mailu for DNS validation"
echo ""
echo "To check pod status: kubectl get pods -n bakery-ia | grep unbound"
''',
resource_deps=['security-setup'],
labels=['01-infrastructure'],
auto_init=True # Auto-deploy with Tilt startup
)
# Mail Infrastructure (Mailu) - Manual trigger for Helm deployment
local_resource(
'mailu-helm',
cmd='''
echo "Deploying Mailu via Helm..."
echo ""
# =====================================================
# Step 1: Ensure Unbound is deployed and get its IP
# =====================================================
echo "Checking Unbound DNS resolver..."
if ! kubectl get svc unbound-dns -n bakery-ia &>/dev/null; then
echo "ERROR: Unbound DNS service not found!"
echo "Please deploy Unbound first by triggering 'unbound-helm' resource"
exit 1
fi
UNBOUND_IP=$(kubectl get svc unbound-dns -n bakery-ia -o jsonpath='{.spec.clusterIP}')
echo "Unbound DNS service IP: $UNBOUND_IP"
# =====================================================
# Step 2: Configure CoreDNS to forward to Unbound
# =====================================================
echo ""
echo "Configuring CoreDNS to forward external queries to Unbound for DNSSEC validation..."
# Check current CoreDNS forward configuration
CURRENT_FORWARD=$(kubectl get configmap coredns -n kube-system -o jsonpath='{.data.Corefile}' | grep -o 'forward \\. [0-9.]*' | awk '{print $3}')
if [ "$CURRENT_FORWARD" != "$UNBOUND_IP" ]; then
echo "Updating CoreDNS to forward to Unbound ($UNBOUND_IP)..."
# Change to project root to ensure correct file paths
cd /Users/urtzialfaro/Documents/bakery-ia
# Create a temporary Corefile with the forwarding configuration
# Create a temporary Corefile with the DNS-over-TLS configuration
TEMP_COREFILE=$(mktemp)
cat > "$TEMP_COREFILE" << EOF
.:53 {
@@ -836,8 +756,9 @@ local_resource(
ttl 30
}
prometheus :9153
forward . $UNBOUND_IP {
max_concurrent 1000
forward . tls://1.1.1.1 tls://1.0.0.1 {
tls_servername cloudflare-dns.com
health_check 5s
}
cache 30 {
disable success cluster.local
@@ -871,13 +792,44 @@ EOF
kubectl rollout restart deployment coredns -n kube-system
echo "Waiting for CoreDNS to restart..."
kubectl rollout status deployment coredns -n kube-system --timeout=60s
echo "CoreDNS configured successfully"
echo "CoreDNS configured successfully with DNS-over-TLS"
else
echo "CoreDNS already configured to forward to Unbound"
echo "CoreDNS already configured with DNS-over-TLS"
fi
# Get CoreDNS service IP
COREDNS_IP=$(kubectl get svc kube-dns -n kube-system -o jsonpath='{.spec.clusterIP}')
echo ""
echo "CoreDNS DNSSEC Configuration:"
echo " CoreDNS IP: $COREDNS_IP"
echo " Upstream: Cloudflare DNS-over-TLS (1.1.1.1, 1.0.0.1)"
echo " DNSSEC: Validated by Cloudflare"
echo " Used by: Mailu for DNS validation"
echo ""
echo "To check CoreDNS status: kubectl get pods -n kube-system -l k8s-app=kube-dns"
''',
resource_deps=['security-setup'],
labels=['01-infrastructure'],
auto_init=True # Auto-deploy with Tilt startup
)
# Mail Infrastructure (Mailu) - Manual trigger for Helm deployment
local_resource(
'mailu-helm',
cmd='''
echo "Deploying Mailu via Helm..."
echo ""
# =====================================================
# Step 3: Create self-signed TLS certificate for Mailu Front
# Step 1: Get CoreDNS service IP
# =====================================================
echo "Getting CoreDNS service IP..."
COREDNS_IP=$(kubectl get svc kube-dns -n kube-system -o jsonpath='{.spec.clusterIP}')
echo "CoreDNS service IP: $COREDNS_IP"
# =====================================================
# Step 2: Create self-signed TLS certificate for Mailu Front
# =====================================================
echo ""
echo "Checking Mailu TLS certificates..."
@@ -905,7 +857,7 @@ EOF
fi
# =====================================================
# Step 4: Deploy Mailu via Helm
# Step 3: Deploy Mailu via Helm
# =====================================================
echo ""
@@ -938,6 +890,7 @@ EOF
--create-namespace \
-f infrastructure/platform/mail/mailu-helm/values.yaml \
-f infrastructure/platform/mail/mailu-helm/dev/values.yaml \
--set global.custom_dns_servers="$COREDNS_IP" \
--timeout 10m
else
helm upgrade --install mailu mailu/mailu \
@@ -945,6 +898,7 @@ EOF
--create-namespace \
-f infrastructure/platform/mail/mailu-helm/values.yaml \
-f infrastructure/platform/mail/mailu-helm/prod/values.yaml \
--set global.custom_dns_servers="$COREDNS_IP" \
--timeout 10m
fi
@@ -953,7 +907,7 @@ EOF
fi
# =====================================================
# Step 5: Apply Mailu Ingress
# Step 4: Apply Mailu Ingress
# =====================================================
echo ""
echo "Applying Mailu ingress configuration..."
@@ -962,7 +916,7 @@ EOF
echo "Mailu ingress applied for mail.bakery-ia.dev"
# =====================================================
# Step 6: Wait for pods and show status
# Step 5: Wait for pods and show status
# =====================================================
echo ""
echo "Waiting for Mailu pods to be ready..."
@@ -975,16 +929,20 @@ EOF
echo ""
echo "Mailu Access Information:"
echo " Admin Panel: https://mail.bakery-ia.dev/admin"
echo " Webmail: https://mail.bakery-ia.ldev/webmail"
echo " Webmail: https://mail.bakery-ia.dev/webmail"
echo " SMTP: mail.bakery-ia.dev:587 (STARTTLS)"
echo " IMAP: mail.bakery-ia.dev:993 (SSL/TLS)"
echo ""
echo "DNS Configuration:"
echo " CoreDNS IP: $COREDNS_IP"
echo " DNSSEC: Provided via DNS-over-TLS (Cloudflare)"
echo ""
echo "To create admin user:"
echo " Admin user created automatically via initialAccount feature in Helm values"
echo ""
echo "To check pod status: kubectl get pods -n bakery-ia | grep mailu"
''',
resource_deps=['unbound-helm'], # Ensure Unbound is deployed first
resource_deps=['coredns-dnssec'], # Ensure CoreDNS DNSSEC is configured first
labels=['01-infrastructure'],
auto_init=False, # Manual trigger only
)

View File

@@ -91,7 +91,7 @@ The Bakery-IA platform is organized into distinct infrastructure layers, each wi
│ PostgreSQL (18 DBs) │ Redis │ RabbitMQ │ MinIO │
├─────────────────────────────────────────────────────────────────────────────┤
│ LAYER 2: NETWORK & SECURITY │
Unbound DNS │ CoreDNS │ Ingress Controller │ Cert-Manager │ TLS
CoreDNS (DNS-over-TLS) │ Ingress Controller │ Cert-Manager │ TLS │
├─────────────────────────────────────────────────────────────────────────────┤
│ LAYER 1: FOUNDATION │
│ Namespaces │ Storage Classes │ RBAC │ ConfigMaps │ Secrets │
@@ -112,11 +112,9 @@ Components must be deployed in a specific order due to dependencies:
3. TLS Certificates (internal + ingress)
4. Unbound DNS Resolver (required for Mailu DNSSEC)
4. CoreDNS Configuration (DNS-over-TLS for DNSSEC)
5. CoreDNS Configuration (forward to Unbound)
6. Ingress Controller & Resources
5. Ingress Controller & Resources
7. Data Layer: PostgreSQL, Redis, RabbitMQ, MinIO
@@ -146,7 +144,6 @@ Components must be deployed in a specific order due to dependencies:
| **Redis** | Caching & sessions | Yes | bakery-ia |
| **RabbitMQ** | Message broker | Yes | bakery-ia |
| **MinIO** | Object storage (ML models) | Yes | bakery-ia |
| **Unbound DNS** | DNSSEC resolver | For Mailu | bakery-ia |
| **Mailu** | Self-hosted email server | Optional | bakery-ia |
| **Nominatim** | Geocoding service | Optional | bakery-ia |
| **Gitea** | Git server + container registry | Optional | gitea |
@@ -945,9 +942,8 @@ SMTP_PORT: 587
#### Prerequisites
Before deploying Mailu, ensure:
1. **Unbound DNS is deployed** (for DNSSEC validation)
2. **CoreDNS is configured** to forward to Unbound
3. **DNS records are configured** for your domain
1. **CoreDNS is configured** with DNS-over-TLS for DNSSEC validation
2. **DNS records are configured** for your domain
#### Step 1: Configure DNS Records
@@ -963,55 +959,64 @@ TXT _dmarc v=DMARC1; p=reject; rua=... Auto
**DKIM record** will be generated after Mailu is running - you'll add it later.
#### Step 2: Deploy Unbound DNS Resolver
#### Step 2: Configure CoreDNS for DNSSEC (DNS-over-TLS)
Unbound provides DNSSEC validation required by Mailu for email authentication.
Mailu requires DNSSEC validation. Configure CoreDNS to use DNS-over-TLS with Cloudflare:
```bash
# On VPS - Deploy Unbound via Helm
helm upgrade --install unbound infrastructure/platform/networking/dns/unbound-helm \
-n bakery-ia \
--create-namespace \
-f infrastructure/platform/networking/dns/unbound-helm/values.yaml \
-f infrastructure/platform/networking/dns/unbound-helm/prod/values.yaml \
--timeout 5m \
--wait
# Check if CoreDNS is already configured with DNS-over-TLS
kubectl get configmap coredns -n kube-system -o jsonpath='{.data.Corefile}' | grep -o 'tls://1.1.1.1' || echo "Not configured"
# Verify Unbound is running
kubectl get pods -n bakery-ia | grep unbound
# Should show: unbound-xxx 1/1 Running
# If not configured, update CoreDNS
cat > /tmp/coredns-corefile.yaml << 'EOF'
apiVersion: v1
kind: ConfigMap
metadata:
name: coredns
namespace: kube-system
data:
Corefile: |
.:53 {
errors
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
prometheus :9153
forward . tls://1.1.1.1 tls://1.0.0.1 {
tls_servername cloudflare-dns.com
health_check 5s
}
cache 30 {
disable success cluster.local
disable denial cluster.local
}
loop
reload
loadbalance
}
EOF
# Get Unbound service IP (needed for CoreDNS configuration)
UNBOUND_IP=$(kubectl get svc unbound-dns -n bakery-ia -o jsonpath='{.spec.clusterIP}')
echo "Unbound DNS IP: $UNBOUND_IP"
```
#### Step 3: Configure CoreDNS for DNSSEC
Mailu requires DNSSEC validation. Configure CoreDNS to forward external queries to Unbound:
```bash
# Get the Unbound service IP
UNBOUND_IP=$(kubectl get svc unbound-dns -n bakery-ia -o jsonpath='{.spec.clusterIP}')
# Patch CoreDNS to forward to Unbound
kubectl patch configmap coredns -n kube-system --type merge -p "{
\"data\": {
\"Corefile\": \".:53 {\\n errors\\n health {\\n lameduck 5s\\n }\\n ready\\n kubernetes cluster.local in-addr.arpa ip6.arpa {\\n pods insecure\\n fallthrough in-addr.arpa ip6.arpa\\n ttl 30\\n }\\n prometheus :9153\\n forward . $UNBOUND_IP {\\n max_concurrent 1000\\n }\\n cache 30 {\\n disable success cluster.local\\n disable denial cluster.local\\n }\\n loop\\n reload\\n loadbalance\\n}\\n\"
}
}"
kubectl apply -f /tmp/coredns-corefile.yaml
# Restart CoreDNS to apply changes
kubectl rollout restart deployment coredns -n kube-system
kubectl rollout status deployment coredns -n kube-system --timeout=60s
# Verify DNSSEC is working
kubectl run -it --rm debug --image=alpine --restart=Never -- \
sh -c "apk add drill && drill -D google.com"
# Should show: ;; flags: ... ad ... (ad = authenticated data = DNSSEC valid)
# Get CoreDNS service IP (needed for Mailu configuration)
COREDNS_IP=$(kubectl get svc kube-dns -n kube-system -o jsonpath='{.spec.clusterIP}')
echo "CoreDNS IP: $COREDNS_IP"
# Verify DNS resolution is working
kubectl run -it --rm dns-test --image=busybox --restart=Never -- nslookup google.com
```
#### Step 4: Create TLS Certificate Secret
#### Step 3: Create TLS Certificate Secret
Mailu Front pod requires a TLS certificate:
@@ -1036,7 +1041,7 @@ rm -rf "$TEMP_DIR"
kubectl get secret mailu-certificates -n bakery-ia
```
#### Step 5: Create Admin Credentials Secret
#### Step 4: Create Admin Credentials Secret
```bash
# Generate a secure password (or use your own)
@@ -1050,30 +1055,35 @@ kubectl create secret generic mailu-admin-credentials \
-n bakery-ia
```
#### Step 6: Deploy Mailu via Helm
#### Step 5: Deploy Mailu via Helm
```bash
# Add Mailu Helm repository
helm repo add mailu https://mailu.github.io/helm-charts
helm repo update mailu
# Get CoreDNS service IP
COREDNS_IP=$(kubectl get svc kube-dns -n kube-system -o jsonpath='{.spec.clusterIP}')
# Deploy Mailu with production values
# Admin user is created automatically via initialAccount feature
# CoreDNS provides DNSSEC validation via DNS-over-TLS to Cloudflare
helm upgrade --install mailu mailu/mailu \
-n bakery-ia \
--create-namespace \
-f infrastructure/platform/mail/mailu-helm/values.yaml \
-f infrastructure/platform/mail/mailu-helm/prod/values.yaml \
--set global.custom_dns_servers="$COREDNS_IP" \
--timeout 10m
# Wait for pods to be ready (may take 5-10 minutes for ClamAV)
kubectl get pods -n bakery-ia -l app.kubernetes.io/instance=mailu -w
# Admin user (admin@bakewise.ai) is created automatically!
# Password is the one you set in Step 5
# Password is the one you set in Step 4
```
#### Step 7: Configure DKIM
#### Step 6: Configure DKIM
After Mailu is running, get the DKIM key and add it to DNS:
@@ -1087,7 +1097,7 @@ kubectl exec -n bakery-ia deployment/mailu-admin -- \
# Value: (the key from above)
```
#### Step 8: Verify Email Setup
#### Step 7: Verify Email Setup
```bash
# Check all Mailu pods are running
@@ -1117,11 +1127,11 @@ kubectl port-forward -n bakery-ia svc/mailu-front 8080:80
**Issue: Admin pod CrashLoopBackOff with "DNSSEC validation" error**
```bash
# Verify CoreDNS is forwarding to Unbound
kubectl get configmap coredns -n kube-system -o yaml | grep forward
# Should show: forward . <unbound-ip>
# Verify CoreDNS is configured with DNS-over-TLS
kubectl get configmap coredns -n kube-system -o yaml | grep 'tls://'
# Should show: tls://1.1.1.1 tls://1.0.0.1
# If not, re-run Step 3 above
# If not, re-run Step 2 above
```
**Issue: Front pod stuck in ContainerCreating**
@@ -1129,7 +1139,7 @@ kubectl get configmap coredns -n kube-system -o yaml | grep forward
# Check for missing certificate secret
kubectl describe pod -n bakery-ia -l app.kubernetes.io/component=front | grep -A5 Events
# If missing mailu-certificates, re-run Step 4 above
# If missing mailu-certificates, re-run Step 3 above
```
**Issue: Admin pod can't connect to Redis**
@@ -2018,41 +2028,22 @@ Mailu is a full-featured, self-hosted email server with built-in antispam, webma
### Prerequisites
Before deploying Mailu:
- [ ] Unbound DNS resolver deployed (for DNSSEC validation)
- [ ] CoreDNS configured with DNS-over-TLS for DNSSEC validation
- [ ] DNS records configured for mail domain
- [ ] TLS certificates available
- [ ] Mailgun account created and domain verified (for outbound email relay)
### Step 1: Deploy Unbound DNS Resolver
### Step 1: Configure CoreDNS for DNSSEC (DNS-over-TLS)
Mailu requires DNSSEC validation for email authentication (DKIM/SPF/DMARC).
CoreDNS is configured to use DNS-over-TLS with Cloudflare for DNSSEC validation.
```bash
# Deploy Unbound via Helm
helm upgrade --install unbound infrastructure/platform/networking/dns/unbound-helm \
-n bakery-ia \
--create-namespace \
-f infrastructure/platform/networking/dns/unbound-helm/values.yaml \
-f infrastructure/platform/networking/dns/unbound-helm/prod/values.yaml \
--timeout 5m \
--wait
# Check if CoreDNS is already configured with DNS-over-TLS
kubectl get configmap coredns -n kube-system -o jsonpath='{.data.Corefile}' | grep -o 'tls://1.1.1.1' || echo "Not configured"
# Verify Unbound is running
kubectl get pods -n bakery-ia | grep unbound
# Get Unbound service IP
UNBOUND_IP=$(kubectl get svc unbound-dns -n bakery-ia -o jsonpath='{.spec.clusterIP}')
echo "Unbound DNS IP: $UNBOUND_IP"
```
### Step 2: Configure CoreDNS for DNSSEC
```bash
# Get Unbound IP
UNBOUND_IP=$(kubectl get svc unbound-dns -n bakery-ia -o jsonpath='{.spec.clusterIP}')
# Create updated CoreDNS ConfigMap
cat > /tmp/coredns-config.yaml <<EOF
# If not configured, update CoreDNS ConfigMap
cat > /tmp/coredns-config.yaml << 'EOF'
apiVersion: v1
kind: ConfigMap
metadata:
@@ -2072,8 +2063,9 @@ data:
ttl 30
}
prometheus :9153
forward . $UNBOUND_IP {
max_concurrent 1000
forward . tls://1.1.1.1 tls://1.0.0.1 {
tls_servername cloudflare-dns.com
health_check 5s
}
cache 30 {
disable success cluster.local
@@ -2092,10 +2084,12 @@ kubectl apply -f /tmp/coredns-config.yaml
kubectl rollout restart deployment coredns -n kube-system
kubectl rollout status deployment coredns -n kube-system --timeout=60s
# Verify DNSSEC is working
kubectl run -it --rm dns-test --image=alpine --restart=Never -- \
sh -c "apk add drill && drill -D google.com"
# Look for "ad" flag (authenticated data) in output
# Get CoreDNS service IP
COREDNS_IP=$(kubectl get svc kube-dns -n kube-system -o jsonpath='{.spec.clusterIP}')
echo "CoreDNS IP: $COREDNS_IP"
# Verify DNS resolution is working
kubectl run -it --rm dns-test --image=busybox --restart=Never -- nslookup google.com
```
### Step 3: Configure Mailgun (External SMTP Relay)
@@ -2216,15 +2210,20 @@ kubectl apply -f infrastructure/platform/mail/mailu-helm/configs/mailu-admin-cre
helm repo add mailu https://mailu.github.io/helm-charts
helm repo update mailu
# Get CoreDNS service IP for Mailu DNS configuration
COREDNS_IP=$(kubectl get svc kube-dns -n kube-system -o jsonpath='{.spec.clusterIP}')
# Deploy Mailu with production values
# Note:
# - externalRelay uses Mailgun via the secret created in Step 3
# - initialAccount creates admin user automatically using the secret from Step 6
# - CoreDNS provides DNSSEC validation via DNS-over-TLS (Cloudflare)
helm upgrade --install mailu mailu/mailu \
-n bakery-ia \
--create-namespace \
-f infrastructure/platform/mail/mailu-helm/values.yaml \
-f infrastructure/platform/mail/mailu-helm/prod/values.yaml \
--set global.custom_dns_servers="$COREDNS_IP" \
--timeout 10m
# Wait for pods to be ready (ClamAV may take 5-10 minutes)
@@ -2306,11 +2305,11 @@ kubectl port-forward -n bakery-ia svc/mailu-front 8080:80
#### Admin Pod CrashLoopBackOff with DNSSEC Error
```bash
# Verify CoreDNS is forwarding to Unbound
kubectl get configmap coredns -n kube-system -o yaml | grep forward
# Should show: forward . <unbound-ip>
# Verify CoreDNS is configured with DNS-over-TLS
kubectl get configmap coredns -n kube-system -o yaml | grep 'tls://'
# Should show: tls://1.1.1.1 tls://1.0.0.1
# If not configured, re-run Step 2
# If not configured, re-run Step 1
```
#### Front Pod Stuck in ContainerCreating
@@ -3419,8 +3418,7 @@ kubectl scale deployment monitoring -n bakery-ia --replicas=0
- [ ] End-to-end pipeline test successful
### Email Infrastructure (Optional - Mailu)
- [ ] Unbound DNS resolver deployed
- [ ] CoreDNS configured for DNSSEC
- [ ] CoreDNS configured with DNS-over-TLS for DNSSEC
- [ ] Mailu TLS certificate created
- [ ] Mailu deployed via Helm
- [ ] Admin user created
@@ -3473,8 +3471,7 @@ kubectl scale deployment monitoring -n bakery-ia --replicas=0
- Webhook integration and end-to-end testing
- Troubleshooting guide for CI/CD issues
- **NEW: Mailu Email Server Deployment** - Comprehensive self-hosted email setup
- Unbound DNS resolver deployment for DNSSEC
- CoreDNS configuration for mail authentication
- CoreDNS configuration with DNS-over-TLS for DNSSEC validation
- Mailu Helm deployment with all components
- DKIM/SPF/DMARC configuration
- Troubleshooting common Mailu issues

View File

@@ -1,38 +0,0 @@
# CoreDNS ConfigMap patch to forward external DNS queries to Unbound for DNSSEC validation
# This is required for Mailu Admin which requires DNSSEC-validating DNS resolver
#
# Apply with: kubectl apply -f coredns-unbound-patch.yaml
# Then restart CoreDNS: kubectl rollout restart deployment coredns -n kube-system
#
# Note: The Unbound service IP (10.104.127.213) may change when the cluster is recreated.
# The setup script will automatically update this based on the actual Unbound service IP.
apiVersion: v1
kind: ConfigMap
metadata:
name: coredns
namespace: kube-system
data:
Corefile: |
.:53 {
errors
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
prometheus :9153
forward . UNBOUND_SERVICE_IP {
max_concurrent 1000
}
cache 30 {
disable success cluster.local
disable denial cluster.local
}
loop
reload
loadbalance
}

View File

@@ -1,34 +1,19 @@
# Development-tuned Mailu configuration
global:
# Using Unbound DNS for DNSSEC validation (required by Mailu admin)
# This value is dynamically set via --set during helm install:
# UNBOUND_IP=$(kubectl get svc unbound-dns -n bakery-ia -o jsonpath='{.spec.clusterIP}')
# helm upgrade --install mailu ... --set global.custom_dns_servers="$UNBOUND_IP"
# Default fallback to Kubernetes DNS (will be overridden by --set)
custom_dns_servers: "10.96.0.10" # Override with Unbound IP via --set
# Using Kubernetes CoreDNS for DNS resolution
# CoreDNS is configured with DNS-over-TLS (Cloudflare) for DNSSEC validation
# Default to Kubernetes DNS IP (will be overridden dynamically if needed)
custom_dns_servers: "10.96.0.10" # Kubernetes DNS IP
# Redis configuration - use built-in Mailu Redis (no authentication needed)
externalRedis:
enabled: false
# Component-specific DNS configuration
# Admin requires DNSSEC validation - use Unbound DNS (forwards cluster.local to kube-dns)
# NOTE: dnsConfig.nameservers is dynamically set via --set during helm install
# DNS configuration - use Kubernetes DNS (ClusterFirst)
# CoreDNS provides DNSSEC validation via DNS-over-TLS to Cloudflare
admin:
dnsPolicy: "None"
dnsConfig:
nameservers:
- "10.96.0.10" # Override with Unbound IP via --set admin.dnsConfig.nameservers[0]
searches:
- "bakery-ia.svc.cluster.local"
- "svc.cluster.local"
- "cluster.local"
options:
- name: ndots
value: "5"
dnsPolicy: "ClusterFirst"
# RSPAMD needs Unbound for DNSSEC validation (DKIM/SPF/DMARC checks)
# Using ClusterFirst with search domains + Kubernetes DNS which can forward to Unbound
rspamd:
dnsPolicy: "ClusterFirst"

View File

@@ -1,15 +1,15 @@
# Production-tuned Mailu configuration
global:
# Using Kubernetes cluster DNS for name resolution
custom_dns_servers: "10.96.0.10" # Kubernetes cluster DNS IP
# Using Kubernetes CoreDNS for DNS resolution
# CoreDNS is configured with DNS-over-TLS (Cloudflare) for DNSSEC validation
custom_dns_servers: "10.152.183.10" # MicroK8s CoreDNS IP
# Redis configuration - use built-in Mailu Redis (no authentication needed for internal)
externalRedis:
enabled: false
# DNS configuration for production
# Use Kubernetes DNS (ClusterFirst) which forwards to Unbound via CoreDNS
# This is configured automatically by the mailu-helm Tilt resource
# Use Kubernetes DNS (ClusterFirst) - CoreDNS provides DNSSEC via DNS-over-TLS
admin:
dnsPolicy: "ClusterFirst"

View File

@@ -4,11 +4,10 @@
# =============================================================================
# This script automates the deployment of Mailu mail server for production.
# It handles:
# 1. Unbound DNS deployment (for DNSSEC validation)
# 2. CoreDNS configuration (forward to Unbound)
# 3. TLS certificate secret creation
# 4. Admin credentials secret creation
# 5. Mailu Helm deployment (admin user created automatically via initialAccount)
# 1. CoreDNS configuration with DNS-over-TLS for DNSSEC validation
# 2. TLS certificate secret creation
# 3. Admin credentials secret creation
# 4. Mailu Helm deployment (admin user created automatically via initialAccount)
#
# Usage:
# ./deploy-mailu-prod.sh [--domain DOMAIN] [--admin-password PASSWORD]
@@ -99,52 +98,15 @@ fi
print_success "Prerequisites check passed"
# =============================================================================
# Step 1: Deploy Unbound DNS Resolver
# Step 1: Configure CoreDNS with DNS-over-TLS for DNSSEC
# =============================================================================
print_step "Step 1: Deploying Unbound DNS resolver..."
print_step "Step 1: Configuring CoreDNS with DNS-over-TLS for DNSSEC validation..."
if kubectl get deployment unbound -n "$NAMESPACE" &>/dev/null; then
print_success "Unbound already deployed"
else
helm upgrade --install unbound "$MAILU_HELM_DIR/../../networking/dns/unbound-helm" \
-n "$NAMESPACE" \
-f "$MAILU_HELM_DIR/../../networking/dns/unbound-helm/values.yaml" \
-f "$MAILU_HELM_DIR/../../networking/dns/unbound-helm/prod/values.yaml" \
--timeout 5m \
--wait
# Check if CoreDNS is already configured with DNS-over-TLS
CURRENT_FORWARD=$(kubectl get configmap coredns -n kube-system -o jsonpath='{.data.Corefile}' 2>/dev/null | grep -o 'tls://1.1.1.1' || echo "")
print_success "Unbound deployed"
fi
# Wait for Unbound to be ready
kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=unbound -n "$NAMESPACE" --timeout=120s
# Get Unbound service IP (dynamic resolution)
echo "Waiting for Unbound service to get assigned IP..."
for i in {1..30}; do
UNBOUND_IP=$(kubectl get svc unbound-dns -n "$NAMESPACE" -o jsonpath='{.spec.clusterIP}' 2>/dev/null || echo "")
if [ -n "$UNBOUND_IP" ] && [ "$UNBOUND_IP" != "<none>" ]; then
echo "Unbound DNS service IP: $UNBOUND_IP"
break
fi
if [ $i -eq 30 ]; then
print_error "Failed to get Unbound service IP"
exit 1
fi
sleep 2
echo "Waiting for Unbound service IP... (attempt $i/30)"
done
# =============================================================================
# Step 2: Configure CoreDNS to Forward to Unbound
# =============================================================================
print_step "Step 2: Configuring CoreDNS for DNSSEC validation..."
# Check current CoreDNS forward configuration
CURRENT_FORWARD=$(kubectl get configmap coredns -n kube-system -o jsonpath='{.data.Corefile}' | grep -o 'forward \. [0-9.]*' | awk '{print $3}' || echo "")
if [ "$CURRENT_FORWARD" != "$UNBOUND_IP" ]; then
echo "Updating CoreDNS to forward to Unbound ($UNBOUND_IP)..."
if [ -z "$CURRENT_FORWARD" ]; then
echo "Updating CoreDNS to use DNS-over-TLS with Cloudflare for DNSSEC validation..."
# Create a temporary file with the CoreDNS configuration
TEMP_COREFILE=$(mktemp)
@@ -161,8 +123,9 @@ if [ "$CURRENT_FORWARD" != "$UNBOUND_IP" ]; then
ttl 30
}
prometheus :9153
forward . $UNBOUND_IP {
max_concurrent 1000
forward . tls://1.1.1.1 tls://1.0.0.1 {
tls_servername cloudflare-dns.com
health_check 5s
}
cache 30 {
disable success cluster.local
@@ -187,15 +150,19 @@ EOF
kubectl rollout restart deployment coredns -n kube-system
kubectl rollout status deployment coredns -n kube-system --timeout=60s
print_success "CoreDNS configured to forward to Unbound"
print_success "CoreDNS configured with DNS-over-TLS for DNSSEC validation"
else
print_success "CoreDNS already configured for Unbound"
print_success "CoreDNS already configured with DNS-over-TLS"
fi
# Get CoreDNS service IP for Mailu configuration
COREDNS_IP=$(kubectl get svc kube-dns -n kube-system -o jsonpath='{.spec.clusterIP}')
echo "CoreDNS service IP: $COREDNS_IP"
# =============================================================================
# Step 3: Create TLS Certificate Secret
# Step 2: Create TLS Certificate Secret
# =============================================================================
print_step "Step 3: Creating TLS certificate secret..."
print_step "Step 2: Creating TLS certificate secret..."
if kubectl get secret mailu-certificates -n "$NAMESPACE" &>/dev/null; then
print_success "TLS certificate secret already exists"
@@ -217,9 +184,9 @@ else
fi
# =============================================================================
# Step 4: Create Admin Credentials Secret
# Step 3: Create Admin Credentials Secret
# =============================================================================
print_step "Step 4: Creating admin credentials secret..."
print_step "Step 3: Creating admin credentials secret..."
if kubectl get secret mailu-admin-credentials -n "$NAMESPACE" &>/dev/null; then
print_success "Admin credentials secret already exists"
@@ -243,33 +210,28 @@ else
fi
# =============================================================================
# Step 5: Deploy Mailu via Helm
# Step 4: Deploy Mailu via Helm
# =============================================================================
print_step "Step 5: Deploying Mailu via Helm..."
print_step "Step 4: Deploying Mailu via Helm..."
# Add Mailu Helm repository
helm repo add mailu https://mailu.github.io/helm-charts 2>/dev/null || true
helm repo update mailu
# Create temporary values file with dynamic DNS server
TEMP_VALUES=$(mktemp)
cat "$MAILU_HELM_DIR/values.yaml" | sed "s/# custom_dns_servers: \"\" # Will be set dynamically by deployment script/custom_dns_servers: \"$UNBOUND_IP\"/" > "$TEMP_VALUES"
# Deploy Mailu with dynamic DNS configuration
# Deploy Mailu with CoreDNS configuration
helm upgrade --install mailu mailu/mailu \
-n "$NAMESPACE" \
-f "$TEMP_VALUES" \
-f "$MAILU_HELM_DIR/values.yaml" \
-f "$MAILU_HELM_DIR/prod/values.yaml" \
--set global.custom_dns_servers="$COREDNS_IP" \
--timeout 10m
rm -f "$TEMP_VALUES"
print_success "Mailu Helm release deployed (admin user will be created automatically)"
# =============================================================================
# Step 6: Wait for Pods to be Ready
# Step 5: Wait for Pods to be Ready
# =============================================================================
print_step "Step 6: Waiting for Mailu pods to be ready..."
print_step "Step 5: Waiting for Mailu pods to be ready..."
echo "This may take 5-10 minutes (ClamAV takes time to initialize)..."
@@ -307,6 +269,10 @@ echo " Webmail: https://mail.$DOMAIN/webmail"
echo " SMTP: mail.$DOMAIN:587 (STARTTLS)"
echo " IMAP: mail.$DOMAIN:993 (SSL)"
echo ""
echo "DNS Configuration:"
echo " CoreDNS is configured with DNS-over-TLS (Cloudflare) for DNSSEC validation"
echo " CoreDNS IP: $COREDNS_IP"
echo ""
echo "Next Steps:"
echo " 1. Configure DNS records (A, MX, SPF, DMARC)"
echo " 2. Get DKIM key: kubectl exec -n $NAMESPACE deployment/mailu-admin -- cat /dkim/$DOMAIN.dkim.pub"

View File

@@ -3,16 +3,13 @@
# Phase 7: Deploy Optional Services - Fixed Version
# =============================================================================
# This script deploys the optional services for production:
# 1. Unbound DNS (with dynamic IP resolution)
# 2. CoreDNS configuration for DNSSEC
# 3. Mailu Email Server
# 4. SigNoz Monitoring
# 1. CoreDNS configuration with DNS-over-TLS for DNSSEC validation
# 2. Mailu Email Server
# 3. SigNoz Monitoring
#
# Fixed issues:
# - Removed static ClusterIP that caused CIDR range conflicts
# - Implemented dynamic IP resolution for Unbound DNS
# - Updated CoreDNS patching to use dynamic IP
# - Updated Mailu configuration to use dynamic DNS server
# DNS Architecture:
# - CoreDNS uses DNS-over-TLS with Cloudflare (1.1.1.1) for DNSSEC validation
# - Mailu uses CoreDNS for DNS resolution (internal K8s + external DNSSEC)
# =============================================================================
set -e
@@ -40,49 +37,15 @@ print_success() {
}
# =============================================================================
# Step 7.1: Deploy Unbound DNS (with dynamic IP)
# Step 7.1: Configure CoreDNS with DNS-over-TLS for DNSSEC
# =============================================================================
print_step "Step 7.1: Deploying Unbound DNS resolver (dynamic IP)..."
print_step "Step 7.1: Configuring CoreDNS with DNS-over-TLS for DNSSEC validation..."
if kubectl get deployment unbound -n "$NAMESPACE" &>/dev/null; then
print_success "Unbound already deployed"
else
helm upgrade --install unbound infrastructure/platform/networking/dns/unbound-helm \
-n "$NAMESPACE" \
-f infrastructure/platform/networking/dns/unbound-helm/values.yaml \
-f infrastructure/platform/networking/dns/unbound-helm/prod/values.yaml \
--timeout 5m \
--wait
print_success "Unbound deployed"
fi
# Check if CoreDNS is already configured with DNS-over-TLS
CURRENT_FORWARD=$(kubectl get configmap coredns -n kube-system -o jsonpath='{.data.Corefile}' 2>/dev/null | grep -o 'tls://1.1.1.1' || echo "")
# Wait for Unbound service to get assigned IP
print_step "Waiting for Unbound service to get assigned IP..."
for i in {1..30}; do
UNBOUND_IP=$(kubectl get svc unbound-dns -n "$NAMESPACE" -o jsonpath='{.spec.clusterIP}' 2>/dev/null || echo "")
if [ -n "$UNBOUND_IP" ] && [ "$UNBOUND_IP" != "<none>" ]; then
echo "Unbound DNS service IP: $UNBOUND_IP"
break
fi
if [ $i -eq 30 ]; then
print_error "Failed to get Unbound service IP"
exit 1
fi
sleep 2
echo "Waiting for Unbound service IP... (attempt $i/30)"
done
# =============================================================================
# Step 7.2: Configure CoreDNS for DNSSEC (dynamic IP)
# =============================================================================
print_step "Step 7.2: Configuring CoreDNS for DNSSEC validation..."
# Check current CoreDNS forward configuration
CURRENT_FORWARD=$(kubectl get configmap coredns -n kube-system -o jsonpath='{.data.Corefile}' | grep -o 'forward \. [0-9.]*' | awk '{print $3}' || echo "")
if [ "$CURRENT_FORWARD" != "$UNBOUND_IP" ]; then
echo "Updating CoreDNS to forward to Unbound ($UNBOUND_IP)..."
if [ -z "$CURRENT_FORWARD" ]; then
echo "Updating CoreDNS to use DNS-over-TLS with Cloudflare..."
# Create a temporary file with the CoreDNS configuration
TEMP_COREFILE=$(mktemp)
@@ -99,8 +62,9 @@ if [ "$CURRENT_FORWARD" != "$UNBOUND_IP" ]; then
ttl 30
}
prometheus :9153
forward . $UNBOUND_IP {
max_concurrent 1000
forward . tls://1.1.1.1 tls://1.0.0.1 {
tls_servername cloudflare-dns.com
health_check 5s
}
cache 30 {
disable success cluster.local
@@ -125,33 +89,32 @@ EOF
kubectl rollout restart deployment coredns -n kube-system
kubectl rollout status deployment coredns -n kube-system --timeout=60s
print_success "CoreDNS configured to forward to Unbound"
print_success "CoreDNS configured with DNS-over-TLS"
else
print_success "CoreDNS already configured for Unbound"
print_success "CoreDNS already configured with DNS-over-TLS"
fi
# Get CoreDNS service IP
COREDNS_IP=$(kubectl get svc kube-dns -n kube-system -o jsonpath='{.spec.clusterIP}')
echo "CoreDNS service IP: $COREDNS_IP"
# =============================================================================
# Step 7.3: Deploy Mailu Email Server (dynamic DNS)
# Step 7.2: Deploy Mailu Email Server
# =============================================================================
print_step "Step 7.3: Deploying Mailu Email Server..."
print_step "Step 7.2: Deploying Mailu Email Server..."
# Add Mailu Helm repository
helm repo add mailu https://mailu.github.io/helm-charts 2>/dev/null || true
helm repo update mailu
# Create temporary values file with dynamic DNS server
TEMP_VALUES=$(mktemp)
cat infrastructure/platform/mail/mailu-helm/values.yaml | sed "s/# custom_dns_servers: \"\" # Will be set dynamically by deployment script/custom_dns_servers: \"$UNBOUND_IP\"/" > "$TEMP_VALUES"
# Deploy Mailu with dynamic DNS configuration
# Deploy Mailu with CoreDNS configuration
helm upgrade --install mailu mailu/mailu \
-n "$NAMESPACE" \
-f "$TEMP_VALUES" \
-f infrastructure/platform/mail/mailu-helm/values.yaml \
-f infrastructure/platform/mail/mailu-helm/prod/values.yaml \
--set global.custom_dns_servers="$COREDNS_IP" \
--timeout 10m
rm -f "$TEMP_VALUES"
print_success "Mailu Helm release deployed"
# Wait for Mailu pods to be ready
@@ -165,9 +128,9 @@ kubectl wait --for=condition=ready pod -l app.kubernetes.io/component=admin -n "
print_success "Mailu deployment completed"
# =============================================================================
# Step 7.4: Deploy SigNoz Monitoring
# Step 7.3: Deploy SigNoz Monitoring
# =============================================================================
print_step "Step 7.4: Deploying SigNoz Monitoring..."
print_step "Step 7.3: Deploying SigNoz Monitoring..."
# Add SigNoz Helm repository
helm repo add signoz https://charts.signoz.io 2>/dev/null || true
@@ -196,9 +159,8 @@ echo -e "${GREEN}Phase 7 Deployment Complete!${NC}"
echo "=============================================="
echo ""
echo "Deployed Services:"
echo "Unbound DNS (IP: $UNBOUND_IP)"
echo " ✓ CoreDNS (configured for DNSSEC)"
echo " ✓ Mailu Email Server"
echo " ✓ CoreDNS (configured with DNS-over-TLS for DNSSEC)"
echo "Mailu Email Server (using CoreDNS IP: $COREDNS_IP)"
echo " ✓ SigNoz Monitoring"
echo ""
echo "Next Steps:"

View File

@@ -3,8 +3,8 @@
# Global DNS configuration for DNSSEC validation
global:
# Using Unbound DNS resolver directly for DNSSEC validation
# Unbound service is available at unbound-dns.bakery-ia.svc.cluster.local
# Using Kubernetes CoreDNS with DNS-over-TLS for DNSSEC validation
# CoreDNS is configured to forward external queries to Cloudflare (tls://1.1.1.1)
# DNS server IP will be dynamically resolved during deployment
# custom_dns_servers: "" # Will be set dynamically by deployment script
@@ -230,6 +230,5 @@ networkPolicy:
# DNS Policy Configuration
# Use Kubernetes DNS (ClusterFirst) for internal service resolution
# DNSSEC validation for email is handled by rspamd component
# Note: For production with DNSSEC needs, configure CoreDNS to forward to Unbound
# DNSSEC validation is provided by CoreDNS with DNS-over-TLS (Cloudflare)
dnsPolicy: "ClusterFirst"

View File

@@ -1,18 +0,0 @@
apiVersion: v2
name: unbound
description: A Helm chart for deploying Unbound DNS resolver for Bakery-IA
type: application
version: 0.1.0
appVersion: "1.19.1"
maintainers:
- name: Bakery-IA Team
email: devops@bakery-ia.com
keywords:
- dns
- resolver
- caching
- unbound
home: https://www.nlnetlabs.nl/projects/unbound/
sources:
- https://github.com/NLnetLabs/unbound
- https://hub.docker.com/r/mvance/unbound

View File

@@ -1,64 +0,0 @@
# Development values for unbound DNS resolver
# Using same configuration as production for consistency
# Use official image for development (same as production)
image:
repository: "mvance/unbound"
tag: "latest"
pullPolicy: "IfNotPresent"
# Resource settings (slightly lower than production for dev)
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "300m"
memory: "384Mi"
# Single replica for development (can be scaled if needed)
replicaCount: 1
# Development annotations
podAnnotations:
environment: "development"
managed-by: "helm"
# Probe settings (same as production but slightly faster)
probes:
readiness:
initialDelaySeconds: 10
periodSeconds: 30
command: "drill @127.0.0.1 -p 53 example.org || echo 'DNS query test'"
liveness:
initialDelaySeconds: 30
periodSeconds: 60
command: "drill @127.0.0.1 -p 53 example.org || echo 'DNS query test'"
# Custom Unbound forward records for Kubernetes DNS
config:
enabled: true
# The mvance/unbound image includes forward-records.conf
# We need to add Kubernetes-specific forwarding zones
forwardRecords: |
# Forward all queries to Cloudflare with DNSSEC (catch-all)
forward-zone:
name: "."
forward-tls-upstream: yes
forward-addr: 1.1.1.1@853#cloudflare-dns.com
forward-addr: 1.0.0.1@853#cloudflare-dns.com
# Additional server config to mark cluster.local as insecure (no DNSSEC)
# and use stub zones for Kubernetes internal DNS (more reliable than forward)
serverConfig: |
domain-insecure: "cluster.local."
private-domain: "cluster.local."
local-zone: "10.in-addr.arpa." nodefault
stub-zone:
name: "cluster.local."
stub-addr: 10.96.0.10
stub-zone:
name: "10.in-addr.arpa."
stub-addr: 10.96.0.10

View File

@@ -1,130 +0,0 @@
# Production-specific values for unbound DNS resolver
# Overrides for the production environment
#
# ARCHITECTURE NOTE:
# Unbound provides DNSSEC validation required by Mailu (rspamd for DKIM/SPF/DMARC).
# CoreDNS does NOT support DNSSEC, so we need Unbound as a dedicated resolver.
#
# Two deployment options:
# 1. Mailu-only: Only Mailu pods use Unbound (via dnsPolicy: None)
# - CoreDNS forwards to public DNS (8.8.8.8, 1.1.1.1)
# - Lower resource usage, simpler architecture
#
# 2. Cluster-wide: CoreDNS forwards ALL external queries to Unbound
# - All pods get DNSSEC validation
# - Higher resource usage, single point of failure for DNS
# Use official image for production
image:
repository: "mvance/unbound"
tag: "latest"
pullPolicy: "IfNotPresent"
# Production resource settings - MINIMAL for single-node clusters
# Unbound is very lightweight - DNS queries use minimal CPU
resources:
requests:
cpu: "50m"
memory: "64Mi"
limits:
cpu: "200m"
memory: "256Mi"
# Single replica for single-node clusters (saves resources)
# Increase to 2 for multi-node HA deployments
replicaCount: 1
# Production annotations
podAnnotations:
environment: "production"
critical: "true"
# Anti-affinity disabled for single-node clusters
# Uncomment for multi-node HA deployments
# affinity:
# podAntiAffinity:
# preferredDuringSchedulingIgnoredDuringExecution:
# - weight: 100
# podAffinityTerm:
# labelSelector:
# matchExpressions:
# - key: app.kubernetes.io/name
# operator: In
# values:
# - unbound
# topologyKey: "kubernetes.io/hostname"
# Production probe settings (more conservative)
# NOTE: mvance/unbound image does NOT have 'nc' (netcat), use 'drill' instead
probes:
readiness:
initialDelaySeconds: 10
periodSeconds: 30
command: "drill @127.0.0.1 localhost || exit 1"
liveness:
initialDelaySeconds: 30
periodSeconds: 60
command: "drill @127.0.0.1 localhost || exit 1"
# Custom unbound configuration to forward internal Kubernetes zones to CoreDNS
config:
enabled: true
content: |
server:
interface: 0.0.0.0
port: 53
do-ip4: yes
do-ip6: no
do-udp: yes
do-tcp: yes
# Access control - allow all private networks
access-control: 10.0.0.0/8 allow
access-control: 172.16.0.0/12 allow
access-control: 192.168.0.0/16 allow
access-control: 127.0.0.0/8 allow
# DNSSEC validation (required for Mailu)
auto-trust-anchor-file: "/opt/unbound/etc/unbound/root.key"
# Performance tuning
num-threads: 2
msg-cache-size: 32m
rrset-cache-size: 64m
cache-min-ttl: 60
cache-max-ttl: 86400
# Logging
verbosity: 1
log-queries: no
log-replies: no
# Private addresses - don't send to upstream
private-address: 10.0.0.0/8
private-address: 172.16.0.0/12
private-address: 192.168.0.0/16
# Forward Kubernetes internal zones to CoreDNS (10.152.183.10 for MicroK8s)
forward-zone:
name: "cluster.local."
forward-addr: 10.152.183.10
forward-zone:
name: "svc.cluster.local."
forward-addr: 10.152.183.10
forward-zone:
name: "bakery-ia.svc.cluster.local."
forward-addr: 10.152.183.10
# Forward in-addr.arpa for reverse DNS lookups within cluster
forward-zone:
name: "in-addr.arpa."
forward-addr: 10.152.183.10
# Forward all other queries to upstream DNS with DNSSEC
forward-zone:
name: "."
forward-tls-upstream: yes
forward-addr: 1.1.1.1@853#cloudflare-dns.com
forward-addr: 8.8.8.8@853#dns.google

View File

@@ -1,63 +0,0 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "unbound.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
*/}}
{{- define "unbound.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- if contains $name .Release.Name -}}
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "unbound.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Common labels
*/}}
{{- define "unbound.labels" -}}
helm.sh/chart: {{ include "unbound.chart" . }}
{{ include "unbound.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end -}}
{{/*
Selector labels
*/}}
{{- define "unbound.selectorLabels" -}}
app.kubernetes.io/name: {{ include "unbound.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/component: dns
app.kubernetes.io/part-of: bakery-ia
{{- end -}}
{{/*
Create the name of the service account to use
*/}}
{{- define "unbound.serviceAccountName" -}}
{{- if .Values.serviceAccount.create -}}
{{ default (include "unbound.fullname" .) .Values.serviceAccount.name }}
{{- else -}}
{{ default "default" .Values.serviceAccount.name }}
{{- end -}}
{{- end -}}

View File

@@ -1,22 +0,0 @@
{{- if .Values.config.enabled }}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "unbound.fullname" . }}-config
namespace: {{ .Values.global.namespace }}
labels:
{{- include "unbound.labels" . | nindent 4 }}
data:
{{- if .Values.config.forwardRecords }}
forward-records.conf: |
{{ .Values.config.forwardRecords | indent 4 }}
{{- end }}
{{- if .Values.config.serverConfig }}
a-records.conf: |
{{ .Values.config.serverConfig | indent 4 }}
{{- end }}
{{- if .Values.config.content }}
unbound.conf: |
{{ .Values.config.content | indent 4 }}
{{- end }}
{{- end }}

View File

@@ -1,117 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "unbound.fullname" . }}
namespace: {{ .Values.global.namespace }}
labels:
{{- include "unbound.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "unbound.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "unbound.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "unbound.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: dns-udp
containerPort: {{ .Values.service.ports.dnsUdp }}
protocol: UDP
- name: dns-tcp
containerPort: {{ .Values.service.ports.dnsTcp }}
protocol: TCP
{{- if .Values.probes.readiness.enabled }}
readinessProbe:
exec:
command:
- sh
- -c
- {{ .Values.probes.readiness.command | quote }}
initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.readiness.periodSeconds }}
{{- end }}
{{- if .Values.probes.liveness.enabled }}
livenessProbe:
exec:
command:
- sh
- -c
- {{ .Values.probes.liveness.command | quote }}
initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.liveness.periodSeconds }}
{{- end }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
volumeMounts:
{{- if .Values.config.enabled }}
{{- if .Values.config.forwardRecords }}
- name: unbound-config
mountPath: /opt/unbound/etc/unbound/forward-records.conf
subPath: forward-records.conf
{{- end }}
{{- if .Values.config.serverConfig }}
- name: unbound-config
mountPath: /opt/unbound/etc/unbound/a-records.conf
subPath: a-records.conf
{{- end }}
{{- if .Values.config.content }}
- name: unbound-config
mountPath: /opt/unbound/etc/unbound/unbound.conf
subPath: unbound.conf
{{- end }}
{{- end }}
{{- with .Values.volumeMounts }}
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.env }}
env:
{{- toYaml . | nindent 12 }}
{{- end }}
volumes:
{{- if .Values.config.enabled }}
- name: unbound-config
configMap:
name: {{ include "unbound.fullname" . }}-config
{{- end }}
{{- with .Values.volumes }}
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.extraInitContainers }}
initContainers:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.extraContainers }}
containers:
{{- toYaml . | nindent 8 }}
{{- end }}

View File

@@ -1,27 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: {{ .Values.global.dnsServiceName }}
namespace: {{ .Values.global.namespace }}
labels:
{{- include "unbound.labels" . | nindent 4 }}
{{- with .Values.serviceAnnotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
type: {{ .Values.service.type }}
{{- if .Values.service.clusterIP }}
clusterIP: {{ .Values.service.clusterIP }}
{{- end }}
ports:
- name: dns-udp
port: {{ .Values.service.ports.dnsUdp }}
targetPort: {{ .Values.service.ports.dnsUdp }}
protocol: UDP
- name: dns-tcp
port: {{ .Values.service.ports.dnsTcp }}
targetPort: {{ .Values.service.ports.dnsTcp }}
protocol: TCP
selector:
{{- include "unbound.selectorLabels" . | nindent 4 }}

View File

@@ -1,13 +0,0 @@
{{- if .Values.serviceAccount.create -}}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "unbound.serviceAccountName" . }}
namespace: {{ .Values.global.namespace }}
labels:
{{- include "unbound.labels" . | nindent 4 }}
{{- with .Values.serviceAccount.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end -}}

View File

@@ -1,105 +0,0 @@
# Default values for unbound DNS resolver
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
#
# PURPOSE: Provides DNSSEC validation for Mailu email server
# CoreDNS does NOT support DNSSEC, so Unbound fills this gap.
# Mailu's rspamd requires DNSSEC for DKIM/SPF/DMARC validation.
# Global settings
global:
# DNS service name for other services to reference
dnsServiceName: "unbound-dns"
namespace: "bakery-ia"
# Unbound image configuration
image:
repository: "mvance/unbound"
tag: "latest"
pullPolicy: "IfNotPresent"
# Deployment configuration
replicaCount: 1
# Resource limits and requests
# Unbound is very lightweight - these minimal resources are sufficient
resources:
requests:
cpu: "25m"
memory: "32Mi"
limits:
cpu: "100m"
memory: "128Mi"
# Security context
securityContext:
capabilities:
add: ["NET_BIND_SERVICE"]
# Service configuration
service:
type: "ClusterIP"
# Dynamic ClusterIP - Kubernetes will assign automatically
# clusterIP: "" # Leave empty for automatic assignment
ports:
dnsUdp: 53
dnsTcp: 53
# Health probes configuration
# NOTE: mvance/unbound image does NOT have 'nc' (netcat), use 'drill' instead
probes:
readiness:
enabled: true
initialDelaySeconds: 10
periodSeconds: 30
# Use drill (DNS lookup tool included in unbound image)
command: "drill @127.0.0.1 localhost || exit 1"
liveness:
enabled: true
initialDelaySeconds: 30
periodSeconds: 60
# Use drill (DNS lookup tool included in unbound image)
command: "drill @127.0.0.1 localhost || exit 1"
# Additional environment variables
env: {}
# Additional volume mounts
volumeMounts: []
# Additional volumes
volumes: []
# Node selector
nodeSelector: {}
# Tolerations
tolerations: []
# Affinity
affinity: {}
# Pod annotations
podAnnotations: {}
# Service annotations
serviceAnnotations: {}
# Custom unbound configuration
config:
enabled: false
# Additional containers (sidecars)
extraContainers: []
# Additional init containers
extraInitContainers: []
# Service account configuration
serviceAccount:
create: false
annotations: {}
name: ""
# Pod security context
podSecurityContext: {}

View File

@@ -45,6 +45,7 @@ spec:
containers:
- name: postgres
image: postgres:17-alpine
command: ["docker-entrypoint.sh", "-c", "config_file=/etc/postgresql/postgresql.conf"]
ports:
- containerPort: 5432
name: postgres
@@ -66,11 +67,23 @@ spec:
value: demo_session_db
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
- name: POSTGRES_HOST_SSL
value: "on"
- name: PGSSLCERT
value: /tls/server-cert.pem
- name: PGSSLKEY
value: /tls/server-key.pem
- name: PGSSLROOTCERT
value: /tls/ca-cert.pem
volumeMounts:
- name: demo-session-db-data
mountPath: /var/lib/postgresql/data
- name: init-scripts
mountPath: /docker-entrypoint-initdb.d
- name: tls-certs-writable
mountPath: /tls
- name: postgres-config
mountPath: /etc/postgresql
readOnly: true
resources:
requests:
@@ -103,6 +116,9 @@ spec:
- name: demo-session-db-data
persistentVolumeClaim:
claimName: demo-session-db-pvc
- name: init-scripts
configMap:
name: postgres-init-config
- name: tls-certs-source
secret:
secretName: postgres-tls
@@ -115,6 +131,9 @@ spec:
path: ca-cert.pem
- name: tls-certs-writable
emptyDir: {}
- name: postgres-config
configMap:
name: postgres-logging-config
---
@@ -153,4 +172,4 @@ spec:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storage: 1Gi