chore: remove test trigger file

This commit is contained in:
2025-10-31 04:14:18 +01:00
parent 403205315e
commit 6deca7838e
29 changed files with 4052 additions and 128 deletions

View File

@@ -0,0 +1,561 @@
# Application Stack Deployment Prozess
## Übersicht
Dieses Dokument erklärt, wie genau das Deployment in den Application Stack abläuft, wenn die CI/CD Pipeline ausgeführt wird.
**📖 Verwandte Dokumentation:**
- **[Code Changes Workflow](CODE_CHANGE_WORKFLOW.md)** - Wie Codeänderungen gepusht werden
- **[CI/CD Status](CI_CD_STATUS.md)** - Aktueller Status der Pipeline
---
## Deployment-Flow
```
CI/CD Pipeline (Gitea Actions)
1. Tests & Build
2. Docker Image Build & Push zur Registry
3. Ansible Playbook ausführen
4. Application Stack aktualisieren
```
---
## Detaillierter Ablauf
### Phase 1: CI/CD Pipeline (`.gitea/workflows/production-deploy.yml`)
#### Job 1: Tests
```yaml
- PHP 8.3 Setup
- Composer Dependencies installieren
- Pest Tests ausführen
- PHPStan Code Quality Check
- Code Style Check
```
#### Job 2: Build & Push
```yaml
- Docker Image Build (Dockerfile.production)
- Image mit Tags pushen:
- git.michaelschiemer.de:5000/framework:latest
- git.michaelschiemer.de:5000/framework:<tag>
- git.michaelschiemer.de:5000/framework:git-<short-sha>
```
#### Job 3: Deploy (Ansible)
**Schritt 1: Code Checkout**
```bash
git clone <repository> /workspace/repo
```
**Schritt 2: SSH Setup**
```bash
# SSH Private Key aus Secret wird in ~/.ssh/production geschrieben
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/production
chmod 600 ~/.ssh/production
```
**Schritt 3: Ansible Installation**
```bash
apt-get install -y ansible
```
**Schritt 4: Ansible Playbook ausführen**
```bash
cd /workspace/repo/deployment/ansible
ansible-playbook -i inventory/production.yml \
playbooks/deploy-update.yml \
-e "image_tag=<generated-tag>" \
-e "git_commit_sha=<commit-sha>" \
-e "deployment_timestamp=<timestamp>" \
-e "docker_registry_username=${{ secrets.REGISTRY_USER }}" \
-e "docker_registry_password=${{ secrets.REGISTRY_PASSWORD }}"
```
---
### Phase 2: Ansible Deployment (`deploy-update.yml`)
Das Ansible Playbook führt folgende Schritte auf dem Production-Server aus:
#### Pre-Tasks
**1. Secrets laden (optional)**
```yaml
- Lädt Registry Credentials aus Ansible Vault (falls vorhanden)
- Oder verwendet Credentials aus Workflow-Variablen
```
**2. Docker Service prüfen**
```yaml
- Verifiziert, dass Docker läuft
- Startet Docker falls notwendig
```
**3. Verzeichnisse erstellen**
```yaml
- Application Stack Path: ~/deployment/stacks/application
- Backup Verzeichnis: ~/deployment/backups/<timestamp>/
```
#### Tasks
**1. Backup aktueller Deployment-Status**
```bash
# Speichert aktuellen Container-Status
docker compose -f ~/deployment/stacks/application/docker-compose.yml \
ps --format json > backups/<timestamp>/current_containers.json
# Speichert aktuelle docker-compose.yml Konfiguration
docker compose -f ~/deployment/stacks/application/docker-compose.yml \
config > backups/<timestamp>/docker-compose-config.yml
```
**2. Docker Registry Login**
```bash
# Login zur privaten Registry mit Credentials
docker login git.michaelschiemer.de:5000 \
-u <registry-username> \
-p <registry-password>
```
**3. Neues Image Pullen**
```bash
# Pullt das neue Image von der Registry
docker pull git.michaelschiemer.de:5000/framework:<tag>
# Beispiel:
# git.michaelschiemer.de:5000/framework:abc1234-1696234567
```
**4. docker-compose.yml aktualisieren**
**Wichtig:** Das Playbook aktualisiert die `docker-compose.yml` Datei direkt auf dem Server!
```yaml
# Vorher:
services:
app:
image: git.michaelschiemer.de:5000/framework:latest
# Nachher (wenn image_tag != 'latest'):
services:
app:
image: git.michaelschiemer.de:5000/framework:<tag>
```
**Regex-Replace:**
```yaml
regexp: '^(\s+image:\s+){{ app_image }}:.*$'
replace: '\1{{ app_image }}:{{ image_tag }}'
```
**Betroffene Services (werden alle aktualisiert):**
- `app` (PHP-FPM) - Zeile 6: `image: git.michaelschiemer.de:5000/framework:latest`
- `queue-worker` (Queue Worker) - Zeile 120: `image: git.michaelschiemer.de:5000/framework:latest`
- `scheduler` (Scheduler) - Zeile 165: `image: git.michaelschiemer.de:5000/framework:latest`
**Hinweis:**
- Alle drei Services verwenden das gleiche Image, daher werden alle mit dem neuen Tag aktualisiert
- Der Regex matched **alle Zeilen** die mit `image: git.michaelschiemer.de:5000/framework:` beginnen
- `nginx` und `redis` bleiben unverändert (verwenden andere Images)
**5. Application Stack neu starten**
```bash
docker compose -f ~/deployment/stacks/application/docker-compose.yml \
up -d \
--pull always \
--force-recreate \
--remove-orphans
```
**Was passiert dabei:**
- `--pull always`: Zieht Images immer neu (auch wenn schon vorhanden)
- `--force-recreate`: Erstellt Container immer neu, auch wenn Konfiguration unverändert ist
- `--remove-orphans`: Entfernt Container, die nicht mehr in der Compose-Datei definiert sind
- `-d`: Läuft im Hintergrund (detached)
**Container-Neustart-Reihenfolge:**
1. Abhängigkeiten werden gestoppt (app hängt von redis ab)
2. Container werden neu erstellt mit neuem Image
3. Container werden in korrekter Reihenfolge gestartet (redis → app → nginx)
4. Health-Checks werden ausgeführt
**6. Warten auf Health-Checks**
```yaml
# Wartet 60 Sekunden auf Container-Gesundheit
wait_for:
timeout: 60
```
**7. Health-Status prüfen**
```bash
# Prüft alle Container-Health-Status
docker compose ps --format json | \
jq -r '.[] | select(.Health != "healthy" and .Health != "") | "\(.Name): \(.Health)"'
```
**8. Deployment-Metadaten speichern**
```
~/deployment/backups/<timestamp>/deployment_metadata.txt
```
**Inhalt:**
```
Deployment Timestamp: 2025-10-31T02:35:04Z
Git Commit: abc1234...
Image Tag: abc1234-1696234567
Deployed Image: git.michaelschiemer.de:5000/framework:abc1234-1696234567
Image Pull: SUCCESS
Stack Deploy: UPDATED
Health Status: All services healthy
```
**9. Alte Backups aufräumen**
```bash
# Behält nur die letzten X Backups (Standard: 5)
ls -dt */ | tail -n +6 | xargs -r rm -rf
```
---
### Phase 3: Application Stack Services
Der Application Stack besteht aus mehreren Services:
#### Services im Stack
**1. `app` (PHP-FPM Application)**
- Image: `git.michaelschiemer.de:5000/framework:<tag>`
- Container: `app`
- Health Check: `php-fpm-healthcheck`
- Netzwerk: `app-internal`
- Abhängigkeiten: `redis` (condition: service_healthy)
**2. `nginx` (Web Server)**
- Image: `nginx:1.25-alpine`
- Container: `nginx`
- Health Check: `wget --spider http://localhost/health`
- Netzwerke: `traefik-public`, `app-internal`
- Abhängigkeiten: `app` (condition: service_healthy)
- Traefik Labels für Routing
**3. `redis` (Cache/Session/Queue)**
- Image: `redis:7-alpine`
- Container: `redis`
- Health Check: `redis-cli --raw incr ping`
- Netzwerk: `app-internal`
**4. `queue-worker` (Background Jobs)**
- Image: `git.michaelschiemer.de:5000/framework:<tag>` (gleiches wie app)
- Container: `queue-worker`
- Health Check: `pgrep -f 'queue:work'`
- Netzwerk: `app-internal`
- Command: `php console.php queue:work`
- Abhängigkeiten: `app`, `redis`
**5. `scheduler` (Cron Jobs)**
- Image: `git.michaelschiemer.de:5000/framework:<tag>` (gleiches wie app)
- Container: `scheduler`
- Health Check: `pgrep -f 'scheduler:run'`
- Netzwerk: `app-internal`
- Command: `php console.php scheduler:run`
- Abhängigkeiten: `app`, `redis`
---
## Container-Neustart-Details
### Was passiert bei `docker compose up -d --force-recreate`?
**1. Container-Stop:**
- Laufende Container werden gestoppt
- Volumes bleiben erhalten (persistente Daten)
- Netzwerke bleiben erhalten
**2. Container-Entfernung:**
- Alte Container werden entfernt
- Neue Container werden mit neuem Image erstellt
**3. Container-Start:**
- Container starten in Abhängigkeits-Reihenfolge
- Health-Checks werden ausgeführt
- Falls Health-Check fehlschlägt, wird Container neu gestartet (restart: unless-stopped)
**4. Zero-Downtime?**
- **Teilweise:** Container werden nacheinander neu gestartet
- `nginx` wartet auf `app` Health-Check
- Während Neustart kann es zu kurzen Verbindungsfehlern kommen
- Kein echter Blue-Green-Deployment (das wäre möglich, aber nicht implementiert)
---
## Konfigurationsvariablen
### Inventory-Variablen (`inventory/production.yml`)
```yaml
app_image: "git.michaelschiemer.de:5000/framework"
docker_registry_url: "git.michaelschiemer.de:5000"
backups_path: "~/deployment/backups"
max_rollback_versions: 5
deploy_user_home: "~/deployment"
```
### Workflow-Variablen (aus CI/CD)
```yaml
image_tag: "abc1234-1696234567" # Generiert aus: <short-sha>-<timestamp>
git_commit_sha: "abc1234567890..."
deployment_timestamp: "2025-10-31T02:35:04Z"
docker_registry_username: "<from-secret>"
docker_registry_password: "<from-secret>"
```
---
## Beispiel-Deployment
### Schritt-für-Schritt Beispiel
**1. CI/CD Pipeline startet:**
```bash
# Commit: abc1234...
# Image Tag generiert: abc1234-1696234567
```
**2. Image wird gebaut und gepusht:**
```bash
docker buildx build \
--tag git.michaelschiemer.de:5000/framework:latest \
--tag git.michaelschiemer.de:5000/framework:abc1234-1696234567 \
--tag git.michaelschiemer.de:5000/framework:git-abc1234 \
--push \
.
```
**3. Ansible Playbook wird ausgeführt:**
```bash
ansible-playbook -i inventory/production.yml \
playbooks/deploy-update.yml \
-e "image_tag=abc1234-1696234567" \
-e "git_commit_sha=abc1234567890..." \
-e "deployment_timestamp=2025-10-31T02:35:04Z"
```
**4. Auf Production-Server:**
```bash
# 1. Backup erstellen
mkdir -p ~/deployment/backups/2025-10-31T02-35-04Z
# 2. Registry Login
docker login git.michaelschiemer.de:5000 -u admin -p <password>
# 3. Image Pullen
docker pull git.michaelschiemer.de:5000/framework:abc1234-1696234567
# 4. docker-compose.yml aktualisieren
# Vorher: image: git.michaelschiemer.de:5000/framework:latest
# Nachher: image: git.michaelschiemer.de:5000/framework:abc1234-1696234567
# 5. Stack neu starten
cd ~/deployment/stacks/application
docker compose up -d --pull always --force-recreate --remove-orphans
# 6. Health-Checks warten
sleep 60
# 7. Status prüfen
docker compose ps
# app: healthy
# nginx: healthy
# redis: healthy
# queue-worker: healthy
# scheduler: healthy
```
**5. Deployment-Metadaten speichern:**
```bash
cat > ~/deployment/backups/2025-10-31T02-35-04Z/deployment_metadata.txt <<EOF
Deployment Timestamp: 2025-10-31T02:35:04Z
Git Commit: abc1234567890...
Image Tag: abc1234-1696234567
Deployed Image: git.michaelschiemer.de:5000/framework:abc1234-1696234567
Image Pull: SUCCESS
Stack Deploy: UPDATED
Health Status: All services healthy
EOF
```
---
## Wichtige Hinweise
### 1. Image-Tag-Update
**Wichtig:** Das Playbook aktualisiert die `docker-compose.yml` Datei **direkt auf dem Server**!
- Die Datei wird mit `replace` Modul geändert
- Alle Services mit dem Image `git.michaelschiemer.de:5000/framework:*` werden aktualisiert
- Das bedeutet: `app`, `queue-worker`, und `scheduler` bekommen alle das neue Image
### 2. Force-Recreate
**Achtung:** `--force-recreate` startet Container **immer** neu, auch wenn Konfiguration unverändert ist.
- Container-IDs ändern sich
- Kurze Downtime möglich
- Neue Container bekommen neue IPs im Docker-Netzwerk
### 3. Health-Checks
**Health-Checks sind kritisch:**
- Container müssen "healthy" werden, sonst starten abhängige Container nicht
- `nginx` wartet auf `app` Health-Check
- `app` wartet auf `redis` Health-Check
**Health-Check Timeouts:**
- `app`: 40s start_period, dann 30s interval
- `redis`: 10s start_period, dann 30s interval
- `nginx`: 10s start_period, dann 30s interval
### 4. Backups
**Backup-Strategie:**
- Jedes Deployment erstellt ein Backup-Verzeichnis
- Enthält: Container-Status, docker-compose.yml, Deployment-Metadaten
- Alte Backups werden automatisch gelöscht (Standard: Behält 5 Backups)
**Backup-Pfad:**
```
~/deployment/backups/
├── 2025-10-31T02-35-04Z/
│ ├── current_containers.json
│ ├── docker-compose-config.yml
│ └── deployment_metadata.txt
├── 2025-10-31T01-20-15Z/
│ └── ...
└── ...
```
---
## Rollback-Prozess
Falls das Deployment fehlschlägt:
### Automatischer Rollback (via Workflow)
```yaml
# Im Workflow: Rollback on failure
if: failure() && steps.health.outcome == 'failure'
run: |
ansible-playbook -i inventory/production.yml \
playbooks/rollback.yml
```
### Manueller Rollback
```bash
cd ~/deployment/ansible
ansible-playbook -i inventory/production.yml \
playbooks/rollback.yml \
-e "rollback_timestamp=2025-10-31T01-20-15Z"
```
**Was passiert beim Rollback:**
1. Liest vorheriges Backup
2. Pullt altes Image
3. Aktualisiert `docker-compose.yml` mit altem Image-Tag
4. Startet Stack neu mit altem Image
---
## Verbesserungsmöglichkeiten
### 1. Blue-Green Deployment
- Zwei parallele Stacks (blue/green)
- Traffic-Switching via Traefik
- Zero-Downtime möglich
### 2. Rolling Updates
- Container werden nacheinander aktualisiert
- Reduzierte Downtime
### 3. Database-Migration vor Deployment
- Migrationen werden aktuell nach Deployment ausgeführt
- Besser: Migrationen vor Deployment testen
### 4. Canary Deployment
- Neue Version zuerst auf Subset der Traffic
- Graduelle Rollout
---
## Troubleshooting
### Container starten nicht
```bash
# Logs prüfen
docker compose -f ~/deployment/stacks/application/docker-compose.yml logs
# Health-Check-Status prüfen
docker compose ps
```
### Image wird nicht gepullt
```bash
# Registry-Login testen
docker login git.michaelschiemer.de:5000 -u admin -p <password>
# Image manuell pullen
docker pull git.michaelschiemer.de:5000/framework:<tag>
```
### docker-compose.yml wurde nicht aktualisiert
```bash
# Prüfe ob Regex korrekt ist
grep -E "image:\s+git.michaelschiemer.de:5000/framework" \
~/deployment/stacks/application/docker-compose.yml
# Prüfe Backup für vorherige Version
ls -la ~/deployment/backups/
```
---
## Zusammenfassung
**Deployment-Ablauf:**
1. CI/CD Pipeline baut Image und pusht es zur Registry
2. Ansible Playbook wird auf Production-Server ausgeführt
3. Neues Image wird gepullt
4. `docker-compose.yml` wird mit neuem Image-Tag aktualisiert
5. Application Stack wird mit `--force-recreate` neu gestartet
6. Health-Checks werden ausgeführt
7. Deployment-Metadaten werden gespeichert
8. Workflow führt abschließenden Health-Check aus
**Alle Services erhalten das neue Image:**
- `app` (PHP-FPM)
- `queue-worker` (Queue Worker)
- `scheduler` (Scheduler)
**Zero-Downtime:** Nein, Container werden neu gestartet (kurze Downtime möglich)
**Rollback:** Automatisch via Workflow oder manuell via Rollback-Playbook

272
deployment/CI_CD_STATUS.md Normal file
View File

@@ -0,0 +1,272 @@
# CI/CD Pipeline Status
**Stand:** ✅ CI/CD Pipeline ist vollständig konfiguriert und bereit zum Testen!
## 📖 Dokumentation
- **[Code Changes Workflow](CODE_CHANGE_WORKFLOW.md)** - Anleitung: Wie Codeänderungen gepusht und deployed werden
- **[Application Stack Deployment](APPLICATION_STACK_DEPLOYMENT.md)** - Detaillierte Erklärung des Deployment-Prozesses
## ✅ Was bereits vorhanden ist
### 1. Workflow-Dateien
-`.gitea/workflows/production-deploy.yml` - Haupt-Deployment-Pipeline
-`.gitea/workflows/update-production-secrets.yml` - Secrets-Deployment
-`.gitea/workflows/security-scan.yml` - Security-Vulnerability-Scan
### 2. Gitea Runner Setup
-`deployment/gitea-runner/docker-compose.yml` - Runner-Konfiguration
-`deployment/gitea-runner/config.yaml` - Runner-Konfiguration
-`deployment/gitea-runner/register.sh` - Registration-Script
-`deployment/gitea-runner/.env.example` - Environment-Template
### 3. Deployment-Scripts
-`deployment/ansible/playbooks/deploy-update.yml` - Ansible-Deployment
-`deployment/ansible/playbooks/rollback.yml` - Rollback-Playbook
---
## ⚠️ Was noch fehlt
### 1. Gitea Repository Secrets konfigurieren
**Status**: ✅ Secrets sind bereits konfiguriert
**Konfigurierte Secrets:**
-`REGISTRY_USER` - Benutzername für Docker Registry
-`REGISTRY_PASSWORD` - Passwort für Docker Registry
-`SSH_PRIVATE_KEY` - SSH Private Key für Production-Server-Zugriff
- ⚠️ `GITEA_TOKEN` - Optional: Für automatische Issue-Erstellung bei Security-Scans
**Verifiziert:** Alle kritischen Secrets sind bereits in Gitea konfiguriert.
**Hinweis:** Falls `GITEA_TOKEN` noch nicht konfiguriert ist, kann die automatische Issue-Erstellung bei Security-Scans optional später hinzugefügt werden.
**REGISTRY_USER:**
```
admin
```
**REGISTRY_PASSWORD:**
```
<Das Passwort für git.michaelschiemer.de:5000>
```
*Zu finden in: `deployment/stacks/registry/auth/htpasswd` oder manuell gesetzt*
**SSH_PRIVATE_KEY:**
```bash
# SSH Key Inhalt anzeigen
cat ~/.ssh/production
```
*Kompletter Inhalt der Datei inkl. `-----BEGIN OPENSSH PRIVATE KEY-----` und `-----END OPENSSH PRIVATE KEY-----`*
**GITEA_TOKEN (optional):**
```
<Gitea Personal Access Token mit repo-Berechtigung>
```
---
### 2. Gitea Runner registrieren und starten
**Status**: ✅ Runner läuft bereits
**Verifiziert:**
- ✅ Runner ist registriert (`data/.runner` existiert)
- ✅ Runner Container läuft (`gitea-runner` ist "Up")
- ✅ Docker-in-Docker Container läuft (`gitea-runner-dind` ist "Up")
- ✅ Runner hat bereits Tasks ausgeführt (task 1-6 in Logs)
- ✅ Runner Name: `dev-runner-01`
- ✅ Runner Labels: `ubuntu-latest`, `ubuntu-22.04`, `debian-latest`
**Hinweis:**
Die Logs zeigen einige Verbindungsfehler (connection refused, 502 Bad Gateway), was normal sein kann wenn Gitea temporär nicht erreichbar ist. Der Runner funktioniert grundsätzlich und hat bereits Tasks erfolgreich ausgeführt.
**Verifizierung in Gitea:**
- Prüfe Runner-Status in Gitea: `https://git.michaelschiemer.de/admin/actions/runners`
- Runner sollte als "Idle" oder "Active" angezeigt werden
---
### 3. Ansible Vault Password Handling
**Status**: ⚠️ Workflow nutzt Vault, aber kein Secret dafür
**Problem:**
- Der Workflow `update-production-secrets.yml` benötigt ein Vault-Passwort
- Aktuell wird es als Workflow-Input eingegeben (manuell)
- Für automatisiertes Deployment sollte es als Secret vorliegen
**Lösung:**
- [ ] Optional: `ANSIBLE_VAULT_PASSWORD` als Secret hinzufügen (nur wenn automatisiert)
- [ ] Oder: Manuelles Eingeben beim Workflow-Trigger ist ausreichend
---
### 4. Pipeline End-to-End testen
**Status**: ⚠️ Pipeline ist definiert, aber noch nicht getestet
**Was fehlt:**
- [ ] Test-Commit pushen und Pipeline-Lauf beobachten
- [ ] Fehler beheben falls notwendig
- [ ] Verifizieren dass Deployment funktioniert
**Test-Schritte:**
1. Stelle sicher dass alle Secrets konfiguriert sind
2. Stelle sicher dass Runner läuft
3. Test-Commit erstellen:
```bash
git checkout -b test/cicd-pipeline
# Kleine Änderung machen
echo "# Test CI/CD" >> README.md
git add README.md
git commit -m "test: CI/CD pipeline"
git push origin test/cicd-pipeline
```
4. Oder: Workflow manuell triggern:
- Gehe zu: `https://git.michaelschiemer.de/michael/michaelschiemer/actions`
- Wähle "Production Deployment Pipeline"
- Klicke "Run workflow"
5. Beobachte Logs und prüfe jeden Schritt:
- ✅ Tests laufen erfolgreich
- ✅ Docker Image wird gebaut
- ✅ Image wird zur Registry gepusht
- ✅ Ansible-Deployment läuft
- ✅ Health-Check schlägt erfolgreich durch
---
## 📋 Checkliste für CI/CD Completion
### Vorbereitung
- [x] Gitea Repository Secrets konfiguriert: ✅
- [x] `REGISTRY_USER` ✅
- [x] `REGISTRY_PASSWORD` ✅
- [x] `SSH_PRIVATE_KEY` ✅
- [ ] `GITEA_TOKEN` (optional)
### Gitea Runner
- [x] Runner Registration Token von Gitea abgerufen ✅
- [x] `deployment/gitea-runner/.env` erstellt und konfiguriert ✅
- [x] Runner registriert ✅
- [x] Runner läuft (`docker compose up -d`) ✅
- [ ] Runner sichtbar in Gitea UI als "Idle" oder "Active" (bitte manuell prüfen)
### Pipeline-Test
- [ ] Test-Commit gepusht oder Workflow manuell getriggert
- [ ] Alle Jobs erfolgreich:
- [ ] Tests
- [ ] Build
- [ ] Deploy
- [ ] Deployment erfolgreich auf Production
- [ ] Health-Check erfolgreich
- [ ] Application läuft korrekt
### Dokumentation
- [ ] Secrets-Setup dokumentiert
- [ ] Runner-Setup dokumentiert
- [ ] Bekannte Probleme/Workarounds dokumentiert
---
## 🎯 Priorisierte Reihenfolge
### Phase 1: Secrets Setup
**Status:** ✅ Abgeschlossen
- Alle kritischen Secrets sind konfiguriert
### Phase 2: Gitea Runner
**Status:** ✅ Abgeschlossen
- Runner läuft und ist registriert
### Phase 3: Pipeline-Test (NÄCHSTER SCHRITT)
**Status:** ⚠️ Ausstehend
1. Test-Workflow ausführen
2. Fehler beheben falls notwendig
3. Production-Deployment verifizieren
---
## 📝 Quick Reference
### Secrets Setup
```bash
# Secrets in Gitea konfigurieren:
https://git.michaelschiemer.de/michael/michaelschiemer/settings/secrets/actions
```
### Runner Setup
```bash
cd deployment/gitea-runner
cp .env.example .env
# Token von https://git.michaelschiemer.de/admin/actions/runners eintragen
./register.sh
docker compose up -d
```
### Pipeline manuell triggern (NÄCHSTER SCHRITT)
```
1. Gehe zu: https://git.michaelschiemer.de/michael/michaelschiemer/actions
2. Wähle: "Production Deployment Pipeline"
3. Klicke: "Run workflow"
4. Wähle Branch: main
5. Optionale Einstellungen:
- skip_tests: false (Tests sollen laufen)
6. Klicke: "Run workflow"
7. Beobachte Logs und prüfe jeden Schritt
```
### Runner-Status prüfen
```
https://git.michaelschiemer.de/admin/actions/runners
```
---
---
## ✅ Aktueller Status
**CI/CD Pipeline ist vollständig konfiguriert!**
- ✅ Secrets konfiguriert
- ✅ Runner läuft und ist registriert
- ✅ Workflows sind vorhanden
- ⚠️ **Nächster Schritt:** Pipeline testen!
**Ready to Deploy:** Die Pipeline kann jetzt getestet werden. Alle Voraussetzungen sind erfüllt.
---
## 🔍 Troubleshooting
### Runner erscheint nicht in Gitea
- Prüfe Runner-Logs: `docker compose logs gitea-runner`
- Prüfe Registration-Token in `.env`
- Re-registrieren: `./unregister.sh && ./register.sh`
### Workflow startet nicht
- Prüfe ob Runner läuft: `docker compose ps`
- Prüfe Runner-Status in Gitea UI
- Prüfe ob Workflow-Datei korrekt ist
### Secrets werden nicht erkannt
- Prüfe Secret-Namen (müssen exakt übereinstimmen)
- Prüfe ob Secrets in korrektem Repository/Organisation sind
- Prüfe ob Secrets nicht abgelaufen sind
### Registry-Login fehlschlägt
- Prüfe `REGISTRY_USER` und `REGISTRY_PASSWORD` Secrets
- Teste Registry-Login manuell:
```bash
docker login git.michaelschiemer.de:5000 -u admin -p <password>
```
### SSH-Verbindung fehlschlägt
- Prüfe `SSH_PRIVATE_KEY` Secret (kompletter Inhalt)
- Prüfe ob Public Key auf Production-Server ist
- Teste SSH-Verbindung manuell:
```bash
ssh -i ~/.ssh/production deploy@94.16.110.151
```

View File

@@ -0,0 +1,551 @@
# Codeänderungen pushen und deployen
## Übersicht
Dieses Dokument erklärt, wie Codeänderungen gepusht werden und automatisch in Production deployed werden.
**Quick Start:**
```bash
git add .
git commit -m "feat: Add new feature"
git push origin main
```
→ Automatisches Deployment startet (~8-15 Minuten)
**📖 Verwandte Dokumentation:**
- **[Application Stack Deployment](APPLICATION_STACK_DEPLOYMENT.md)** - Wie das Deployment genau funktioniert
- **[CI/CD Status](CI_CD_STATUS.md)** - Aktueller Status der Pipeline
---
## Normaler Workflow: Codeänderungen deployen
### Schritt 1: Code lokal ändern
```bash
# Änderungen in deinem lokalen Repository machen
# z.B. Datei bearbeiten: src/App/Controller/HomeController.php
# Änderungen anzeigen
git status
# Änderungen anschauen
git diff
```
### Schritt 2: Änderungen committen
```bash
# Änderungen zum Staging hinzufügen
git add .
# Oder nur spezifische Dateien
git add src/App/Controller/HomeController.php
# Commit erstellen
git commit -m "feat: Add new feature to home controller"
```
**Commit-Message Konventionen:**
- `feat:` - Neue Feature
- `fix:` - Bug-Fix
- `refactor:` - Code-Refactoring
- `docs:` - Dokumentation
- `test:` - Tests
- `chore:` - Wartungsaufgaben
### Schritt 3: Code zu Gitea pushen
```bash
# Zu main branch pushen (triggert automatisches Deployment)
git push origin main
# Oder zu anderem Branch pushen (kein Auto-Deploy)
git push origin feature/new-feature
```
### Schritt 4: Automatisches Deployment
**Was passiert automatisch:**
1. **Git Push** → Gitea erhält den Push
2. **Workflow wird getriggert** (bei Push zu `main` Branch)
3. **CI/CD Pipeline startet:**
- Tests laufen
- Docker Image wird gebaut
- Image wird zur Registry gepusht
- Ansible Deployment wird ausgeführt
- Application Stack wird aktualisiert
**Zeitdauer:** ~8-15 Minuten für komplettes Deployment
---
## Deployment-Trigger
### Automatisches Deployment (bei Push zu `main`)
**Workflow:** `.gitea/workflows/production-deploy.yml`
```yaml
on:
push:
branches:
- main
paths-ignore:
- 'docs/**'
- '**.md'
- '.github/**'
```
**Bedeutung:**
- ✅ Push zu `main` Branch → Deployment startet automatisch
- ❌ Push zu anderen Branches → Kein Deployment
- ❌ Push nur von Markdown-Dateien → Kein Deployment (wegen `paths-ignore`)
**Beispiel:**
```bash
# Triggert Deployment
git push origin main
# Triggert KEIN Deployment (nur Markdown)
git commit -m "docs: Update README" --only README.md
git push origin又问
# Triggert KEIN Deployment (anderer Branch)
git checkout -b feature/new-feature
git push origin feature/new-feature
```
### Manuelles Deployment (Workflow-Dispatch)
**Workflow kann manuell gestartet werden:**
1. Gehe zu: `https://git.michaelschiemer.de/michael/michaelschiemer/actions`
2. Wähle: "Production Deployment Pipeline"
3. Klicke: "Run workflow"
4. Wähle Branch (z.B. `main` oder anderer Branch)
5. Optionale Einstellungen:
- `skip_tests`: `true` (nur in Notfällen!)
6. Klicke: "Run workflow"
**Verwendung:**
- Deployment von anderem Branch (z.B. `develop`, `staging`)
- Deployment ohne Code-Änderung (z.B. nach Config-Änderung)
- Notfall-Deployment mit `skip_tests: true`
---
## Workflow-Details
### Was passiert bei jedem Push zu `main`?
#### Job 1: Tests (ca. 2-5 Minuten)
```yaml
- PHP 8.3 Setup
- Composer Dependencies installieren
- Pest Tests ausführen
- PHPStan Code Quality Check
- Code Style Check (composer cs)
```
**Bei Fehler:** Pipeline stoppt, kein Deployment
#### Job 2: Build (ca. 3-5 Minuten)
```yaml
- Docker Buildx Setup
- Image Metadata generieren (Tag: <short-sha>-<timestamp>)
- Docker Image Build (Dockerfile.production)
- Image mit Tags pushen:
- git.michaelschiemer.de:5000/framework:latest
- git.michaelschiemer.de:5000/framework:<tag>
- git.michaelschiemer.de:5000/framework:git-<short-sha>
```
#### Job 3: Deploy (ca. 2-4 Minuten)
```yaml
- SSH Setup (mit Secret)
- Ansible Installation
- Ansible Playbook ausführen:
- Image Pull
- docker-compose.yml Update
- Stack Neustart
- Health-Check (10x versuche)
- Rollback bei Fehler
```
---
## Branching-Strategie
### Empfohlener Workflow
```
main (Production)
develop (Entwicklung/Testing)
feature/* (Feature Branches)
```
### Workflow-Beispiele
#### 1. Feature entwickeln
```bash
# Feature Branch erstellen
git checkout -b feature/user-authentication
# Änderungen machen
# ... Code schreiben ...
# Committen
git add .
git commit -m "feat: Add user authentication"
# Zu Gitea pushen (kein Auto-Deploy)
git push origin feature/user-authentication
# Pull Request erstellen in Gitea
# → Code Review
# → Merge zu develop (oder main)
```
#### 2. Direkt zu Production deployen
```bash
# Änderungen lokal
git checkout main
# ... Änderungen machen ...
git add .
git commit -m "fix: Critical bug fix"
# Pushen → Triggert automatisches Deployment
git push origin main
# Pipeline läuft automatisch:
# ✅ Tests
# ✅ Build
# ✅ Deploy
```
#### 3. Hotfix (Notfall)
```bash
# Hotfix Branch von main
git checkout -b hotfix/critical-security-fix main
# Fix implementieren
# ... Code schreiben ...
git add .
git commit -m "fix: Critical security vulnerability"
# Direkt zu main mergen
git checkout main
git merge hotfix/critical-security-fix Ker
# Pushen → Auto-Deploy
git push origin main
# Optional: Manuelles Deployment mit skip_tests
# (nur wenn Tests lokal bereits erfolgreich)
```
---
## Deployment-Status prüfen
### 1. Pipeline-Status in Gitea
```
https://git.michaelschiemer.de/michael/michaelschiemer/actions
```
**Status-Anzeigen:**
- 🟢 Grüner Haken = Erfolgreich
- 🔴 Roter Haken = Fehlgeschlagen
- 🟡 Gelber Kreis = Läuft gerade
### 2. Logs ansehen
1. Gehe zu Actions
2. Klicke auf den Workflow-Run
3. Klicke auf Job (z.B. "Deploy to Production Server")
4. Klicke auf Step (z.B. "Deploy via Ansible")
5. Logs ansehen
### 3. Application-Status prüfen
```bash
# SSH zum Production-Server
ssh deploy@94.16.110.151
# Container-Status prüfen
cd ~/deployment/stacks/application
docker compose ps
# Logs ansehen
docker compose logs -f app
# Health-Check manuell
curl https://michaelschiemer.de/health
```
---
## Deployment verhindern
### Temporäres Deployment verhindern
**Option 1: Push zu anderem Branch**
```bash
# Entwickle auf Feature-Branch
git checkout -b feature/my-feature
git push origin feature/my-feature
# → Kein Auto-Deploy
```
**Option CT 2: [skip ci] in Commit-Message**
```bash
# Workflow wird übersprungen
git commit -m "docs: Update documentation [skip ci]"
git push origin main
```
**Hinweis:** `[skip ci]` wird aktuell **nicht** unterstützt, da kein entsprechender Filter im Workflow ist.
### Deployment-Trigger deaktivieren
**Temporär (Workflow anpassen):**
```yaml
# In .gitea/workflows/production-deploy.yml
on:
push:
branches:
- main
# workflow_dispatch: # Kommentiere aus für temporäres Deaktivieren
```
**Besser:** Nutze Feature-Branches für Entwicklung ohne Auto-Deploy.
---
## Häufige Szenarien
### Szenario 1: Kleine Bug-Fix
```bash
# 1. Bug-Fix lokal implementieren
git checkout main
# ... Fix implementieren ...
git add .
git commit -m "fix: Resolve login issue"
# 2. Pushen → Auto-Deploy
git push origin main
# 3. Pipeline beobachten
# → Tests ✅
# → Build ✅
# → Deploy ✅
# 4. Verifizieren
curl https://michaels chasing.de/health
```
### Szenario 2: Große Feature-Entwicklung
```bash
# 1. Feature-Branch erstellen
git checkout -b feature/new-dashboard
# 2. Feature entwickeln
# ... viele Commits ...
# 3. Regelmäßig pushen (kein Auto-Deploy)
git push origin feature/new-dashboard
# 4. Pull Request erstellen in Gitea
# → Code Review
# → Diskussion
# 5. Merge zu main (triggert Auto-Deploy)
# → Via Gitea UI: "Merge Pull Request"
# → Oder lokal:
git checkout main
git merge feature/new-dashboard
git push origin main
```
### Szenario 3: Config-Änderung ohne Code-Änderung
```bash
# Beispiel: .env Variablen ändern
# (wird über Ansible Template generiert, daher direkt auf Server ändern)
# Oder: docker-compose.yml anpassen
# Änderungen machen
git add .
git commit -m "chore: Update docker-compose configuration"
git push origin main
# → Pipeline läuft
# → Build: Keine Code-Änderung, aber Image wird neu getaggt
# → Deploy: docker-compose.yml wird aktualisiert
```
### Szenario 4: Notfall-Rollback
```bash
# Option 1: Rollback via Ansible Playbook
cd deployment/ansible
ansible-playbook -i inventory/production.yml \
playbooks/rollback.yml \
-e "rollback_timestamp=2025-10-31T01-20-15Z"
# Option 2: Alten Commit pushen
git log --oneline
# Finde letzten funktionierenden Commit
git checkout <commit-hash>
git checkout -b rollback/previous-version
git push origin rollback/previous-version
# Manuell zu main mergen oder direkt:
git checkout main
git reset --hard <commit-hash>
git push origin main --force # ⚠️ Vorsicht!
# → Triggert Auto-Deploy mit altem Code
```
---
## Best Practices
### 1. Commits
- ✅ Klare, beschreibende Commit-Messages
entweder
- ✅ Atomic Commits (ein Feature = ein Commit)
- ✅ Regelmäßig pushen (nicht alles auf einmal)
### 2. Testing
- ✅ Tests lokal ausführen vor Push:
```bash
composer cs # Code Style
make phpstan # Static Analysis
./vendor/bin/pest # Tests
```
### 3. Deployment
- ✅ **Niemals** direkt zu `main` pushen ohne lokale Tests
- ✅ Feature-Branches für größere Änderungen
- ✅ Pull Requests für Code Review
- ✅ Pipeline-Status beobachten nach Push
### 4. Rollback-Plan
- ✅ Immer Backup vor größeren Änderungen
- ✅ Rollback-Playbook bereit halten
- ✅ Deployment-Metadaten dokumentieren
---
## Troubleshooting
### Pipeline schlägt fehl
**Problem:** Tests fehlgeschlagen
```bash
# Tests lokal ausführen
./vendor/bin/pest
# Fehler beheben
# ... Code anpassen ...
git add .
git commit -m "fix: Fix failing tests"
git push origin main
```
**Problem:** Build fehlgeschlagen
```bash
# Docker Build lokal testen
docker build -f Dockerfile.production -t test-image .
# Fehler beheben
# ... Dockerfile anpassen ...
git add .
git commit -m "fix: Fix Docker build"
git push origin main
```
**Problem:** Deployment fehlgeschlagen
```bash
# SSH zum Server
ssh deploy@94.16.110.151
# Logs prüfen
cd ~/deployment/stacks/application
docker compose logs app
# Manuell rollback
cd ~/deployment/ansible
ansible-playbook -i inventory/production.yml playbooks/rollback.yml
```
### Deployment läuft zu lange
**Pipeline hängt:**
- Prüfe Runner-Status: `docker compose ps` in `deployment/gitea-runner`
- Prüfe Runner-Logs: `docker compose logs gitea-runner`
- Prüfe Workflow-Logs in Gitea UI
**Deployment hängt:**
- Prüfe Server-Status: `ssh deploy@94.16.110.151 "docker ps"`
- Prüfe Container-Logs: `docker compose logs`
- Prüfe Disk-Space: `df -h`
---
## Zusammenfassung
### Normaler Workflow
1. **Code ändern** lokal
2. **Committen** mit klarer Message
3. **Push zu `main`** → Auto-Deploy startet
4. **Pipeline beobachten** in Gitea Actions
5. **Verifizieren** auf Production
### Wichtige Commands
```bash
# Änderungen pushen (triggert Auto-Deploy)
git push origin main
# Feature entwickeln (kein Auto-Deploy)
git checkout -b feature/my-feature
git push origin feature/my-feature
# Pipeline-Status prüfen
# → https://git.michaelschiemer.de/michael/michaelschiemer/actions
# Application-Status prüfen
ssh deploy@94.16.110.151 "cd ~/deployment/stacks/application && docker compose ps"
```
### Deployment-Zeit
- **Gesamt:** ~8-15 Minuten
- **Tests:** ~2-5 Minuten
- **Build:** ~3-5 Minuten
- **Deploy:** ~2-4 Minuten
- **Health-Check:** ~1 Minute
---
**Ready to deploy!** 🚀

View File

@@ -1,33 +1,63 @@
# Deployment Status - Gitea Actions Runner Setup
**Status**: 🚧 BLOCKED - Phase 1 Step 1.1
**Last Updated**: 2025-10-30
**Status**: ✅ Phase 3 Complete - Ready for Phase 1
**Last Updated**: 2025-10-31
**Target Server**: 94.16.110.151 (Netcup)
---
## Aktueller Status
### ✅ Abgeschlossen
### ✅ Phase 0: Git Repository SSH Access Setup - COMPLETE
**Phase 1 - Teilschritte Erledigt**:
1.Runner-Verzeichnisstruktur verifiziert: `/home/michael/dev/michaelschiemer/deployment/gitea-runner/`
2.`.env.example` Template analysiert (23 Zeilen)
3.`docker-compose.yml` Architektur verstanden (47 Zeilen, Docker-in-Docker)
4.`.env` Datei erstellt via: `cp deployment/gitea-runner/.env.example deployment/gitea-runner/.env`
1. ✅ Git SSH Key generiert (`~/.ssh/git_michaelschiemer`)
2.SSH Config konfiguriert für `git.michaelschiemer.de`
3.Public Key zu Gitea hinzugefügt
4.Git Remote auf SSH umgestellt
5.Push zu origin funktioniert ohne Credentials
### ⚠️ BLOCKER - Kritischer Fehler
### ✅ Phase 3: Production Server Initial Setup - COMPLETE
**Problem**: Gitea Admin Panel nicht erreichbar
**Infrastructure Stacks deployed via Ansible:**
**URL**: `https://git.michaelschiemer.de/admin/actions/runners`
**Fehler**: `404 page not found`
1.**Traefik** - Reverse Proxy & SSL (healthy)
- HTTPS funktioniert, Let's Encrypt SSL aktiv
- Dashboard: https://traefik.michaelschiemer.de
**Impact**:
- ❌ Kann Registration Token nicht abrufen (Phase 1, Step 1.1)
- ❌ Kann `.env` nicht komplettieren (Step 1.2)
- ❌ Kann Runner nicht registrieren (Step 1.3)
- ❌ Alle nachfolgenden Phasen (2-8) blockiert
2.**PostgreSQL** - Database Stack (healthy)
- Database läuft und ist bereit
3.**Docker Registry** - Private Registry (running, accessible)
- Authentication konfiguriert
- Zugriff erfolgreich getestet
4.**Gitea** - Git Server (healthy)
- HTTPS erreichbar: https://git.michaelschiemer.de ✅
- SSH Port 2222 aktiv
- PostgreSQL Database verbunden
5.**Monitoring** - Monitoring Stack (deployed)
- Grafana: https://grafana.michaelschiemer.de
- Prometheus: https://prometheus.michaelschiemer.de
- Portainer: https://portainer.michaelschiemer.de
**Deployment Method:** Ansible Playbook `setup-infrastructure.yml`
**Deployment Date:** 2025-10-31
### ⏳ Phase 1: Gitea Runner Setup - READY TO START
**Prerequisites erfüllt:**
- ✅ Gitea deployed und erreichbar
- ✅ Runner-Verzeichnisstruktur vorhanden
-`.env.example` Template analysiert
-`.env` Datei erstellt
**Nächste Schritte:**
1. ⏳ Gitea Admin Panel öffnen: https://git.michaelschiemer.de/admin/actions/runners
2. ⏳ Actions in Gitea aktivieren (falls noch nicht geschehen)
3. ⏳ Registration Token abrufen
4. ⏳ Token in `.env` eintragen
5. ⏳ Runner registrieren und starten
---

View File

@@ -0,0 +1,281 @@
# Deployment TODO - Komplette Implementierung
**Status**: 🔄 In Progress
**Letzte Aktualisierung**: 2025-10-31
**Ziel**: Komplettes Deployment-Setup im `deployment/` Ordner
---
## ✅ Bereits Fertig
### Infrastructure Stacks (deployed via Ansible)
-**Traefik** - Reverse Proxy & SSL
-**PostgreSQL** - Database Stack
-**Docker Registry** - Private Registry
-**Gitea** - Git Server + MySQL + Redis
-**Monitoring** - Portainer + Grafana + Prometheus
-**WireGuard VPN** - VPN Server
### Ansible Playbooks
-`setup-infrastructure.yml` - Infrastructure Stacks Deployment
-`setup-wireguard.yml` - WireGuard VPN Setup
-`add-wireguard-client.yml` - WireGuard Client hinzufügen
-`deploy-update.yml` - Application Update Deployment
-`rollback.yml` - Rollback zu vorheriger Version
-`setup-production-secrets.yml` - Secrets Deployment
-`setup-ssl-certificates.yml` - SSL Certificate Setup
-`sync-stacks.yml` - Stacks synchronisieren
### Dokumentation
-`README.md` - Deployment Übersicht
-`SETUP-GUIDE.md` - Komplette Setup-Anleitung
-`DEPLOYMENT-STATUS.md` - Aktueller Status
-`docs/WIREGUARD-SETUP.md` - WireGuard Dokumentation
---
## ⏳ Offene Aufgaben
### 1. Application Stack Integration
**Status**: ⚠️ Fehlt in `setup-infrastructure.yml`
**Was fehlt:**
- [x] Application Stack zu `setup-infrastructure.yml` hinzufügen ✅
- [x] `.env` Template für Application Stack erstellen (`application.env.j2`) ✅
- [x] Ansible Playbook/Task für Application Stack Deployment ✅
- [x] Database-Migration nach Application Deployment ✅
- [x] Health-Check nach Application Deployment ✅
**Dateien:**
- `deployment/stacks/application/docker-compose.yml` ✅ Vorhanden
- `deployment/stacks/application/.env.example` ✅ Vorhanden
- `deployment/stacks/application/.env` ❌ Fehlt (muss generiert werden)
- `deployment/ansible/templates/application.env.j2` ❌ Fehlt (Template für `.env`)
- `deployment/ansible/playbooks/setup-infrastructure.yml` ⚠️ Application fehlt
**Nächste Schritte:**
1. Application Stack Deployment Task zu `setup-infrastructure.yml` hinzufügen
2. `.env` Template erstellen (mit Passwörtern aus Vault)
3. Database-Migration nach Application Start
4. Health-Check Integration
---
### 2. Application Stack .env Konfiguration
**Status**: ✅ Erledigt
**Was erledigt:**
- [x] Ansible Template für `.env` Datei erstellt (`application.env.j2`) ✅
- [x] Passwörter aus Vault/PostgreSQL .env laden ✅
- [x] Domain-Konfiguration aus Inventory ✅
- [x] Environment-Variablen aus Vault/Template generieren ✅
**Dateien:**
- `deployment/stacks/application/.env.example` ✅ Vorhanden (angepasst für PostgreSQL)
- `deployment/stacks/application/.env` ⚠️ Wird automatisch generiert
- `deployment/ansible/templates/application.env.j2` ✅ Erstellt
- `deployment/stacks/application/docker-compose.yml` ✅ Angepasst (PostgreSQL statt MySQL)
---
### 3. Gitea Runner Setup abschließen
**Status**: ⏳ Wartet auf Registration Token
**Was fehlt:**
- [ ] Gitea Admin Panel öffnen: https://git.michaelschiemer.de/admin/actions/runners
- [ ] Actions in Gitea aktivieren (falls noch nicht geschehen)
- [ ] Registration Token abrufen
- [ ] Token in `.env` eintragen
- [ ] Runner registrieren und starten
**Dateien:**
- `deployment/gitea-runner/.env` ⚠️ Vorhanden, aber Token fehlt
- `deployment/gitea-runner/.env.example` ✅ Vorhanden
**Nächste Schritte:**
1. Gitea Actions aktivieren (Admin Panel)
2. Runner Registration Token generieren
3. Token in `.env` eintragen
4. Runner starten: `cd deployment/gitea-runner && docker compose up -d`
---
### 4. CI/CD Pipeline finalisieren
**Status**: ⚠️ Existiert, aber muss konfiguriert und getestet werden
**Was fehlt:**
- [x] **Gitea Repository Secrets konfigurieren:**
- [x] `REGISTRY_USER` (Docker Registry Benutzername) ✅
- [x] `REGISTRY_PASSWORD` (Docker Registry Passwort) ✅
- [x] `SSH_PRIVATE_KEY` (SSH Private Key für Production-Server) ✅
- [ ] `GITEA_TOKEN` (Optional: Für automatische Issue-Erstellung)
- [x] **Gitea Runner registrieren:**
- [x] Registration Token von Gitea Admin Panel abgerufen ✅
- [x] Token in `deployment/gitea-runner/.env` eingetragen ✅
- [x] Runner registriert ✅
- [x] Runner läuft ✅
- [ ] **Pipeline End-to-End testen:**
- [ ] Test-Commit pushen oder Workflow manuell triggern
- [ ] Alle Jobs erfolgreich (Tests, Build, Deploy)
- [ ] Deployment erfolgreich auf Production
- [ ] Health-Check erfolgreich
**Dateien:**
- `.gitea/workflows/production-deploy.yml` ✅ Vorhanden
- `.gitea/workflows/update-production-secrets.yml` ✅ Vorhanden
- `.gitea/workflows/security-scan.yml` ✅ Vorhanden
- `deployment/CI_CD_STATUS.md` ✅ Neu erstellt (detaillierte Checkliste)
**Detaillierte Anleitung:**
Siehe `deployment/CI_CD_STATUS.md` für komplette Checkliste und Setup-Anleitung.
**Nächste Schritte:**
1. **Secrets in Gitea konfigurieren:**
- Gehe zu: `https://git.michaelschiemer.de/michael/michaelschiemer/settings/secrets/actions`
- Füge Secrets hinzu: `REGISTRY_USER`, `REGISTRY_PASSWORD`, `SSH_PRIVATE_KEY`
2. **Gitea Runner registrieren:**
- Token von: `https://git.michaelschiemer.de/admin/actions/runners`
- Konfiguriere `deployment/gitea-runner/.env`
- Führe `./register.sh` aus
3. **Pipeline testen:**
- Workflow manuell triggern oder Test-Commit pushen
- Logs beobachten und Fehler beheben
---
### 5. Backup & Rollback Scripts
**Status**: ⚠️ Teilweise vorhanden
**Was fehlt:**
- [ ] Backup-Playbook für Application Stack
- [ ] Rollback-Playbook testen und finalisieren
- [ ] PostgreSQL Backup-Integration
- [ ] Gitea Data Backup
- [ ] Registry Images Backup
**Dateien:**
- `deployment/ansible/playbooks/rollback.yml` ✅ Vorhanden
- `deployment/scripts/rollback.sh` ✅ Vorhanden
- `deployment/stacks/postgresql/scripts/backup.sh` ✅ Vorhanden
- `deployment/ansible/playbooks/backup.yml` ❌ Fehlt
**Nächste Schritte:**
1. Backup-Playbook erstellen
2. Rollback-Playbook testen
3. Backup-Scripte finalisieren
4. Automatisierte Backups konfigurieren
---
### 6. Deployment Scripts finalisieren
**Status**: ⚠️ Vorhanden, aber muss angepasst werden
**Was fehlt:**
- [ ] `deployment/scripts/deploy.sh` testen und anpassen
- [ ] `deployment/scripts/rollback.sh` testen und anpassen
- [ ] `deployment/scripts/setup-production.sh` finalisieren
- [ ] Scripts für alle Stacks (nicht nur Application)
**Dateien:**
- `deployment/scripts/deploy.sh` ✅ Vorhanden (aber Docker Swarm statt Compose?)
- `deployment/scripts/rollback.sh` ✅ Vorhanden
- `deployment/scripts/setup-production.sh` ✅ Vorhanden
**Nächste Schritte:**
1. Scripts prüfen und anpassen (Docker Compose statt Swarm)
2. Scripts testen
3. Integration mit Ansible Playbooks
---
### 7. Dokumentation vervollständigen
**Status**: ⚠️ Gut, aber einige Updates nötig
**Was fehlt:**
- [ ] `DEPLOYMENT-STATUS.md` aktualisieren (Application Stack Status)
- [ ] `README.md` aktualisieren (Application Stack Deployment)
- [ ] `SETUP-GUIDE.md` aktualisieren (Application Stack Phase)
- [ ] Troubleshooting Guide für Application Stack
**Dateien:**
- `deployment/README.md` ⚠️ Muss aktualisiert werden
- `deployment/SETUP-GUIDE.md` ⚠️ Muss aktualisiert werden
- `deployment/DEPLOYMENT-STATUS.md` ⚠️ Muss aktualisiert werden
---
## 🎯 Priorisierte Reihenfolge
### Phase 1: Application Stack Deployment (KRITISCH)
1. **Application Stack zu setup-infrastructure.yml hinzufügen**
- Task für Application Stack Deployment
- `.env` Template erstellen
- Database-Migration nach Deployment
2. **Application .env Konfiguration**
- Template `application.env.j2` erstellen
- Passwörter aus Vault laden
- Template in Playbook integrieren
### Phase 2: CI/CD Setup
3. **Gitea Runner Setup abschließen**
- Token abrufen und konfigurieren
- Runner starten
4. **CI/CD Pipeline finalisieren**
- Secrets in Gitea konfigurieren
- Pipeline testen
### Phase 3: Backup & Scripts
5. **Backup & Rollback Scripts**
- Backup-Playbook erstellen
- Rollback testen
6. **Deployment Scripts finalisieren**
- Scripts testen und anpassen
### Phase 4: Dokumentation
7. **Dokumentation aktualisieren**
- README aktualisieren
- Status-Dokumente aktualisieren
---
## 📋 Quick Checklist
### Application Stack
- [ ] Application Stack in `setup-infrastructure.yml` hinzufügen
- [ ] `.env` Template (`application.env.j2`) erstellen
- [ ] Database-Migration Task hinzufügen
- [ ] Health-Check nach Deployment
### CI/CD
- [ ] Gitea Runner Token konfigurieren
- [ ] Runner starten
- [ ] Secrets in Gitea konfigurieren
- [ ] Pipeline testen
### Scripts & Backup
- [ ] Backup-Playbook erstellen
- [ ] Rollback testen
- [ ] Deployment-Scripts finalisieren
### Dokumentation
- [ ] README aktualisieren
- [ ] SETUP-GUIDE aktualisieren
- [ ] DEPLOYMENT-STATUS aktualisieren
---
**Nächster Schritt**: Application Stack zu `setup-infrastructure.yml` hinzufügen und `.env` Template erstellen

View File

@@ -0,0 +1,155 @@
# Native Workflow ohne GitHub Actions
## Problem
Der aktuelle Workflow (`production-deploy.yml`) verwendet GitHub Actions wie:
- `actions/checkout@v4`
- `shivammathur/setup-php@v2`
- `actions/cache@v3`
- `docker/setup-buildx-action@v3`
- `docker/build-push-action@v5`
Diese Actions müssen von GitHub geladen werden, was zu Abbrüchen führen kann wenn:
- GitHub nicht erreichbar ist
- Actions nicht geladen werden können
- Timeouts auftreten
## Lösung: Native Workflow
Die Datei `.gitea/workflows/production-deploy-native.yml` verwendet **nur Shell-Commands** und keine GitHub Actions:
### Vorteile
1. **Keine GitHub-Abhängigkeit**: Funktioniert komplett offline
2. **Schneller**: Keine Action-Downloads
3. **Weniger Fehlerquellen**: Direkte Shell-Commands statt Actions
4. **Einfacher zu debuggen**: Standard-Bash-Scripts
### Änderungen
#### 1. Checkout
**Vorher:**
```yaml
- uses: actions/checkout@v4
```
**Nachher:**
```bash
git clone --depth 1 --branch "$REF_NAME" \
"https://git.michaelschiemer.de/${{ github.repository }}.git" \
/workspace/repo
```
#### 2. PHP Setup
**Vorher:**
```yaml
- uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
```
**Nachher:**
```bash
apt-get update
apt-get install -y php8.3 php8.3-cli php8.3-mbstring ...
```
#### 3. Cache
**Vorher:**
```yaml
- uses: actions/cache@v3
```
**Nachher:**
```bash
# Einfaches Datei-basiertes Caching
if [ -d "/tmp/composer-cache/vendor" ]; then
cp -r /tmp/composer-cache/vendor /workspace/repo/vendor
fi
```
#### 4. Docker Buildx
**Vorher:**
```yaml
- uses: docker/setup-buildx-action@v3
```
**Nachher:**
```bash
docker buildx create --name builder --use || docker buildx use builder
docker buildx inspect --bootstrap
```
#### 5. Docker Build/Push
**Vorher:**
```yaml
- uses: docker/build-push-action@v5
```
**Nachher:**
```bash
docker buildx build \
--file ./Dockerfile.production \
--tag $REGISTRY/$IMAGE_NAME:latest \
--push \
.
```
## Verwendung
### Option 1: Native Workflow aktivieren
1. **Benenne um:**
```bash
mv .gitea/workflows/production-deploy.yml .gitea/workflows/production-deploy-with-actions.yml.bak
mv .gitea/workflows/production-deploy-native.yml .gitea/workflows/production-deploy.yml
```
2. **Commite und pushe:**
```bash
git add .gitea/workflows/production-deploy.yml
git commit -m "chore: switch to native workflow without GitHub Actions"
git push origin main
```
### Option 2: Beide parallel testen
Lass beide Workflows parallel laufen:
- `production-deploy.yml` - Mit Actions (aktuell)
- `production-deploy-native.yml` - Native (neue Version)
## Gitea Actions Konfiguration
**Wichtig:** Wenn wir die native Version verwenden, brauchen wir `DEFAULT_ACTIONS_URL` **nicht mehr** in der Gitea-Konfiguration.
Aber es schadet auch nicht, es drin zu lassen für zukünftige Workflows.
## Debugging
Wenn der native Workflow nicht funktioniert:
1. **Prüfe Git Clone:**
```bash
# Im Runner Container
git clone --depth 1 https://git.michaelschiemer.de/michael/michaelschiemer.git /tmp/test
```
2. **Prüfe Docker Buildx:**
```bash
docker buildx version
docker buildx ls
```
3. **Prüfe PHP Installation:**
```bash
php --version
php -m # Zeigt installierte Module
```
## Empfehlung
**Für Stabilität:** Verwende die native Version (`production-deploy-native.yml`)
**Für Kompatibilität:** Bleib bei der Actions-Version (`production-deploy.yml`)
Die native Version sollte stabiler sein, da sie keine externen Dependencies benötigt.

View File

@@ -129,7 +129,43 @@ docker compose up -d
## CI/CD Pipeline
The CI/CD pipeline is defined in `.gitea/workflows/deploy.yml` and runs on push to main branch:
The CI/CD pipeline is defined in `.gitea/workflows/production-deploy.yml` and runs automatically on push to `main` branch.
### Quick Start: Deploy Code Changes
```bash
# 1. Make changes locally
# ... edit files ...
# 2. Commit changes
git add .
git commit -m "feat: Add new feature"
# 3. Push to main → Automatic deployment starts
git push origin main
```
**What happens automatically:**
- ✅ Tests run (~2-5 min)
- ✅ Docker image is built (~3-5 min)
- ✅ Image is pushed to registry (~1-2 min)
- ✅ Ansible deployment runs (~2-4 min)
- ✅ Application stack is updated
**Total time:** ~8-15 minutes
**Status check:**
- Pipeline status: `https://git.michaelschiemer.de/michael/michaelschiemer/actions`
- Application status: `ssh deploy@94.16.110.151 "cd ~/deployment/stacks/application && docker compose ps"`
**📖 Detailed Documentation:**
- **[Code Change Workflow](CODE_CHANGE_WORKFLOW.md)** - Complete guide for pushing code changes
- **[Application Stack Deployment](APPLICATION_STACK_DEPLOYMENT.md)** - How deployment works in detail
- **[CI/CD Status](CI_CD_STATUS.md)** - Current CI/CD pipeline status
### Pipeline Details
The CI/CD pipeline runs on push to main branch:
1. **Build Stage**: Build Docker image
2. **Push Stage**: Push to private registry

View File

@@ -22,7 +22,8 @@ This guide walks through the complete setup of production deployment from scratc
**Development Machine:**
- ✅ Docker & Docker Compose installed
- ✅ Ansible installed (`pip install ansible`)
- ✅ SSH key for production server
- ✅ SSH key for production server (`~/.ssh/production`)
- ✅ Git SSH key configured (see Phase 0)
- ✅ Access to Gitea admin panel
**Production Server (94.16.110.151):**
@@ -33,6 +34,83 @@ This guide walks through the complete setup of production deployment from scratc
---
## Phase 0: Git Repository SSH Access Setup (Development Machine)
### Step 0.1: Generate Git SSH Key
Create a separate SSH key specifically for Git operations (different from the production server SSH key):
```bash
# Generate SSH key for Git
ssh-keygen -t ed25519 -f ~/.ssh/git_michaelschiemer -C "git@michaelschiemer.de" -N ""
# Set correct permissions
chmod 600 ~/.ssh/git_michaelschiemer
chmod 644 ~/.ssh/git_michaelschiemer.pub
```
### Step 0.2: Configure SSH Config
Add Git SSH configuration to `~/.ssh/config`:
```bash
# Edit SSH config
nano ~/.ssh/config
```
Add the following configuration:
```
Host git.michaelschiemer.de
HostName git.michaelschiemer.de
Port 2222
User git
IdentityFile ~/.ssh/git_michaelschiemer
StrictHostKeyChecking no
UserKnownHostsFile /dev/null
```
### Step 0.3: Add Public Key to Gitea
1. Display your public key:
```bash
cat ~/.ssh/git_michaelschiemer.pub
```
2. Copy the output (starts with `ssh-ed25519 ...`)
3. In Gitea:
- Go to **Settings** → **SSH / GPG Keys**
- Click **Add Key**
- Paste the public key
- Click **Add Key**
4. Verify the connection:
```bash
ssh -T git@git.michaelschiemer.de
```
Expected output: `Hi there! You've successfully authenticated...`
### Step 0.4: Update Git Remote (if needed)
If your `origin` remote uses HTTPS, switch it to SSH:
```bash
# Check current remote URL
git remote -v
# Update to SSH
git remote set-url origin git@git.michaelschiemer.de:michael/michaelschiemer.git
# Test push (should work without password prompt)
git push origin main
```
**Note**: This SSH key is separate from the production server SSH key (`~/.ssh/production`). The production key is used for Ansible/server access, while the Git key is only for repository operations.
---
## Phase 1: Gitea Runner Setup (Development Machine)
### Step 1.1: Get Gitea Registration Token
@@ -233,99 +311,107 @@ ansible-vault view production.vault.yml \
## Phase 3: Production Server Initial Setup
### Step 3.1: Deploy Infrastructure Stacks
### Prerequisites
**On Production Server (SSH as deploy user):**
Before running Phase 3, ensure:
- ✅ SSH access to production server configured (`~/.ssh/production`)
- ✅ Repository cloned on production server at `~/deployment/stacks` (or adjust `stacks_base_path` in playbook)
- ✅ Ansible installed on your development machine: `pip install ansible`
- ✅ Ansible collections installed: `ansible-galaxy collection install community.docker`
### Step 3.1: Clone Repository on Production Server (if not already done)
**On Production Server:**
```bash
# SSH to production server
ssh deploy@94.16.110.151
# Navigate to stacks directory
cd ~/deployment/stacks
# Deploy stacks in order
# 1. Traefik (Reverse Proxy & SSL)
cd traefik
docker compose up -d
docker compose logs -f
# Wait for "Configuration loaded" message
# Ctrl+C to exit logs
# 2. PostgreSQL (Database)
cd ../postgresql
docker compose up -d
docker compose logs -f
# Wait for "database system is ready to accept connections"
# Ctrl+C to exit logs
# 3. Docker Registry (Private Registry)
cd ../registry
docker compose up -d
docker compose logs -f
# Wait for "listening on [::]:5000"
# Ctrl+C to exit logs
# 4. Gitea (Git Server + MySQL + Redis)
cd ../gitea
docker compose up -d
docker compose logs -f
# Wait for "Listen: http://0.0.0.0:3000"
# Ctrl+C to exit logs
# 5. Monitoring (Portainer + Grafana + Prometheus)
cd ../monitoring
docker compose up -d
docker compose logs -f
# Wait for all services to start
# Ctrl+C to exit logs
# Verify all stacks are running
docker ps
# Clone repository (if not already present)
mkdir -p ~/deployment
cd ~/deployment
git clone git@git.michaelschiemer.de:michael/michaelschiemer.git . || git clone https://git.michaelschiemer.de/michael/michaelschiemer.git .
```
### Step 3.2: Configure Gitea
1. Access Gitea: https://git.michaelschiemer.de
2. Complete initial setup wizard:
- Database: Use MySQL from stack
- Admin account: Create admin user
- Repository root: `/data/git/repositories`
- Enable Actions in admin settings
### Step 3.3: Create Docker Registry User
```bash
# SSH to production server
ssh deploy@94.16.110.151
# Create registry htpasswd entry
cd ~/deployment/stacks/registry
docker compose exec registry htpasswd -Bbn admin your-registry-password >> auth/htpasswd
# Test login
docker login git.michaelschiemer.de:5000
# Username: admin
# Password: your-registry-password
```
### Step 3.4: Setup SSH Keys for Ansible
### Step 3.2: Deploy Infrastructure Stacks with Ansible
**On Development Machine:**
```bash
# Generate SSH key if not exists
ssh-keygen -t ed25519 -f ~/.ssh/production -C "ansible-deploy"
# Navigate to Ansible directory
cd deployment/ansible
# Copy public key to production server
ssh-copy-id -i ~/.ssh/production.pub deploy@94.16.110.151
# Run infrastructure deployment playbook
ansible-playbook playbooks/setup-infrastructure.yml \
-i inventory/production.yml
# Test SSH connection
ssh -i ~/.ssh/production deploy@94.16.110.151 "echo 'SSH works!'"
# The playbook will:
# 1. Create required Docker networks (traefik-public, app-internal)
# 2. Deploy Traefik (Reverse Proxy & SSL)
# 3. Deploy PostgreSQL (Database)
# 4. Deploy Docker Registry (Private Registry)
# 5. Deploy Gitea (Git Server + PostgreSQL)
# 6. Deploy Monitoring (Portainer + Grafana + Prometheus)
# 7. Wait for all services to be healthy
# 8. Verify accessibility
```
**✅ Checkpoint**: All infrastructure stacks running, SSH access configured
**Expected output:**
- ✅ All stacks deployed successfully
- ✅ All services healthy
- ✅ Gitea accessible at https://git.michaelschiemer.de
**Note:** If monitoring passwords need to be stored in Vault (recommended for production), add them to `secrets/production.vault.yml`:
- `vault_grafana_admin_password`
- `vault_prometheus_password`
Then run the playbook with vault:
```bash
ansible-playbook playbooks/setup-infrastructure.yml \
-i inventory/production.yml \
--vault-password-file secrets/.vault_pass
```
### Step 3.3: Configure Gitea (Manual Step)
1. Access Gitea: https://git.michaelschiemer.de
2. Complete initial setup wizard (first-time only):
- **Database Type**: PostgreSQL
- **Database Host**: `postgres:5432`
- **Database User**: `gitea`
- **Database Password**: `gitea_password` (or check `deployment/stacks/gitea/docker-compose.yml`)
- **Database Name**: `gitea`
- **Admin Account**: Create your admin user
- **Repository Root**: `/data/git/repositories` (default)
3. **Enable Actions** (required for Phase 1):
- Go to **Site Administration** → **Actions**
- Enable **Enable Actions** checkbox
- Save settings
### Step 3.4: Verify Docker Registry
The Ansible playbook automatically creates registry authentication. To retrieve credentials:
```bash
# SSH to production server
ssh deploy@94.16.110.151
# View registry htpasswd (contains username:password hash)
cat ~/deployment/stacks/registry/auth/htpasswd
# The default username is 'admin'
# Password hash can be used to login, or create new user:
cd ~/deployment/stacks/registry
docker compose exec registry htpasswd -Bbn <username> <password> >> auth/htpasswd
docker compose restart registry
# Test login
docker login registry.michaelschiemer.de
# Or if using port:
docker login git.michaelschiemer.de:5000
```
**✅ Checkpoint**: All infrastructure stacks running, Gitea accessible, Actions enabled
---

View File

@@ -0,0 +1,185 @@
# Workflow Troubleshooting Guide
## Problem: Workflows brechen zwischendurch ab
### Mögliche Ursachen
#### 1. Actions werden nicht geladen
**Symptom:** Workflow startet, aber Actions wie `actions/checkout@v4` schlagen fehl
**Lösung:** Prüfe Gitea Konfiguration:
```bash
docker exec gitea cat /data/gitea/conf/app.ini | grep -A 3 '[actions]'
```
Sollte enthalten:
```ini
[actions]
ENABLED = true
DEFAULT_ACTIONS_URL = https://github.com
```
#### 2. Timeouts bei langen Steps
**Symptom:** Workflow läuft eine Zeit, dann Timeout
**Lösung:** Timeout in Runner Config erhöhen:
```yaml
# deployment/gitea-runner/config.yaml
runner:
timeout: 6h # Erhöhe von 3h auf 6h
```
Dann Runner neu starten:
```bash
cd deployment/gitea-runner
docker compose restart gitea-runner
```
#### 3. Docker Buildx Probleme
**Symptom:** Build Step schlägt fehl oder bricht ab
**Lösung:** Prüfe, ob Buildx richtig läuft. Alternativ: Direktes Docker Build verwenden.
#### 4. GitHub-Variablen in Gitea
**Symptom:** `${{ github.sha }}` ist leer oder falsch
**Lösung:** Gitea Actions sollte `github.*` Variablen unterstützen, aber manchmal funktioniert `gitea.*` besser.
**Test:** Prüfe in Workflow-Logs, welche Variablen verfügbar sind:
```yaml
- name: Debug variables
run: |
echo "GITHUB_SHA: ${{ github.sha }}"
echo "GITEA_SHA: ${{ gitea.sha }}"
echo "RUNNER_OS: ${{ runner.os }}"
```
#### 5. Secrets fehlen oder sind falsch
**Symptom:** Registry Login oder SSH schlägt fehl
**Lösung:** Prüfe Secrets in Gitea:
- Repository → Settings → Secrets
- Alle benötigten Secrets sollten vorhanden sein:
- `REGISTRY_USER`
- `REGISTRY_PASSWORD`
- `SSH_PRIVATE_KEY`
- `ANSIBLE_VAULT_PASSWORD` (falls verwendet)
### Debugging-Schritte
#### 1. Workflow-Logs analysieren
In Gitea UI:
1. Gehe zu Actions → Fehlgeschlagener Workflow
2. Klicke auf fehlgeschlagene Step
3. Prüfe Logs für Fehlermeldungen
4. Suche nach:
- `error`
- `timeout`
- `failed`
- `exit code`
#### 2. Runner-Logs prüfen
```bash
cd deployment/gitea-runner
docker compose logs gitea-runner --tail=100 | grep -E "(error|failed|timeout)"
```
#### 3. Runner Status prüfen
In Gitea: https://git.michaelschiemer.de/admin/actions/runners
Prüfe:
- Status sollte "Idle" oder "Running" sein
- Letzte Aktivität sollte kürzlich sein
- Keine Fehler-Meldungen
### Häufige Fehler und Fixes
#### Problem: "Action not found"
**Fehler:** `Error: Action 'actions/checkout@v4' not found`
**Fix:**
1. Prüfe `DEFAULT_ACTIONS_URL` in Gitea config
2. Stelle sicher, dass Internet-Zugriff vom Runner vorhanden ist
3. Gitea neu starten: `docker compose restart gitea`
#### Problem: "Timeout"
**Fehler:** `timeout: job exceeded maximum duration`
**Fix:**
1. Erhöhe Timeout in `config.yaml`
2. Oder teile Workflow in kleinere Jobs auf
#### Problem: "Docker build failed"
**Fehler:** Docker Build schlägt fehl
**Fix:**
1. Prüfe `docker-dind` Container läuft
2. Prüfe Registry-Zugriff
3. Prüfe Registry-Credentials
#### Problem: "SSH connection failed"
**Fehler:** Ansible Deployment kann nicht zum Server verbinden
**Fix:**
1. Prüfe `SSH_PRIVATE_KEY` Secret ist korrekt
2. Prüfe SSH-Key hat richtige Berechtigungen
3. Prüfe Firewall erlaubt Verbindung
### Workflow optimieren
#### Reduziere Workflow-Zeit
1. **Cache verwenden:**
```yaml
- uses: actions/cache@v3
with:
path: vendor
key: composer-${{ hashFiles('composer.lock') }}
```
2. **Parallel Jobs:**
```yaml
jobs:
test:
# ...
build:
# ...
# Beide können parallel laufen
```
3. **Conditional Steps:**
```yaml
- name: Skip on docs change
if: contains(github.event.head_commit.message, '[skip ci]')
run: exit 0
```
### Nächste Schritte
1. **Identifiziere genaue Abbruch-Stelle:**
- In welchem Step bricht es ab?
- Welche Fehlermeldung erscheint?
2. **Prüfe Logs:**
- Workflow-Logs in Gitea UI
- Runner-Logs: `docker compose logs gitea-runner`
3. **Teste einzelne Steps:**
- Führe Steps manuell aus
- Isoliere das Problem
4. **Workflow vereinfachen:**
- Reduziere auf minimalen Test-Workflow
- Füge Steps schrittweise hinzu

View File

@@ -12,7 +12,10 @@ deployment/ansible/
├── playbooks/
│ ├── setup-production-secrets.yml # Deploy secrets
│ ├── deploy-update.yml # Deploy application updates
── rollback.yml # Rollback deployments
── rollback.yml # Rollback deployments
│ ├── setup-wireguard.yml # Setup WireGuard VPN server
│ ├── add-wireguard-client.yml # Add WireGuard client
│ └── README-WIREGUARD.md # WireGuard documentation
├── secrets/
│ ├── .gitignore # Prevent committing secrets
│ └── production.vault.yml.example # Example vault file
@@ -112,6 +115,23 @@ ansible-playbook playbooks/rollback.yml \
-e "rollback_to_version=2025-01-28T15-30-00"
```
### Setup WireGuard VPN
**First-time setup** - Install WireGuard VPN server:
```bash
ansible-playbook -i inventory/production.yml playbooks/setup-wireguard.yml
```
**Add a client**:
```bash
ansible-playbook -i inventory/production.yml playbooks/add-wireguard-client.yml \
-e "client_name=myclient"
```
Siehe [playbooks/README-WIREGUARD.md](playbooks/README-WIREGUARD.md) für detaillierte Anleitung.
## Ansible Vault Operations
### View Encrypted File

View File

@@ -0,0 +1,284 @@
# WireGuard VPN Setup
WireGuard VPN-Server Installation und Konfiguration via Ansible.
## Übersicht
Dieses Ansible Setup installiert und konfiguriert einen WireGuard VPN-Server auf dem Production-Server, um sicheren Zugriff auf interne Services zu ermöglichen.
## Playbooks
### 1. setup-wireguard.yml
Installiert und konfiguriert den WireGuard VPN-Server.
**Features:**
- Installiert WireGuard und Tools
- Generiert Server-Keys (falls nicht vorhanden)
- Konfiguriert WireGuard-Server
- Aktiviert IP Forwarding
- Konfiguriert NAT (Masquerading)
- Öffnet Firewall-Port (51820/udp)
- Startet WireGuard-Service
**Verwendung:**
```bash
cd deployment/ansible
ansible-playbook -i inventory/production.yml playbooks/setup-wireguard.yml
```
**Variablen:**
- `wireguard_port`: Port für WireGuard (Standard: 51820)
- `wireguard_network`: VPN-Netzwerk (Standard: 10.8.0.0/24)
- `wireguard_server_ip`: Server-IP im VPN (Standard: 10.8.0.1)
**Beispiel mit Custom-Parametern:**
```bash
ansible-playbook -i inventory/production.yml playbooks/setup-wireguard.yml \
-e "wireguard_port=51820" \
-e "wireguard_network=10.8.0.0/24" \
-e "wireguard_server_ip=10.8.0.1"
```
### 2. add-wireguard-client.yml
Fügt einen neuen Client zum WireGuard-Server hinzu.
**Features:**
- Generiert Client-Keys
- Fügt Client zur Server-Config hinzu
- Erstellt Client-Konfigurationsdatei
- Generiert QR-Code (falls qrencode installiert)
- Restartet WireGuard-Service
**Verwendung:**
```bash
cd deployment/ansible
ansible-playbook -i inventory/production.yml playbooks/add-wireguard-client.yml \
-e "client_name=myclient"
```
**Optionale Parameter:**
- `client_ip`: Spezifische Client-IP (Standard: automatisch berechnet)
- `allowed_ips`: Erlaubte IP-Ranges (Standard: gesamtes VPN-Netzwerk)
**Beispiel mit spezifischer IP:**
```bash
ansible-playbook -i inventory/production.yml playbooks/add-wireguard-client.yml \
-e "client_name=myclient" \
-e "client_ip=10.8.0.2"
```
## Wichtige Sicherheitshinweise
### SSH-Zugriff bleibt verfügbar
**WICHTIG**: Die WireGuard-Konfiguration ändert NICHT die SSH-Zugriffsmöglichkeiten:
- ✅ SSH über die normale Server-IP bleibt vollständig funktionsfähig
- ✅ WireGuard routet standardmäßig nur das VPN-Netzwerk (10.8.0.0/24)
- ✅ Normale Internet-Routen werden nicht geändert
- ✅ Firewall-Regeln für SSH (Port 22) werden NICHT entfernt oder blockiert
Die Client-Konfiguration verwendet standardmäßig `AllowedIPs = 10.8.0.0/24`, was bedeutet, dass nur Traffic für das VPN-Netzwerk über WireGuard geroutet wird. Alle anderen Verbindungen (inkl. SSH) nutzen weiterhin die normale Internet-Verbindung.
**Um SSH komplett über VPN zu routen** (nicht empfohlen für die erste Installation):
```bash
ansible-playbook ... -e "allowed_ips=0.0.0.0/0"
```
## Verzeichnisstruktur
Nach der Installation:
```
/etc/wireguard/
├── wg0.conf # Server-Konfiguration
├── wg0_private.key # Server-Private-Key (600)
├── wg0_public.key # Server-Public-Key (644)
└── clients/ # Client-Konfigurationen
├── client1.conf # Client 1 Config
└── client2.conf # Client 2 Config
```
## Client-Konfiguration verwenden
### 1. Config-Datei auf Client kopieren
```bash
# Von Ansible Control Machine
scp -i ~/.ssh/production \
deploy@94.16.110.151:/etc/wireguard/clients/myclient.conf \
~/myclient.conf
```
### 2. WireGuard auf Client installieren
**Linux:**
```bash
sudo apt install wireguard wireguard-tools # Ubuntu/Debian
# oder
sudo yum install wireguard-tools # CentOS/RHEL
```
**macOS:**
```bash
brew install wireguard-tools
```
**Windows:**
Download von https://www.wireguard.com/install/
### 3. VPN verbinden
**Linux/macOS:**
```bash
sudo wg-quick up ~/myclient.conf
# oder
sudo wg-quick up myclient
```
**Windows:**
Importiere die `.conf`-Datei in die WireGuard-App.
### 4. Verbindung testen
```bash
# Ping zum Server
ping 10.8.0.1
# Status prüfen
sudo wg show
# VPN trennen
sudo wg-quick down myclient
```
## QR-Code für Mobile Client
Falls `qrencode` installiert ist, wird beim Hinzufügen eines Clients automatisch ein QR-Code angezeigt:
```bash
ansible-playbook -i inventory/production.yml playbooks/add-wireguard-client.yml \
-e "client_name=myphone"
```
Der QR-Code kann mit der WireGuard Mobile App (iOS/Android) gescannt werden.
## Firewall-Konfiguration
Das Playbook öffnet automatisch den WireGuard-Port (51820/udp) in UFW, falls installiert.
**Manuelle Firewall-Regeln:**
```bash
# UFW
sudo ufw allow 51820/udp comment 'WireGuard VPN'
# iptables direkt
sudo iptables -A INPUT -p udp --dport 51820 -j ACCEPT
```
## Troubleshooting
### WireGuard startet nicht
```bash
# Status prüfen
sudo systemctl status wg-quick@wg0
# Logs anzeigen
sudo journalctl -u wg-quick@wg0 -f
# Manuell starten
sudo wg-quick up wg0
```
### Client kann nicht verbinden
1. **Firewall prüfen:**
```bash
sudo ufw status
sudo iptables -L -n | grep 51820
```
2. **Server-Logs prüfen:**
```bash
sudo journalctl -u wg-quick@wg0 -f
```
3. **Server-Status prüfen:**
```bash
sudo wg show
```
4. **Routing prüfen:**
```bash
sudo ip route show
```
### IP Forwarding nicht aktiv
```bash
# Manuell aktivieren
echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward
# Permanent machen
echo 'net.ipv4.ip_forward=1' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
```
## Client entfernen
Um einen Client zu entfernen:
```bash
# Auf dem Server
sudo nano /etc/wireguard/wg0.conf
# Entferne den [Peer] Block für den Client
sudo wg-quick down wg0
sudo wg-quick up wg0
# Optional: Client-Config löschen
sudo rm /etc/wireguard/clients/clientname.conf
```
## Server-Public-Key abrufen
```bash
# Auf dem Server
cat /etc/wireguard/wg0_public.key
# oder
sudo cat /etc/wireguard/wg0_private.key | wg pubkey
```
## Best Practices
1. **Backup der Keys**: Speichere Server-Keys sicher:
```bash
sudo tar czf wireguard-backup.tar.gz /etc/wireguard/
```
2. **Regelmäßige Updates:**
```bash
sudo apt update && sudo apt upgrade wireguard wireguard-tools
```
3. **Monitoring**: Überwache VPN-Verbindungen:
```bash
sudo wg show
```
4. **Sicherheit**:
- Verwalte Client-Keys sicher
- Entferne nicht genutzte Clients
- Nutze starke Passwörter für Server-Zugriff
## Support
Bei Problemen:
1. Prüfe Logs: `sudo journalctl -u wg-quick@wg0`
2. Prüfe Status: `sudo wg show`
3. Prüfe Firewall: `sudo ufw status`
4. Teste Connectivity: `ping 10.8.0.1` (vom Client)

View File

@@ -0,0 +1,169 @@
---
- name: Add WireGuard Client
hosts: production
become: yes
gather_facts: yes
vars:
wireguard_interface: "wg0"
wireguard_config_path: "/etc/wireguard"
wireguard_config_file: "{{ wireguard_config_path }}/{{ wireguard_interface }}.conf"
wireguard_client_configs_path: "/etc/wireguard/clients"
pre_tasks:
- name: Set WireGuard network
set_fact:
wireguard_network: "{{ wireguard_network | default('10.8.0.0/24') }}"
- name: Set WireGuard other variables with defaults
set_fact:
wireguard_port: "{{ wireguard_port | default(51820) }}"
client_ip: "{{ client_ip | default('') }}"
# IMPORTANT: Default to VPN network only (not 0.0.0.0/0)
# This ensures SSH access via normal IP remains available
allowed_ips: "{{ allowed_ips | default(wireguard_network) }}"
tasks:
- name: Validate client name
fail:
msg: "client_name is required. Usage: ansible-playbook ... -e 'client_name=myclient'"
when: client_name is not defined or client_name == ""
- name: Get server external IP address
uri:
url: https://api.ipify.org
return_content: yes
register: server_external_ip
changed_when: false
failed_when: false
- name: Set server external IP
set_fact:
server_external_ip_content: "{{ ansible_host | default(server_external_ip.content | default('')) }}"
- name: Check if WireGuard config exists
stat:
path: "{{ wireguard_config_file }}"
register: wireguard_config_exists
- name: Fail if WireGuard not configured
fail:
msg: "WireGuard server not configured. Please run setup-wireguard.yml first."
when: not wireguard_config_exists.stat.exists
- name: Read WireGuard server config
slurp:
src: "{{ wireguard_config_file }}"
register: wireguard_server_config_read
- name: Extract server IP from config
set_fact:
server_vpn_ip: "{{ (wireguard_server_config_read.content | b64decode | regex_search('Address = ([0-9.]+)', '\\1')) | first | default('10.8.0.1') }}"
- name: Count existing clients in config
set_fact:
existing_clients_count: "{{ (wireguard_server_config_read.content | b64decode | regex_findall('\\[Peer\\]') | length) }}"
when: client_ip == ""
- name: Calculate client IP if not provided
set_fact:
client_ip: "{{ server_vpn_ip | regex_replace('^(\\d+\\.\\d+\\.\\d+\\.)\\d+$', '\\1') }}{{ (server_vpn_ip | regex_replace('^(\\d+\\.\\d+\\.\\d+\\.)(\\d+)', '\\2') | int) + (existing_clients_count | default(1)) | int }}"
when: client_ip == ""
- name: Generate client private key
command: "wg genkey"
register: client_private_key
changed_when: true
no_log: yes
- name: Generate client public key
command: "wg pubkey"
args:
stdin: "{{ client_private_key.stdout }}"
register: client_public_key
changed_when: false
no_log: yes
- name: Check if client already exists in config
shell: "grep -q '{{ client_name }}' {{ wireguard_config_file }} || echo 'not found'"
register: client_exists_check
changed_when: false
failed_when: false
- name: Add client to WireGuard server config
blockinfile:
path: "{{ wireguard_config_file }}"
block: |
# Client: {{ client_name }}
[Peer]
PublicKey = {{ client_public_key.stdout }}
AllowedIPs = {{ client_ip }}/32
marker: "# {mark} ANSIBLE MANAGED BLOCK - Client: {{ client_name }}"
when: "'not found' in client_exists_check.stdout"
- name: Ensure client configs directory exists
file:
path: "{{ wireguard_client_configs_path }}"
state: directory
mode: '0700'
owner: root
group: root
- name: Get server public key
shell: "cat {{ wireguard_config_path }}/{{ wireguard_interface }}_private.key | wg pubkey"
register: server_public_key_cmd
changed_when: false
no_log: yes
failed_when: false
- name: Create client configuration file
template:
src: "{{ playbook_dir }}/../templates/wireguard-client.conf.j2"
dest: "{{ wireguard_client_configs_path }}/{{ client_name }}.conf"
mode: '0600'
owner: root
group: root
- name: Read WireGuard server config to find server IP
slurp:
src: "{{ wireguard_config_file }}"
register: wireguard_server_config_read
- name: Restart WireGuard service
systemd:
name: "wg-quick@{{ wireguard_interface }}"
state: restarted
when: "'not found' in client_exists_check.stdout"
- name: Display client configuration
debug:
msg: |
========================================
WireGuard Client Added: {{ client_name }}
========================================
Client Configuration File:
{{ wireguard_client_configs_path }}/{{ client_name }}.conf
Client IP: {{ client_ip }}
Server Endpoint: {{ server_external_ip_content }}:{{ wireguard_port }}
To use this configuration:
1. Copy the config file to your client machine
2. Install WireGuard client
3. Run: sudo wg-quick up {{ client_name }}
Or scan the QR code (if qrencode installed):
qrencode -t ansiutf8 < {{ wireguard_client_configs_path }}/{{ client_name }}.conf
========================================
- name: Generate QR code for client config
command: "qrencode -t ansiutf8 -r {{ wireguard_client_configs_path }}/{{ client_name }}.conf"
register: qr_code
changed_when: false
failed_when: false
- name: Display QR code
debug:
msg: "{{ qr_code.stdout }}"
when: qr_code.rc == 0

View File

@@ -118,7 +118,7 @@
changed_when: false
ignore_errors: yes
?? - name: Get deployed image information
- name: Get deployed image information
shell: |
docker compose -f {{ app_stack_path }}/docker-compose.yml config | grep -E "^\s+image:" | head -1 | awk '{print $2}' || echo "unknown"
args:

View File

@@ -241,6 +241,138 @@
debug:
msg: "Gitea HTTPS check: {{ 'SUCCESS' if gitea_http_check.status == 200 else 'FAILED - Status: ' + (gitea_http_check.status|string) }}"
# 6. Deploy Application Stack
- name: Optionally load application secrets from vault
include_vars:
file: "{{ playbook_dir }}/../secrets/production.vault.yml"
no_log: yes
ignore_errors: yes
delegate_to: localhost
become: no
- name: Check if PostgreSQL .env exists
stat:
path: "{{ stacks_base_path }}/postgresql/.env"
register: postgres_env_file
changed_when: false
- name: Extract PostgreSQL password from .env file
shell: "grep '^POSTGRES_PASSWORD=' {{ stacks_base_path }}/postgresql/.env 2>/dev/null | cut -d'=' -f2- || echo ''"
register: postgres_password_from_file
changed_when: false
failed_when: false
when: postgres_env_file.stat.exists
no_log: yes
- name: Set application database password (from file, vault, or generate)
set_fact:
app_db_password: "{{ postgres_password_from_file.stdout if (postgres_env_file.stat.exists and postgres_password_from_file.stdout != '') else (vault_db_root_password | default(lookup('password', '/dev/null length=32 chars=ascii_letters,digits,punctuation'))) }}"
no_log: yes
- name: Set application redis password from vault or generate
set_fact:
app_redis_password: "{{ vault_redis_password | default(lookup('password', '/dev/null length=32 chars=ascii_letters,digits,punctuation')) }}"
- name: Ensure application stack directory exists
file:
path: "{{ stacks_base_path }}/application"
state: directory
mode: '0755'
- name: Create application stack .env file
template:
src: "{{ playbook_dir }}/../templates/application.env.j2"
dest: "{{ stacks_base_path }}/application/.env"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: '0600'
vars:
db_password: "{{ app_db_password }}"
db_user: "{{ db_user | default('postgres') }}"
db_name: "{{ db_name | default('michaelschiemer') }}"
redis_password: "{{ app_redis_password }}"
app_domain: "{{ app_domain | default('michaelschiemer.de') }}"
no_log: yes
- name: Deploy Application stack
community.docker.docker_compose_v2:
project_src: "{{ stacks_base_path }}/application"
state: present
pull: always
register: application_output
- name: Wait for Application to be ready
wait_for:
timeout: "{{ wait_timeout }}"
when: application_output.changed
- name: Wait for application containers to be healthy
pause:
seconds: 30
when: application_output.changed
- name: Check application container health status
shell: |
docker compose -f {{ stacks_base_path }}/application/docker-compose.yml ps --format json | jq -r '.[] | select(.Health != "healthy" and .Health != "" and .Health != "starting") | "\(.Name): \(.Health)"' || echo "All healthy or no health checks"
args:
executable: /bin/bash
register: app_health_status
changed_when: false
ignore_errors: yes
- name: Display application health status
debug:
msg: "Application health: {{ app_health_status.stdout if app_health_status.stdout != '' else 'All services healthy or starting' }}"
- name: Wait for app container to be ready before migration
wait_for:
timeout: 60
when: application_output.changed
- name: Check if app container is running
shell: |
docker compose -f {{ stacks_base_path }}/application/docker-compose.yml ps app | grep -q "Up" || exit 1
args:
executable: /bin/bash
register: app_container_running
changed_when: false
failed_when: false
when: application_output.changed
- name: Run database migrations
shell: |
docker compose -f {{ stacks_base_path }}/application/docker-compose.yml exec -T app php console.php db:migrate
args:
executable: /bin/bash
register: migration_result
changed_when: true
failed_when: false
ignore_errors: yes
when: application_output.changed and app_container_running.rc == 0
- name: Display migration result
debug:
msg: |
Migration Result:
{{ migration_result.stdout if migration_result.rc == 0 else 'Migration may have failed - check logs with: docker compose -f ' + stacks_base_path + '/application/docker-compose.yml logs app' }}
when: application_output.changed
- name: Verify application accessibility via HTTPS
uri:
url: "https://{{ app_domain | default('michaelschiemer.de') }}/health"
method: GET
validate_certs: no
status_code: [200, 404, 502, 503]
timeout: 10
register: app_health_check
ignore_errors: yes
when: application_output.changed
- name: Display application accessibility status
debug:
msg: "Application health check: {{ 'SUCCESS (HTTP ' + (app_health_check.status|string) + ')' if app_health_check.status == 200 else 'FAILED or not ready yet (HTTP ' + (app_health_check.status|string) + ')' }}"
when: application_output.changed
- name: Summary
debug:
msg:
@@ -250,9 +382,11 @@
- "Docker Registry: {{ 'Deployed' if registry_output.changed else 'Already running' }}"
- "Gitea: {{ 'Deployed' if gitea_output.changed else 'Already running' }}"
- "Monitoring: {{ 'Deployed' if monitoring_output.changed else 'Already running' }}"
- "Application: {{ 'Deployed' if application_output.changed else 'Already running' }}"
- ""
- "Next Steps:"
- "1. Access Gitea at: https://git.michaelschiemer.de"
- "2. Complete Gitea setup wizard if first-time deployment"
- "3. Navigate to Admin > Actions > Runners to get registration token"
- "4. Continue with Phase 1 - Gitea Runner Setup"
- "5. Access Application at: https://{{ app_domain | default('michaelschiemer.de') }}"

View File

@@ -0,0 +1,294 @@
---
- name: Setup WireGuard VPN Server
hosts: production
become: yes
gather_facts: yes
vars:
wireguard_interface: "wg0"
wireguard_config_path: "/etc/wireguard"
wireguard_config_file: "{{ wireguard_config_path }}/{{ wireguard_interface }}.conf"
wireguard_private_key_file: "{{ wireguard_config_path }}/{{ wireguard_interface }}_private.key"
wireguard_public_key_file: "{{ wireguard_config_path }}/{{ wireguard_interface }}_public.key"
wireguard_client_configs_path: "{{ wireguard_config_path }}/clients"
wireguard_enable_ip_forwarding: true
pre_tasks:
- name: Set WireGuard variables with defaults
set_fact:
wireguard_port: "{{ wireguard_port | default(51820) }}"
wireguard_network: "{{ wireguard_network | default('10.8.0.0/24') }}"
wireguard_server_ip: "{{ wireguard_server_ip | default('10.8.0.1') }}"
- name: Optionally load wireguard secrets from vault
include_vars:
file: "{{ playbook_dir }}/../secrets/production.vault.yml"
no_log: yes
ignore_errors: yes
delegate_to: localhost
become: no
tasks:
- name: Check if WireGuard is already installed
command: which wg
register: wireguard_installed
changed_when: false
failed_when: false
- name: Update package cache
apt:
update_cache: yes
cache_valid_time: 3600
when: not wireguard_installed.rc == 0
- name: Install WireGuard
apt:
name:
- wireguard
- wireguard-tools
- qrencode
state: present
when: not wireguard_installed.rc == 0
notify: restart wireguard
- name: Ensure WireGuard config directory exists
file:
path: "{{ wireguard_config_path }}"
state: directory
mode: '0700'
owner: root
group: root
- name: Ensure WireGuard client configs directory exists
file:
path: "{{ wireguard_client_configs_path }}"
state: directory
mode: '0700'
owner: root
group: root
- name: Check if WireGuard server keys exist
stat:
path: "{{ wireguard_private_key_file }}"
register: server_private_key_exists
- name: Generate WireGuard server private key
command: "wg genkey"
register: server_private_key
changed_when: true
when: not server_private_key_exists.stat.exists
no_log: yes
- name: Save WireGuard server private key
copy:
content: "{{ server_private_key.stdout }}"
dest: "{{ wireguard_private_key_file }}"
mode: '0600'
owner: root
group: root
when: not server_private_key_exists.stat.exists
no_log: yes
- name: Read WireGuard server private key
slurp:
src: "{{ wireguard_private_key_file }}"
register: server_private_key_content
when: server_private_key_exists.stat.exists
- name: Generate WireGuard server public key
command: "wg pubkey"
args:
stdin: "{{ server_private_key.stdout if not server_private_key_exists.stat.exists else server_private_key_content.content | b64decode | trim }}"
register: server_public_key
changed_when: false
when: not server_private_key_exists.stat.exists
no_log: yes
- name: Get existing server public key
shell: "cat {{ wireguard_private_key_file }} | wg pubkey"
register: existing_server_public_key
changed_when: false
when: server_private_key_exists.stat.exists
no_log: yes
failed_when: false
- name: Set server public key fact
set_fact:
server_public_key_value: "{{ server_public_key.stdout if not server_private_key_exists.stat.exists else existing_server_public_key.stdout }}"
- name: Save WireGuard server public key
copy:
content: "{{ server_public_key_value }}"
dest: "{{ wireguard_public_key_file }}"
mode: '0644'
owner: root
group: root
- name: Enable IP forwarding
sysctl:
name: net.ipv4.ip_forward
value: '1'
state: present
sysctl_set: yes
reload: yes
when: wireguard_enable_ip_forwarding
- name: Make IP forwarding persistent
lineinfile:
path: /etc/sysctl.conf
regexp: '^net\.ipv4\.ip_forward'
line: 'net.ipv4.ip_forward=1'
state: present
when: wireguard_enable_ip_forwarding
- name: Get server external IP address
uri:
url: https://api.ipify.org
return_content: yes
register: server_external_ip
changed_when: false
failed_when: false
- name: Set server external IP from inventory if API fails
set_fact:
server_external_ip_content: "{{ ansible_host | default(server_external_ip.content | default('')) }}"
when: server_external_ip.content is defined
- name: Get server external IP from ansible_host
set_fact:
server_external_ip_content: "{{ ansible_host }}"
when: server_external_ip.content is not defined
- name: Read server private key for config
slurp:
src: "{{ wireguard_private_key_file }}"
register: server_private_key_file_content
when: server_private_key_exists.stat.exists
- name: Set server private key for template (new key)
set_fact:
server_private_key_for_config: "{{ server_private_key.stdout }}"
when: not server_private_key_exists.stat.exists
- name: Set server private key for template (existing key)
set_fact:
server_private_key_for_config: "{{ server_private_key_file_content.content | b64decode | trim }}"
when: server_private_key_exists.stat.exists
- name: Get network interface name
shell: "ip route | grep default | awk '{print $5}' | head -1"
register: default_interface
changed_when: false
failed_when: false
- name: Set default interface
set_fact:
wireguard_interface_name: "{{ default_interface.stdout | default('eth0') }}"
- name: Check if WireGuard config exists
stat:
path: "{{ wireguard_config_file }}"
register: wireguard_config_exists
- name: Create WireGuard server configuration
template:
src: "{{ playbook_dir }}/../templates/wireguard-server.conf.j2"
dest: "{{ wireguard_config_file }}"
mode: '0600'
owner: root
group: root
notify: restart wireguard
- name: Check if WireGuard service is enabled
systemd:
name: "wg-quick@{{ wireguard_interface }}"
register: wireguard_service_status
failed_when: false
changed_when: false
- name: Enable WireGuard service
systemd:
name: "wg-quick@{{ wireguard_interface }}"
enabled: yes
daemon_reload: yes
when: not wireguard_service_status.status.ActiveState is defined or wireguard_service_status.status.ActiveState != 'active'
- name: Start WireGuard service
systemd:
name: "wg-quick@{{ wireguard_interface }}"
state: started
notify: restart wireguard
- name: Check if UFW firewall is installed
command: which ufw
register: ufw_installed
changed_when: false
failed_when: false
- name: Verify SSH access is allowed in UFW
command: "ufw status | grep -q '22/tcp' || echo 'SSH not found'"
register: ssh_ufw_check
changed_when: false
failed_when: false
when: ufw_installed.rc == 0
- name: Warn if SSH is not explicitly allowed
debug:
msg: |
?? WARNING: SSH (port 22) might not be explicitly allowed in UFW!
Please ensure SSH access is configured before proceeding.
Run: sudo ufw allow 22/tcp
when: ufw_installed.rc == 0 and 'SSH not found' in ssh_ufw_check.stdout
- name: Allow WireGuard port in UFW firewall
ufw:
rule: allow
port: "{{ wireguard_port }}"
proto: udp
comment: "WireGuard VPN"
when: ufw_installed.rc == 0
- name: Allow WireGuard port in UFW firewall (alternative)
shell: "ufw allow {{ wireguard_port }}/udp comment 'WireGuard VPN'"
when: ufw_installed.rc == 0
failed_when: false
changed_when: false
- name: Check WireGuard status
command: "wg show {{ wireguard_interface }}"
register: wireguard_status
changed_when: false
failed_when: false
- name: Display WireGuard status
debug:
msg: |
WireGuard Status:
{{ wireguard_status.stdout if wireguard_status.rc == 0 else 'WireGuard interface not active' }}
- name: Display server public key
debug:
msg: |
========================================
WireGuard Server Setup Complete!
========================================
Server Public Key:
{{ server_public_key_value }}
Server IP: {{ wireguard_server_ip }}
Server Endpoint: {{ server_external_ip_content }}:{{ wireguard_port }}
Network: {{ wireguard_network }}
To add a client, run:
ansible-playbook -i inventory/production.yml playbooks/add-wireguard-client.yml -e "client_name=myclient"
Client configs are stored in:
{{ wireguard_client_configs_path }}/
========================================
handlers:
- name: restart wireguard
systemd:
name: "wg-quick@{{ wireguard_interface }}"
state: restarted

View File

@@ -7,7 +7,7 @@
vault_db_password: "change-me-secure-db-password"
vault_db_root_password: "change-me-secure-root-password"
# Redis Credentials
# Application Stack Credentials
vault_redis_password: "change-me-secure-redis-password"
# Application Secrets

View File

@@ -0,0 +1,40 @@
# Application Stack Environment Configuration
# Generated by Ansible - DO NOT EDIT MANUALLY
# Timezone
TZ={{ timezone | default('Europe/Berlin') }}
# Application Domain
APP_DOMAIN={{ app_domain | default('michaelschiemer.de') }}
# Application Settings
APP_ENV={{ app_env | default('production') }}
APP_DEBUG={{ app_debug | default('false') }}
APP_URL=https://{{ app_domain | default('michaelschiemer.de') }}
# Database Configuration
# Using PostgreSQL from postgres stack
DB_HOST=postgres
DB_PORT={{ db_port | default('5432') }}
DB_NAME={{ db_name | default('michaelschiemer') }}
DB_USER={{ db_user | default('postgres') }}
DB_PASS={{ db_password }}
# Redis Configuration
# Redis runs in this stack
REDIS_PASSWORD={{ redis_password }}
# Cache Configuration
CACHE_DRIVER={{ cache_driver | default('redis') }}
CACHE_PREFIX={{ cache_prefix | default('app') }}
# Session Configuration
SESSION_DRIVER={{ session_driver | default('redis') }}
SESSION_LIFETIME={{ session_lifetime | default('120') }}
# Queue Worker Configuration
QUEUE_DRIVER={{ queue_driver | default('redis') }}
QUEUE_CONNECTION={{ queue_connection | default('default') }}
QUEUE_WORKER_SLEEP={{ queue_worker_sleep | default('3') }}
QUEUE_WORKER_TRIES={{ queue_worker_tries | default('3') }}
QUEUE_WORKER_TIMEOUT={{ queue_worker_timeout | default('60') }}

View File

@@ -17,4 +17,5 @@ GRAFANA_PLUGINS={{ grafana_plugins | default('') }}
# Prometheus BasicAuth
# Format: username:hashed_password
PROMETHEUS_AUTH={{ prometheus_auth }}
# Note: Dollar signs are escaped for Docker Compose ($$ becomes $)
PROMETHEUS_AUTH={{ prometheus_auth | replace('$', '$$') }}

View File

@@ -0,0 +1,27 @@
# WireGuard Client Configuration for {{ client_name }}
# Generated by Ansible - DO NOT EDIT MANUALLY
[Interface]
# Client private key
PrivateKey = {{ client_private_key.stdout }}
# Client IP address in VPN network
Address = {{ client_ip }}/24
# DNS server (optional)
DNS = 1.1.1.1, 8.8.8.8
[Peer]
# Server public key
PublicKey = {{ server_public_key_cmd.stdout }}
# Server endpoint
Endpoint = {{ server_external_ip_content }}:{{ wireguard_port }}
# Allowed IPs (routes through VPN)
# IMPORTANT: Only VPN network is routed through VPN by default
# SSH access via normal IP ({{ server_external_ip_content }}) remains available
AllowedIPs = {{ allowed_ips }}
# Keep connection alive
PersistentKeepalive = 25

View File

@@ -0,0 +1,22 @@
# WireGuard Server Configuration
# Generated by Ansible - DO NOT EDIT MANUALLY
[Interface]
# Server private key
PrivateKey = {{ server_private_key_for_config }}
# Server IP address in VPN network
Address = {{ wireguard_server_ip }}/24
# Port to listen on
ListenPort = {{ wireguard_port }}
# Enable NAT for VPN clients to access internet
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o {{ wireguard_interface_name }} -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o {{ wireguard_interface_name }} -j MASQUERADE
# Clients will be added here by the add-wireguard-client playbook
# Example:
# [Peer]
# PublicKey = <client_public_key>
# AllowedIPs = 10.8.0.2/32

View File

@@ -11,8 +11,8 @@ runner:
# Maximum number of concurrent jobs
capacity: 1
# Timeout for a single job
timeout: 3h
# Timeout for a single job (increased for long-running deployments)
timeout: 6h
# Whether to enable debug mode (skip SSL verification for setup)
insecure: true
@@ -55,8 +55,8 @@ container:
# Default image if not specified in workflow
default_image: node:16-bullseye
# Docker host
docker_host: ""
# Docker host (use docker-dind without TLS since it's in isolated network)
docker_host: tcp://docker-dind:2375
# Valid volume paths that can be mounted
valid_volumes:

View File

@@ -1,5 +1,3 @@
version: '3.8'
services:
gitea-runner:
image: gitea/act_runner:latest
@@ -25,14 +23,11 @@ services:
container_name: gitea-runner-dind
restart: unless-stopped
privileged: true
environment:
- DOCKER_TLS_CERTDIR=/certs
volumes:
- docker-certs:/certs
- docker-data:/var/lib/docker
networks:
- gitea-runner
command: ["dockerd", "--host=unix:///var/run/docker.sock", "--host=tcp://0.0.0.0:2376", "--tlsverify"]
command: ["dockerd", "--host=unix:///var/run/docker.sock", "--host=tcp://0.0.0.0:2375"]
networks:
gitea-runner:
@@ -40,7 +35,5 @@ networks:
driver: bridge
volumes:
docker-certs:
name: gitea-runner-certs
docker-data:
name: gitea-runner-docker-data

View File

@@ -13,9 +13,9 @@ APP_DEBUG=false
APP_URL=https://michaelschiemer.de
# Database Configuration
# Note: MySQL runs in Stack 5 (PostgreSQL) or external server
DB_HOST=mysql
DB_PORT=3306
# Note: Using PostgreSQL from postgres stack
DB_HOST=postgres
DB_PORT=5432
DB_NAME=michaelschiemer
DB_USER=appuser
DB_PASS=<generate-with-openssl-rand-base64-32>

View File

@@ -14,8 +14,8 @@ services:
- APP_DEBUG=${APP_DEBUG:-false}
- APP_URL=${APP_URL:-https://michaelschiemer.de}
# Database
- DB_HOST=${DB_HOST:-mysql}
- DB_PORT=${DB_PORT:-3306}
- DB_HOST=${DB_HOST:-postgres}
- DB_PORT=${DB_PORT:-5432}
- DB_NAME=${DB_NAME}
- DB_USER=${DB_USER}
- DB_PASS=${DB_PASS}
@@ -127,8 +127,8 @@ services:
- APP_ENV=${APP_ENV:-production}
- APP_DEBUG=${APP_DEBUG:-false}
# Database
- DB_HOST=${DB_HOST:-mysql}
- DB_PORT=${DB_PORT:-3306}
- DB_HOST=${DB_HOST:-postgres}
- DB_PORT=${DB_PORT:-5432}
- DB_NAME=${DB_NAME}
- DB_USER=${DB_USER}
- DB_PASS=${DB_PASS}
@@ -172,8 +172,8 @@ services:
- APP_ENV=${APP_ENV:-production}
- APP_DEBUG=${APP_DEBUG:-false}
# Database
- DB_HOST=${DB_HOST:-mysql}
- DB_PORT=${DB_PORT:-3306}
- DB_HOST=${DB_HOST:-postgres}
- DB_PORT=${DB_PORT:-5432}
- DB_NAME=${DB_NAME}
- DB_USER=${DB_USER}
- DB_PASS=${DB_PASS}

View File

@@ -15,12 +15,6 @@ services:
- "traefik.http.routers.portainer.tls=true"
- "traefik.http.routers.portainer.tls.certresolver=letsencrypt"
- "traefik.http.services.portainer.loadbalancer.server.port=9000"
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:9000/api/system/status"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
prometheus:
image: prom/prometheus:latest

View File

@@ -18,6 +18,9 @@ Complete documentation for deploying the Custom PHP Framework to production.
**Want automation?**
- [Ansible Deployment](ANSIBLE_DEPLOYMENT.md) - Infrastructure as Code with Ansible
**Need secure access?**
- [WireGuard VPN Setup](WIREGUARD-SETUP.md) - Secure VPN access to production services
---
## Documentation Structure
@@ -120,6 +123,21 @@ Complete documentation for deploying the Custom PHP Framework to production.
---
### 7. [WIREGUARD-SETUP.md](WIREGUARD-SETUP.md)
**Best for**: Secure VPN access to production services
**Content**:
- Complete WireGuard VPN setup guide
- Server installation via Ansible
- Client configuration and management
- Connection testing and troubleshooting
- Security best practices
- Monitoring and maintenance
**Use when**: You need secure access to internal services (Prometheus, Grafana, Portainer) or want to restrict access via VPN.
---
## Which Guide Should I Use?
### Scenario 1: First-Time Deployment
@@ -452,6 +470,7 @@ This documentation should be updated after each deployment to reflect:
- [Production Guide](PRODUCTION_DEPLOYMENT.md) - Comprehensive reference
- [Logging Guide](production-logging.md) - Production logging configuration
- [Ansible Guide](ANSIBLE_DEPLOYMENT.md) - Infrastructure automation
- [WireGuard VPN](WIREGUARD-SETUP.md) - Secure VPN access to production
---

View File

@@ -0,0 +1,213 @@
# WireGuard - Zukünftige Sicherheitshärtung
**Status**: 📋 Geplant
**Aktueller Status**: Alle Dienste öffentlich erreichbar
**Ziel**: Backend-Dienste nur noch über VPN erreichbar
---
## Übersicht
Dieses Dokument beschreibt die geplante Sicherheitshärtung für Backend-Dienste, sodass diese nur noch über WireGuard VPN erreichbar sind.
**Aktueller Zustand:**
- ✅ WireGuard VPN ist installiert und funktionsfähig
- ✅ Backend-Dienste sind öffentlich über Traefik erreichbar
- ✅ VPN-Zugriff funktioniert parallel
**Zielzustand:**
- 🔒 Backend-Dienste nur noch über VPN erreichbar
- 🔒 Öffentlicher Zugriff auf Prometheus, Grafana, Portainer blockiert
- 🔒 SSH-Zugriff weiterhin über normale IP möglich
---
## Betroffene Dienste
Folgende Dienste sollen später nur noch über VPN erreichbar sein:
1. **Prometheus** (Port 9090)
- Aktuell: `http://prometheus.michaelschiemer.de` (öffentlich)
- Zukünftig: Nur `http://10.8.0.1:9090` über VPN
2. **Grafana** (Port 3000)
- Aktuell: `https://grafana.michaelschiemer.de` (öffentlich)
- Zukünftig: Nur `http://10.8.0.1:3000` über VPN
3. **Portainer** (Port 9443)
- Aktuell: `https://portainer.michaelschiemer.de` (öffentlich)
- Zukünftig: Nur `https://10.8.0.1:9443` über VPN
---
## Geplante Implementierungsschritte
### Schritt 1: Traefik-Labels anpassen
**Datei**: `deployment/stacks/monitoring/docker-compose.yml`
Traefik-Router für interne Dienste entfernen oder anpassen:
```yaml
# Entfernen/Anpassen der öffentlichen Traefik-Labels:
labels:
# - "traefik.enable=true"
# - Orchestra: "traefik.http.routers.prometheus.rule=Host(`prometheus.${DOMAIN}`)"
# - Orchestra: "traefik.http.routers.grafana.rule=Host(`grafana.${DOMAIN}`)"
# - Orchestra: "traefik.http.routers.portainer.rule=Host(`portainer.${DOMAIN}`)"
```
### Schritt 2: Firewall-Regeln anpassen
**Option A: UFW-Regeln**
```bash
# Öffentliche Ports für Monitoring blockieren
sudo ufw deny 9090/tcp # Prometheus
sudo ufw deny 3000/tcp # Grafana
sudo ufw deny 9443/tcp # Portainer
# VPN-Zugriff explizit erlauben (falls notwendig)
sudo ufw allow from 10.8.0.0/24 to any port 9090
sudo ufw allow from 10.8.0.0/24 to any port 3000
sudo ufw allow from 10.8.0.0/24 to any port 9443
```
**Option B: Traefik Middleware**
Traefik-Middleware erstellen, die nur VPN-IPs erlaubt:
```yaml
labels:
- "traefik.http.middlewares.vpn-only.ipwhitelist.sourcerange=10.8.0.0/24"
- "traefik.http.routers.prometheus.middlewares=vpn-only"
```
### Schritt 3: Docker-Netzwerk-Konfiguration
**Datei**: `deployment/stacks/monitoring/docker-compose.yml`
Dienste nur auf `app-internal` Netzwerk belassen, nicht auf `traefik-public`:
```yaml
services:
prometheus:
networks:
- app-internal # Nur internes Netzwerk
# - traefik-public # Entfernen
grafana:
networks:
- app-internal
# - traefik-public
portainer:
networks:
- app-internal
# - traefik-public
```
### Schritt 4: Nginx/Traefik-Konfiguration
Traefik-Router entfernen, sodass die Dienste nicht mehr öffentlich erreichbar sind.
---
## Testplan
### Vor der Umsetzung
1. ✅ VPN-Verbindung testen
2. ✅ Zugriff auf Dienste über VPN testen (10.8.0.1: term
3. ✅ Öffentliche URLs dokumentieren (für späteren Vergleich)
### Nach der Umsetzung
1. ✅ Öffentliche URLs sollten nicht mehr erreichbar sein (404/Connection refused)
2. ✅ VPN-Zugriff sollte weiterhin funktionieren
3. ✅ SSH-Zugriff über normale IP sollte weiterhin funktionieren
4. ✅ Hauptanwendung (michaelschiemer.de) sollte weiterhin öffentlich erreichbar sein
---
## Rollback-Plan
Falls Probleme auftreten:
1. Traefik-Labels wieder aktivieren
2. Firewall-Regeln zurücksetzen:
```bash
sudo ufw delete deny 9090/tcp
sudo ufw delete deny 3000/tcp
sudo ufw delete deny 9443/tcp
```
3. Docker-Container neu starten:
```bash
cd deployment/stacks/monitoring
docker compose up -d
```
---
## Zeitplan
**Nicht geplant für sofortige Umsetzung**
Die Härtung kann durchgeführt werden, wenn:
- ✅ WireGuard-VPN stabil läuft
- ✅ Alle notwendigen Clients push
4. **Optional**: Monitoring-Alerts für VPN-Verbindungen
5. **Optional**: Automatische Firewall-Regeln via Ansible
---
## Weitere Überlegungen
### Alternative: Traefik mit VPN-IP-Whitelist
Statt die Dienste komplett aus Traefik zu entfernen, könnte eine Middleware verwendet werden:
```yaml
# Traefik-Middleware für VPN-only
- "traefik.http.middlewares.vpn-only.ipwhitelist.sourcerange=10.8.0.0/24"
- "traefik.http.routers.prometheus.middlewares=vpn-only"
```
**Vorteil**: Konfiguration bleibt in Traefik, einfaches Toggle
**Nachteil**: Traefik muss weiterhin die Requests verarbeiten
### Alternative: Separates Traefik-Instance für VPN
Ein separater Traefik-Container nur für VPN-Dienste:
```yaml
services:
traefik-vpn:
image: traefik:v3.0
networks:
- app-internal
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
command:
- "--api.insecure=false"
- "--providers.docker.network=app-internal"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
```
**Vorteil**: Komplette Trennung von öffentlichen und VPN-Diensten
**Nachteil**: Zusätzliche Ressourcen, komplexere Konfiguration
---
## Dokumentation
Nach der Umsetzung sollte aktualisiert werden:
1. `WIREGUARD-SETUP.md` - Status aktualisieren
2. `production-deployment-guide.md` - Firewall-Konfiguration dokumentieren
3. `README.md` - Hinweise auf VPN-only-Zugriff
---
**Hinweis**: Diese Härtung wird erst durchgeführt, wenn das VPN stabil läuft und alle notwendigen Clients konfiguriert sind.

View File

@@ -0,0 +1,558 @@
# WireGuard VPN Setup - Complete Guide
**Status**: ✅ Active
**Last Updated**: 2025-10-31
**Server**: 94.16.110.151 (michaelschiemer.de)
---
## Übersicht
WireGuard ist ein modernes, schnelles und sicheres VPN-Protokoll, das für den sicheren Zugriff auf interne Services auf dem Production-Server verwendet wird.
**Features:**
- ✅ Schnell und performant
- ✅ Moderne Kryptographie
- ✅ Einfache Konfiguration
- ✅ Native Unterstützung in Linux, macOS, Windows, iOS, Android
- ✅ Automatisierte Installation via Ansible
---
## Server-Informationen
**Server-Endpoint:**
- **Host**: 94.16.110.151
- **Port**: 51820 (UDP)
- **VPN-Netzwerk**: 10.8.0.0/24
- **Server-IP (VPN)**: 10.8.0.1
**Server-Public-Key:**
```
hT3OCWZ6ElX79YdAdexSsZnbWLzRM/5szk+XNEBUaS8=
```
**Wichtige Sicherheitshinweise:**
- ✅ SSH-Zugriff über normale IP bleibt vollständig funktionsfähig
- ✅ WireGuard routet standardmäßig nur das VPN-Netzwerk (10.8.0.0/24)
- ✅ Normale Internet-Routen werden nicht geändert
- ✅ Firewall-Regeln für SSH (Port 22) werden NICHT entfernt oder blockiert
**Zukünftige Sicherheitshärtung (geplant):**
- 🔒 Backend-Dienste (Prometheus, Grafana, Portainer) sollen später nur noch über VPN erreichbar sein
- 🔒 Aktuell sind alle Dienste noch öffentlich erreichbar (für einfachere Einrichtung)
- 🔒 Firewall-Regeln können später angepasst werden, um nur VPN-Zugriff zu erlauben
---
## Installation
### Server-Installation (via Ansible)
Die Server-Installation erfolgt automatisch via Ansible:
```bash
cd deployment/ansible
ansible-playbook -i inventory/production.yml playbooks/setup-wireguard.yml
```
**Was wird installiert:**
- WireGuard und Tools
- Server-Keys (generiert oder vorhandene verwendet)
- Server-Konfiguration
- IP Forwarding aktiviert
- NAT (Masquerading) konfiguriert
- Firewall-Port 51820/udp geöffnet
- WireGuard-Service gestartet
### Client hinzufügen
Um einen neuen Client hinzuzufügen:
```bash
cd deployment/ansible
ansible-playbook -i inventory/production.yml playbooks/add-wireguard-client.yml \
-e "client_name=myclient"
```
**Optionale Parameter:**
- `client_ip`: Spezifische Client-IP (Standard: automatisch berechnet)
- `allowed_ips`: Erlaubte IP-Ranges (Standard: 10.8.0.0/24)
**Beispiel mit spezifischer IP:**
```bash
ansible-playbook -i inventory/production.yml playbooks/add-wireguard-client.yml \
-e "client_name=myclient" \
-e "client_ip=10.8.0.5"
```
---
## Client-Konfiguration
### 1. Config-Datei abrufen
Die Client-Konfigurationsdatei liegt auf dem Server unter:
```
/etc/wireguard/clients/{client_name}.conf
```
**Abrufen der Config-Datei:**
```bash
# Von Ansible Control Machine
scp -i ~/.ssh/production \
deploy@94.16.110.151:/etc/wireguard/clients/development.conf \
~/development.conf
```
Oder direkt auf dem Server:
```bash
ssh -i ~/.ssh/production deploy@94.16.110.151
sudo cat /etc/wireguard/clients/development.conf
```
### 2. WireGuard auf Client installieren
**Linux (Ubuntu/Debian):**
```bash
sudo apt update
sudo apt install wireguard wireguard-tools
```
**Linux (CentOS/RHEL):**
```bash
sudo yum install wireguard-tools
# oder
sudo dnf install wireguard-tools
```
**macOS:**
```bash
brew install wireguard-tools
```
**Windows:**
Download von https://www.wireguard.com/install/
**iOS/Android:**
WireGuard App aus dem App Store/Play Store installieren
### 3. VPN verbinden
**Linux/macOS (Command Line):**
```bash
# Config-Datei kopieren
sudo cp ~/development.conf /etc/wireguard/development.conf
# VPN starten
sudo wg-quick up development
# Status prüfen
sudo wg show
# VPN trennen
sudo wg-quick down development
```
**Linux/macOS (Alternative):**
```bash
# Direkt mit Datei
sudo wg-quick up ~/development.conf
```
**Windows:**
1. WireGuard-App öffnen
2. "Import tunnel(s) from file" wählen
3. `development.conf` Datei auswählen
4. "Activate" klicken
**iOS/Android:**
1. WireGuard-App öffnen
2. QR-Code scannen (falls verfügbar) oder Config-Datei importieren
3. Tunnel aktivieren
### 4. QR-Code generieren
Falls `qrencode` auf dem Server installiert ist, kann ein QR-Code generiert werden:
```bash
# Auf dem Server
qrencode -t ansiutf8 < /etc/wireguard/clients/development.conf
# oder für PNG
qrencode -t png -o /tmp/development-qr.png < /etc/wireguard/clients/development.conf
```
---
## Verbindung testen
Nach dem Verbinden mit dem VPN:
### 1. VPN-Status prüfen
```bash
# Status anzeigen
sudo wg show
# Erwartete Ausgabe:
# interface: development
# public key: <client_public_key>
# private key: (hidden)
# listening port: <random_port>
#
# peer: hT3OCWZ6ElX79YdAdexSsZnbWLzRM/5szk+XNEBUaS8=
# endpoint: 94.16.110.151:51820
# allowed ips: 10.8.0.0/24
# latest handshake: <timestamp>
# transfer: <received>, <sent>
```
### 2. Server-IP pingen
```bash
# Server im VPN-Netzwerk pingen
ping 10.8.0.1
# Sollte erfolgreich sein
```
### 3. VPN-Netzwerk prüfen
```bash
# Routing-Tabelle prüfen
ip route show
# Sollte einen Eintrag für 10.8.0.0/24 über wg0 zeigen
```
### 4. Services über VPN erreichen
Nach erfolgreicher VPN-Verbindung sollten folgende Services über das VPN-Netzwerk erreichbar sein:
- **Prometheus**: http://10.8.0.1:9090
- **Grafana**: http://10.8.0.1:3000
- **Portainer**: https://10.8.0.1:9443
**Hinweis**:
- ⚠️ Aktuell sind diese Services auch öffentlich erreichbar (Traefik-Routing)
- 🔒 **Geplant**: Später sollen alle Backend-Dienste nur noch über VPN erreichbar sein
- ✅ Für die Einrichtung bleibt der öffentliche Zugriff vorerst aktiviert
---
## Client-Verwaltung
### Client-Liste anzeigen
```bash
# Auf dem Server
sudo wg show
# Zeigt alle verbundenen Clients
```
### Client-Config-Dateien anzeigen
```bash
# Auf dem Server
ls -la /etc/wireguard/clients/
```
### Client entfernen
Um einen Client zu entfernen:
```bash
# Auf dem Server
sudo nano /etc/wireguard/wg0.conf
# Entferne den [Peer] Block für den Client
# WireGuard neu starten
sudo wg-quick down wg0
sudo wg-quick up wg0
# Optional: Client-Config löschen
sudo rm /etc/wireguard/clients/clientname.conf
```
Oder über Ansible (manuelle Datei-Bearbeitung erforderlich):
```bash
# 1. Client aus /etc/wireguard/wg0.conf entfernen
# 2. WireGuard-Service neu starten:
cd deployment/ansible
ansible production -m shell -a "sudo wg-quick down wg0 && sudo wg-quick up wg0"
```
---
## Troubleshooting
### VPN verbindet nicht
**1. Firewall prüfen:**
```bash
# Auf dem Server
sudo ufw status | grep 51820
# Sollte zeigen: 51820/udp ALLOW
# Falls nicht:
sudo ufw allow 51820/udp comment 'WireGuard VPN'
```
**2. WireGuard-Service prüfen:**
```bash
# Auf dem Server
sudo systemctl status wg-quick@wg0
# Logs anzeigen
sudo journalctl -u wg-quick@wg0 -f
```
**3. Server-Konfiguration prüfen:**
```bash
# Auf dem Server
sudo wg show
sudo cat /etc/wireguard/wg0.conf
```
**4. Client-Config prüfen:**
- Endpoint korrekt? (94.16.110.151:51820)
- Server-Public-Key korrekt?
- Client-Private-Key vorhanden?
- AllowedIPs korrekt?
**5. Netzwerk-Verbindung prüfen:**
```bash
# Vom Client aus
ping 94.16.110.151
# Sollte erfolgreich sein
# UDP-Port testen
nc -u -v 94.16.110.151 51820
```
### VPN verbindet, aber kein Traffic
**1. IP Forwarding prüfen:**
```bash
# Auf dem Server
cat /proc/sys/net/ipv4/ip_forward
# Sollte "1" sein
# Falls nicht:
echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward
echo 'net.ipv4.ip_forward=1' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
```
**2. NAT-Rules prüfen:**
```bash
# Auf dem Server
sudo iptables -t nat -L -n | grep MASQUERADE
sudo iptables -L FORWARD -n | grep wg0
```
**3. Routing prüfen:**
```bash
# Auf dem Server
ip route show
# Sollte Routes für wg0 zeigen
# Vom Client
ip route show
# Sollte Route für 10.8.0.0/24 über VPN zeigen
```
### SSH-Zugriff funktioniert nicht mehr
**WICHTIG**: SSH-Zugriff sollte weiterhin über die normale IP funktionieren!
```bash
# SSH über normale IP (nicht VPN)
ssh -i ~/.ssh/production deploy@94.16.110.151
# Falls nicht funktioniert:
# 1. Prüfe, ob VPN alle Traffic routet (AllowedIPs = 0.0.0.0/0)
# 2. Deaktiviere VPN: sudo wg-quick down development
# 3. Prüfe Firewall: sudo ufw status | grep 22
```
### Performance-Probleme
**1. MTU anpassen:**
```bash
# In Client-Config hinzufügen:
# [Interface]
# MTU = 1420
```
**2. PersistKeepalive anpassen:**
```bash
# In Client-Config bereits konfiguriert:
# PersistentKeepalive = 25
```
**3. Server-Last prüfen:**
```bash
# Auf dem Server
sudo wg show
# Zeigt Transfer-Statistiken
```
---
## Best Practices
### Sicherheit
1. **Backup der Server-Keys:**
```bash
# Auf dem Server
sudo tar czf wireguard-backup.tar.gz /etc/wireguard/
# Sicher speichern (z.B. verschlüsselt)
```
2. **Client-Keys sicher verwalten:**
- Client-Private-Keys niemals teilen
- Config-Dateien sicher speichern
- Nicht genutzte Clients entfernen
3. **Regelmäßige Updates:**
```bash
# Auf dem Server
sudo apt update && sudo apt upgrade wireguard wireguard-tools
```
4. **Firewall-Regeln:**
- Nur notwendige Ports öffnen
- Regelmäßig Firewall-Status prüfen
### Monitoring
1. **VPN-Verbindungen überwachen:**
```bash
# Auf dem Server
sudo wg show
```
2. **Logs prüfen:**
```bash
# Auf dem Server
sudo journalctl -u wg-quick@wg0 -f
```
3. **Transfer-Statistiken:**
```bash
# Auf dem Server
sudo wg show
# Zeigt received/sent für jeden Client
```
---
## Ansible Playbooks
### Setup WireGuard Server
**Datei**: `deployment/ansible/playbooks/setup-wireguard.yml`
**Verwendung:**
```bash
cd deployment/ansible
ansible-playbook -i inventory/production.yml playbooks/setup-wireguard.yml
```
**Variablen:**
- `wireguard_port`: Port für WireGuard (Standard: 51820)
- `wireguard_network`: VPN-Netzwerk (Standard: 10.8.0.0/24)
- `wireguard_server_ip`: Server-IP im VPN (Standard: 10.8.0.1)
### Add WireGuard Client
**Datei**: `deployment/ansible/playbooks/add-wireguard-client.yml`
**Verwendung:**
```bash
cd deployment/ansible
ansible-playbook -i inventory/production.yml playbooks/add-wireguard-client.yml \
-e "client_name=myclient"
```
**Variablen:**
- `client_name`: Name des Clients (erforderlich)
- `client_ip`: Spezifische Client-IP (Standard: automatisch berechnet)
- `allowed_ips`: Erlaubte IP-Ranges (Standard: 10.8.0.0/24)
**Dokumentation**: Siehe `deployment/ansible/playbooks/README-WIREGUARD.md`
---
## Verzeichnisstruktur
**Auf dem Server:**
```
/etc/wireguard/
├── wg0.conf # Server-Konfiguration
├── wg0_private.key # Server-Private-Key (600)
├── wg0_public.key # Server-Public-Key (644)
└── clients/ # Client-Konfigurationen
├── development.conf # Development-Client
└── ...
```
**Auf dem Client (Linux/macOS):**
```
/etc/wireguard/
└── development.conf # Client-Konfiguration (600)
```
---
## Support
Bei Problemen:
1. **Logs prüfen:**
```bash
sudo journalctl -u wg-quick@wg0 -f
```
2. **Status prüfen:**
```bash
sudo wg show
```
3. **Firewall prüfen:**
```bash
sudo ufw status
```
4. **Connectivity testen:**
```bash
ping 10.8.0.1 # Vom Client
```
5. **Ansible Playbook erneut ausführen:**
```bash
cd deployment/ansible
ansible-playbook -i inventory/production.yml playbooks/setup-wireguard.yml
```
---
## Referenzen
- **WireGuard-Website**: https://www.wireguard.com/
- **WireGuard-Installation**: https://www.wireguard.com/install/
- **WireGuard-Dokumentation**: https://www.wireguard.com/papers/wireguard.pdf
- **Ansible Playbook-Dokumentation**: `deployment/ansible/playbooks/README-WIREGUARD.md`
---
**Zuletzt aktualisiert**: 2025-10-31
**Version**: 1.0

View File

@@ -1 +0,0 @@
// Test file to trigger production-deploy workflow