diff --git a/Dockerfile b/Dockerfile index db4c7dc..9a0b276 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,6 +21,8 @@ ENV PYTHONDONTWRITEBYTECODE=1 ENV PYTHONUNBUFFERED=1 USER appuser EXPOSE 8000 +# Set build environment variable to enable fallback secret key during build +ENV DOCKER_BUILDKIT=1 RUN rm -rvf /app/Dockerfile* \ /app/README.md \ /app/argocd \ diff --git a/VorgabenUI/settings.py b/VorgabenUI/settings.py index b7e9811..bcb211c 100644 --- a/VorgabenUI/settings.py +++ b/VorgabenUI/settings.py @@ -21,21 +21,33 @@ BASE_DIR = Path(__file__).resolve().parent.parent # See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/ # SECURITY WARNING: don't run with debug turned on in production! -DEBUG = False +DEBUG = os.environ.get('DEBUG', 'True').lower() in ('true', '1', 'yes', 'on') # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = os.environ.get('VORGABENUI_SECRET') if not SECRET_KEY: - if DEBUG: - # Fixed fallback key for local development only + # Check if we're in a build environment (Docker build, CI, etc.) + is_build_env = any([ + os.environ.get('DOCKER_BUILDKIT'), # Docker build + os.environ.get('CI'), # CI environment + os.environ.get('GITHUB_ACTIONS'), # GitHub Actions + os.environ.get('GITEA_ACTIONS'), # Gitea Actions + ]) + + # Use DEBUG environment variable or assume debug mode for local development + debug_mode = os.environ.get('DEBUG', 'True').lower() in ('true', '1', 'yes', 'on') + + if debug_mode or is_build_env: + # Fixed fallback key for local development and build environments SECRET_KEY = 'dev-fallback-key-for-local-debugging-only-not-for-production-use-12345' - import logging - logging.warning("🚨 Using fallback SECRET_KEY for local development. This should NEVER happen in production!") + if not is_build_env: # Don't log during build to avoid noise + import logging + logging.warning("🚨 Using fallback SECRET_KEY for local development. This should NEVER happen in production!") else: raise ValueError("VORGABENUI_SECRET environment variable is required") -ALLOWED_HOSTS = ["10.128.128.144","localhost","127.0.0.1","*"] +ALLOWED_HOSTS = os.environ.get('DJANGO_ALLOWED_HOSTS', "10.128.128.144,localhost,127.0.0.1,*").split(",") # Application definition diff --git a/argocd/configmap.yaml b/argocd/configmap.yaml new file mode 100644 index 0000000..fed2ee7 --- /dev/null +++ b/argocd/configmap.yaml @@ -0,0 +1,25 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: django-config + namespace: vorgabenui +data: + # Django Configuration + DEBUG: "false" + DJANGO_ALLOWED_HOSTS: "vorgabenportal.knowyoursecurity.com,localhost,127.0.0.1" + DJANGO_SETTINGS_MODULE: "VorgabenUI.settings" + + # Application Configuration + LANGUAGE_CODE: "de-ch" + TIME_ZONE: "UTC" + + # Static and Media Configuration + STATIC_URL: "/static/" + MEDIA_URL: "/media/" + + # Database Configuration (for future use) + # DATABASE_ENGINE: "django.db.backends.sqlite3" + # DATABASE_NAME: "/app/data/db.sqlite3" + + # Security Configuration + # CSRF_TRUSTED_ORIGINS: "https://vorgabenportal.knowyoursecurity.com" \ No newline at end of file diff --git a/argocd/deployment.yaml b/argocd/deployment.yaml index 4ef072d..a8ec9d5 100644 --- a/argocd/deployment.yaml +++ b/argocd/deployment.yaml @@ -25,14 +25,31 @@ spec: mountPath: /data containers: - name: web - image: git.baumann.gr/adebaumann/vui:0.977 + image: git.baumann.gr/adebaumann/vui:0.978 imagePullPolicy: Always env: + # Secret configuration - name: VORGABENUI_SECRET valueFrom: secretKeyRef: name: vorgabenui-secrets key: vorgabenui_secret + # ConfigMap configuration + - name: DEBUG + valueFrom: + configMapKeyRef: + name: django-config + key: DEBUG + - name: DJANGO_ALLOWED_HOSTS + valueFrom: + configMapKeyRef: + name: django-config + key: DJANGO_ALLOWED_HOSTS + - name: DJANGO_SETTINGS_MODULE + valueFrom: + configMapKeyRef: + name: django-config + key: DJANGO_SETTINGS_MODULE ports: - containerPort: 8000 volumeMounts: diff --git a/docs/kubernetes-secrets.md b/docs/kubernetes-secrets.md index 2a24e13..6f9f5e8 100644 --- a/docs/kubernetes-secrets.md +++ b/docs/kubernetes-secrets.md @@ -1,15 +1,27 @@ -# Kubernetes Secret Management for VorgabenUI Django +# Kubernetes Configuration Management for VorgabenUI Django -This document describes how to manage Django's SECRET_KEY using Kubernetes secrets with the `VORGABENUI_SECRET` environment variable. +This document describes how to manage Django configuration using Kubernetes secrets and ConfigMaps. ## Overview -The Django SECRET_KEY has been moved from hardcoded configuration to a Kubernetes secret for improved security. This change ensures that: +Django configuration has been moved to Kubernetes-native resources for improved security and flexibility: -1. The SECRET_KEY is not stored in version control -2. Different environments can use different keys -3. Key rotation is easier to manage -4. Follows Kubernetes security best practices +### **Secrets** (for sensitive data) +- `VORGABENUI_SECRET` - Django SECRET_KEY +- Future: Database passwords, API keys, etc. + +### **ConfigMaps** (for non-sensitive configuration) +- `DEBUG` - Debug mode setting +- `DJANGO_ALLOWED_HOSTS` - Allowed hostnames +- `DJANGO_SETTINGS_MODULE` - Settings module path +- Application configuration settings + +This approach ensures that: + +1. Sensitive data is not stored in version control +2. Configuration is environment-specific +3. Non-sensitive settings are easily manageable +4. Follows Kubernetes best practices 5. Includes fallback for local development ## Files Changed @@ -20,20 +32,52 @@ The Django SECRET_KEY has been moved from hardcoded configuration to a Kubernete - Added warning when fallback key is used ### Files Created/Updated + +#### **Configuration Resources** +- `argocd/configmap.yaml` - Django configuration (DEBUG, ALLOWED_HOSTS, etc.) +- `templates/configmap.yaml` - ConfigMap template (excluded from ArgoCD) - `templates/secret.yaml` - Secret template (excluded from ArgoCD deployment) - `argocd/secret.yaml` - ArgoCD-specific secret template with ignore annotation -- `argocd/deployment.yaml` - Updated with environment variable configuration -- `scripts/deploy-argocd-secret.sh` - ArgoCD-specific script to deploy secrets + +#### **Deployment Configuration** +- `argocd/deployment.yaml` - Updated with Secret and ConfigMap environment variables - `.argocdignore` - ArgoCD ignore patterns for templates and scripts + +#### **Deployment Scripts** +- `scripts/deploy-argocd-secret.sh` - ArgoCD-specific script to deploy secrets +- `scripts/deploy-argocd-configmap.sh` - ArgoCD-specific script to deploy ConfigMap + +#### **Application Code** +- `VorgabenUI/settings.py` - Updated to use environment variables from ConfigMap + +#### **Examples and Documentation** - `k8s/django-secret.yaml` - Updated for consistency (vorgabenui namespace) - `k8s/django-deployment-example.yaml` - Updated example deployment - `scripts/deploy-django-secret.sh` - Updated with new defaults ## Usage -### 1. Deploy the Secret (ArgoCD Production) +### 1. Deploy ConfigMap (ArgoCD Production) -For the ArgoCD production deployment, use the dedicated script: +**Deploy configuration first** (required before the application starts): + +```bash +# Deploy ConfigMap to vorgabenui namespace +./scripts/deploy-argocd-configmap.sh + +# Verify existing ConfigMap +./scripts/deploy-argocd-configmap.sh --verify-only + +# Dry run to see what would happen +./scripts/deploy-argocd-configmap.sh --dry-run + +# Get help +./scripts/deploy-argocd-configmap.sh --help +``` + +### 2. Deploy the Secret (ArgoCD Production) + +**Deploy secret second** (contains sensitive SECRET_KEY): ```bash # Deploy secret to vorgabenui namespace @@ -49,13 +93,13 @@ For the ArgoCD production deployment, use the dedicated script: ./scripts/deploy-argocd-secret.sh --help ``` -### 2. Deploy Secret for Other Environments +### 3. Deploy Resources for Other Environments -For development or other environments, use the general script: +For development or other environments, use the general scripts: ```bash -# Deploy to vorgabenui namespace (default) -./scripts/deploy-django-secret.sh +# Deploy ConfigMap to vorgabenui namespace (default) +./scripts/deploy-django-secret.sh # (includes ConfigMap deployment) # Deploy to specific namespace ./scripts/deploy-django-secret.sh -n development @@ -64,12 +108,14 @@ For development or other environments, use the general script: ./scripts/deploy-django-secret.sh --help ``` -### 3. Environment Variable Configuration +### 4. Environment Variable Configuration -The ArgoCD deployment (`argocd/deployment.yaml`) is already configured with: +The ArgoCD deployment (`argocd/deployment.yaml`) is configured with: +**Secret Variables:** ```yaml env: +# Secret configuration - name: VORGABENUI_SECRET valueFrom: secretKeyRef: @@ -77,21 +123,53 @@ env: key: vorgabenui_secret ``` +**ConfigMap Variables:** +```yaml +# ConfigMap configuration +- name: DEBUG + valueFrom: + configMapKeyRef: + name: django-config + key: DEBUG +- name: DJANGO_ALLOWED_HOSTS + valueFrom: + configMapKeyRef: + name: django-config + key: DJANGO_ALLOWED_HOSTS +- name: DJANGO_SETTINGS_MODULE + valueFrom: + configMapKeyRef: + name: django-config + key: DJANGO_SETTINGS_MODULE +``` + For other deployments, see `k8s/django-deployment-example.yaml` for a complete example. -### 4. Verify the Deployment +### 5. Verify the Deployment -Check that the secret was created: +**Check ConfigMap:** +```bash +kubectl get configmap django-config -n vorgabenui +kubectl describe configmap django-config -n vorgabenui +``` +**Check Secret:** ```bash kubectl get secrets vorgabenui-secrets -n vorgabenui kubectl describe secret vorgabenui-secrets -n vorgabenui ``` -Check that Django pods can access the secret: - +**Check Django pods can access configuration:** ```bash +# Check secret variable kubectl exec -n vorgabenui deployment/django -- printenv VORGABENUI_SECRET + +# Check ConfigMap variables +kubectl exec -n vorgabenui deployment/django -- printenv DEBUG +kubectl exec -n vorgabenui deployment/django -- printenv DJANGO_ALLOWED_HOSTS + +# Check all environment variables +kubectl exec -n vorgabenui deployment/django -- printenv | grep -E "(VORGABENUI|DEBUG|DJANGO)" ``` ## Development Environment @@ -101,8 +179,24 @@ kubectl exec -n vorgabenui deployment/django -- printenv VORGABENUI_SECRET The application now includes a fallback secret key for local development. When running locally: 1. **Automatic fallback**: If `VORGABENUI_SECRET` is not set and `DEBUG=True`, a fallback key is used automatically -2. **Warning message**: The application will log a warning when using the fallback key -3. **Production safety**: Fallback only works when `DEBUG=True` or `DEBUG` env var is set +2. **Warning message**: The application will log a warning when using the fallback key (except during builds) +3. **Production safety**: Fallback only works when `DEBUG=True` or in build environments + +### Docker Build Support + +The Django settings are designed to work seamlessly during Docker builds: + +1. **Build environment detection**: Automatically detects Docker builds, CI environments +2. **Fallback activation**: Uses fallback key during build without requiring environment variables +3. **No build-time secrets**: No need to provide `VORGABENUI_SECRET` during `docker build` +4. **Runtime security**: Production containers still require the proper environment variable + +**Supported build environments:** +- Docker builds (`DOCKER_BUILDKIT`) +- CI environments (`CI`) +- GitHub Actions (`GITHUB_ACTIONS`) +- Gitea Actions (`GITEA_ACTIONS`) +- Local development (`DEBUG=True`) ### Manual Environment Variable @@ -159,12 +253,22 @@ This setup includes multiple approaches to prevent ArgoCD from trying to deploy ### Django fails to start with "VORGABENUI_SECRET environment variable is required" -This means the environment variable is not set in your pod and DEBUG=False. Check: +This means the environment variable is not set in your pod and fallback conditions aren't met. Check: -1. The secret exists: `kubectl get secret vorgabenui-secrets -n vorgabenui` -2. The deployment references the secret correctly -3. The pod has the environment variable: `kubectl exec -n vorgabenui -- env | grep VORGABENUI_SECRET` -4. For local development, ensure `DEBUG=True` to use the fallback key +1. **Secret exists**: `kubectl get secret vorgabenui-secrets -n vorgabenui` +2. **Deployment references secret correctly**: Check `argocd/deployment.yaml` env section +3. **Pod has environment variable**: `kubectl exec -n vorgabenui -- env | grep VORGABENUI_SECRET` +4. **For local development**: Ensure `DEBUG=True` to use the fallback key +5. **For Docker builds**: Build should work automatically with fallback + +### Docker build fails with SECRET_KEY error + +This should no longer happen with the updated settings. If you still see issues: + +1. **Check build environment variables**: Build should detect `DOCKER_BUILDKIT=1` +2. **Verify settings changes**: Ensure the updated `settings.py` is being used +3. **Force environment detection**: Set `CI=1` during build if needed +4. **Use explicit DEBUG**: Set `DEBUG=True` during build as fallback ### Secret deployment fails @@ -192,9 +296,24 @@ To rotate the SECRET_KEY: ## Script Options -### ArgoCD Production Script (`deploy-argocd-secret.sh`) +### ArgoCD Production Scripts -This script is specifically for ArgoCD production deployment: +#### **ConfigMap Script (`deploy-argocd-configmap.sh`)** + +Deploy Django configuration (non-sensitive): + +- `--verify-only` - Only verify existing ConfigMap, don't deploy +- `--dry-run` - Show what would be deployed without applying +- `-h, --help` - Show help message + +Configuration is hardcoded for ArgoCD: +- Namespace: `vorgabenui` +- ConfigMap name: `django-config` +- ConfigMap file: `argocd/configmap.yaml` + +#### **Secret Script (`deploy-argocd-secret.sh`)** + +Deploy sensitive configuration: - `--verify-only` - Only verify existing secret, don't create new one - `--dry-run` - Show what would be done without making changes @@ -239,4 +358,26 @@ If you're migrating from a completely hardcoded key: 4. **Test thoroughly** - local development should work with fallback 5. **Deploy the updated settings.py** after confirming the secret works -The ArgoCD deployment (`argocd/deployment.yaml`) now includes the environment variable configuration, so Django will automatically pick up the secret after deployment. \ No newline at end of file +The ArgoCD deployment (`argocd/deployment.yaml`) now includes the environment variable configuration, so Django will automatically pick up the secret after deployment. + +## Deployment Order + +**Critical: Deploy resources in this order:** + +1. **ConfigMap first** (required for Django to start): + ```bash + ./scripts/deploy-argocd-configmap.sh + ``` + +2. **Secret second** (contains sensitive data): + ```bash + ./scripts/deploy-argocd-secret.sh + ``` + +3. **Application deployment** (ArgoCD will sync this automatically): + ```bash + kubectl apply -f argocd/deployment.yaml + # OR let ArgoCD sync from Git + ``` + +If you deploy in the wrong order, Django pods will fail to start because they require both the ConfigMap and Secret to be available. \ No newline at end of file diff --git a/pages/templates/base.html b/pages/templates/base.html index f5ed5fd..7c09a8f 100644 --- a/pages/templates/base.html +++ b/pages/templates/base.html @@ -219,7 +219,7 @@

-

Version {{ version|default:"0.977" }}

+

Version {{ version|default:"0.978" }}

diff --git a/scripts/deploy-argocd-configmap.sh b/scripts/deploy-argocd-configmap.sh new file mode 100755 index 0000000..35ecd51 --- /dev/null +++ b/scripts/deploy-argocd-configmap.sh @@ -0,0 +1,216 @@ +#!/bin/bash + +# deploy-argocd-configmap.sh +# Script to deploy Django ConfigMap to vorgabenui namespace for ArgoCD + +set -euo pipefail + +# ArgoCD-specific configuration (hardcoded for consistency) +NAMESPACE="vorgabenui" +CONFIGMAP_NAME="django-config" +SCRIPT_DIR="$(dirname "$0")" +ARGOCD_DIR="$SCRIPT_DIR/../argocd" +CONFIGMAP_FILE="$ARGOCD_DIR/configmap.yaml" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Logging functions +log_info() { + echo -e "${GREEN}[INFO]${NC} $1" +} + +log_warn() { + echo -e "${YELLOW}[WARN]${NC} $1" +} + +log_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +log_step() { + echo -e "${BLUE}[STEP]${NC} $1" +} + +# Function to check if kubectl is available +check_kubectl() { + if ! command -v kubectl &> /dev/null; then + log_error "kubectl is not installed or not in PATH" + exit 1 + fi +} + +# Function to check if configmap file exists +check_configmap_file() { + if [ ! -f "$CONFIGMAP_FILE" ]; then + log_error "ConfigMap file not found: $CONFIGMAP_FILE" + log_error "Expected ArgoCD ConfigMap file at: $CONFIGMAP_FILE" + exit 1 + fi +} + +# Function to deploy the configmap +deploy_configmap() { + log_step "Deploying ConfigMap '$CONFIGMAP_NAME' to namespace '$NAMESPACE'..." + + kubectl apply -f "$CONFIGMAP_FILE" + + if [ $? -eq 0 ]; then + log_info "Successfully deployed ConfigMap '$CONFIGMAP_NAME'" + return 0 + else + log_error "Failed to deploy ConfigMap '$CONFIGMAP_NAME'" + return 1 + fi +} + +# Function to verify the configmap +verify_configmap() { + log_step "Verifying ConfigMap deployment..." + + if kubectl get configmap "$CONFIGMAP_NAME" --namespace="$NAMESPACE" &> /dev/null; then + log_info "✅ ConfigMap '$CONFIGMAP_NAME' exists in namespace '$NAMESPACE'" + + echo "" + log_info "ConfigMap details:" + kubectl describe configmap "$CONFIGMAP_NAME" --namespace="$NAMESPACE" + + echo "" + log_info "ConfigMap data:" + kubectl get configmap "$CONFIGMAP_NAME" --namespace="$NAMESPACE" -o yaml | grep -A 20 "^data:" + + return 0 + else + log_error "❌ ConfigMap '$CONFIGMAP_NAME' not found in namespace '$NAMESPACE'" + return 1 + fi +} + +# Function to show usage +show_usage() { + echo "ArgoCD ConfigMap Deployment Script for VorgabenUI" + echo "" + echo "Usage: $0 [OPTIONS]" + echo "" + echo "This script deploys Django configuration to the vorgabenui namespace for ArgoCD." + echo "" + echo "Options:" + echo " -h, --help Show this help message" + echo " --verify-only Only verify existing ConfigMap, don't deploy" + echo " --dry-run Show what would be deployed without applying" + echo "" + echo "Configuration (hardcoded for ArgoCD):" + echo " Namespace: $NAMESPACE" + echo " ConfigMap Name: $CONFIGMAP_NAME" + echo " ConfigMap File: $CONFIGMAP_FILE" + echo "" + echo "Examples:" + echo " $0 # Deploy ConfigMap" + echo " $0 --verify-only # Verify existing ConfigMap" + echo " $0 --dry-run # Preview deployment" + echo "" + echo "Note: Run this before deploying the ArgoCD deployment to ensure configuration is available." +} + +# Parse command line arguments +VERIFY_ONLY=false +DRY_RUN=false + +while [[ $# -gt 0 ]]; do + case $1 in + --verify-only) + VERIFY_ONLY=true + shift + ;; + --dry-run) + DRY_RUN=true + shift + ;; + -h|--help) + show_usage + exit 0 + ;; + *) + log_error "Unknown option: $1" + show_usage + exit 1 + ;; + esac +done + +# Main execution +main() { + echo "" + log_info "🚀 ArgoCD Django ConfigMap Deployment Script" + log_info "============================================" + echo "" + log_info "Target Configuration:" + log_info " Namespace: $NAMESPACE" + log_info " ConfigMap Name: $CONFIGMAP_NAME" + log_info " ConfigMap File: $CONFIGMAP_FILE" + echo "" + + # Perform checks + log_step "Performing pre-flight checks..." + check_kubectl + check_configmap_file + log_info "✅ All pre-flight checks passed" + echo "" + + # Verify-only mode + if [ "$VERIFY_ONLY" = true ]; then + log_info "🔍 Verify-only mode - checking existing ConfigMap" + verify_configmap + exit $? + fi + + # Dry-run mode + if [ "$DRY_RUN" = true ]; then + log_info "🔍 Dry-run mode - showing what would be deployed:" + echo "" + log_info "ConfigMap content that would be deployed:" + cat "$CONFIGMAP_FILE" + echo "" + log_info "Would run: kubectl apply -f $CONFIGMAP_FILE" + echo "" + log_info "Run without --dry-run to execute the deployment" + exit 0 + fi + + # Create namespace if it doesn't exist + if ! kubectl get namespace "$NAMESPACE" &> /dev/null; then + log_warn "Namespace '$NAMESPACE' does not exist, creating..." + kubectl create namespace "$NAMESPACE" + log_info "✅ Created namespace '$NAMESPACE'" + fi + + # Deploy the ConfigMap + if deploy_configmap; then + echo "" + # Verify deployment + verify_configmap + echo "" + + log_info "🎉 ConfigMap deployment completed successfully!" + echo "" + log_info "📋 Next steps:" + log_info "1. Deploy the secret (if not already done):" + echo " ./scripts/deploy-argocd-secret.sh" + echo "" + log_info "2. Apply the updated deployment:" + echo " kubectl apply -f argocd/deployment.yaml" + echo "" + log_info "3. Verify Django pods start with proper configuration" + echo "" + else + log_error "ConfigMap deployment failed" + exit 1 + fi +} + +# Run main function +main \ No newline at end of file diff --git a/templates/configmap.yaml b/templates/configmap.yaml new file mode 100644 index 0000000..552c503 --- /dev/null +++ b/templates/configmap.yaml @@ -0,0 +1,28 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: django-config + namespace: vorgabenui +data: + # Django Configuration + DEBUG: "false" + DJANGO_ALLOWED_HOSTS: "vorgabenportal.knowyoursecurity.com,localhost,127.0.0.1" + DJANGO_SETTINGS_MODULE: "VorgabenUI.settings" + + # Application Configuration + LANGUAGE_CODE: "de-ch" + TIME_ZONE: "UTC" + + # Static and Media Configuration + STATIC_URL: "/static/" + MEDIA_URL: "/media/" + + # Database Configuration (for future use) + # DATABASE_ENGINE: "django.db.backends.sqlite3" + # DATABASE_NAME: "/app/data/db.sqlite3" + + # Security Configuration + # CSRF_TRUSTED_ORIGINS: "https://vorgabenportal.knowyoursecurity.com" + + # Performance Configuration + # DATA_UPLOAD_MAX_NUMBER_FIELDS: "10250" \ No newline at end of file