fix: Gitea Traefik routing and connection pool optimization
Some checks failed
🚀 Build & Deploy Image / Determine Build Necessity (push) Failing after 10m14s
🚀 Build & Deploy Image / Build Runtime Base Image (push) Has been skipped
🚀 Build & Deploy Image / Build Docker Image (push) Has been skipped
🚀 Build & Deploy Image / Run Tests & Quality Checks (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Staging (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Production (push) Has been skipped
Security Vulnerability Scan / Check for Dependency Changes (push) Failing after 11m25s
Security Vulnerability Scan / Composer Security Audit (push) Has been cancelled

- Remove middleware reference from Gitea Traefik labels (caused routing issues)
- Optimize Gitea connection pool settings (MAX_IDLE_CONNS=30, authentication_timeout=180s)
- Add explicit service reference in Traefik labels
- Fix intermittent 504 timeouts by improving PostgreSQL connection handling

Fixes Gitea unreachability via git.michaelschiemer.de
This commit is contained in:
2025-11-09 14:46:15 +01:00
parent 85c369e846
commit 36ef2a1e2c
1366 changed files with 104925 additions and 28719 deletions

View File

@@ -0,0 +1,435 @@
#!/bin/bash
# Production Health Check Script
# Comprehensive health monitoring for all production services
#
# Usage:
# ./scripts/health-check.sh [--verbose] [--json]
#
# Options:
# --verbose Show detailed output
# --json Output in JSON format
set -euo pipefail
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
VERBOSE=false
JSON_OUTPUT=false
# Parse arguments
for arg in "$@"; do
case $arg in
--verbose)
VERBOSE=true
;;
--json)
JSON_OUTPUT=true
;;
esac
done
# Colors
GREEN="\e[32m"
YELLOW="\e[33m"
RED="\e[31m"
BLUE="\e[34m"
RESET="\e[0m"
# Health check results
declare -A HEALTH_RESULTS
OVERALL_HEALTHY=true
# Logging functions
log() {
if [[ "$JSON_OUTPUT" == "false" ]]; then
echo -e "${BLUE}[$(date +'%H:%M:%S')]${RESET} $1"
fi
}
success() {
if [[ "$JSON_OUTPUT" == "false" ]]; then
echo -e "${GREEN}$1${RESET}"
fi
}
warning() {
if [[ "$JSON_OUTPUT" == "false" ]]; then
echo -e "${YELLOW}⚠️ $1${RESET}"
fi
}
error() {
if [[ "$JSON_OUTPUT" == "false" ]]; then
echo -e "${RED}$1${RESET}"
fi
}
# Check Docker daemon
check_docker() {
log "Checking Docker daemon..."
if docker info &>/dev/null; then
HEALTH_RESULTS[docker]="healthy"
success "Docker daemon is running"
return 0
else
HEALTH_RESULTS[docker]="unhealthy"
error "Docker daemon is not running"
OVERALL_HEALTHY=false
return 1
fi
}
# Check Docker Compose services
check_docker_services() {
log "Checking Docker Compose services..."
cd "$PROJECT_ROOT"
local services=("web" "php" "db" "redis" "queue-worker")
local all_healthy=true
for service in "${services[@]}"; do
if docker compose ps "$service" | grep -q "Up"; then
HEALTH_RESULTS["service_${service}"]="healthy"
[[ "$VERBOSE" == "true" ]] && success "$service is running"
else
HEALTH_RESULTS["service_${service}"]="unhealthy"
error "$service is not running"
all_healthy=false
fi
done
if [[ "$all_healthy" == "true" ]]; then
success "All Docker services are running"
else
error "Some Docker services are not running"
OVERALL_HEALTHY=false
fi
}
# Check web server response
check_web_response() {
log "Checking web server response..."
local max_retries=3
local retry_count=0
while [[ $retry_count -lt $max_retries ]]; do
if curl -f -s -k -H "User-Agent: Mozilla/5.0 (Health Check)" "https://localhost" > /dev/null 2>&1; then
HEALTH_RESULTS[web_response]="healthy"
success "Web server is responding"
return 0
fi
retry_count=$((retry_count + 1))
sleep 2
done
HEALTH_RESULTS[web_response]="unhealthy"
error "Web server is not responding"
OVERALL_HEALTHY=false
return 1
}
# Check health endpoint
check_health_endpoint() {
log "Checking /health endpoint..."
if response=$(curl -f -s -k -H "User-Agent: Mozilla/5.0 (Health Check)" "https://localhost/health" 2>&1); then
HEALTH_RESULTS[health_endpoint]="healthy"
success "Health endpoint is responding"
if [[ "$VERBOSE" == "true" ]]; then
echo "$response" | head -20
fi
return 0
else
HEALTH_RESULTS[health_endpoint]="unhealthy"
error "Health endpoint is not responding"
OVERALL_HEALTHY=false
return 1
fi
}
# Check database connectivity
check_database() {
log "Checking database connectivity..."
cd "$PROJECT_ROOT"
if docker compose exec -T db pg_isready -U postgres &>/dev/null; then
HEALTH_RESULTS[database]="healthy"
success "Database is accepting connections"
# Get connection count
if [[ "$VERBOSE" == "true" ]]; then
local conn_count=$(docker compose exec -T db psql -U postgres -t -c "SELECT count(*) FROM pg_stat_activity;" | tr -d ' ')
log "Active connections: $conn_count"
fi
return 0
else
HEALTH_RESULTS[database]="unhealthy"
error "Database is not accepting connections"
OVERALL_HEALTHY=false
return 1
fi
}
# Check Redis connectivity
check_redis() {
log "Checking Redis connectivity..."
cd "$PROJECT_ROOT"
if docker compose exec -T redis redis-cli ping &>/dev/null; then
HEALTH_RESULTS[redis]="healthy"
success "Redis is responding"
# Get Redis info
if [[ "$VERBOSE" == "true" ]]; then
local used_memory=$(docker compose exec -T redis redis-cli info memory | grep "used_memory_human" | cut -d: -f2 | tr -d '\r')
log "Redis memory usage: $used_memory"
fi
return 0
else
HEALTH_RESULTS[redis]="unhealthy"
error "Redis is not responding"
OVERALL_HEALTHY=false
return 1
fi
}
# Check SSL certificate
check_ssl_certificate() {
log "Checking SSL certificate..."
cd "$PROJECT_ROOT"
if docker compose exec -T php php console.php ssl:status 2>/dev/null | grep -q "Certificate is valid"; then
HEALTH_RESULTS[ssl]="healthy"
success "SSL certificate is valid"
if [[ "$VERBOSE" == "true" ]]; then
docker compose exec -T php php console.php ssl:status
fi
return 0
else
HEALTH_RESULTS[ssl]="warning"
warning "SSL certificate status unclear"
return 1
fi
}
# Check Vault connectivity
check_vault() {
log "Checking Vault connectivity..."
cd "$PROJECT_ROOT"
if docker compose exec -T php php console.php vault:list &>/dev/null; then
HEALTH_RESULTS[vault]="healthy"
success "Vault is accessible"
return 0
else
HEALTH_RESULTS[vault]="unhealthy"
error "Vault is not accessible"
OVERALL_HEALTHY=false
return 1
fi
}
# Check disk space
check_disk_space() {
log "Checking disk space..."
local disk_usage=$(df -h "$PROJECT_ROOT" | tail -1 | awk '{print $5}' | tr -d '%')
if [[ $disk_usage -lt 80 ]]; then
HEALTH_RESULTS[disk_space]="healthy"
success "Disk space usage: ${disk_usage}%"
elif [[ $disk_usage -lt 90 ]]; then
HEALTH_RESULTS[disk_space]="warning"
warning "Disk space usage: ${disk_usage}% (consider cleanup)"
else
HEALTH_RESULTS[disk_space]="critical"
error "Disk space usage: ${disk_usage}% (critical)"
OVERALL_HEALTHY=false
fi
}
# Check memory usage
check_memory() {
log "Checking memory usage..."
local mem_usage=$(free | grep Mem | awk '{printf "%.0f", $3/$2 * 100}')
if [[ $mem_usage -lt 80 ]]; then
HEALTH_RESULTS[memory]="healthy"
success "Memory usage: ${mem_usage}%"
elif [[ $mem_usage -lt 90 ]]; then
HEALTH_RESULTS[memory]="warning"
warning "Memory usage: ${mem_usage}% (high)"
else
HEALTH_RESULTS[memory]="critical"
error "Memory usage: ${mem_usage}% (critical)"
OVERALL_HEALTHY=false
fi
}
# Check queue worker status
check_queue_workers() {
log "Checking queue workers..."
cd "$PROJECT_ROOT"
local worker_count=$(docker compose ps queue-worker | grep "Up" | wc -l)
if [[ $worker_count -ge 2 ]]; then
HEALTH_RESULTS[queue_workers]="healthy"
success "Queue workers: $worker_count running"
elif [[ $worker_count -ge 1 ]]; then
HEALTH_RESULTS[queue_workers]="warning"
warning "Queue workers: only $worker_count running (expected 2)"
else
HEALTH_RESULTS[queue_workers]="unhealthy"
error "Queue workers: none running"
OVERALL_HEALTHY=false
fi
}
# Check logs for errors
check_recent_errors() {
log "Checking recent errors in logs..."
cd "$PROJECT_ROOT"
local error_count=$(docker compose logs --tail=1000 php 2>/dev/null | grep -ci "error\|exception\|fatal" || echo "0")
if [[ $error_count -lt 5 ]]; then
HEALTH_RESULTS[recent_errors]="healthy"
success "Recent errors: $error_count (last 1000 lines)"
elif [[ $error_count -lt 20 ]]; then
HEALTH_RESULTS[recent_errors]="warning"
warning "Recent errors: $error_count (last 1000 lines)"
else
HEALTH_RESULTS[recent_errors]="critical"
error "Recent errors: $error_count (last 1000 lines)"
OVERALL_HEALTHY=false
fi
}
# Output JSON report
output_json() {
echo "{"
echo " \"timestamp\": \"$(date -Iseconds)\","
echo " \"overall_status\": \"$([ "$OVERALL_HEALTHY" == "true" ] && echo "healthy" || echo "unhealthy")\","
echo " \"checks\": {"
local first=true
for check in "${!HEALTH_RESULTS[@]}"; do
if [[ "$first" == "true" ]]; then
first=false
else
echo ","
fi
echo -n " \"$check\": \"${HEALTH_RESULTS[$check]}\""
done
echo ""
echo " }"
echo "}"
}
# Display summary
display_summary() {
echo ""
echo -e "${BLUE}========================================${RESET}"
echo -e "${BLUE} Production Health Check Summary${RESET}"
echo -e "${BLUE}========================================${RESET}"
echo ""
local healthy_count=0
local warning_count=0
local unhealthy_count=0
for status in "${HEALTH_RESULTS[@]}"; do
case $status in
healthy)
healthy_count=$((healthy_count + 1))
;;
warning)
warning_count=$((warning_count + 1))
;;
unhealthy|critical)
unhealthy_count=$((unhealthy_count + 1))
;;
esac
done
echo "📊 Health Status:"
echo " ✅ Healthy: $healthy_count"
echo " ⚠️ Warnings: $warning_count"
echo " ❌ Unhealthy: $unhealthy_count"
echo ""
if [[ "$OVERALL_HEALTHY" == "true" ]]; then
echo -e "${GREEN}Overall Status: HEALTHY ✅${RESET}"
echo ""
echo "🎉 All critical systems are operational"
else
echo -e "${RED}Overall Status: UNHEALTHY ❌${RESET}"
echo ""
echo "⚠️ Critical issues detected - immediate action required"
echo ""
echo "📝 Recommended Actions:"
echo " 1. Check Docker logs: docker compose logs -f --tail=100"
echo " 2. Review service status: docker compose ps"
echo " 3. Check system resources: df -h && free -h"
echo " 4. Review recent deployments for issues"
fi
echo ""
echo -e "${BLUE}========================================${RESET}"
}
# Main health check execution
main() {
if [[ "$JSON_OUTPUT" == "false" ]]; then
log "🔍 Starting production health check..."
echo ""
fi
check_docker
check_docker_services
check_web_response
check_health_endpoint
check_database
check_redis
check_ssl_certificate
check_vault
check_disk_space
check_memory
check_queue_workers
check_recent_errors
if [[ "$JSON_OUTPUT" == "true" ]]; then
output_json
else
display_summary
fi
# Exit with appropriate code
if [[ "$OVERALL_HEALTHY" == "true" ]]; then
exit 0
else
exit 1
fi
}
# Run main
main "$@"

View File

@@ -0,0 +1,244 @@
#!/bin/bash
#
# Migration Script: .env → .env.base + .env.local
#
# This script helps migrate from the legacy single .env file
# to the new Base+Override Pattern (.env.base + .env.local)
#
# Usage:
# ./scripts/migrate-env-to-base-override.sh
#
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
cd "$PROJECT_ROOT"
echo "🔄 Migration: .env → .env.base + .env.local"
echo ""
# Check if .env exists
if [ ! -f .env ]; then
echo "❌ .env Datei nicht gefunden"
echo "💡 Erstelle zuerst .env aus .env.example"
exit 1
fi
# Backup existing .env
BACKUP_FILE=".env.backup.$(date +%Y%m%d-%H%M%S)"
echo "📦 Backup erstellen: $BACKUP_FILE"
cp .env "$BACKUP_FILE"
echo "✅ Backup erstellt"
# Check if .env.base exists
if [ -f .env.base ]; then
echo ""
echo "⚠️ .env.base existiert bereits"
echo "💡 .env.base wird als Basis verwendet"
USE_EXISTING_BASE=true
else
USE_EXISTING_BASE=false
fi
# Check if .env.local exists
if [ -f .env.local ]; then
echo ""
echo "⚠️ .env.local existiert bereits"
read -p "Überschreiben? (j/n): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Jj]$ ]]; then
echo "❌ Abgebrochen"
exit 1
fi
BACKUP_LOCAL=".env.local.backup.$(date +%Y%m%d-%H%M%S)"
cp .env.local "$BACKUP_LOCAL"
echo "📦 Backup von .env.local erstellt: $BACKUP_LOCAL"
fi
echo ""
echo "📝 Analysiere .env Datei..."
# Common variables that should go to .env.base
# (These are typically environment-agnostic)
BASE_VARS=(
"APP_NAME"
"APP_TIMEZONE"
"APP_LOCALE"
"DB_DRIVER"
"DB_PORT"
"DB_CHARSET"
"REDIS_PORT"
"CACHE_DRIVER"
"SESSION_DRIVER"
"SESSION_LIFETIME"
"QUEUE_DRIVER"
"QUEUE_CONNECTION"
"QUEUE_WORKER_SLEEP"
"QUEUE_WORKER_TRIES"
"QUEUE_WORKER_TIMEOUT"
"SECURITY_RATE_LIMIT_PER_MINUTE"
"SECURITY_RATE_LIMIT_BURST"
"CACHE_PREFIX"
)
# Local-specific variables (development overrides)
LOCAL_VARS=(
"APP_ENV"
"APP_DEBUG"
"APP_URL"
"APP_KEY"
"APP_DOMAIN"
"DB_HOST"
"DB_DATABASE"
"DB_USERNAME"
"DB_PASSWORD"
"REDIS_HOST"
"REDIS_PASSWORD"
"SECURITY_ALLOWED_HOSTS"
"FORCE_HTTPS"
"XDEBUG_MODE"
"PHP_IDE_CONFIG"
)
# Variables that should NOT be in .env.base (secrets)
SECRET_PATTERNS=(
"PASSWORD"
"SECRET"
"KEY"
"TOKEN"
"ENCRYPTION"
"VAULT"
)
echo ""
echo "📋 Trenne Variablen in Base und Local..."
# Create temporary files
TMP_BASE=$(mktemp)
TMP_LOCAL=$(mktemp)
# Read .env line by line
while IFS= read -r line || [ -n "$line" ]; do
# Skip empty lines and comments
if [[ -z "$line" || "$line" =~ ^[[:space:]]*# ]]; then
echo "$line" >> "$TMP_BASE"
echo "$line" >> "$TMP_LOCAL"
continue
fi
# Extract variable name
if [[ "$line" =~ ^([A-Za-z_][A-Za-z0-9_]*)= ]]; then
VAR_NAME="${BASH_REMATCH[1]}"
# Check if it's a secret
IS_SECRET=false
for pattern in "${SECRET_PATTERNS[@]}"; do
if [[ "$VAR_NAME" == *"$pattern"* ]]; then
IS_SECRET=true
break
fi
done
if [ "$IS_SECRET" = true ]; then
# Secrets go to .env.local (or should be in Docker Secrets)
echo "# TODO: Möglicherweise in Docker Secrets verschieben" >> "$TMP_LOCAL"
echo "$line" >> "$TMP_LOCAL"
continue
fi
# Check if it's a base variable
IS_BASE=false
for base_var in "${BASE_VARS[@]}"; do
if [[ "$VAR_NAME" == "$base_var" ]]; then
IS_BASE=true
break
fi
done
# Check if it's a local variable
IS_LOCAL=false
for local_var in "${LOCAL_VARS[@]}"; do
if [[ "$VAR_NAME" == "$local_var" ]]; then
IS_LOCAL=true
break
fi
done
if [ "$IS_BASE" = true ]; then
# Go to .env.base
echo "$line" >> "$TMP_BASE"
elif [ "$IS_LOCAL" = true ]; then
# Go to .env.local
echo "$line" >> "$TMP_LOCAL"
else
# Unknown: Ask or put in local as default
echo "# TODO: Prüfen ob Base oder Local" >> "$TMP_LOCAL"
echo "$line" >> "$TMP_LOCAL"
fi
else
# Non-standard line format: keep in both (shouldn't happen)
echo "$line" >> "$TMP_BASE"
echo "$line" >> "$TMP_LOCAL"
fi
done < .env
# Create .env.base if it doesn't exist
if [ "$USE_EXISTING_BASE" = false ]; then
echo ""
echo "📝 Erstelle .env.base..."
cat > .env.base << 'EOF'
# Base Environment Configuration
# This file contains shared environment variables for all environments.
# Use with environment-specific override files:
# - .env.local (local development overrides)
# - .env.staging (staging-specific overrides, optional)
# - .env.production (production - generated by Ansible)
#
# Framework automatically loads: .env.base → .env.local (if exists)
# See ENV_SETUP.md for details
#
EOF
cat "$TMP_BASE" >> .env.base
echo "✅ .env.base erstellt"
else
echo ""
echo " .env.base existiert bereits, wird nicht überschrieben"
fi
# Create .env.local
echo ""
echo "📝 Erstelle .env.local..."
cat > .env.local << 'EOF'
# Local Development Environment Overrides
# This file overrides .env.base with local development-specific settings.
# This file is gitignored - each developer has their own version.
#
# Framework loads: .env.base → .env.local (this file) → System ENV vars
# See ENV_SETUP.md for details
#
EOF
cat "$TMP_LOCAL" >> .env.local
echo "✅ .env.local erstellt"
# Cleanup
rm -f "$TMP_BASE" "$TMP_LOCAL"
echo ""
echo "✅ Migration abgeschlossen!"
echo ""
echo "📋 Nächste Schritte:"
echo " 1. Prüfe .env.base - entferne Secrets falls vorhanden"
echo " 2. Prüfe .env.local - passe lokale Overrides an"
echo " 3. Teste die Anwendung: make up"
echo " 4. Optional: .env kann später entfernt werden (wird als Fallback geladen)"
echo ""
echo "📝 Backup-Dateien:"
echo " - $BACKUP_FILE"
if [ -n "$BACKUP_LOCAL" ]; then
echo " - $BACKUP_LOCAL"
fi
echo ""
echo "💡 Siehe ENV_SETUP.md für Details zur neuen Struktur"

View File

@@ -0,0 +1,34 @@
#!/bin/bash
# Helper Script to prepare secrets for Gitea manual setup
# Usage: ./scripts/prepare-secrets.sh
set -euo pipefail
echo "=== Gitea Secrets - Werte zum Kopieren ==="
echo ""
echo "1. REGISTRY_USER:"
echo "─────────────────"
echo "admin"
echo ""
echo ""
echo "2. REGISTRY_PASSWORD:"
echo "─────────────────────"
echo "registry-secure-password-2025"
echo ""
echo ""
echo "3. SSH_PRIVATE_KEY:"
echo "───────────────────"
if [ -f ~/.ssh/production ]; then
cat ~/.ssh/production
else
echo "⚠️ FEHLER: ~/.ssh/production nicht gefunden!"
echo "Bitte SSH Key erstellen oder Pfad anpassen."
exit 1
fi
echo ""
echo ""
echo "=== Nächste Schritte ==="
echo "1. Gehe zu: https://git.michaelschiemer.de/<username>/michaelschiemer/settings/secrets"
echo "2. Füge jedes Secret oben einzeln hinzu"
echo "3. Kopiere die Werte von oben für jedes Secret"
echo ""

View File

@@ -0,0 +1,238 @@
#!/bin/bash
# Setup script for autossh persistent SSH connections
# Usage: ./scripts/setup-autossh.sh [production|git|both]
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
SSH_CONFIG="$HOME/.ssh/config"
SERVICE_TYPE="${1:-both}"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# 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"
}
# Check if autossh is installed
check_autossh() {
if ! command -v autossh &> /dev/null; then
log_error "autossh is not installed!"
echo ""
echo "Installation:"
echo " Ubuntu/Debian: sudo apt install autossh"
echo " macOS: brew install autossh"
exit 1
fi
log_info "autossh is installed: $(which autossh)"
}
# Check if SSH config exists
check_ssh_config() {
if [ ! -d "$HOME/.ssh" ]; then
log_info "Creating ~/.ssh directory"
mkdir -p "$HOME/.ssh"
chmod 700 "$HOME/.ssh"
fi
if [ ! -f "$SSH_CONFIG" ]; then
log_info "Creating SSH config file"
touch "$SSH_CONFIG"
chmod 600 "$SSH_CONFIG"
fi
}
# Add SSH config entries
add_ssh_config() {
log_info "Checking SSH config..."
# Production server config
if ! grep -q "Host production" "$SSH_CONFIG" 2>/dev/null; then
log_info "Adding production server config to SSH config"
cat >> "$SSH_CONFIG" << 'EOF'
# Production Server - Persistent Connection
Host production
HostName 94.16.110.151
User deploy
IdentityFile ~/.ssh/production
ServerAliveInterval 60
ServerAliveCountMax 3
TCPKeepAlive yes
Compression yes
StrictHostKeyChecking accept-new
EOF
else
log_info "Production server config already exists in SSH config"
fi
# Git server config
if ! grep -q "Host git.michaelschiemer.de" "$SSH_CONFIG" 2>/dev/null; then
log_info "Adding git server config to SSH config"
cat >> "$SSH_CONFIG" << 'EOF'
# Git Server - Persistent Connection
Host git.michaelschiemer.de
HostName git.michaelschiemer.de
Port 2222
User git
IdentityFile ~/.ssh/git_michaelschiemer
ServerAliveInterval 60
ServerAliveCountMax 3
TCPKeepAlive yes
Compression yes
StrictHostKeyChecking no
UserKnownHostsFile /dev/null
EOF
else
log_info "Git server config already exists in SSH config"
fi
}
# Create systemd service
create_systemd_service() {
local host=$1
local port=$2
local service_name="autossh-${host}"
local service_dir="$HOME/.config/systemd/user"
log_info "Creating systemd service for ${host}..."
mkdir -p "$service_dir"
cat > "${service_dir}/${service_name}.service" << EOF
[Unit]
Description=AutoSSH for ${host}
After=network.target
[Service]
Type=simple
Environment="AUTOSSH_GATETIME=0"
Environment="AUTOSSH_POLL=10"
ExecStart=/usr/bin/autossh -M ${port} -N -o "ServerAliveInterval=60" -o "ServerAliveCountMax=3" ${host}
Restart=always
RestartSec=10
[Install]
WantedBy=default.target
EOF
log_info "Systemd service created: ${service_dir}/${service_name}.service"
}
# Setup systemd services
setup_systemd_services() {
if ! systemctl --user --version &> /dev/null; then
log_warn "systemd user services not available (might be on macOS or non-systemd system)"
log_info "Skipping systemd service setup. See docs/deployment/AUTOSSH-SETUP.md for manual setup."
return
fi
log_info "Setting up systemd services..."
case "$SERVICE_TYPE" in
production)
create_systemd_service "production" "20000"
systemctl --user daemon-reload
log_info "To enable: systemctl --user enable autossh-production.service"
log_info "To start: systemctl --user start autossh-production.service"
;;
git)
create_systemd_service "git.michaelschiemer.de" "20001"
systemctl --user daemon-reload
log_info "To enable: systemctl --user enable autossh-git.michaelschiemer.de.service"
log_info "To start: systemctl --user start autossh-git.michaelschiemer.de.service"
;;
both)
create_systemd_service "production" "20000"
create_systemd_service "git.michaelschiemer.de" "20001"
systemctl --user daemon-reload
log_info "To enable:"
log_info " systemctl --user enable autossh-production.service"
log_info " systemctl --user enable autossh-git.michaelschiemer.de.service"
log_info "To start:"
log_info " systemctl --user start autossh-production.service"
log_info " systemctl --user start autossh-git.michaelschiemer.de.service"
;;
*)
log_error "Invalid service type: $SERVICE_TYPE"
log_info "Usage: $0 [production|git|both]"
exit 1
;;
esac
}
# Test SSH connections
test_connections() {
log_info "Testing SSH connections..."
case "$SERVICE_TYPE" in
production)
if ssh -o ConnectTimeout=5 production "echo 'Connection successful'" 2>/dev/null; then
log_info "? Production server connection successful"
else
log_warn "?? Production server connection failed"
log_info "Make sure SSH key is set up: ssh-keygen -t ed25519 -f ~/.ssh/production"
fi
;;
git)
if ssh -o ConnectTimeout=5 git.michaelschiemer.de "echo 'Connection successful'" 2>/dev/null; then
log_info "? Git server connection successful"
else
log_warn "?? Git server connection failed"
log_info "Make sure SSH key is set up: ssh-keygen -t ed25519 -f ~/.ssh/git_michaelschiemer"
fi
;;
both)
if ssh -o ConnectTimeout=5 production "echo 'Connection successful'" 2>/dev/null; then
log_info "? Production server connection successful"
else
log_warn "?? Production server connection failed"
fi
if ssh -o ConnectTimeout=5 git.michaelschiemer.de "echo 'Connection successful'" 2>/dev/null; then
log_info "? Git server connection successful"
else
log_warn "?? Git server connection failed"
fi
;;
esac
}
# Main execution
main() {
log_info "Setting up autossh for persistent SSH connections"
echo ""
check_autossh
check_ssh_config
add_ssh_config
setup_systemd_services
test_connections
echo ""
log_info "Setup complete!"
echo ""
log_info "Next steps:"
echo " 1. Review SSH config: cat ~/.ssh/config"
echo " 2. Enable systemd services (see output above)"
echo " 3. Start services (see output above)"
echo " 4. Check status: systemctl --user status autossh-*.service"
echo ""
log_info "Documentation: docs/deployment/AUTOSSH-SETUP.md"
}
main

View File

@@ -0,0 +1,123 @@
#!/bin/bash
# Interactive Script to set Gitea Repository Secrets via API
# Usage: ./scripts/setup-gitea-secrets-interactive.sh
set -euo pipefail
GITEA_URL="${GITEA_URL:-https://git.michaelschiemer.de}"
REPO_OWNER="${REPO_OWNER:-michael}"
REPO_NAME="${REPO_NAME:-michaelschiemer}"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
echo -e "${BLUE}=== Gitea Repository Secrets Setup ===${NC}"
echo ""
echo "Repository: ${REPO_OWNER}/${REPO_NAME}"
echo "Gitea URL: ${GITEA_URL}"
echo ""
# Check for existing token
if [ -z "${GITEA_TOKEN:-}" ]; then
echo -e "${YELLOW}Gitea Access Token benötigt${NC}"
echo ""
echo "Bitte generiere einen Token:"
echo "1. Gehe zu: ${GITEA_URL}/user/settings/applications"
echo "2. Klicke 'Generate New Token'"
echo "3. Name: 'secrets-setup'"
echo "4. Scopes: 'write:repository' (oder alle)"
echo "5. Kopiere den Token"
echo ""
read -sp "Gitea Token: " GITEA_TOKEN
echo ""
echo ""
fi
if [ -z "${GITEA_TOKEN:-}" ]; then
echo -e "${RED}❌ Token erforderlich - Abbruch${NC}"
exit 1
fi
# Function to create/update secret via API
set_secret() {
local secret_name=$1
local secret_value=$2
echo -n "Setting $secret_name... "
# Gitea API endpoint: PUT /repos/{owner}/{repo}/actions/secrets/{secretname}
local response=$(curl -s -w "\n%{http_code}" \
-X PUT \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
"${GITEA_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/actions/secrets/${secret_name}" \
-d "{
\"data\": \"$(printf '%s' "$secret_value" | base64 | tr -d '\n')\"
}" 2>&1)
local http_code=$(echo "$response" | tail -n1)
local body=$(echo "$response" | sed '$d')
if [ "$http_code" = "204" ] || [ "$http_code" = "201" ]; then
echo -e "${GREEN}✅ OK${NC}"
return 0
elif [ "$http_code" = "404" ]; then
echo -e "${YELLOW}⚠️ Repository oder Token-Berechtigung fehlt${NC}"
return 1
else
echo -e "${RED}❌ FAILED (HTTP $http_code)${NC}"
echo "Response: $body"
return 1
fi
}
# Get registry password (default)
REGISTRY_PASSWORD="${REGISTRY_PASSWORD:-registry-secure-password-2025}"
# Get SSH private key
if [ -f ~/.ssh/production ]; then
SSH_PRIVATE_KEY=$(cat ~/.ssh/production)
echo -e "${GREEN}✓ SSH private key gefunden${NC}"
else
echo -e "${RED}✗ SSH private key nicht gefunden in ~/.ssh/production${NC}"
exit 1
fi
echo ""
echo "Setting secrets for repository: ${REPO_OWNER}/${REPO_NAME}"
echo ""
# Test API connection first
echo -n "Testing API connection... "
test_response=$(curl -s -o /dev/null -w "%{http_code}" \
-H "Authorization: token ${GITEA_TOKEN}" \
"${GITEA_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}")
if [ "$test_response" != "200" ]; then
者可 echo -e "${RED}❌ FAILED (HTTP $test_response)${NC}"
echo ""
echo "Mögliche Probleme:"
echo "- Token ungültig oder fehlende Berechtigungen"
echo "- Repository nicht gefunden: ${REPO_OWNER}/${REPO_NAME}"
echo "- Netzwerkproblem"
exit 1
fi
echo -e "${GREEN}✅ OK${NC}"
echo ""
# Set secrets
set_secret "REGISTRY_USER" "admin"
set_secret "REGISTRY_PASSWORD" "$REGISTRY_PASSWORD"
set_secret "SSH_PRIVATE_KEY" "$SSH_PRIVATE_KEY"
echo ""
echo -e "${GREEN}=== Secrets Setup Complete ===${NC}"
echo ""
echo "Prüfe Secrets in Gitea UI:"
echo "${GITEA_URL}/${REPO_OWNER}/${REPO_NAME}/settings/secrets/actions"
echo ""

View File

@@ -0,0 +1,120 @@
#!/bin/bash
# Set Gitea Repository Secrets with Token
# Usage: ./scripts/setup-gitea-secrets-with-token.sh <GITEA_TOKEN>
# or: GITEA_TOKEN=xxx ./scripts/setup-gitea-secrets-with-token.sh
set -euo pipefail
GITEA_URL="${GITEA_URL:-https://git.michaelschiemer.de}"
REPO_OWNER="${REPO_OWNER:-michael}"
REPO_NAME="${REPO_NAME:-michaelschiemer}"
GITEA_TOKEN="${1:-${GITEA_TOKEN:-}}"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
echo -e "${BLUE}=== Gitea Repository Secrets Setup ===${NC}"
echo ""
echo "Repository: ${REPO_OWNER}/${REPO_NAME}"
echo "Gitea URL: ${GITEA_URL}"
echo ""
# Check if token is provided
if [ -z "$GITEA_TOKEN" ]; then
echo -e "${RED}❌ Fehler: GITEA_TOKEN nicht angegeben${NC}"
echo ""
echo "Verwendung:"
echo " $0 <GITEA_TOKEN>"
echo " oder:"
echo " GITEA_TOKEN=<token> $0"
echo ""
exit 1
fi
# Function to create/update secret via API
set_secret() {
local secret_name=$1
local secret_value=$2
echo -n "Setting $secret_name... "
# Base64 encode the secret value
local encoded_value=$(printf '%s' "$secret_value" | base64 | tr -d '\n')
# Gitea API endpoint: PUT /repos/{owner}/{repo}/actions/secrets/{secretname}
local response=$(curl -s -w "\n%{http_code}" \
-X PUT \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
"${GITEA_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/actions/secrets/${secret_name}" \
-d "{
\"data\": \"${encoded_value}\"
}" 2>&1)
local http_code=$(echo "$response" | tail -n1)
local body=$(echo "$response" | sed '$d')
if [ "$http_code" = "204" ] || [ "$http_code" = "201" ]; then
echo -e "${GREEN}✅ OK${NC}"
return 0
elif [ "$http_code" = "404" ]; then
echo -e "${YELLOW}⚠️ Repository oder Token-Berechtigung fehlt${NC}"
echo "Response: $body"
return 1
else
echo -e "${RED}❌ FAILED (HTTP $http_code)${NC}"
echo "Response: $body"
return 1
fi
}
# Get registry password (default)
REGISTRY_PASSWORD="${REGISTRY_PASSWORD:-registry-secure-password-2025}"
# Get SSH private key
if [ -f ~/.ssh/production ]; then
SSH_PRIVATE_KEY=$(cat ~/.ssh/production)
echo -e "${GREEN}✓ SSH private key gefunden${NC}"
else
echo -e "${RED}✗ SSH private key nicht gefunden in ~/.ssh/production${NC}"
exit 1
fi
echo ""
echo "Testing API connection..."
# Test API connection first
test_response=$(curl -s -o /dev/null -w "%{http_code}" \
-H "Authorization: token ${GITEA_TOKEN}" \
"${GITEA_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}")
if [ "$test_response" != "200" ]; then
echo -e "${RED}❌ API-Verbindung fehlgeschlagen (HTTP $test_response)${NC}"
echo ""
echo "Mögliche Probleme:"
echo "- Token ungültig oder fehlende Berechtigungen"
echo "- Repository nicht gefunden: ${REPO_OWNER}/${REPO_NAME}"
echo "- Netzwerkproblem"
exit 1
fi
echo -e "${GREEN}✅ API-Verbindung erfolgreich${NC}"
echo ""
echo "Setting secrets..."
echo ""
# Set secrets
set_secret "REGISTRY_USER" "admin"
set_secret "REGISTRY_PASSWORD" "$REGISTRY_PASSWORD"
set_secret "SSH_PRIVATE_KEY" "$SSH_PRIVATE_KEY"
echo ""
echo -e "${GREEN}=== Secrets Setup Complete ===${NC}"
echo ""
echo "Prüfe Secrets in Gitea UI:"
echo "${GITEA_URL}/${REPO_OWNER}/${REPO_NAME}/settings/secrets/actions"
echo ""

View File

@@ -0,0 +1,96 @@
#!/bin/bash
# Helper Script to set Gitea Repository Secrets
# Usage: ./scripts/setup-gitea-secrets.sh [GITEA_TOKEN]
set -euo pipefail
GITEA_URL="${GITEA_URL:-https://git.michaelschiemer.de}"
REPO_OWNER="${REPO_OWNER:-$(git config user.name || echo 'michael')}"
REPO_NAME="${REPO_NAME:-michaelschiemer}"
GITEA_TOKEN="${1:-${GITEA_TOKEN:-}}"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
echo -e "${GREEN}=== Gitea Repository Secrets Setup ===${NC}"
echo ""
# Check if token is provided
if [ -z "$GITEA_TOKEN" ]; then
echo -e "${YELLOW}⚠️ GITEA_TOKEN nicht gesetzt${NC}"
echo ""
echo "Bitte generiere einen Gitea Access Token:"
echo "1. Gehe zu: ${GITEA_URL}/user/settings/applications"
echo "2. Klicke 'Generate New Token'"
echo "3. Name: 'secrets-setup'"
echo "4. Scopes: 'write:repository'"
echo "5. Kopiere den Token"
echo ""
echo "Dann führe aus:"
echo " export GITEA_TOKEN='dein-token'"
echo " ./scripts/setup-gitea-secrets.sh"
echo ""
exit 1
fi
# Function to create/update secret
set_secret() {
local secret_name=$1
local secret_value=$2
echo -n "Setting $secret_name... "
# Gitea API endpoint for repository secrets
local response=$(curl -s -w "\n%{http_code}" \
-X PUT \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
"${GITEA_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/actions/secrets/${secret_name}" \
-d "{
\"data\": \"${secret_value}\"
}")
local http_code=$(echo "$response" | tail -n1)
local body=$(echo "$response" | sed '$d')
if [ "$http_code" = "204" ] || [ "$http_code" = "201" ]; then
echo -e "${GREEN}✅ OK${NC}"
return 0
else
echo -e "${RED}❌ FAILED (HTTP $http_code)${NC}"
echo "Response: $body"
return 1
fi
}
# Get registry password (default or from vault)
REGISTRY_PASSWORD="${REGISTRY_PASSWORD:-registry-secure-password-2025}"
# Get SSH private key
if [ -f ~/.ssh/production ]; then
SSH_PRIVATE_KEY=$(cat ~/.ssh/production)
echo -e "${GREEN}✓ SSH private key gefunden${NC}"
else
echo -e "${RED}✗ SSH private key nicht gefunden in ~/.ssh/production${NC}"
echo "Bitte SSH key Pfad anpassen oder manuell setzen"
exit 1
fi
echo ""
echo "Setting secrets for repository: ${REPO_OWNER}/${REPO_NAME}"
echo ""
# Set secrets
set_secret "REGISTRY_USER" "admin"
set_secret "REGISTRY_PASSWORD" "$REGISTRY_PASSWORD"
set_secret "SSH_PRIVATE_KEY" "$SSH_PRIVATE_KEY"
echo ""
echo -e "${GREEN}=== Secrets Setup Complete ===${NC}"
echo ""
echo "Prüfe Secrets in Gitea UI:"
echo "${GITEA_URL}/${REPO_OWNER}/${REPO_NAME}/enu/repo/settings/secrets"
echo ""

View File

@@ -0,0 +1,85 @@
#!/bin/bash
# ==============================================================================
# Production Secrets Setup Script
# ==============================================================================
# This script creates Docker Secrets on the production server from .env values
# Run this ONCE during initial setup on the production server.
# ==============================================================================
set -e
echo "🔐 Docker Secrets Setup for Production"
echo "======================================"
echo ""
# Check if running on production server
if [ ! -f /home/deploy/framework/.env ]; then
echo "❌ ERROR: /home/deploy/framework/.env not found"
echo " Please ensure .env file exists on production server"
exit 1
fi
# Check if Docker Swarm is initialized
if ! docker info | grep -q "Swarm: active"; then
echo "❌ ERROR: Docker Swarm is not initialized"
echo " Run: docker swarm init"
exit 1
fi
echo "📋 Reading secrets from .env file..."
cd /home/deploy/framework
# Function to create secret from .env
create_secret() {
local secret_name=$1
local env_key=$2
# Extract value from .env
local value=$(grep "^${env_key}=" .env | cut -d'=' -f2- | sed 's/^"\(.*\)"$/\1/')
if [ -z "$value" ]; then
echo "⚠️ WARNING: ${env_key} not found in .env, skipping ${secret_name}"
return
fi
# Check if secret already exists
if docker secret ls --format "{{.Name}}" | grep -q "^${secret_name}$"; then
echo " Secret '${secret_name}' already exists, skipping..."
return
fi
# Create secret
echo "$value" | docker secret create "$secret_name" - 2>/dev/null
if [ $? -eq 0 ]; then
echo "✅ Created secret: ${secret_name}"
else
echo "❌ Failed to create secret: ${secret_name}"
fi
}
echo ""
echo "🔑 Creating Docker Secrets..."
echo ""
# Create all required secrets
create_secret "db_password" "DB_PASSWORD"
create_secret "app_key" "APP_KEY"
create_secret "vault_encryption_key" "VAULT_ENCRYPTION_KEY"
create_secret "shopify_webhook_secret" "SHOPIFY_WEBHOOK_SECRET"
create_secret "rapidmail_password" "RAPIDMAIL_PASSWORD"
echo ""
echo "📊 Verifying Secrets..."
echo ""
docker secret ls
echo ""
echo "✅ Secrets setup completed!"
echo ""
echo "Next steps:"
echo " 1. Deploy the stack: docker stack deploy -c docker-compose.prod.yml framework"
echo " 2. Monitor deployment: watch docker stack ps framework"
echo " 3. Check logs: docker service logs framework_web"
echo ""

View File

@@ -0,0 +1,320 @@
#!/bin/bash
#
# Production SSL Certificate Setup Script for michaelschiemer.de
# Sets up Let's Encrypt SSL certificates on the production server
#
set -euo pipefail
# Configuration
SERVER_USER="deploy"
SERVER_IP="94.16.110.151"
REMOTE_PATH="/home/deploy/michaelschiemer"
DOMAIN="michaelschiemer.de"
EMAIL="michael@michaelschiemer.de" # Change this to your email
SSH_OPTS="-i ~/.ssh/production -o StrictHostKeyChecking=no"
# Colors for output
GREEN="\e[32m"
YELLOW="\e[33m"
RED="\e[31m"
BLUE="\e[34m"
RESET="\e[0m"
log_info() {
echo -e "${BLUE}[INFO]${RESET} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${RESET} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${RESET} $1"
}
log_error() {
echo -e "${RED}[ERROR]${RESET} $1"
}
# Check if domain resolves to production server
check_dns() {
log_info "Checking DNS resolution for $DOMAIN..."
local resolved_ip=$(dig +short "$DOMAIN" | head -1)
if [[ "$resolved_ip" == "$SERVER_IP" ]]; then
log_success "Domain $DOMAIN resolves correctly to $SERVER_IP"
else
log_warning "Domain $DOMAIN resolves to $resolved_ip (expected: $SERVER_IP)"
log_warning "SSL certificate might fail if DNS is incorrect"
fi
}
# Install certbot on production server
install_certbot() {
log_info "Installing certbot on production server..."
ssh $SSH_OPTS "$SERVER_USER@$SERVER_IP" '
# Update package list
sudo apt update
# Install snapd if not present
if ! command -v snap &> /dev/null; then
sudo apt install -y snapd
fi
# Install certbot via snap
if ! command -v certbot &> /dev/null; then
sudo snap install core; sudo snap refresh core
sudo snap install --classic certbot
sudo ln -sf /snap/bin/certbot /usr/bin/certbot
fi
# Verify certbot installation
certbot --version
'
log_success "Certbot installed successfully"
}
# Stop nginx temporarily for certificate generation
prepare_nginx() {
log_info "Preparing nginx for certificate generation..."
ssh $SSH_OPTS "$SERVER_USER@$SERVER_IP" "
cd $REMOTE_PATH
# Stop nginx container temporarily
docker compose down web 2>/dev/null || true
# Create ssl directory
mkdir -p ssl
# Create temporary nginx config for certbot
cat > ssl/nginx-temp.conf << 'EOF'
server {
listen 80;
server_name $DOMAIN;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://\$server_name\$request_uri;
}
}
EOF
# Start temporary nginx for certificate validation
docker run -d --name nginx-temp \\
-p 80:80 \\
-v \$(pwd)/ssl/nginx-temp.conf:/etc/nginx/conf.d/default.conf \\
-v \$(pwd)/ssl/certbot-webroot:/var/www/certbot \\
nginx:alpine
"
log_success "Nginx prepared for certificate generation"
}
# Generate Let's Encrypt certificate
generate_certificate() {
log_info "Generating Let's Encrypt certificate for $DOMAIN..."
ssh $SSH_OPTS "$SERVER_USER@$SERVER_IP" "
cd $REMOTE_PATH
# Create webroot directory
mkdir -p ssl/certbot-webroot
# Generate certificate
docker run --rm \\
-v \$(pwd)/ssl/certbot-webroot:/var/www/certbot \\
-v \$(pwd)/ssl:/etc/letsencrypt \\
certbot/certbot certonly \\
--webroot \\
--webroot-path=/var/www/certbot \\
--email $EMAIL \\
--agree-tos \\
--no-eff-email \\
-d $DOMAIN
# Copy certificates to expected location
if [ -d \"ssl/live/$DOMAIN\" ]; then
cp ssl/live/$DOMAIN/fullchain.pem ssl/fullchain.pem
cp ssl/live/$DOMAIN/privkey.pem ssl/privkey.pem
chmod 644 ssl/fullchain.pem ssl/privkey.pem
log_success 'SSL certificates generated and copied'
else
log_error 'Certificate generation failed'
exit 1
fi
"
log_success "SSL certificate generated successfully"
}
# Clean up temporary nginx
cleanup_nginx() {
log_info "Cleaning up temporary nginx..."
ssh $SSH_OPTS "$SERVER_USER@$SERVER_IP" "
cd $REMOTE_PATH
# Stop and remove temporary nginx
docker stop nginx-temp 2>/dev/null || true
docker rm nginx-temp 2>/dev/null || true
# Clean up temporary files
rm -f ssl/nginx-temp.conf
rm -rf ssl/certbot-webroot
"
log_success "Temporary nginx cleaned up"
}
# Start the application with SSL
start_application() {
log_info "Starting application with SSL certificates..."
ssh $SSH_OPTS "$SERVER_USER@$SERVER_IP" "
cd $REMOTE_PATH
# Start all services
docker compose up -d
# Wait for services to start
sleep 10
# Check container status
docker compose ps
"
log_success "Application started with SSL certificates"
}
# Test SSL certificate
test_ssl() {
log_info "Testing SSL certificate..."
# Test HTTPS connection
if curl -sSf "https://$DOMAIN" > /dev/null 2>&1; then
log_success "HTTPS connection successful!"
else
log_warning "HTTPS connection test failed - checking certificate..."
fi
# Check certificate details
echo | openssl s_client -servername "$DOMAIN" -connect "$DOMAIN:443" 2>/dev/null | openssl x509 -noout -dates
log_info "SSL setup completed!"
log_success "Your site should now be accessible at: https://$DOMAIN"
}
# Setup certificate renewal
setup_renewal() {
log_info "Setting up automatic certificate renewal..."
ssh $SSH_OPTS "$SERVER_USER@$SERVER_IP" "
# Create renewal script
cat > /home/deploy/renew-certificates.sh << 'RENEWAL_SCRIPT'
#!/bin/bash
cd /home/deploy/michaelschiemer
# Stop nginx container
docker compose down web
# Renew certificate
docker run --rm \\
-v \$(pwd)/ssl:/etc/letsencrypt \\
-v \$(pwd)/ssl/certbot-webroot:/var/www/certbot \\
certbot/certbot renew \\
--webroot \\
--webroot-path=/var/www/certbot
# Copy renewed certificates
if [ -f \"ssl/live/$DOMAIN/fullchain.pem\" ]; then
cp ssl/live/$DOMAIN/fullchain.pem ssl/fullchain.pem
cp ssl/live/$DOMAIN/privkey.pem ssl/privkey.pem
chmod 644 ssl/fullchain.pem ssl/privkey.pem
fi
# Restart services
docker compose up -d
RENEWAL_SCRIPT
chmod +x /home/deploy/renew-certificates.sh
# Add cron job for automatic renewal (runs twice daily)
(crontab -l 2>/dev/null; echo '0 12,0 * * * /home/deploy/renew-certificates.sh >> /var/log/letsencrypt-renewal.log 2>&1') | crontab -
"
log_success "Automatic certificate renewal configured"
}
# Main execution
main() {
log_info "Starting SSL certificate setup for $DOMAIN"
# Check DNS
check_dns
# Install certbot
install_certbot
# Prepare nginx
prepare_nginx
# Generate certificate
generate_certificate
# Clean up temporary nginx
cleanup_nginx
# Start application
start_application
# Test SSL
test_ssl
# Setup renewal
setup_renewal
log_success "SSL certificate setup completed successfully!"
log_info "Next steps:"
echo " 1. Test your site: https://$DOMAIN"
echo " 2. Certificates will auto-renew twice daily"
echo " 3. Check renewal logs: ssh $SERVER_USER@$SERVER_IP 'tail -f /var/log/letsencrypt-renewal.log'"
}
# Show usage
show_usage() {
echo "Usage: $0 [--help]"
echo ""
echo "This script sets up Let's Encrypt SSL certificates for $DOMAIN"
echo ""
echo "Prerequisites:"
echo " - Domain $DOMAIN must point to $SERVER_IP"
echo " - SSH access to production server configured"
echo " - Docker and docker-compose installed on server"
echo ""
echo "Options:"
echo " --help Show this help message"
}
# Parse arguments
case "${1:-}" in
--help|-h)
show_usage
exit 0
;;
"")
main
;;
*)
log_error "Unknown option: $1"
show_usage
exit 1
;;
esac

View File

@@ -0,0 +1,183 @@
#!/bin/bash
# Interaktives Script zum Setzen der Gitea Secrets via API
# Fragt nach Token falls nicht vorhanden
set -euo pipefail
# Repository Info aus git remote
REPO_FULL=$(git remote get-url origin 2>/dev/null | sed -E 's/.*[:/]([^/]+)\/([^/]+)\.git$/\1\/\2/' || echo "michael/michaelschiemer")
REPO_OWNER=$(echo "$REPO_FULL" | cut -d'/' -f1)
REPO_NAME=$(echo "$REPO_FULL" | cut -d'/' -f2)
GITEA_URL="${GITEA_URL:-https://git.michaelschiemer.de}"
GITEA_TOKEN="${GITEA_TOKEN:-}"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
echo -e "${BLUE}════════════════════════════════════════════════════════════${NC}"
echo -e "${BLUE}Gitea Repository Secrets Setup via API${NC}"
echo -e "${BLUE}════════════════════════════════════════════════════════════${NC}"
echo ""
echo -e "Repository: ${GREEN}${REPO_OWNER}/${REPO_NAME}${NC}"
echo -e "Gitea URL: ${GREEN}${GITEA_URL}${NC}"
echo ""
# Prüfe ob Token vorhanden
if [ -z "$GITEA_TOKEN" ]; then
echo -e "${YELLOW}⚠️ GITEA_TOKEN nicht gefunden${NC}"
echo ""
echo -e "${BLUE}Du musst zuerst einen Gitea Access Token erstellen:${NC}"
echo ""
echo "1. Öffne im Browser:"
echo -e " ${GREEN}${GITEA_URL}/user/settings/applications${NC}"
echo ""
echo "2. Scrolle zu 'Generate New Token'"
echo ""
echo "3. Konfiguration:"
echo " - Name: z.B. 'CI/CD Secrets Setup'"
echo " - Scopes: ✅ write:repository (mindestens)"
echo ""
echo "4. Klicke 'Generate Token'"
echo ""
echo "5. Kopiere den Token (wird nur einmal angezeigt!)"
echo ""
echo -e "${YELLOW}Dann füge den Token hier ein (wird nicht angezeigt):${NC}"
read -s -p "Gitea Token: " GITEA_TOKEN
echo ""
echo ""
fi
# Funktion zum Setzen eines Secrets
set_secret() {
local secret_name=$1
local secret_value=$2
echo -n "Setting $secret_name... "
local response=$(curl -s -w "\n%{http_code}" \
-X PUT \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
"${GITEA_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/actions/secrets/${secret_name}" \
-d "{
\"data\": \"${secret_value}\"
}" 2>&1)
local http_code=$(echo "$response" | tail -n1)
local body=$(echo "$response" | sed '$d')
if [ "$http_code" = "204" ] || [ "$http_code" = "201" ]; then
echo -e "${GREEN}✅ OK${NC}"
return 0
elif [ "$http_code" = "404" ]; then
echo -e "${RED}❌ Repository nicht gefunden (404)${NC}"
echo " Prüfe: REPO_OWNER=${REPO_OWNER}, REPO_NAME=${REPO_NAME}"
return 1
elif [ "$http_code" = "403" ]; then
echo -e "${RED}❌ Keine Berechtigung (403)${NC}"
echo " Token benötigt 'write:repository' Scope"
return 1
elif [ "$http_code" = "401" ]; then
echo -e "${RED}❌ Ungültiger Token (401)${NC}"
return 1
else
echo -e "${RED}❌ FAILED (HTTP $http_code)${NC}"
echo "Response: $body"
return 1
fi
}
# Prüfe Token-Gültigkeit
echo -e "${BLUE}Prüfe Token-Gültigkeit...${NC}"
TOKEN_CHECK=$(curl -s -o /dev/null -w "%{http_code}" \
-H "Authorization: token ${GITEA_TOKEN}" \
"${GITEA_URL}/api/v1/user" 2>&1)
if [ "$TOKEN_CHECK" != "200" ]; then
echo -e "${RED}❌ Token ist ungültig oder hat keine ausreichenden Berechtigungen${NC}"
echo "HTTP Status: $TOKEN_CHECK"
exit 1
fi
echo -e "${GREEN}✅ Token ist gültig${NC}"
echo ""
# Registry Password
REGISTRY_PASSWORD="${REGISTRY_PASSWORD:-registry-secure-password-2025}"
# SSH Private Key
SSH_KEY_PATH="${SSH_KEY_PATH:-$HOME/.ssh/production}"
if [ -f "$SSH_KEY_PATH" ]; then
SSH_PRIVATE_KEY=$(cat "$SSH_KEY_PATH")
echo -e "${GREEN}✓ SSH private key gefunden: ${SSH_KEY_PATH}${NC}"
else
echo -e "${YELLOW}⚠️ SSH private key nicht gefunden: ${SSH_KEY_PATH}${NC}"
echo ""
read -p "SSH Key Pfad (oder Enter für Skip): " custom_ssh_path
if [ -n "$custom_ssh_path" ] && [ -f "$custom_ssh_path" ]; then
SSH_PRIVATE_KEY=$(cat "$custom_ssh_path")
echo -e "${GREEN}✓ SSH private key geladen${NC}"
else
echo -e "${YELLOW}⚠️ Überspringe SSH_PRIVATE_KEY${NC}"
SSH_PRIVATE_KEY=""
fi
fi
echo ""
echo -e "${BLUE}Setze Secrets für Repository: ${REPO_OWNER}/${REPO_NAME}${NC}"
echo ""
# Setze Secrets
ERRORS=0
echo -e "${BLUE}Secret 1/3: REGISTRY_USER${NC}"
if set_secret "REGISTRY_USER" "admin"; then
echo ""
else
ERRORS=$((ERRORS + 1))
fi
echo -e "${BLUE}Secret 2/3: REGISTRY_PASSWORD${NC}"
if set_secret "REGISTRY_PASSWORD" "$REGISTRY_PASSWORD"; then
echo ""
else
ERRORS=$((ERRORS + 1))
fi
if [ -n "$SSH_PRIVATE_KEY" ]; then
echo -e "${BLUE}Secret 3/3: SSH_PRIVATE_KEY${NC}"
# Escape JSON special characters
SSH_PRIVATE_KEY_ESCAPED=$(echo "$SSH_PRIVATE_KEY" | sed 's/\\/\\\\/g' | sed 's/"/\\"/g' | sed ':a;N;$!ba;s/\n/\\n/g')
if set_secret "SSH_PRIVATE_KEY" "$SSH_PRIVATE_KEY_ESCAPED"; then
echo ""
else
ERRORS=$((ERRORS + 1))
fi
else
echo -e "${YELLOW}⚠️ Überspringe SSH_PRIVATE_KEY (nicht gefunden)${NC}"
echo ""
fi
# Zusammenfassung
echo -e "${BLUE}════════════════════════════════════════════════════════════${NC}"
if [ $ERRORS -eq 0 ]; then
echo -e "${GREEN}✅ Secrets Setup erfolgreich abgeschlossen!${NC}"
echo ""
echo -e "Verifizierung:"
echo -e " - Gehe zu: ${GREEN}${GITEA_URL}/${REPO_OWNER}/${REPO_NAME}/settings${NC}"
echo -e " - Oder teste den Workflow: ${GREEN}Repository → Actions → Test Registry Credentials${NC}"
exit 0
else
echo -e "${RED}❌ Fehler beim Setzen von $ERRORS Secret(s)${NC}"
echo ""
echo "Troubleshooting:"
echo " - Prüfe Token-Berechtigungen (benötigt: write:repository)"
echo " - Prüfe Repository-Name: ${REPO_OWNER}/${REPO_NAME}"
echo " - Prüfe ob Actions für das Repository aktiviert ist"
exit 1
fi