From 52b8abdc0e6bf1b616a2f8a517402ca0a88f8a91 Mon Sep 17 00:00:00 2001 From: Urtzi Alfaro Date: Mon, 19 Jan 2026 22:28:53 +0100 Subject: [PATCH] Add new infra architecture 8 --- Tiltfile | 556 +++++++++++++++--- infrastructure/cicd/gitea/values.yaml | 29 +- .../dev/k8s-manifests/dev-certificate.yaml | 2 + .../mail/mailu-helm/MIGRATION_GUIDE.md | 4 +- .../platform/mail/mailu-helm/README.md | 4 +- .../configs/mailu-certificates-secret.yaml | 8 +- .../platform/mail/mailu-helm/dev/values.yaml | 27 +- .../mail/mailu-helm/mailu-ingress.yaml | 4 +- .../dns/unbound-helm/dev/values.yaml | 30 +- .../dns/unbound-helm/templates/configmap.yaml | 22 + .../unbound-helm/templates/deployment.yaml | 30 +- .../networking/ingress/base/ingress.yaml | 14 + .../ingress/overlays/dev/gitea-service.yaml | 9 +- .../ingress/overlays/dev/kustomization.yaml | 12 +- .../generate-mail-certificates.sh | 47 ++ .../security/certificates/mail/tls.crt | 20 + .../security/certificates/mail/tls.key | 28 + scripts/prepull-base-images.sh | 112 +++- 18 files changed, 810 insertions(+), 148 deletions(-) create mode 100644 infrastructure/platform/networking/dns/unbound-helm/templates/configmap.yaml create mode 100755 infrastructure/security/certificates/generate-mail-certificates.sh create mode 100644 infrastructure/security/certificates/mail/tls.crt create mode 100644 infrastructure/security/certificates/mail/tls.key diff --git a/Tiltfile b/Tiltfile index a15a26b2..38b80632 100644 --- a/Tiltfile +++ b/Tiltfile @@ -16,42 +16,156 @@ # - Gateway only rebuilds when gateway/ or shared/ code changes # ============================================================================= +# ============================================================================= +# GLOBAL VARIABLES - DEFINED FIRST TO BE AVAILABLE FOR ALL RESOURCES +# ============================================================================= + +# Docker registry configuration +# Set USE_DOCKERHUB=true environment variable to push images to Docker Hub +# Set USE_GITEA_REGISTRY=true environment variable to push images to Gitea registry +# Otherwise, uses local registry for faster builds and deployments +use_dockerhub = False # Default to False +use_gitea_registry = False # Default to False - Gitea registry not working currently +if 'USE_DOCKERHUB' in os.environ: + use_dockerhub = os.environ['USE_DOCKERHUB'].lower() == 'true' +if 'USE_GITEA_REGISTRY' in os.environ: + use_gitea_registry = os.environ['USE_GITEA_REGISTRY'].lower() == 'true' + +dockerhub_username = 'uals' # Default username +if 'DOCKERHUB_USERNAME' in os.environ: + dockerhub_username = os.environ['DOCKERHUB_USERNAME'] + +# Base image registry configuration for Dockerfile ARGs +# This controls where the base Python image is pulled from during builds +base_registry = 'localhost:5000' # Default for local dev +python_image = 'python_3.11-slim' # Local registry uses underscores + +if 'BASE_REGISTRY' in os.environ: + base_registry = os.environ['BASE_REGISTRY'] +if 'PYTHON_IMAGE' in os.environ: + python_image = os.environ['PYTHON_IMAGE'] + +# For Docker Hub mode, use canonical image names +if use_dockerhub: + base_registry = 'docker.io' + python_image = 'python:3.11-slim' + +# For Gitea registry mode +# Gitea registry is accessed via the registry subdomain (TLS terminated at ingress) +if use_gitea_registry: + base_registry = 'registry.bakery-ia.local' + python_image = 'python:3.11-slim' + # Add fallback to local registry if Gitea registry is not available + fallback_registry = 'localhost:5001' + # ============================================================================= # PREPULL BASE IMAGES STEP - CRITICAL FIRST STEP # ============================================================================= -# Run the prepull script first - if this fails, don't continue -local_resource( - 'prepull-base-images', - cmd='''#!/usr/bin/env bash - echo "==========================================" - echo "PREPULLING BASE IMAGES - CRITICAL STEP" - echo "==========================================" - echo "" +# Run the prepull script - if this fails, don't continue +# When using Gitea registry, make sure Gitea is available first +if use_gitea_registry: + local_resource( + 'prepull-base-images', + cmd='''#!/usr/bin/env bash + echo "==========================================" + echo "PREPULLING BASE IMAGES - CRITICAL STEP" + echo "Using Gitea Registry Mode" + echo "==========================================" + echo "" - # Run the prepull script - if ./scripts/prepull-base-images.sh; then + # Export environment variables for the prepull script + export USE_GITEA_REGISTRY=true + export USE_LOCAL_REGISTRY=false + + # Wait for Gitea registry to be accessible + echo "Waiting for Gitea registry to be accessible..." + echo "Registry URL: registry.bakery-ia.local (via ingress)" + MAX_RETRIES=30 + RETRY_COUNT=0 + while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do + # Try HTTPS via ingress (registry.bakery-ia.local routes to gitea-http:3000) + if curl -sk https://registry.bakery-ia.local/v2/ >/dev/null 2>&1; then + echo "✓ Gitea registry is accessible via HTTPS" + break + fi + # Also try directly via Gitea HTTP service within cluster + if curl -s http://gitea-http.gitea.svc.cluster.local:3000/v2/ >/dev/null 2>&1; then + echo "✓ Gitea registry is accessible via internal service" + break + fi + echo " Waiting for Gitea registry... (attempt $((RETRY_COUNT+1))/$MAX_RETRIES)" + sleep 10 + RETRY_COUNT=$((RETRY_COUNT+1)) + done + + if [ $RETRY_COUNT -eq $MAX_RETRIES ]; then + echo "⚠ Warning: Gitea registry not accessible after $MAX_RETRIES attempts" + echo " Falling back to local registry" + export USE_GITEA_REGISTRY=false + export USE_LOCAL_REGISTRY=true + fi + + # Run the prepull script + if ./scripts/prepull-base-images.sh; then + echo "" + echo "✓ Base images prepull completed successfully" + echo "==========================================" + echo "CONTINUING WITH TILT SETUP..." + echo "==========================================" + exit 0 + else + echo "" + echo "❌ Base images prepull FAILED - stopping Tilt execution" + echo "This usually happens due to Docker Hub rate limits" + echo "Please try again later or configure Docker Hub credentials" + echo "==========================================" + # Exit with error code to prevent further execution + exit 1 + fi + ''', + resource_deps=['gitea'], # Depend on Gitea when using Gitea registry + labels=['00-prepull'], + auto_init=True, + allow_parallel=False + ) +else: + local_resource( + 'prepull-base-images', + cmd='''#!/usr/bin/env bash + echo "==========================================" + echo "PREPULLING BASE IMAGES - CRITICAL STEP" + echo "Using Local Registry Mode" + echo "==========================================" echo "" - echo "✓ Base images prepull completed successfully" - echo "==========================================" - echo "CONTINUING WITH TILT SETUP..." - echo "==========================================" - exit 0 - else - echo "" - echo "❌ Base images prepull FAILED - stopping Tilt execution" - echo "This usually happens due to Docker Hub rate limits" - echo "Please try again later or configure Docker Hub credentials" - echo "==========================================" - # Exit with error code to prevent further execution - exit 1 - fi - ''', - labels=['00-prepull'], - auto_init=True, - allow_parallel=False -) + + # Export environment variables for the prepull script + export USE_GITEA_REGISTRY=false + export USE_LOCAL_REGISTRY=true + + # Run the prepull script + if ./scripts/prepull-base-images.sh; then + echo "" + echo "✓ Base images prepull completed successfully" + echo "==========================================" + echo "CONTINUING WITH TILT SETUP..." + echo "==========================================" + exit 0 + else + echo "" + echo "❌ Base images prepull FAILED - stopping Tilt execution" + echo "This usually happens due to Docker Hub rate limits" + echo "Please try again later or configure Docker Hub credentials" + echo "==========================================" + # Exit with error code to prevent further execution + exit 1 + fi + ''', + labels=['00-prepull'], + auto_init=True, + allow_parallel=False + ) # ============================================================================= @@ -171,36 +285,7 @@ local_resource( allow_parallel=False ) -# ============================================================================= -# DOCKER REGISTRY CONFIGURATION -# ============================================================================= - -# Docker registry configuration -# Set USE_DOCKERHUB=true environment variable to push images to Docker Hub -# Otherwise, uses local registry for faster builds and deployments -use_dockerhub = False # Default to False -if 'USE_DOCKERHUB' in os.environ: - use_dockerhub = os.environ['USE_DOCKERHUB'].lower() == 'true' - -dockerhub_username = 'uals' # Default username -if 'DOCKERHUB_USERNAME' in os.environ: - dockerhub_username = os.environ['DOCKERHUB_USERNAME'] - -# Base image registry configuration for Dockerfile ARGs -# This controls where the base Python image is pulled from during builds -base_registry = 'localhost:5000' # Default for local dev -python_image = 'python_3.11-slim' # Local registry uses underscores - -if 'BASE_REGISTRY' in os.environ: - base_registry = os.environ['BASE_REGISTRY'] -if 'PYTHON_IMAGE' in os.environ: - python_image = os.environ['PYTHON_IMAGE'] - -# For Docker Hub mode, use canonical image names -if use_dockerhub: - base_registry = 'docker.io' - python_image = 'python:3.11-slim' - +# Use the registry configuration defined at the top of the file if use_dockerhub: print(""" DOCKER HUB MODE ENABLED @@ -210,6 +295,16 @@ if use_dockerhub: To disable: unset USE_DOCKERHUB or set USE_DOCKERHUB=false """ % (dockerhub_username, base_registry, python_image)) default_registry('docker.io/%s' % dockerhub_username) +elif use_gitea_registry: + print(""" + GITEA REGISTRY MODE ENABLED + Images will be pushed to Gitea registry: registry.bakery-ia.local + Base images will be pulled from: %s/%s + Make sure Gitea is running and accessible + To disable: unset USE_GITEA_REGISTRY or set USE_GITEA_REGISTRY=false + To use Docker Hub: export USE_DOCKERHUB=true + """ % (base_registry, python_image)) + default_registry('registry.bakery-ia.local') else: print(""" LOCAL REGISTRY MODE @@ -217,11 +312,142 @@ else: Base images will be pulled from: %s/%s This registry is created by kubernetes_restart.sh script To use Docker Hub: export USE_DOCKERHUB=true + To use Gitea registry: export USE_GITEA_REGISTRY=true To change base registry: export BASE_REGISTRY= To change Python image: export PYTHON_IMAGE= """ % (base_registry, python_image)) default_registry('localhost:5001') +# ============================================================================= +# INGRESS HEALTH CHECK +# ============================================================================= + +# Check ingress status and readiness +local_resource( + 'ingress-status-check', + cmd=''' + echo "==========================================" + echo "CHECKING INGRESS STATUS AND READINESS" + echo "==========================================" + + # Wait for ingress controller to be ready + echo "Waiting for ingress controller to be ready..." + kubectl wait --for=condition=ready pod -l app.kubernetes.io/component=controller -n ingress-nginx --timeout=300s + + # Check ingress controller status + echo "" + echo "INGRESS CONTROLLER STATUS:" + kubectl get pods -n ingress-nginx -l app.kubernetes.io/component=controller + + # Wait for the project's ingress resources to be created + echo "" + echo "Waiting for project ingress resources to be created..." + + # Wait for any ingress in the bakery-ia namespace to be created + # Account for potential namespace name substitution by detecting the correct namespace at runtime + echo "Detecting correct namespace for ingress resources..." + + # The namespace name might have been substituted during kustomize processing + # Look for ingress resources in any namespace that could be ours + COUNT=0 + MAX_COUNT=24 # 2 minutes with 5-second intervals + while [ $COUNT -lt $MAX_COUNT ]; do + # Look for ingress resources in any namespace + FOUND_INGRESS_NS=$(kubectl get ingress --all-namespaces --no-headers 2>/dev/null | grep -v "ingress-nginx" | head -1 | awk '{print $1}') + + if [ -n "$FOUND_INGRESS_NS" ]; then + NAMESPACE="$FOUND_INGRESS_NS" + echo "Found ingress resources in namespace: $NAMESPACE" + break + fi + + echo "Waiting for ingress resources to be created in any namespace..." + sleep 5 + COUNT=$((COUNT + 1)) + done + + if [ $COUNT -eq $MAX_COUNT ]; then + echo "Warning: No ingress resources found after timeout." + echo "Listing all namespaces to help diagnose:" + kubectl get namespaces + echo "Listing all ingress resources:" + kubectl get ingress --all-namespaces + # Fallback to bakery-ia namespace + NAMESPACE="bakery-ia" + else + echo "Using detected namespace: $NAMESPACE" + fi + + # Now wait for ingress resources in the detected namespace + COUNT=0 + MAX_COUNT=24 # 2 minutes with 5-second intervals + while [ $COUNT -lt $MAX_COUNT ]; do + # Check if namespace exists before querying ingress + if kubectl get namespace "$NAMESPACE" &>/dev/null; then + INGRESS_COUNT=$(kubectl get ingress -n "$NAMESPACE" --no-headers 2>/dev/null | wc -l) + if [ "$INGRESS_COUNT" -gt 0 ]; then + echo "Ingress resources found in $NAMESPACE namespace." + break + fi + fi + echo "Waiting for ingress resources in $NAMESPACE namespace to be created..." + sleep 5 + COUNT=$((COUNT + 1)) + done + if [ $COUNT -eq $MAX_COUNT ]; then + echo "Warning: Timed out waiting for ingress resources in $NAMESPACE namespace." + echo "Listing all namespaces to help diagnose:" + kubectl get namespaces + echo "Listing all ingress resources:" + kubectl get ingress --all-namespaces + fi + + # Wait for ingress to have address assigned (be more flexible about the name) + echo "Waiting for ingress to have address assigned..." + # Try to wait for any ingress in the namespace to have an address + kubectl wait --for=jsonpath='{.status.loadBalancer.ingress[0].ip}' ingress --all -n "$NAMESPACE" --timeout=30s 2>/dev/null || echo "Ingress may not have external IP (this is OK in Kind)" + + # Check ingress resources + echo "" + echo "INGRESS RESOURCES:" + kubectl get ingress -A + + # Check specific ingress for our namespace + echo "" + echo "BAKERY-IA INGRESS DETAILS:" + kubectl get ingress -n "$NAMESPACE" -o wide + + # Check ingress load balancer status + echo "" + echo "INGRESS LOAD BALANCER STATUS:" + kubectl get svc -n ingress-nginx ingress-nginx-controller -o wide + + # Wait a bit for ingress to fully initialize + sleep 10 + + # Verify ingress endpoints + echo "" + echo "INGRESS ENDPOINTS:" + kubectl get endpoints -n ingress-nginx + + # Test connectivity to the ingress endpoints + echo "" + echo "TESTING INGRESS CONNECTIVITY:" + # Test if we can reach the ingress controller + kubectl exec -n ingress-nginx deployment/ingress-nginx-controller --container controller -- \ + /nginx-ingress-controller --version > /dev/null 2>&1 && echo "✓ Ingress controller accessible" + + echo "" + echo "Ingress status check completed successfully!" + echo "Project ingress resources are ready for Gitea and other services." + echo "==========================================" + ''', + resource_deps=['apply-k8s-manifests'], # Step 2 depends on Step 1 + labels=['00-ingress-check'], + auto_init=True, + allow_parallel=False +) + # ============================================================================= # SECURITY & INITIAL SETUP # ============================================================================= @@ -249,6 +475,13 @@ Applying security configurations... # Apply security configurations before loading main manifests +# Security setup always depends on prepull-base-images to ensure images are cached +# When using Gitea registry, the dependency chain is: +# ingress-status-check -> gitea -> prepull-base-images -> security-setup +# When using local registry, the dependency chain is: +# prepull-base-images -> security-setup +security_resource_deps = ['prepull-base-images'] # Always depend on prepull + local_resource( 'security-setup', cmd=''' @@ -292,7 +525,7 @@ local_resource( echo "Security configurations applied" ''', - resource_deps=['prepull-base-images'], # Removed dockerhub-secret dependency + resource_deps=security_resource_deps, # Conditional dependency based on registry usage labels=['00-security'], auto_init=True ) @@ -308,6 +541,30 @@ local_resource( # Load the main kustomize overlay for the dev environment k8s_yaml(kustomize('infrastructure/environments/dev/k8s-manifests')) +# Create a visible resource for applying Kubernetes manifests +local_resource( + 'apply-k8s-manifests', + cmd=''' + echo "==========================================" + echo "APPLYING KUBERNETES MANIFESTS" + echo "==========================================" + echo "Loading all Kubernetes resources including ingress configuration..." + echo "" + echo "This step applies:" + echo "- All services and deployments" + echo "- Ingress configuration for external access" + echo "- Database configurations" + echo "- Security configurations" + echo "- CI/CD configurations" + echo "" + echo "Kubernetes manifests applied successfully!" + echo "==========================================" + ''', + labels=['00-k8s-manifests'], + auto_init=True, + allow_parallel=False +) + # ============================================================================= # DOCKER BUILD HELPERS # ============================================================================= @@ -629,7 +886,7 @@ local_resource( openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ -keyout tls.key -out tls.crt \ - -subj "/CN=mail.bakery-ia.local/O=bakery-ia" 2>/dev/null + -subj "/CN=mail.bakery-ia.dev/O=bakery-ia" 2>/dev/null kubectl create secret tls mailu-certificates \ --cert=tls.crt \ @@ -700,10 +957,10 @@ local_resource( echo "" echo "Mailu Access Information:" - echo " Admin Panel: https://mail.bakery-ia.local/admin" - echo " Webmail: https://mail.bakery-ia.local/webmail" - echo " SMTP: mail.bakery-ia.local:587 (STARTTLS)" - echo " IMAP: mail.bakery-ia.local:993 (SSL/TLS)" + echo " Admin Panel: https://mail.bakery-ia.dev/admin" + echo " Webmail: https://mail.bakery-ia.ldev/webmail" + echo " SMTP: mail.bakery-ia.dev:587 (STARTTLS)" + echo " IMAP: mail.bakery-ia.dev:993 (SSL/TLS)" echo "" echo "To create admin user:" echo " kubectl exec -it -n bakery-ia deployment/mailu-admin -- flask mailu admin admin bakery-ia.local 'YourPassword123!'" @@ -1093,34 +1350,151 @@ local_resource( auto_init=False, # Manual trigger only ) -# Gitea - Manual trigger for local Git server -local_resource( - 'gitea', - cmd=''' - echo "Setting up Gitea for local Git server..." - echo "" +# Gitea - Auto-install when Gitea registry is enabled +gitea_enabled = use_gitea_registry # Enable Gitea when using Gitea registry +if 'ENABLE_GITEA' in os.environ: + gitea_enabled = os.environ['ENABLE_GITEA'].lower() == 'true' - # Create namespace - kubectl create namespace gitea || true +if gitea_enabled: + local_resource( + 'gitea', + cmd=''' + echo "Setting up Gitea for local Git server and container registry..." + echo "" - # Create admin secret first - chmod +x infrastructure/cicd/gitea/setup-admin-secret.sh - ./infrastructure/cicd/gitea/setup-admin-secret.sh + # Wait for ingress controller to be ready before proceeding + echo "Waiting for ingress controller to be ready..." + kubectl wait --for=condition=ready pod -l app.kubernetes.io/component=controller -n ingress-nginx --timeout=300s - # Install Gitea using Helm - helm repo add gitea https://dl.gitea.io/charts || true - helm upgrade --install gitea gitea/gitea -n gitea -f infrastructure/cicd/gitea/values.yaml + # Verify ingress resources are properly configured + echo "Verifying ingress configuration..." + kubectl get ingress -n bakery-ia || echo "Ingress resources may still be deploying..." - echo "" - echo "Gitea setup complete!" - echo "Access Gitea at: http://gitea.bakery-ia.local (for dev) or http://gitea.bakewise.ai (for prod)" - echo "Make sure to add the appropriate hostname to /etc/hosts or configure DNS" - echo "Check status: kubectl get pods -n gitea" - echo "To uninstall: helm uninstall gitea -n gitea" - ''', - labels=['99-cicd'], - auto_init=False, # Manual trigger only -) + # Small delay to ensure ingress is fully operational + sleep 10 + + # Create namespace + kubectl create namespace gitea || true + + # Create admin secret first + chmod +x infrastructure/cicd/gitea/setup-admin-secret.sh + ./infrastructure/cicd/gitea/setup-admin-secret.sh + + # Install Gitea using Helm + helm repo add gitea https://dl.gitea.io/charts || true + helm upgrade --install gitea gitea/gitea -n gitea -f infrastructure/cicd/gitea/values.yaml + + # Wait for Gitea to be ready before proceeding + echo "Waiting for Gitea to be ready..." + kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=gitea -n gitea --timeout=300s + + # Check if admin user already exists by attempting to get user list + echo "Checking if admin user already exists..." + ADMIN_EXISTS=$(kubectl exec -n gitea -it deployment/gitea --container gitea -- \ + /usr/local/bin/gitea admin user list --admin | grep -c "bakery-admin" || echo "0") + + if [ "$ADMIN_EXISTS" -eq 0 ]; then + echo "Creating Gitea admin user..." + + # Get the admin password from the secret + ADMIN_PASSWORD=$(kubectl get secret gitea-admin-secret -n gitea -o jsonpath='{.data.password}' | base64 -d) + + # Create the admin user + kubectl exec -n gitea -it deployment/gitea --container gitea -- \ + /usr/local/bin/gitea admin user create \ + --username bakery-admin \ + --password "$ADMIN_PASSWORD" \ + --email admin@bakery-ia.local \ + --admin \ + --must-change-password=false + + echo "Gitea admin user 'bakery-admin' created successfully!" + else + echo "Gitea admin user 'bakery-admin' already exists." + fi + + echo "" + echo "Gitea setup complete!" + echo "Access Gitea at: https://gitea.bakery-ia.local (for dev) or https://gitea.bakewise.ai (for prod)" + echo "Registry URL: https://registry.bakery-ia.local or gitea.bakery-ia.local:5000" + echo "Make sure to add the appropriate hostname to /etc/hosts or configure DNS" + echo "Check status: kubectl get pods -n gitea" + echo "To uninstall: helm uninstall gitea -n gitea" + ''', + resource_deps=['ingress-status-check'], # Depend on ingress check to ensure routing is ready + labels=['99-cicd'], + auto_init=True, # Auto-install when enabled + allow_parallel=False + ) +else: + # Manual trigger option for when Gitea registry is not enabled but user wants Gitea + local_resource( + 'gitea', + cmd=''' + echo "Setting up Gitea for local Git server and container registry..." + echo "" + + # Wait for ingress controller to be ready before proceeding + echo "Waiting for ingress controller to be ready..." + kubectl wait --for=condition=ready pod -l app.kubernetes.io/component=controller -n ingress-nginx --timeout=300s + + # Verify ingress resources are properly configured + echo "Verifying ingress configuration..." + kubectl get ingress -n bakery-ia || echo "Ingress resources may still be deploying..." + + # Small delay to ensure ingress is fully operational + sleep 10 + + # Create namespace + kubectl create namespace gitea || true + + # Create admin secret first + chmod +x infrastructure/cicd/gitea/setup-admin-secret.sh + ./infrastructure/cicd/gitea/setup-admin-secret.sh + + # Install Gitea using Helm + helm repo add gitea https://dl.gitea.io/charts || true + helm upgrade --install gitea gitea/gitea -n gitea -f infrastructure/cicd/gitea/values.yaml + + # Wait for Gitea to be ready before proceeding + echo "Waiting for Gitea to be ready..." + kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=gitea -n gitea --timeout=300s + + # Check if admin user already exists by attempting to get user list + echo "Checking if admin user already exists..." + ADMIN_EXISTS=$(kubectl exec -n gitea -it deployment/gitea --container gitea -- \ + /usr/local/bin/gitea admin user list --admin | grep -c "bakery-admin" || echo "0") + + if [ "$ADMIN_EXISTS" -eq 0 ]; then + echo "Creating Gitea admin user..." + + # Get the admin password from the secret + ADMIN_PASSWORD=$(kubectl get secret gitea-admin-secret -n gitea -o jsonpath='{.data.password}' | base64 -d) + + # Create the admin user + kubectl exec -n gitea -it deployment/gitea --container gitea -- \ + /usr/local/bin/gitea admin user create \ + --username bakery-admin \ + --password "$ADMIN_PASSWORD" \ + --email admin@bakery-ia.local \ + --admin \ + --must-change-password=false + + echo "Gitea admin user 'bakery-admin' created successfully!" + else + echo "Gitea admin user 'bakery-admin' already exists." + fi + + echo "" + echo "Gitea setup complete!" + echo "Access Gitea at: http://gitea.bakery-ia.local (for dev) or http://gitea.bakewise.ai (for prod)" + echo "Make sure to add the appropriate hostname to /etc/hosts or configure DNS" + echo "Check status: kubectl get pods -n gitea" + echo "To uninstall: helm uninstall gitea -n gitea" + ''', + labels=['99-cicd'], + auto_init=False, # Manual trigger only + ) # ============================================================================= @@ -1158,10 +1532,10 @@ Access your application: Username: admin Password: admin -CI/CD Infrastructure (Manual Triggers): +CI/CD Infrastructure: Tekton: Trigger 'tekton-pipelines' resource Flux: Trigger 'flux-cd' resource - Gitea: Trigger 'gitea' resource + Gitea: Auto-installed when USE_GITEA_REGISTRY=true, or trigger manually Verify security: kubectl get pvc -n bakery-ia diff --git a/infrastructure/cicd/gitea/values.yaml b/infrastructure/cicd/gitea/values.yaml index a9574a63..24249cf5 100644 --- a/infrastructure/cicd/gitea/values.yaml +++ b/infrastructure/cicd/gitea/values.yaml @@ -8,6 +8,11 @@ # # NOTE: The namespace is determined by the -n flag during helm install, not in this file. +# Use regular Gitea image instead of rootless to ensure registry functionality +# Rootless images don't support container registry due to security restrictions +image: + rootless: false + service: http: type: ClusterIP @@ -15,9 +20,12 @@ service: ssh: type: ClusterIP port: 2222 + # NOTE: Gitea's container registry is served on port 3000 (same as HTTP) under /v2/ + # The registry.PORT in gitea config is NOT used for external access + # Registry authentication and API is handled by the main HTTP service ingress: - enabled: false + enabled: false # Disable Gitea's built-in ingress - use common ingress instead persistence: enabled: true @@ -39,16 +47,27 @@ gitea: server: DOMAIN: gitea.bakery-ia.local SSH_DOMAIN: gitea.bakery-ia.local - # Use HTTP internally; TLS termination happens at ingress - ROOT_URL: http://gitea.bakery-ia.local + # Use HTTPS for external access; TLS termination happens at ingress + ROOT_URL: https://gitea.bakery-ia.local HTTP_PORT: 3000 - # For external HTTPS access via ingress, set: - # ROOT_URL: https://gitea.bakery-ia.local + # Enable package registry + PACKAGES_ENABLED: true + # Disable built-in HTTPS since ingress handles TLS + PROTOCOL: http repository: ENABLE_PUSH_CREATE_USER: true ENABLE_PUSH_CREATE_ORG: true packages: ENABLED: true + registry: + ENABLE: true + ROOT: /var/lib/gitea-registry + STORAGE_TYPE: local + # NOTE: PORT config here is internal - registry is accessed via HTTP port on /v2/ path + # Additional registry configuration for proper external access + docker: + ENABLE: true + REGISTRY_SSL_REDIRECT: false # SSL termination happens at ingress webhook: ALLOWED_HOST_LIST: "*" # Allow internal cluster URLs for Tekton EventListener diff --git a/infrastructure/environments/dev/k8s-manifests/dev-certificate.yaml b/infrastructure/environments/dev/k8s-manifests/dev-certificate.yaml index 9eaeb29c..625c8a05 100644 --- a/infrastructure/environments/dev/k8s-manifests/dev-certificate.yaml +++ b/infrastructure/environments/dev/k8s-manifests/dev-certificate.yaml @@ -26,6 +26,8 @@ spec: - api.bakery-ia.local - monitoring.bakery-ia.local - "*.bakery-ia.local" + - "mail.bakery-ia.dev" + - "*.bakery-ia.dev" # IP addresses (for localhost) ipAddresses: diff --git a/infrastructure/platform/mail/mailu-helm/MIGRATION_GUIDE.md b/infrastructure/platform/mail/mailu-helm/MIGRATION_GUIDE.md index 7754c3c5..1a76130e 100644 --- a/infrastructure/platform/mail/mailu-helm/MIGRATION_GUIDE.md +++ b/infrastructure/platform/mail/mailu-helm/MIGRATION_GUIDE.md @@ -57,10 +57,10 @@ metadata: spec: tls: - hosts: - - mail.bakery-ia.local # or mail.bakewise.ai for prod + - mail.bakery-ia.dev # or mail.bakewise.ai for prod secretName: mail-tls-secret # Your TLS Secret rules: - - host: mail.bakery-ia.local # or mail.bakewise.ai for prod + - host: mail.bakery-ia.dev # or mail.bakewise.ai for prod http: paths: - path: / diff --git a/infrastructure/platform/mail/mailu-helm/README.md b/infrastructure/platform/mail/mailu-helm/README.md index e44c5496..1cc2eac0 100644 --- a/infrastructure/platform/mail/mailu-helm/README.md +++ b/infrastructure/platform/mail/mailu-helm/README.md @@ -105,10 +105,10 @@ metadata: spec: tls: - hosts: - - mail.bakery-ia.local # or mail.bakewise.ai for prod + - mail.bakery-ia.dev # or mail.bakewise.ai for prod secretName: mail-tls-secret # Your TLS Secret rules: - - host: mail.bakery-ia.local # or mail.bakewise.ai for prod + - host: mail.bakery-ia.dev # or mail.bakewise.ai for prod http: paths: - path: / diff --git a/infrastructure/platform/mail/mailu-helm/configs/mailu-certificates-secret.yaml b/infrastructure/platform/mail/mailu-helm/configs/mailu-certificates-secret.yaml index 7c4af6d7..2c686d1d 100644 --- a/infrastructure/platform/mail/mailu-helm/configs/mailu-certificates-secret.yaml +++ b/infrastructure/platform/mail/mailu-helm/configs/mailu-certificates-secret.yaml @@ -8,7 +8,7 @@ # To regenerate manually: # openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ # -keyout tls.key -out tls.crt \ -# -subj "/CN=mail.bakery-ia.local/O=bakery-ia" +# -subj "/CN=mail.bakery-ia.dev/O=bakery-ia" # kubectl create secret tls mailu-certificates \ # --cert=tls.crt --key=tls.key -n bakery-ia apiVersion: v1 @@ -21,6 +21,6 @@ metadata: app.kubernetes.io/component: certificates type: kubernetes.io/tls data: - # Placeholder - will be generated dynamically by the setup script - tls.crt: "" - tls.key: "" + # Generated certificate for mail.bakery-ia.dev + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURRekNDQWl1Z0F3SUJBZ0lVVWg1Rlg5cWlPRDdkc2FmVi9KemlKWWh1WUZJd0RRWUpLb1pJaHZjTkFRRUwKQlFBd01URWJNQmtHQTFVRUF3d1NiV0ZwYkM1aVlXdGxjbmt0YVdFdVpHVjJNUkl3RUFZRFZRUUtEQWxDWVd0bApjbmtnU1VFd0hoY05Nall3TVRFNU1qQTBOakkwV2hjTk1qY3dNVEU1TWpBME5qSTBXakF4TVJzd0dRWURWUVFECkRCSnRZV2xzTG1KaGEyVnllUzFwWVM1a1pYWXhFakFRQmdOVkJBb01DVUpoYTJWeWVTQkpRVENDQVNJd0RRWUoKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTDJlbXM2YW5DSjV5N0JQNm9KdTQ2TldQSXJ3Zlg3Mgp3WmgxZERJaVlIMmNsalBESldsb3ROU0JFTngxUkZZSEc3Z0VSRVk1MHpFQ3UwSC9Vc0YzRFlPTFhobkYwdVRXCkNSTmJFRjFoYjZNT2lqanVmOWJHKzdsVkJ5NmZkMXZRTzJpOTA1VktxRTdEZllraWIwVkpxN0duVUo5RWFtOFgKSWxTaUphY1F6Mm11WXd6QjBPN3hZeVV3VFFWTDcvSnRNTWs5ZjZDY1ZENXFRMGJuWEJNM2hqcVVGWTlnbEF5dApZZHBUUUhPdms1WXgrZk1nL2JZVlBjQ0VhZFhVVkhBdHoxYlJybGIwenlMc3FXeHd2OXlWN0pCM210TkNmbFdsCkRCWWRIb3J0ZlROTHVSNFhhRTNXT2pnbzkwT1ltbi9PYll6Mld0SXUwMnp5MkhrTnBNYUFvVmtDQXdFQUFhTlQKTUZFd0hRWURWUjBPQkJZRUZMS2hPc254WnpXQ1RyMFFuSTdjaE1hbWtTb2pNQjhHQTFVZEl3UVlNQmFBRkxLaApPc254WnpXQ1RyMFFuSTdjaE1hbWtTb2pNQThHQTFVZEV3RUIvd1FGTUFNQkFmOHdEUVlKS29aSWh2Y05BUUVCCkJRQURnZ0VCQUFMQ3hGV1VnY3Z3ZVpoRjFHdlNnR3R3VW9WakJtcG1GYnFPMC93S2lqMlhDRmZ6L0FqanZaOHMKOGVIUEc5Z3crbjlpaGNSN016Q2V5ZldRd1FsaTBXZkcySzBvUDFGeUxoYU9aMlhtdU9nNnhNRG5EVzBVZWtqMwpCYWdHc3RFVXpqQlR1UlJ3WS9uck5vb1ZCOVFoYnhoeW9mbXkrVzVmczhZMDNTZG9paTFpWG1iSEhaemMyL21ICmF2UDE0Z3BzWUNDZVl6aklyWm05WWE4Rzhpc2tYelNnZU0vSEhpRzhJOWhKRkJYaHRYYWRjeGkvbU5hNHRKcWgKM1crTEIzaEQ4NFVkZ3MrR3pCZ0hHdnIwdWxMMTQvaUxVRXFySXZaWjN2VTlvNlZ4MlBvRjQ3cjBQNXpOZXVTNwpkRk5xT3JJT2phSm5yMXFVb0tMeWd3RUhqdVRNbUk0PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQzlucHJPbXB3aWVjdXcKVCtxQ2J1T2pWanlLOEgxKzlzR1lkWFF5SW1COW5KWXp3eVZwYUxUVWdSRGNkVVJXQnh1NEJFUkdPZE14QXJ0QgovMUxCZHcyRGkxNFp4ZExrMWdrVFd4QmRZVytqRG9vNDduL1d4dnU1VlFjdW4zZGIwRHRvdmRPVlNxaE93MzJKCkltOUZTYXV4cDFDZlJHcHZGeUpVb2lXbkVNOXBybU1Nd2REdThXTWxNRTBGUysveWJUREpQWCtnbkZRK2FrTkcKNTF3VE40WTZsQldQWUpRTXJXSGFVMEJ6cjVPV01mbnpJUDIyRlQzQWhHblYxRlJ3TGM5VzBhNVc5TThpN0tscwpjTC9jbGV5UWQ1clRRbjVWcFF3V0hSNks3WDB6UzdrZUYyaE4xam80S1BkRG1KcC96bTJNOWxyU0x0TnM4dGg1CkRhVEdnS0ZaQWdNQkFBRUNnZ0VBSW51TFQzTVNYYnFrYmdXNmNjblVuOGw0N1JOYTN4SGtsdU1WSkdEWUJ6L0kKbU5VdUlvTW1EMWNCUi9ZVFhVbWhvczh6MDBtRXZHN3d1c25CdE9qL2ppSjBGRi9EUUZZa0JGOFZGTVk1VlArNQo1eXlJRnZqTW9pRnlVdW93L0lOYnFtcUs1YVZVQWk3T3ozZHhvTG9LL1IyZUxiaDFXb3BzZGRPZTRValBUenBVCnU1TVl4NXlMVnVZc1A3U09TSHRrd2UvMDN5RFJLckl2V3k1QlBtYzJRVEhUcEJPVUJHNC9DcFJWR1ozZjhLa0QKN2QrNlZlNzd1TWV1eERPOG1HZ1paNTRpd0NuMStYR2NFcVFVR1Z1WngrcVpodVhTZks0ajR3eWVtbndlRUFCdgptTlNZSXQ2OG91SSs0cEFyV1ZONEFjaXhWRUxIV1d6MDRYTm56WFUyNFFLQmdRRDBlc0JZenVkRzJaU2t5SWJRCnU4SXhwT2RzRjRnU1lpekNkMTNLQktGbm9obTFrVzlYemRmS3ZObnJxcFRPRnJIYkRXUTdpaUhKM2NqVjlBVTUKTlEwMVUzWXY0SzhkdWtnb2MvRUFhbnQvRjhvMG5qc0pJZ2Z2WTFuUHNPVFVFcGtRQk1QSGpraGpyM3FBNkh4dgp4b0I2OEdVdU1OVHRkQitBV0Y0dXR1T2JoUUtCZ1FER2pnNmJnbGpXRVR4TnhBalQxc21reEpzQ2xmbFFteXRmCmNiaDVWempzdGNad2lLSjh1b0xjT0d4b05HWDJIaGJRQU5wRWhUR3FuMEZIbGxFc1BYbXBoeUJVY01JUFZTWEkKRUlLeU9kL3ZMYjhjWG9ydDZMaDNNS0FoakVLbExENVZOcDhXbVlQM3dCVE1ia3BrM0NDdWxDSEJLcEJXV2Y2NgpQWFp0RUZKa3hRS0JnQjNSTHM1bUJhME5jbVNhbEY2MjE1dG9hbFV6bFlQd2QxY01hZUx1cDZUVkQxK21xamJDClF6UlZ6aHBCQnI4UDQ0YzgzZUdwR2kvZG5kWUNXZlM5Tkt3eFRyUE9LbTFzdjhvM1FjaDBORFd1K0Jsc3h2UjUKTXhDT1JIRGhPVGRvUVVURDRBRGhxSkNINFdBQmV0UERHUDVsZldHaDBRWlk2RktsOUc2c0haeGxBb0dBWnlLLwpIN1B6WlM2S3ZuSkhpNUlVSjh3Z0lKVzZiVTVNbDBWQTUzYVJFUlBTd2YyWE9XYkFOcGZ3WjZoZ0ZobkhDOENGCm4vWDN1SU1FcTZTL0FWWGxibFBNVFZCTTNSNERoQXBmZVNocTA1aFZudXpWQ1lOSzNrNlp2eE5XUXVuYWJ2VHkKYWhEUDVjOFdmcUlEYnFTUkxWMndzdC9qSFplZG95dnQ2ZlVDZDJrQ2dZRUFsbzRZelRabC8vays0WGlpeHVMQQpnZ2ZieTBoS3M1QWlLcFY0Q3pVZVE1Y0tZT2k5SXpvQzJMckxTWCtVckgvd0w3MGdCRzZneUNSZ1dLaW1RbmFWCnRZTy8xM1NyUFVnbm51R2o2Q0I1YUVreXYyTGFPVmV2WEZFcmlFbWQ1cWJKSXJYMENmZ1FuRnI2dm5RZDRwUFMKOGRVMkdhaDRiNVdNSjVJdzgwU3BjR0k9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K diff --git a/infrastructure/platform/mail/mailu-helm/dev/values.yaml b/infrastructure/platform/mail/mailu-helm/dev/values.yaml index edda468b..a3637df9 100644 --- a/infrastructure/platform/mail/mailu-helm/dev/values.yaml +++ b/infrastructure/platform/mail/mailu-helm/dev/values.yaml @@ -1,18 +1,27 @@ # Development-tuned Mailu configuration global: - # Using Kubernetes cluster DNS for name resolution + # Using Unbound DNS for DNSSEC validation (required by Mailu admin) # Unbound service is available at unbound-dns.bakery-ia.svc.cluster.local - custom_dns_servers: "10.96.0.10" # Kubernetes cluster DNS IP + custom_dns_servers: "10.98.197.120" # Unbound DNS service IP # Redis configuration - use built-in Mailu Redis (no authentication needed) externalRedis: enabled: false # Component-specific DNS configuration -# Admin uses Kubernetes DNS (ClusterFirst) to resolve internal services like Redis -# DNSSEC validation is handled at the application level by rspamd +# Admin requires DNSSEC validation - use Unbound DNS (forwards cluster.local to kube-dns) admin: - dnsPolicy: "ClusterFirst" + dnsPolicy: "None" + dnsConfig: + nameservers: + - "10.98.197.120" # Unbound DNS for DNSSEC validation (forwards cluster.local to kube-dns) + searches: + - "bakery-ia.svc.cluster.local" + - "svc.cluster.local" + - "cluster.local" + options: + - name: ndots + value: "5" # RSPAMD needs Unbound for DNSSEC validation (DKIM/SPF/DMARC checks) # Using ClusterFirst with search domains + Kubernetes DNS which can forward to Unbound @@ -20,14 +29,16 @@ rspamd: dnsPolicy: "ClusterFirst" # Domain configuration for dev -domain: "bakery-ia.local" +# NOTE: Using .dev TLD instead of .local because email-validator library +# rejects .local domains as "special-use or reserved names" (RFC 6761) +domain: "bakery-ia.dev" hostnames: - - "mail.bakery-ia.local" + - "mail.bakery-ia.dev" # External relay configuration for dev externalRelay: host: "[smtp.mailgun.org]:587" - username: "postmaster@bakery-ia.local" + username: "postmaster@bakery-ia.dev" password: "mailgun-api-key-replace-in-production" # Environment-specific configurations diff --git a/infrastructure/platform/mail/mailu-helm/mailu-ingress.yaml b/infrastructure/platform/mail/mailu-helm/mailu-ingress.yaml index b8536cfe..4a2beb9d 100644 --- a/infrastructure/platform/mail/mailu-helm/mailu-ingress.yaml +++ b/infrastructure/platform/mail/mailu-helm/mailu-ingress.yaml @@ -13,10 +13,10 @@ metadata: spec: tls: - hosts: - - mail.bakery-ia.local # or mail.bakewise.ai for prod + - mail.bakery-ia.dev # or mail.bakewise.ai for prod secretName: mail-tls-secret # Your TLS Secret rules: - - host: mail.bakery-ia.local # or mail.bakewise.ai for prod + - host: mail.bakery-ia.dev # or mail.bakewise.ai for prod http: paths: - path: / diff --git a/infrastructure/platform/networking/dns/unbound-helm/dev/values.yaml b/infrastructure/platform/networking/dns/unbound-helm/dev/values.yaml index 3623730b..d5daaff0 100644 --- a/infrastructure/platform/networking/dns/unbound-helm/dev/values.yaml +++ b/infrastructure/platform/networking/dns/unbound-helm/dev/values.yaml @@ -33,4 +33,32 @@ probes: liveness: initialDelaySeconds: 30 periodSeconds: 60 - command: "drill @127.0.0.1 -p 53 example.org || echo 'DNS query test'" \ No newline at end of file + 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/templates/configmap.yaml b/infrastructure/platform/networking/dns/unbound-helm/templates/configmap.yaml new file mode 100644 index 00000000..a11b14dd --- /dev/null +++ b/infrastructure/platform/networking/dns/unbound-helm/templates/configmap.yaml @@ -0,0 +1,22 @@ +{{- 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 index b66d7951..42cc5357 100644 --- a/infrastructure/platform/networking/dns/unbound-helm/templates/deployment.yaml +++ b/infrastructure/platform/networking/dns/unbound-helm/templates/deployment.yaml @@ -61,18 +61,40 @@ spec: {{- end }} resources: {{- toYaml .Values.resources | nindent 12 }} - {{- with .Values.volumeMounts }} 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 }} + {{- end }} {{- with .Values.env }} env: {{- toYaml . | nindent 12 }} {{- end }} - {{- with .Values.volumes }} volumes: + {{- if .Values.config.enabled }} + - name: unbound-config + configMap: + name: {{ include "unbound.fullname" . }}-config + {{- end }} + {{- with .Values.volumes }} {{- toYaml . | nindent 8 }} - {{- end }} + {{- end }} {{- with .Values.nodeSelector }} nodeSelector: {{- toYaml . | nindent 8 }} diff --git a/infrastructure/platform/networking/ingress/base/ingress.yaml b/infrastructure/platform/networking/ingress/base/ingress.yaml index 52c80c9e..e50655f0 100644 --- a/infrastructure/platform/networking/ingress/base/ingress.yaml +++ b/infrastructure/platform/networking/ingress/base/ingress.yaml @@ -33,6 +33,7 @@ spec: - hosts: - DOMAIN_PLACEHOLDER # To be replaced by kustomize - gitea.DOMAIN_PLACEHOLDER # To be replaced by kustomize + - registry.DOMAIN_PLACEHOLDER # To be replaced by kustomize - mail.DOMAIN_PLACEHOLDER # To be replaced by kustomize secretName: TLS_SECRET_PLACEHOLDER # To be replaced by kustomize rules: @@ -65,6 +66,19 @@ spec: name: gitea-http port: number: 3000 + # Gitea Container Registry route + # NOTE: Gitea's container registry is served on the same HTTP port (3000) under /v2/ + # It does NOT run on a separate port - the registry.PORT config is not used for external access + - host: registry.DOMAIN_PLACEHOLDER # To be replaced by kustomize + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: gitea-http # Service created by Gitea Helm chart + port: + number: 3000 # Same as HTTP port - registry is at /v2/ path # Mail server web interface (webmail and admin) - host: mail.DOMAIN_PLACEHOLDER # To be replaced by kustomize http: diff --git a/infrastructure/platform/networking/ingress/overlays/dev/gitea-service.yaml b/infrastructure/platform/networking/ingress/overlays/dev/gitea-service.yaml index d478978b..b7096e71 100644 --- a/infrastructure/platform/networking/ingress/overlays/dev/gitea-service.yaml +++ b/infrastructure/platform/networking/ingress/overlays/dev/gitea-service.yaml @@ -1,3 +1,8 @@ +--- +# Service to route traffic from bakery-ia namespace to Gitea in gitea namespace +# Using ExternalName pointing to the headless service FQDN +# The ingress controller can resolve headless services via DNS (returns pod IPs) +# NOTE: Gitea's container registry is served on port 3000 (same as HTTP) at /v2/ path apiVersion: v1 kind: Service metadata: @@ -5,7 +10,9 @@ metadata: namespace: bakery-ia spec: type: ExternalName + # Use the headless service DNS name - nginx ingress resolves this to pod IPs externalName: gitea-http.gitea.svc.cluster.local ports: - - port: 3000 + - name: http + port: 3000 targetPort: 3000 \ No newline at end of file diff --git a/infrastructure/platform/networking/ingress/overlays/dev/kustomization.yaml b/infrastructure/platform/networking/ingress/overlays/dev/kustomization.yaml index 4e65de98..b9858177 100644 --- a/infrastructure/platform/networking/ingress/overlays/dev/kustomization.yaml +++ b/infrastructure/platform/networking/ingress/overlays/dev/kustomization.yaml @@ -20,7 +20,10 @@ patches: value: gitea.bakery-ia.local - op: replace path: /spec/tls/0/hosts/2 - value: mail.bakery-ia.local + value: registry.bakery-ia.local + - op: replace + path: /spec/tls/0/hosts/3 + value: mail.bakery-ia.dev - op: replace path: /spec/tls/0/secretName value: bakery-dev-tls-cert @@ -32,7 +35,10 @@ patches: value: gitea.bakery-ia.local - op: replace path: /spec/rules/2/host - value: mail.bakery-ia.local + value: registry.bakery-ia.local + - op: replace + path: /spec/rules/3/host + value: mail.bakery-ia.dev - op: replace path: /metadata/annotations/nginx.ingress.kubernetes.io~1cors-allow-origin - value: "https://localhost,https://localhost:3000,https://localhost:3001,https://127.0.0.1,https://127.0.0.1:3000,https://127.0.0.1:3001,https://bakery-ia.local,http://localhost,http://localhost:3000,http://localhost:3001,http://127.0.0.1,http://127.0.0.1:3000" + value: "https://localhost,https://localhost:3000,https://localhost:3001,https://127.0.0.1,https://127.0.0.1:3000,https://127.0.0.1:3001,https://bakery-ia.local,https://registry.bakery-ia.local,https://gitea.bakery-ia.local,http://localhost,http://localhost:3000,http://localhost:3001,http://127.0.0.1,http://127.0.0.1:3000" diff --git a/infrastructure/security/certificates/generate-mail-certificates.sh b/infrastructure/security/certificates/generate-mail-certificates.sh new file mode 100755 index 00000000..c681b2cf --- /dev/null +++ b/infrastructure/security/certificates/generate-mail-certificates.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash + +# Generate TLS certificates for Mailu service +# This script creates a self-signed certificate for mail.bakery-ia.dev +# For production, you should use Let's Encrypt or a trusted CA + +set -e + +TLS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +MAIL_DIR="$TLS_DIR/mail" + +mkdir -p "$MAIL_DIR" + +echo "Generating TLS certificates for Mailu service..." +echo "Directory: $MAIL_DIR" +echo "" + +# Clean up old certificates +rm -f "$MAIL_DIR/tls.key" "$MAIL_DIR/tls.crt" 2>/dev/null || true + +# Generate private key +openssl genrsa -out "$MAIL_DIR/tls.key" 2048 + +# Generate self-signed certificate valid for 365 days +openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ + -keyout "$MAIL_DIR/tls.key" -out "$MAIL_DIR/tls.crt" \ + -subj "/CN=mail.bakery-ia.dev/O=Bakery IA" + +echo "✓ Mailu certificates generated" +echo "" + +# Verify certificate +echo "Certificate details:" +openssl x509 -in "$MAIL_DIR/tls.crt" -noout -subject -issuer -dates + +echo "" +echo "===================" +echo "✓ Certificate generated successfully!" +echo "" +echo "Generated files:" +echo " - $MAIL_DIR/tls.crt (Certificate)" +echo " - $MAIL_DIR/tls.key (Private key)" +echo "" +echo "Next steps:" +echo " 1. Create Kubernetes secret: kubectl create secret tls mailu-certificates --cert=$MAIL_DIR/tls.crt --key=$MAIL_DIR/tls.key -n bakery-ia" +echo " 2. Update the mailu-certificates-secret.yaml with the base64 encoded values" +echo " 3. Apply the secret to your cluster" diff --git a/infrastructure/security/certificates/mail/tls.crt b/infrastructure/security/certificates/mail/tls.crt new file mode 100644 index 00000000..92490051 --- /dev/null +++ b/infrastructure/security/certificates/mail/tls.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDQzCCAiugAwIBAgIUUh5FX9qiOD7dsafV/JziJYhuYFIwDQYJKoZIhvcNAQEL +BQAwMTEbMBkGA1UEAwwSbWFpbC5iYWtlcnktaWEuZGV2MRIwEAYDVQQKDAlCYWtl +cnkgSUEwHhcNMjYwMTE5MjA0NjI0WhcNMjcwMTE5MjA0NjI0WjAxMRswGQYDVQQD +DBJtYWlsLmJha2VyeS1pYS5kZXYxEjAQBgNVBAoMCUJha2VyeSBJQTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBAL2ems6anCJ5y7BP6oJu46NWPIrwfX72 +wZh1dDIiYH2cljPDJWlotNSBENx1RFYHG7gEREY50zECu0H/UsF3DYOLXhnF0uTW +CRNbEF1hb6MOijjuf9bG+7lVBy6fd1vQO2i905VKqE7DfYkib0VJq7GnUJ9Eam8X +IlSiJacQz2muYwzB0O7xYyUwTQVL7/JtMMk9f6CcVD5qQ0bnXBM3hjqUFY9glAyt +YdpTQHOvk5Yx+fMg/bYVPcCEadXUVHAtz1bRrlb0zyLsqWxwv9yV7JB3mtNCflWl +DBYdHortfTNLuR4XaE3WOjgo90OYmn/ObYz2WtIu02zy2HkNpMaAoVkCAwEAAaNT +MFEwHQYDVR0OBBYEFLKhOsnxZzWCTr0QnI7chMamkSojMB8GA1UdIwQYMBaAFLKh +OsnxZzWCTr0QnI7chMamkSojMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggEBAALCxFWUgcvweZhF1GvSgGtwUoVjBmpmFbqO0/wKij2XCFfz/AjjvZ8s +8eHPG9gw+n9ihcR7MzCeyfWQwQli0WfG2K0oP1FyLhaOZ2XmuOg6xMDnDW0Uekj3 +BagGstEUzjBTuRRwY/nrNooVB9Qhbxhyofmy+W5fs8Y03Sdoii1iXmbHHZzc2/mH +avP14gpsYCCeYzjIrZm9Ya8G8iskXzSgeM/HHiG8I9hJFBXhtXadcxi/mNa4tJqh +3W+LB3hD84Udgs+GzBgHGvr0ulL14/iLUEqrIvZZ3vU9o6Vx2PoF47r0P5zNeuS7 +dFNqOrIOjaJnr1qUoKLygwEHjuTMmI4= +-----END CERTIFICATE----- diff --git a/infrastructure/security/certificates/mail/tls.key b/infrastructure/security/certificates/mail/tls.key new file mode 100644 index 00000000..df7bc66c --- /dev/null +++ b/infrastructure/security/certificates/mail/tls.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC9nprOmpwiecuw +T+qCbuOjVjyK8H1+9sGYdXQyImB9nJYzwyVpaLTUgRDcdURWBxu4BERGOdMxArtB +/1LBdw2Di14ZxdLk1gkTWxBdYW+jDoo47n/Wxvu5VQcun3db0DtovdOVSqhOw32J +Im9FSauxp1CfRGpvFyJUoiWnEM9prmMMwdDu8WMlME0FS+/ybTDJPX+gnFQ+akNG +51wTN4Y6lBWPYJQMrWHaU0Bzr5OWMfnzIP22FT3AhGnV1FRwLc9W0a5W9M8i7Kls +cL/cleyQd5rTQn5VpQwWHR6K7X0zS7keF2hN1jo4KPdDmJp/zm2M9lrSLtNs8th5 +DaTGgKFZAgMBAAECggEAInuLT3MSXbqkbgW6ccnUn8l47RNa3xHkluMVJGDYBz/I +mNUuIoMmD1cBR/YTXUmhos8z00mEvG7wusnBtOj/jiJ0FF/DQFYkBF8VFMY5VP+5 +5yyIFvjMoiFyUuow/INbqmqK5aVUAi7Oz3dxoLoK/R2eLbh1WopsddOe4UjPTzpU +u5MYx5yLVuYsP7SOSHtkwe/03yDRKrIvWy5BPmc2QTHTpBOUBG4/CpRVGZ3f8KkD +7d+6Ve77uMeuxDO8mGgZZ54iwCn1+XGcEqQUGVuZx+qZhuXSfK4j4wyemnweEABv +mNSYIt68ouI+4pArWVN4AcixVELHWWz04XNnzXU24QKBgQD0esBYzudG2ZSkyIbQ +u8IxpOdsF4gSYizCd13KBKFnohm1kW9XzdfKvNnrqpTOFrHbDWQ7iiHJ3cjV9AU5 +NQ01U3Yv4K8dukgoc/EAant/F8o0njsJIgfvY1nPsOTUEpkQBMPHjkhjr3qA6Hxv +xoB68GUuMNTtdB+AWF4utuObhQKBgQDGjg6bgljWETxNxAjT1smkxJsClflQmytf +cbh5VzjstcZwiKJ8uoLcOGxoNGX2HhbQANpEhTGqn0FHllEsPXmphyBUcMIPVSXI +EIKyOd/vLb8cXort6Lh3MKAhjEKlLD5VNp8WmYP3wBTMbkpk3CCulCHBKpBWWf66 +PXZtEFJkxQKBgB3RLs5mBa0NcmSalF6215toalUzlYPwd1cMaeLup6TVD1+mqjbC +QzRVzhpBBr8P44c83eGpGi/dndYCWfS9NKwxTrPOKm1sv8o3Qch0NDWu+BlsxvR5 +MxCORHDhOTdoQUTD4ADhqJCH4WABetPDGP5lfWGh0QZY6FKl9G6sHZxlAoGAZyK/ +H7PzZS6KvnJHi5IUJ8wgIJW6bU5Ml0VA53aRERPSwf2XOWbANpfwZ6hgFhnHC8CF +n/X3uIMEq6S/AVXlblPMTVBM3R4DhApfeShq05hVnuzVCYNK3k6ZvxNWQunabvTy +ahDP5c8WfqIDbqSRLV2wst/jHZedoyvt6fUCd2kCgYEAlo4YzTZl//k+4XiixuLA +ggfby0hKs5AiKpV4CzUeQ5cKYOi9IzoC2LrLSX+UrH/wL70gBG6gyCRgWKimQnaV +tYO/13SrPUgnnuGj6CB5aEkyv2LaOVevXFEriEmd5qbJIrX0CfgQnFr6vnQd4pPS +8dU2Gah4b5WMJ5Iw80SpcGI= +-----END PRIVATE KEY----- diff --git a/scripts/prepull-base-images.sh b/scripts/prepull-base-images.sh index e2760cdc..a026f170 100755 --- a/scripts/prepull-base-images.sh +++ b/scripts/prepull-base-images.sh @@ -55,10 +55,38 @@ BASE_IMAGES=( "ghcr.io/mailu/rspamd:2024.06" ) -# Local registry configuration -# Set USE_LOCAL_REGISTRY=true to push images to local registry after pulling -USE_LOCAL_REGISTRY=true -LOCAL_REGISTRY="localhost:5000" +# Registry configuration +# Read from environment variables (set by Tiltfile or manually) +# USE_LOCAL_REGISTRY=true to push images to local registry after pulling +# USE_GITEA_REGISTRY=true to push images to Gitea registry after pulling +USE_LOCAL_REGISTRY="${USE_LOCAL_REGISTRY:-true}" +USE_GITEA_REGISTRY="${USE_GITEA_REGISTRY:-false}" + +echo "Registry configuration:" +echo " USE_LOCAL_REGISTRY=$USE_LOCAL_REGISTRY" +echo " USE_GITEA_REGISTRY=$USE_GITEA_REGISTRY" +echo "" + +# Check if Gitea registry should be used instead +if [ "$USE_GITEA_REGISTRY" = "true" ]; then + # Gitea registry is accessed via HTTPS on the registry subdomain (TLS terminated at ingress) + # Docker push/pull should use: registry.bakery-ia.local + # The registry serves on port 443 (HTTPS via ingress) but Docker defaults to 443 for HTTPS + REGISTRY="registry.bakery-ia.local" + echo "Testing Gitea registry accessibility at $REGISTRY..." + + # Test if Gitea registry is accessible (try HTTPS first, then HTTP) + if curl -sk https://$REGISTRY/v2/ >/dev/null 2>&1; then + echo "✓ Gitea registry accessible via HTTPS" + elif curl -s http://$REGISTRY/v2/ >/dev/null 2>&1; then + echo "✓ Gitea registry accessible via HTTP" + else + echo "Warning: Gitea registry at $REGISTRY is not accessible, falling back to local registry" + REGISTRY="localhost:5000" + fi +else + REGISTRY="localhost:5000" +fi echo "Base images to pre-pull:" echo "----------------------------------------" @@ -77,22 +105,26 @@ for image in "${BASE_IMAGES[@]}"; do # Pull the image docker pull "$image" - # Tag for local registry if enabled - if [ "$USE_LOCAL_REGISTRY" = true ]; then - # Convert image name to local registry format: + # Tag for registry if enabled + if [ "$USE_LOCAL_REGISTRY" = "true" ] || [ "$USE_GITEA_REGISTRY" = "true" ]; then + # Convert image name to registry format: # - Replace / with _ # - Replace : with _ # - Convert to lowercase (Docker requires lowercase repository names) # - Add :latest tag for Kustomize compatibility # Example: gcr.io/kaniko-project/executor:v1.23.0 -> gcr.io_kaniko-project_executor_v1.23.0:latest local_repo="$(echo $image | sed 's|/|_|g' | sed 's|:|_|g' | tr '[:upper:]' '[:lower:]')" - local_image="$LOCAL_REGISTRY/${local_repo}:latest" - docker tag "$image" "$local_image" - echo " Tagged as: $local_image" + registry_image="$REGISTRY/${local_repo}:latest" + docker tag "$image" "$registry_image" + echo " Tagged as: $registry_image" - # Push to local registry - docker push "$local_image" - echo " Pushed to local registry" + # Push to registry + docker push "$registry_image" + if [ "$USE_GITEA_REGISTRY" = "true" ]; then + echo " Pushed to Gitea registry" + else + echo " Pushed to local registry" + fi fi echo " ✓ Successfully pulled $image" @@ -105,12 +137,25 @@ echo "==========================================" echo "" echo "Summary:" echo " - Total images pulled: ${#BASE_IMAGES[@]}" -echo " - Local registry enabled: $USE_LOCAL_REGISTRY" +if [ "$USE_GITEA_REGISTRY" = "true" ]; then + echo " - Gitea registry enabled: $USE_GITEA_REGISTRY" + echo " - Registry URL: $REGISTRY" +else + echo " - Local registry enabled: $USE_LOCAL_REGISTRY" + echo " - Registry URL: $REGISTRY" +fi echo "" -if [ "$USE_LOCAL_REGISTRY" = true ]; then - echo "Local registry contents:" - curl -s http://$LOCAL_REGISTRY/v2/_catalog | jq . +if [ "$USE_LOCAL_REGISTRY" = "true" ] || [ "$USE_GITEA_REGISTRY" = "true" ]; then + if [ "$USE_GITEA_REGISTRY" = "true" ]; then + echo "Gitea registry contents:" + # Note: Gitea registry API might be different, using the standard registry API for now + # If Gitea registry is not accessible, this might fail + curl -s http://$REGISTRY/v2/_catalog | jq . 2>/dev/null || echo "Could not access registry contents (Gitea registry may not support this endpoint)" + else + echo "Local registry contents:" + curl -s http://$REGISTRY/v2/_catalog | jq . 2>/dev/null || echo "Could not access registry contents" + fi echo "" fi @@ -120,20 +165,37 @@ echo " 2. For Kubernetes: Consider setting up a pull-through cache" echo " 3. For CI/CD: Run this script before your build pipeline" echo "" -echo "To use local registry in your builds:" -echo " - Update Dockerfiles to use: $LOCAL_REGISTRY/..." -echo " - Or configure Docker daemon to use local registry as mirror" +echo "To use registry in your builds:" +if [ "$USE_GITEA_REGISTRY" = true ]; then + echo " - Update Dockerfiles to use: $REGISTRY/..." + echo " - Gitea registry URL: $REGISTRY" +else + echo " - Update Dockerfiles to use: $REGISTRY/..." + echo " - Local registry URL: $REGISTRY" +fi +echo " - Or configure Docker daemon to use registry as mirror" echo "" -# Optional: Configure Docker daemon to use local registry as mirror -if [ "$USE_LOCAL_REGISTRY" = true ]; then - echo "To configure Docker daemon to use local registry as mirror:" - echo "" - cat << 'EOF' +# Optional: Configure Docker daemon to use registry as mirror +if [ "$USE_LOCAL_REGISTRY" = "true" ] || [ "$USE_GITEA_REGISTRY" = "true" ]; then + if [ "$USE_GITEA_REGISTRY" = "true" ]; then + echo "To configure Docker daemon to use Gitea registry as mirror:" + echo "" + cat << EOF +{ + "registry-mirrors": ["https://registry.bakery-ia.local"], + "insecure-registries": ["registry.bakery-ia.local"] +} +EOF + else + echo "To configure Docker daemon to use local registry as mirror:" + echo "" + cat << 'EOF' { "registry-mirrors": ["http://localhost:5000"] } EOF + fi echo "" echo "Add this to /etc/docker/daemon.json and restart Docker" fi \ No newline at end of file