From 0183f3ab7212257fce8f4d7133a8c62c8e990d24 Mon Sep 17 00:00:00 2001 From: Bakery Admin Date: Thu, 22 Jan 2026 11:15:11 +0100 Subject: [PATCH] Fix resources isues 5 --- MAILU_DEPLOYMENT_ARCHITECTURE.md | 228 +++++++----------- PRODUCTION_DEPLOYMENT_GUIDE.md | 159 ++++++------ Tiltfile | 154 +++++------- docs/PILOT_LAUNCH_GUIDE.md | 189 +++++++-------- .../configs/coredns-unbound-patch.yaml | 38 --- .../platform/mail/mailu-helm/dev/values.yaml | 29 +-- .../platform/mail/mailu-helm/prod/values.yaml | 8 +- .../mailu-helm/scripts/deploy-mailu-prod.sh | 102 +++----- .../mailu-helm/scripts/deploy-phase7-fixed.sh | 98 +++----- .../platform/mail/mailu-helm/values.yaml | 7 +- .../networking/dns/unbound-helm/Chart.yaml | 18 -- .../dns/unbound-helm/dev/values.yaml | 64 ----- .../dns/unbound-helm/prod/values.yaml | 130 ---------- .../dns/unbound-helm/templates/_helpers.tpl | 63 ----- .../dns/unbound-helm/templates/configmap.yaml | 22 -- .../unbound-helm/templates/deployment.yaml | 117 --------- .../dns/unbound-helm/templates/service.yaml | 27 --- .../templates/serviceaccount.yaml | 13 - .../networking/dns/unbound-helm/values.yaml | 105 -------- .../services/databases/demo-session-db.yaml | 21 +- 20 files changed, 399 insertions(+), 1193 deletions(-) delete mode 100644 infrastructure/platform/mail/mailu-helm/configs/coredns-unbound-patch.yaml delete mode 100644 infrastructure/platform/networking/dns/unbound-helm/Chart.yaml delete mode 100644 infrastructure/platform/networking/dns/unbound-helm/dev/values.yaml delete mode 100644 infrastructure/platform/networking/dns/unbound-helm/prod/values.yaml delete mode 100644 infrastructure/platform/networking/dns/unbound-helm/templates/_helpers.tpl delete mode 100644 infrastructure/platform/networking/dns/unbound-helm/templates/configmap.yaml delete mode 100644 infrastructure/platform/networking/dns/unbound-helm/templates/deployment.yaml delete mode 100644 infrastructure/platform/networking/dns/unbound-helm/templates/service.yaml delete mode 100644 infrastructure/platform/networking/dns/unbound-helm/templates/serviceaccount.yaml delete mode 100644 infrastructure/platform/networking/dns/unbound-helm/values.yaml diff --git a/MAILU_DEPLOYMENT_ARCHITECTURE.md b/MAILU_DEPLOYMENT_ARCHITECTURE.md index bf1d72a4..a2832bb1 100644 --- a/MAILU_DEPLOYMENT_ARCHITECTURE.md +++ b/MAILU_DEPLOYMENT_ARCHITECTURE.md @@ -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. diff --git a/PRODUCTION_DEPLOYMENT_GUIDE.md b/PRODUCTION_DEPLOYMENT_GUIDE.md index 7200cbdf..dee3ea82 100644 --- a/PRODUCTION_DEPLOYMENT_GUIDE.md +++ b/PRODUCTION_DEPLOYMENT_GUIDE.md @@ -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) diff --git a/Tiltfile b/Tiltfile index 04ee7958..333e31c3 100644 --- a/Tiltfile +++ b/Tiltfile @@ -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 ) diff --git a/docs/PILOT_LAUNCH_GUIDE.md b/docs/PILOT_LAUNCH_GUIDE.md index 38a76377..198e3eb0 100644 --- a/docs/PILOT_LAUNCH_GUIDE.md +++ b/docs/PILOT_LAUNCH_GUIDE.md @@ -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 . +# 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 < /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 . +# 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 diff --git a/infrastructure/platform/mail/mailu-helm/configs/coredns-unbound-patch.yaml b/infrastructure/platform/mail/mailu-helm/configs/coredns-unbound-patch.yaml deleted file mode 100644 index e2ab881a..00000000 --- a/infrastructure/platform/mail/mailu-helm/configs/coredns-unbound-patch.yaml +++ /dev/null @@ -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 - } diff --git a/infrastructure/platform/mail/mailu-helm/dev/values.yaml b/infrastructure/platform/mail/mailu-helm/dev/values.yaml index b1e690cc..368e78ef 100644 --- a/infrastructure/platform/mail/mailu-helm/dev/values.yaml +++ b/infrastructure/platform/mail/mailu-helm/dev/values.yaml @@ -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" diff --git a/infrastructure/platform/mail/mailu-helm/prod/values.yaml b/infrastructure/platform/mail/mailu-helm/prod/values.yaml index 92d4306f..085e78f3 100644 --- a/infrastructure/platform/mail/mailu-helm/prod/values.yaml +++ b/infrastructure/platform/mail/mailu-helm/prod/values.yaml @@ -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" diff --git a/infrastructure/platform/mail/mailu-helm/scripts/deploy-mailu-prod.sh b/infrastructure/platform/mail/mailu-helm/scripts/deploy-mailu-prod.sh index 54de8250..07699bbe 100755 --- a/infrastructure/platform/mail/mailu-helm/scripts/deploy-mailu-prod.sh +++ b/infrastructure/platform/mail/mailu-helm/scripts/deploy-mailu-prod.sh @@ -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" != "" ]; 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" diff --git a/infrastructure/platform/mail/mailu-helm/scripts/deploy-phase7-fixed.sh b/infrastructure/platform/mail/mailu-helm/scripts/deploy-phase7-fixed.sh index 8bcf98af..2a7d7737 100755 --- a/infrastructure/platform/mail/mailu-helm/scripts/deploy-phase7-fixed.sh +++ b/infrastructure/platform/mail/mailu-helm/scripts/deploy-phase7-fixed.sh @@ -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" != "" ]; 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:" diff --git a/infrastructure/platform/mail/mailu-helm/values.yaml b/infrastructure/platform/mail/mailu-helm/values.yaml index 31da1815..43062552 100644 --- a/infrastructure/platform/mail/mailu-helm/values.yaml +++ b/infrastructure/platform/mail/mailu-helm/values.yaml @@ -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" \ No newline at end of file diff --git a/infrastructure/platform/networking/dns/unbound-helm/Chart.yaml b/infrastructure/platform/networking/dns/unbound-helm/Chart.yaml deleted file mode 100644 index eaf9b5d8..00000000 --- a/infrastructure/platform/networking/dns/unbound-helm/Chart.yaml +++ /dev/null @@ -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 \ No newline at end of file diff --git a/infrastructure/platform/networking/dns/unbound-helm/dev/values.yaml b/infrastructure/platform/networking/dns/unbound-helm/dev/values.yaml deleted file mode 100644 index d5daaff0..00000000 --- a/infrastructure/platform/networking/dns/unbound-helm/dev/values.yaml +++ /dev/null @@ -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 \ No newline at end of file diff --git a/infrastructure/platform/networking/dns/unbound-helm/prod/values.yaml b/infrastructure/platform/networking/dns/unbound-helm/prod/values.yaml deleted file mode 100644 index a3b48243..00000000 --- a/infrastructure/platform/networking/dns/unbound-helm/prod/values.yaml +++ /dev/null @@ -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 \ No newline at end of file diff --git a/infrastructure/platform/networking/dns/unbound-helm/templates/_helpers.tpl b/infrastructure/platform/networking/dns/unbound-helm/templates/_helpers.tpl deleted file mode 100644 index 6ca19e9e..00000000 --- a/infrastructure/platform/networking/dns/unbound-helm/templates/_helpers.tpl +++ /dev/null @@ -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 -}} \ No newline at end of file diff --git a/infrastructure/platform/networking/dns/unbound-helm/templates/configmap.yaml b/infrastructure/platform/networking/dns/unbound-helm/templates/configmap.yaml deleted file mode 100644 index a11b14dd..00000000 --- a/infrastructure/platform/networking/dns/unbound-helm/templates/configmap.yaml +++ /dev/null @@ -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 }} diff --git a/infrastructure/platform/networking/dns/unbound-helm/templates/deployment.yaml b/infrastructure/platform/networking/dns/unbound-helm/templates/deployment.yaml deleted file mode 100644 index 42cc5357..00000000 --- a/infrastructure/platform/networking/dns/unbound-helm/templates/deployment.yaml +++ /dev/null @@ -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 }} \ No newline at end of file diff --git a/infrastructure/platform/networking/dns/unbound-helm/templates/service.yaml b/infrastructure/platform/networking/dns/unbound-helm/templates/service.yaml deleted file mode 100644 index d945ac24..00000000 --- a/infrastructure/platform/networking/dns/unbound-helm/templates/service.yaml +++ /dev/null @@ -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 }} \ No newline at end of file diff --git a/infrastructure/platform/networking/dns/unbound-helm/templates/serviceaccount.yaml b/infrastructure/platform/networking/dns/unbound-helm/templates/serviceaccount.yaml deleted file mode 100644 index 1826774d..00000000 --- a/infrastructure/platform/networking/dns/unbound-helm/templates/serviceaccount.yaml +++ /dev/null @@ -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 -}} \ No newline at end of file diff --git a/infrastructure/platform/networking/dns/unbound-helm/values.yaml b/infrastructure/platform/networking/dns/unbound-helm/values.yaml deleted file mode 100644 index 15051386..00000000 --- a/infrastructure/platform/networking/dns/unbound-helm/values.yaml +++ /dev/null @@ -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: {} \ No newline at end of file diff --git a/infrastructure/services/databases/demo-session-db.yaml b/infrastructure/services/databases/demo-session-db.yaml index d02a129e..3878376c 100644 --- a/infrastructure/services/databases/demo-session-db.yaml +++ b/infrastructure/services/databases/demo-session-db.yaml @@ -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 \ No newline at end of file + storage: 1Gi