#!/bin/bash # ============================================================================= # Mailu Production Deployment Script # ============================================================================= # 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) # # Usage: # ./deploy-mailu-prod.sh [--domain DOMAIN] [--admin-password PASSWORD] # # Example: # ./deploy-mailu-prod.sh --domain bakewise.ai --admin-password 'SecurePass123!' # ============================================================================= set -e # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Default values DOMAIN="${DOMAIN:-bakewise.ai}" ADMIN_PASSWORD="${ADMIN_PASSWORD:-}" NAMESPACE="bakery-ia" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" MAILU_HELM_DIR="$(dirname "$SCRIPT_DIR")" # Parse arguments while [[ $# -gt 0 ]]; do case $1 in --domain) DOMAIN="$2" shift 2 ;; --admin-password) ADMIN_PASSWORD="$2" shift 2 ;; --help) echo "Usage: $0 [--domain DOMAIN] [--admin-password PASSWORD]" echo "" echo "Options:" echo " --domain Domain for Mailu (default: bakewise.ai)" echo " --admin-password Password for admin@DOMAIN user" echo "" exit 0 ;; *) echo -e "${RED}Unknown option: $1${NC}" exit 1 ;; esac done print_step() { echo -e "\n${BLUE}==>${NC} ${GREEN}$1${NC}" } print_warning() { echo -e "${YELLOW}WARNING:${NC} $1" } print_error() { echo -e "${RED}ERROR:${NC} $1" } print_success() { echo -e "${GREEN}✓${NC} $1" } # ============================================================================= # Step 0: Prerequisites Check # ============================================================================= print_step "Step 0: Checking prerequisites..." if ! command -v kubectl &> /dev/null; then print_error "kubectl not found. Please install kubectl." exit 1 fi if ! command -v helm &> /dev/null; then print_error "helm not found. Please install helm." exit 1 fi if ! kubectl get namespace "$NAMESPACE" &>/dev/null; then print_warning "Namespace $NAMESPACE does not exist. Creating..." kubectl create namespace "$NAMESPACE" fi print_success "Prerequisites check passed" # ============================================================================= # Step 1: Deploy Unbound DNS Resolver # ============================================================================= print_step "Step 1: Deploying Unbound DNS resolver..." 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 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)..." # Create a temporary file with the CoreDNS configuration TEMP_COREFILE=$(mktemp) cat > "$TEMP_COREFILE" </dev/null; then print_success "TLS certificate secret already exists" else TEMP_DIR=$(mktemp -d) cd "$TEMP_DIR" openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ -keyout tls.key -out tls.crt \ -subj "/CN=mail.$DOMAIN/O=$DOMAIN" 2>/dev/null kubectl create secret tls mailu-certificates \ --cert=tls.crt \ --key=tls.key \ -n "$NAMESPACE" rm -rf "$TEMP_DIR" print_success "TLS certificate secret created" fi # ============================================================================= # Step 4: Create Admin Credentials Secret # ============================================================================= print_step "Step 4: Creating admin credentials secret..." if kubectl get secret mailu-admin-credentials -n "$NAMESPACE" &>/dev/null; then print_success "Admin credentials secret already exists" # Retrieve existing password for summary output if [ -z "$ADMIN_PASSWORD" ]; then ADMIN_PASSWORD=$(kubectl get secret mailu-admin-credentials -n "$NAMESPACE" -o jsonpath='{.data.password}' | base64 -d) fi else if [ -z "$ADMIN_PASSWORD" ]; then # Generate a random password ADMIN_PASSWORD=$(openssl rand -base64 16 | tr -d '/+=' | head -c 16) echo -e "${YELLOW}Generated admin password: $ADMIN_PASSWORD${NC}" echo -e "${YELLOW}Please save this password securely!${NC}" fi kubectl create secret generic mailu-admin-credentials \ --from-literal=password="$ADMIN_PASSWORD" \ -n "$NAMESPACE" print_success "Admin credentials secret created" fi # ============================================================================= # Step 5: Deploy Mailu via Helm # ============================================================================= print_step "Step 5: 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 helm upgrade --install mailu mailu/mailu \ -n "$NAMESPACE" \ -f "$TEMP_VALUES" \ -f "$MAILU_HELM_DIR/prod/values.yaml" \ --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 # ============================================================================= print_step "Step 6: Waiting for Mailu pods to be ready..." echo "This may take 5-10 minutes (ClamAV takes time to initialize)..." # Wait for admin pod first (it's the key dependency) kubectl wait --for=condition=ready pod -l app.kubernetes.io/component=admin -n "$NAMESPACE" --timeout=300s || { print_error "Admin pod failed to start. Checking logs..." kubectl logs -n "$NAMESPACE" -l app.kubernetes.io/component=admin --tail=50 exit 1 } print_success "Admin pod is ready" # Show pod status echo "" echo "Mailu Pod Status:" kubectl get pods -n "$NAMESPACE" | grep mailu print_success "Admin user created automatically via Helm initialAccount" # ============================================================================= # Summary # ============================================================================= echo "" echo "==============================================" echo -e "${GREEN}Mailu Deployment Complete!${NC}" echo "==============================================" echo "" echo "Admin Credentials:" echo " Email: admin@$DOMAIN" echo " Password: $ADMIN_PASSWORD" echo "" echo "Access URLs (configure Ingress/DNS first):" echo " Admin Panel: https://mail.$DOMAIN/admin" echo " Webmail: https://mail.$DOMAIN/webmail" echo " SMTP: mail.$DOMAIN:587 (STARTTLS)" echo " IMAP: mail.$DOMAIN:993 (SSL)" 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" echo " 3. Add DKIM TXT record to DNS" echo " 4. Configure Ingress for mail.$DOMAIN" echo "" echo "To check pod status:" echo " kubectl get pods -n $NAMESPACE | grep mailu" echo ""