mirror of
https://github.com/frappe/frappe_docker.git
synced 2026-06-18 14:15:09 +00:00
249 lines
No EOL
7.8 KiB
Bash
Executable file
249 lines
No EOL
7.8 KiB
Bash
Executable file
#!/bin/bash
|
|
|
|
# Validate Environment Configuration Script
|
|
# Checks for common issues in production env files
|
|
|
|
set -euo pipefail
|
|
|
|
# Colors
|
|
readonly RED='\033[0;31m'
|
|
readonly GREEN='\033[0;32m'
|
|
readonly YELLOW='\033[1;33m'
|
|
readonly NC='\033[0m'
|
|
|
|
# Counters
|
|
errors=0
|
|
warnings=0
|
|
|
|
# Logging functions
|
|
echo_info() { echo -e "${GREEN}✓${NC} $1"; }
|
|
echo_warn() { echo -e "${YELLOW}⚠${NC} $1"; ((warnings++)); }
|
|
echo_error() { echo -e "${RED}✗${NC} $1"; ((errors++)); }
|
|
|
|
# Validation patterns
|
|
readonly WEAK_PASSWORDS="changeit|123456|admin123|password123|qwerty|letmein|welcome"
|
|
readonly PLACEHOLDER_DOMAINS="yourdomain\.com|example\.com|localhost"
|
|
readonly EMAIL_REGEX="^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$"
|
|
|
|
# Get script directory and change to production directory
|
|
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
readonly PRODUCTION_DIR="$(dirname "$SCRIPT_DIR")"
|
|
cd "$PRODUCTION_DIR"
|
|
|
|
# Validate single environment file
|
|
validate_env_file() {
|
|
local file="$1"
|
|
local required_vars=("${@:2}")
|
|
|
|
echo "Checking $file..."
|
|
|
|
if [[ ! -f "$file" ]]; then
|
|
echo_error "$file not found"
|
|
echo " Create it from ${file}.example or run setup script"
|
|
return 1
|
|
fi
|
|
|
|
if [[ ! -s "$file" ]]; then
|
|
echo_error "$file is empty"
|
|
return 1
|
|
fi
|
|
|
|
echo_info "File exists"
|
|
|
|
# Read file content once
|
|
local content
|
|
content=$(grep -v "^\s*#" "$file" 2>/dev/null | grep "=" || true)
|
|
|
|
# Check placeholders
|
|
if echo "$content" | grep -q "CHANGEME_"; then
|
|
echo_error "Contains CHANGEME_ placeholders"
|
|
echo "$content" | grep "CHANGEME_" | sed 's/^/ /'
|
|
return 1
|
|
fi
|
|
echo_info "No CHANGEME_ placeholders"
|
|
|
|
# Check weak passwords
|
|
echo "$content" | cut -d'=' -f2- | grep -qi "$WEAK_PASSWORDS" && echo_warn "Contains weak passwords"
|
|
|
|
# Check required variables
|
|
local missing_vars=()
|
|
for var in "${required_vars[@]}"; do
|
|
echo "$content" | grep -q "^$var=" || missing_vars+=("$var")
|
|
done
|
|
|
|
[[ ${#missing_vars[@]} -gt 0 ]] && { echo_error "Missing: ${missing_vars[*]}"; return 1; }
|
|
return 0
|
|
}
|
|
|
|
# Validate email
|
|
validate_email() {
|
|
local email="$1" var_name="$2"
|
|
[[ ! "$email" =~ $EMAIL_REGEX ]] && { echo_error "$var_name invalid: $email"; return 1; }
|
|
echo "$email" | grep -q "$PLACEHOLDER_DOMAINS" && { echo_error "$var_name has placeholder"; return 1; }
|
|
return 0
|
|
}
|
|
|
|
# Validate password strength
|
|
validate_password_strength() {
|
|
local password="$1" min_length="${2:-16}"
|
|
if [[ ${#password} -lt $min_length ]]; then
|
|
echo_warn "Password < $min_length chars (current: ${#password})"
|
|
return 1
|
|
fi
|
|
echo_info "Password length good (${#password} chars)"
|
|
return 0
|
|
}
|
|
|
|
# Extract value from env file
|
|
get_env_value() {
|
|
local file="$1"
|
|
local var="$2"
|
|
grep "^$var=" "$file" 2>/dev/null | cut -d'=' -f2- || echo ""
|
|
}
|
|
|
|
# Main validation
|
|
main() {
|
|
# Handle help
|
|
if [[ "${1:-}" == "-h" ]] || [[ "${1:-}" == "--help" ]]; then
|
|
cat << EOF
|
|
Usage: $0
|
|
|
|
Validates ERPNext production environment configuration files.
|
|
|
|
Checks:
|
|
- production.env (DB_PASSWORD, SITES, LETSENCRYPT_EMAIL)
|
|
- traefik.env (TRAEFIK_DOMAIN, EMAIL, HASHED_PASSWORD)
|
|
- mariadb.env (DB_PASSWORD)
|
|
- Password strength and cross-file consistency
|
|
- Placeholder and weak password detection
|
|
|
|
Exit Codes:
|
|
0 - Validation passed
|
|
1 - Validation failed (errors found)
|
|
|
|
Examples:
|
|
$0 # Validate all env files
|
|
|
|
Run before deployment to catch configuration issues.
|
|
EOF
|
|
exit 0
|
|
fi
|
|
|
|
echo "🔍 Validating Production Environment Configuration"
|
|
echo "=================================================="
|
|
echo ""
|
|
|
|
# Validate production.env
|
|
if validate_env_file "production.env" "DB_PASSWORD" "DB_HOST" "LETSENCRYPT_EMAIL" "SITES"; then
|
|
# Additional production.env validations
|
|
local letsencrypt_email
|
|
letsencrypt_email=$(get_env_value "production.env" "LETSENCRYPT_EMAIL")
|
|
if [[ -n "$letsencrypt_email" ]]; then
|
|
validate_email "$letsencrypt_email" "LETSENCRYPT_EMAIL"
|
|
fi
|
|
|
|
# Check SITES format
|
|
local sites
|
|
sites=$(get_env_value "production.env" "SITES")
|
|
if [[ -n "$sites" ]] && [[ ! "$sites" =~ ^\`.*\`$ ]]; then
|
|
echo_warn "SITES should be wrapped in backticks: SITES=\`erp.example.com\`"
|
|
fi
|
|
|
|
# Validate DB password strength
|
|
local db_password
|
|
db_password=$(get_env_value "production.env" "DB_PASSWORD")
|
|
if [[ -n "$db_password" ]]; then
|
|
validate_password_strength "$db_password"
|
|
fi
|
|
fi
|
|
|
|
echo ""
|
|
|
|
# Validate traefik.env
|
|
if validate_env_file "traefik.env" "TRAEFIK_DOMAIN" "EMAIL" "HASHED_PASSWORD"; then
|
|
# Additional traefik.env validations
|
|
local traefik_email
|
|
traefik_email=$(get_env_value "traefik.env" "EMAIL")
|
|
if [[ -n "$traefik_email" ]]; then
|
|
validate_email "$traefik_email" "EMAIL"
|
|
fi
|
|
|
|
# Check if password is properly hashed
|
|
local hashed_password
|
|
hashed_password=$(get_env_value "traefik.env" "HASHED_PASSWORD")
|
|
if [[ -n "$hashed_password" ]] && echo "$hashed_password" | grep -q "openssl\|changeit"; then
|
|
echo_error "HASHED_PASSWORD not properly set"
|
|
echo " Generate with: openssl passwd -apr1 yourpassword"
|
|
fi
|
|
|
|
# Check if HASHED_PASSWORD has username prefix (it shouldn't)
|
|
if [[ -n "$hashed_password" ]] && echo "$hashed_password" | grep -q "^admin:"; then
|
|
echo_error "HASHED_PASSWORD should NOT include 'admin:' prefix"
|
|
echo_warn "Remove 'admin:' from the hash in traefik.env"
|
|
echo_warn "The compose file adds it automatically"
|
|
fi
|
|
|
|
# Check domain format
|
|
local traefik_domain
|
|
traefik_domain=$(get_env_value "traefik.env" "TRAEFIK_DOMAIN")
|
|
if [[ -n "$traefik_domain" ]] && echo "$traefik_domain" | grep -q "$PLACEHOLDER_DOMAINS"; then
|
|
echo_error "TRAEFIK_DOMAIN still has placeholder domain"
|
|
fi
|
|
fi
|
|
|
|
echo ""
|
|
|
|
# Validate mariadb.env
|
|
validate_env_file "mariadb.env" "DB_PASSWORD"
|
|
|
|
echo ""
|
|
|
|
# Cross-file validation
|
|
echo "Cross-checking configurations..."
|
|
if [[ -f "production.env" && -f "mariadb.env" ]]; then
|
|
local prod_pass maria_pass
|
|
prod_pass=$(get_env_value "production.env" "DB_PASSWORD")
|
|
maria_pass=$(get_env_value "mariadb.env" "DB_PASSWORD")
|
|
|
|
if [[ -n "$prod_pass" && -n "$maria_pass" ]]; then
|
|
if [[ "$prod_pass" == "$maria_pass" ]]; then
|
|
echo_info "Database passwords match"
|
|
else
|
|
echo_error "Database passwords DO NOT match between files"
|
|
# Don't expose actual passwords in logs
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# Final summary
|
|
echo ""
|
|
echo "=================================================="
|
|
echo "Validation Summary"
|
|
echo "=================================================="
|
|
echo ""
|
|
|
|
if (( errors > 0 )); then
|
|
echo -e "${RED}❌ Validation Failed${NC}"
|
|
echo " Errors: $errors"
|
|
echo " Warnings: $warnings"
|
|
echo ""
|
|
echo "Please fix the errors above before deploying."
|
|
exit 1
|
|
elif (( warnings > 0 )); then
|
|
echo -e "${YELLOW}⚠️ Validation Passed with Warnings${NC}"
|
|
echo " Warnings: $warnings"
|
|
echo ""
|
|
echo "Consider addressing the warnings for better security."
|
|
exit 0
|
|
else
|
|
echo -e "${GREEN}✅ Validation Passed${NC}"
|
|
echo " No errors or warnings found."
|
|
echo ""
|
|
echo "You can now proceed with deployment:"
|
|
echo " ./scripts/deploy.sh"
|
|
exit 0
|
|
fi
|
|
}
|
|
|
|
# Run main function
|
|
main "$@" |