# Ansible Deployment Configuration

This directory contains Ansible playbooks and configuration for deploying the Custom PHP Framework to production.

## Directory Structure

```
deployment/ansible/
├── ansible.cfg                   # Ansible configuration
├── inventory/
│   ├── production.yml         # Production server inventory
│   └── local.yml              # Local testing inventory
├── playbooks/
│   ├── deploy-update.yml            # Deploy application updates
│   ├── system-maintenance.yml       # Betriebssystem-Updates & Wartung
│   ├── rollback.yml                 # Rollback deployments
│   ├── setup-infrastructure.yml     # Provision core stacks
│   ├── setup-production-secrets.yml # Deploy secrets
│   ├── setup-wireguard.yml          # Setup WireGuard VPN server
│   ├── add-wireguard-client.yml     # Add WireGuard client
│   ├── sync-code.yml                # Git-based code sync
│   └── README-WIREGUARD.md          # WireGuard documentation
├── scripts/                     # Helper scripts for secrets & credentials
├── roles/                       # Reusable roles (e.g. application stack)
├── secrets/
│   ├── .gitignore                   # Prevent committing secrets
│   └── production.vault.yml.example # Example vault file
└── templates/
    ├── application.env.j2          # Application stack environment
    ├── gitea-app.ini.j2            # Gitea configuration
    ├── minio.env.j2                # MinIO environment
    ├── monitoring.env.j2           # Monitoring stack environment
    ├── wireguard-client.conf.j2    # WireGuard client config
    └── wireguard-server.conf.j2    # WireGuard server config

## Roles

Stack-spezifische Aufgaben liegen in `roles/` (z. B. `application`, `traefik`, `registry`). Playbooks wie `setup-infrastructure.yml` importieren diese Rollen direkt. Die Application-Rolle kann mit Variablen wie `application_sync_files=false` oder `application_compose_recreate="always"` konfiguriert werden (siehe `playbooks/deploy-update.yml` als Beispiel). Die neue `system`-Rolle hält Betriebssystem-Pakete aktuell und konfiguriert optionale Unattended-Upgrades bevor Docker-Stacks neu gestartet werden.

## Prerequisites

1. **Ansible Installed**:
   ```bash
   pip install ansible
   ```

2. **SSH Access**:
   - SSH key configured at `~/.ssh/production`
   - Key added to production server's authorized_keys for `deploy` user

3. **Ansible Vault Password**:
   - Create `.vault_pass` file in `secrets/` directory
   - Add vault password to this file (one line)
   - File is gitignored for security
   - 📖 **Detaillierte Dokumentation:** [docs/guides/vault-password.md](../docs/guides/vault-password.md)

## Setup Instructions

### 1. Create Production Secrets

```bash
cd deployment/ansible/secrets

# Copy example file
cp production.vault.yml.example production.vault.yml

# Edit with your actual secrets
nano production.vault.yml

# Encrypt the file
ansible-vault encrypt production.vault.yml
# Enter vault password when prompted
```

### 2. Store Vault Password

```bash
# Create vault password file
echo "your-vault-password-here" > secrets/.vault_pass

# Secure the file
chmod 600 secrets/.vault_pass
```

**📖 Für detaillierte Informationen:** Siehe [docs/guides/vault-password.md](../docs/guides/vault-password.md)

### 3. Configure SSH Key

```bash
# Generate SSH key if needed
ssh-keygen -t ed25519 -f ~/.ssh/production -C "ansible-deploy"

# Copy public key to production server
ssh-copy-id -i ~/.ssh/production.pub deploy@94.16.110.151
```

## Running Playbooks

### Deploy Production Secrets

**First-time setup** - Deploy secrets to production server:

```bash
ansible-playbook playbooks/setup-production-secrets.yml \
  --vault-password-file secrets/.vault_pass
```

### Deploy Application Update

**Automated via Gitea Actions** - Or run manually:

```bash
ansible-playbook playbooks/deploy-update.yml \
  -e "image_tag=sha-abc123" \
  -e "git_commit_sha=abc123" \
  -e "deployment_timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
  -e "docker_registry_username=gitea-user" \
  -e "docker_registry_password=your-registry-password"
```

### Rollback Deployment

**Rollback to previous version**:

```bash
ansible-playbook playbooks/rollback.yml
```

**Rollback to specific version**:

```bash
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.

### System Maintenance ausführen

Führt die `system`-Rolle aus, aktualisiert Paketquellen, führt OS-Upgrades durch und aktiviert optional Unattended-Upgrades.

```bash
ansible-playbook -i inventory/production.yml \
  playbooks/system-maintenance.yml
```

Tipp: Mit `--check` lässt sich zunächst ein Dry-Run starten, um anstehende Updates zu prüfen.

## Ansible Vault Operations

### View Encrypted File

```bash
ansible-vault view secrets/production.vault.yml \
  --vault-password-file secrets/.vault_pass
```

### Edit Encrypted File

```bash
ansible-vault edit secrets/production.vault.yml \
  --vault-password-file secrets/.vault_pass
```

### Change Vault Password

```bash
ansible-vault rekey secrets/production.vault.yml \
  --vault-password-file secrets/.vault_pass
```

### Decrypt File (Temporarily)

```bash
ansible-vault decrypt secrets/production.vault.yml \
  --vault-password-file secrets/.vault_pass

# DO NOT COMMIT DECRYPTED FILE!

# Re-encrypt when done
ansible-vault encrypt secrets/production.vault.yml \
  --vault-password-file secrets/.vault_pass
```

## Testing Playbooks

### Test with Check Mode (Dry Run)

```bash
ansible-playbook playbooks/deploy-update.yml \
  --check \
  -e "image_tag=test"
```

### Test Connection

```bash
ansible production -m ping
```

### Verify Inventory

```bash
ansible-inventory --list -y
```

### System Maintenance Dry Run

```bash
ansible-playbook -i inventory/production.yml \
  playbooks/system-maintenance.yml \
  --check
```

## Security Best Practices

1. **Never commit unencrypted secrets**
   - `production.vault.yml` must be encrypted
   - `.vault_pass` is gitignored
   - Use `.example` files for documentation

2. **Rotate secrets regularly**
   - Update vault file
   - Re-run `setup-production-secrets.yml`
   - Restart affected services

3. **Limit SSH key access**
   - Use separate SSH key for Ansible
   - Limit key to `deploy` user only
   - Consider IP restrictions

4. **Vault password security**
   - Store vault password in secure password manager
   - Don't share via insecure channels
   - Use different passwords for dev/staging/prod

## Troubleshooting

### Vault Decryption Failed

**Error**: `Decryption failed (no vault secrets were found)`

**Solution**:
```bash
# Verify vault password is correct
ansible-vault view secrets/production.vault.yml \
  --vault-password-file secrets/.vault_pass

# If password is wrong, you'll need the original password to decrypt
```

### SSH Connection Failed

**Error**: `Failed to connect to the host`

**Solutions**:
```bash
# Test SSH connection manually
ssh -i ~/.ssh/production deploy@94.16.110.151

# Check SSH key permissions
chmod 600 ~/.ssh/production
chmod 644 ~/.ssh/production.pub

# Verify SSH key is added to server
ssh-copy-id -i ~/.ssh/production.pub deploy@94.16.110.151
```

### Docker Registry Authentication Failed

**Error**: `unauthorized: authentication required`

**Solution**:
```bash
# Verify registry credentials in vault file
ansible-vault view secrets/production.vault.yml \
  --vault-password-file secrets/.vault_pass

# Test registry login manually on production server
docker login registry.michaelschiemer.de
```

### Service Not Starting

**Check service logs**:
```bash
# SSH to production server
ssh -i ~/.ssh/production deploy@94.16.110.151

# Check Docker Compose service logs
docker compose -f {{ app_stack_path }}/docker-compose.yml logs app
docker compose -f {{ app_stack_path }}/docker-compose.yml logs nginx

# Check stack status
docker compose -f {{ app_stack_path }}/docker-compose.yml ps
```

## CI/CD Integration

These playbooks are automatically executed by Gitea Actions workflows:

- **`.gitea/workflows/production-deploy.yml`** - Calls `deploy-update.yml` on push to main
- **`.gitea/workflows/update-production-secrets.yml`** - Calls `setup-production-secrets.yml` on manual trigger
- **`.gitea/workflows/system-maintenance.yml`** - Führt `system-maintenance.yml` geplant oder manuell aus, um Pakete aktuell zu halten

Vault password is stored as Gitea Actions secret: `ANSIBLE_VAULT_PASSWORD`

## Inventory Variables

All zentralen Variablen werden in `group_vars/production.yml` gepflegt und können bei Bedarf im Inventory überschrieben werden. Häufig verwendete Werte:

| Variable | Beschreibung | Standardwert |
|----------|--------------|--------------|
| `deploy_user_home` | Home-Verzeichnis des Deploy-Users | `/home/deploy` |
| `stacks_base_path` | Basispfad für Docker Compose Stacks | `/home/deploy/deployment/stacks` |
| `app_stack_path` | Pfad zum Application Stack | `/home/deploy/deployment/stacks/application` |
| `backups_path` | Ablageort für Deployment-Backups | `/home/deploy/deployment/backups` |
| `docker_registry` | Interner Registry-Endpunkt (lokal) | `localhost:5000` |
| `docker_registry_external` | Externer Registry-Endpunkt | `registry.michaelschiemer.de` |
| `app_domain` | Produktions-Domain | `michaelschiemer.de` |
| `health_check_url` | Health-Check Endpoint | `https://michaelschiemer.de/health` |
| `max_rollback_versions` | Anzahl vorgehaltener Backups | `5` |
| `system_update_packages` | Aktiviert OS-Paketupdates via `system`-Rolle | `true` |
| `system_apt_upgrade` | Wert für `apt upgrade` (z. B. `dist`) | `dist` |
| `system_enable_unattended_upgrades` | Aktiviert `unattended-upgrades` | `true` |
| `system_enable_unattended_reboot` | Steuert automatische Reboots nach Updates | `false` |
| `system_unattended_reboot_time` | Reboot-Zeitfenster (wenn aktiviert) | `02:00` |
| `system_enable_unattended_timer` | Aktiviert Systemd-Timer für apt | `true` |
| `system_enable_docker_prune` | Führt nach Updates `docker system prune` aus | `false` |

## Backup Management

Backups are automatically created before each deployment:

- **Location**: `/home/deploy/backups/`
- **Retention**: Last 5 versions kept
- **Contents**:
  - `current_image.txt` - Previously deployed image
  - `stack_status.txt` - Stack status before deployment
  - `deployment_metadata.txt` - Deployment details

### List Available Backups

```bash
ssh -i ~/.ssh/production deploy@94.16.110.151 \
  "ls -lh /home/deploy/backups/"
```

### Manual Backup

```bash
ansible-playbook playbooks/deploy-update.yml \
  --tags backup \
  -e "image_tag=current"
```

## Support

For issues with:
- **Playbooks**: Check this README and playbook comments
- **Vault**: See Ansible Vault documentation
- **Deployment**: Review Gitea Actions logs
- **Production**: SSH to server and check Docker logs
