chore: remove test trigger file
This commit is contained in:
@@ -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
|
||||
|
||||
284
deployment/ansible/playbooks/README-WIREGUARD.md
Normal file
284
deployment/ansible/playbooks/README-WIREGUARD.md
Normal 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)
|
||||
169
deployment/ansible/playbooks/add-wireguard-client.yml
Executable file
169
deployment/ansible/playbooks/add-wireguard-client.yml
Executable 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
|
||||
@@ -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:
|
||||
|
||||
@@ -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') }}"
|
||||
|
||||
294
deployment/ansible/playbooks/setup-wireguard.yml
Executable file
294
deployment/ansible/playbooks/setup-wireguard.yml
Executable 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
|
||||
@@ -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
|
||||
|
||||
40
deployment/ansible/templates/application.env.j2
Normal file
40
deployment/ansible/templates/application.env.j2
Normal 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') }}
|
||||
@@ -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('$', '$$') }}
|
||||
27
deployment/ansible/templates/wireguard-client.conf.j2
Normal file
27
deployment/ansible/templates/wireguard-client.conf.j2
Normal 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
|
||||
22
deployment/ansible/templates/wireguard-server.conf.j2
Normal file
22
deployment/ansible/templates/wireguard-server.conf.j2
Normal 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
|
||||
Reference in New Issue
Block a user