chore: lots of changes

This commit is contained in:
2025-05-24 07:09:22 +02:00
parent 77ee769d5e
commit 899227b0a4
178 changed files with 5145 additions and 53 deletions

View File

@@ -0,0 +1,10 @@
[Interface]
PrivateKey = +DcT11ipmMwPXpzEqmCPGwy7cSmseG1YzZWk+tTtM30=
Address = 10.8.0.2/32
DNS = 1.1.1.1
[Peer]
PublicKey = 3qFEUREx6VfqrKoGVtzHt2ojgaly7LvwxjPQPNsFyxM=
Endpoint = 94.16.110.151:51820
AllowedIPs = 10.8.0.0/24, 94.16.110.151/32
PersistentKeepalive = 25

View File

@@ -1,4 +0,0 @@
- hosts: web
become: false
roles:
- deploy

View File

@@ -0,0 +1,39 @@
# Basis-Konfiguration
app_name: michaelschiemer
app_domain: test.michaelschiemer.de
app_email: kontakt@michaelschiemer.de
# Verzeichnisse
project_root: "{{ playbook_dir | dirname }}"
app_root: /var/www/{{ app_name }}
app_public: "{{ app_root }}/public"
# Docker
docker_version: "20.10"
docker_compose_version: "2.24.5"
# Benutzer
deploy_user: deploy
# Let's Encrypt
letsencrypt_enabled: true
letsencrypt_certbot_method: webroot # oder standalone oder nginx
#netcup_customer_id: "218722"
#netcup_api_key: "dmJINUMyNjRmOG1aNDViajZHN2JkOTFRUjU3ckE5ZjJ1Zm1vUz"
#netcup_api_password: "iGWL8Hl4m93DgESsP/MPXmtDd0hEVkZ3480Na0psTlXRALnopl"
#netcup_vserver_id: "v2202309206672239295"
# fallback_ip:
wg_all_clients_private_keys:
michael: "PITbFZ3UfY5vD5dYUCELO37Qo2W8I4R8+r6D9CeMrm4="
wireguard_clients:
- name: michael
address: 10.8.0.2
public_key: DEIN_PUBLIC_KEY

View File

@@ -0,0 +1,4 @@
wg_privkey: "HIER_DEIN_PRIVATER_KEY_ODER_DATEIPFAD"
wg_all_clients_private_keys:
michael: "PITbFZ3UfY5vD5dYUCELO37Qo2W8I4R8+r6D9CeMrm4="

View File

View File

@@ -1,2 +1,8 @@
#[web]
#localhost ansible_connection=local
[web]
localhost ansible_connection=local
94.16.110.151 ansible_user=deploy ansible_ssh_private_key_file=/mnt/c/Users/Mike/.ssh/test.michaelschiemer.de
[vpn]
94.16.110.151 ansible_user=deploy

View File

@@ -0,0 +1,12 @@
[localhost]
127.0.0.1 ansible_connection=local
[staging]
94.16.110.151 ansible_user=deploy ansible_ssh_private_key_file=/mnt/c/Users/Mike/.ssh/test.michaelschiemer.de
[production]
[vpn]
94.16.110.151 ansible_user=deploy

View File

@@ -0,0 +1,40 @@
#- name: Check ob /ping erreichbar ist
# uri:
# url: "http://localhost/ping"
# status_code: 200
# return_content: yes
# register: ping_response
#
#- debug:
# var: ping_response.content
- name: Healthcheck nach dem Deployment
hosts: localhost
connection: local
gather_facts: false
become: false
vars:
healthcheck_url: "http://127.0.0.1:8080/ping"
max_retries: 10
delay_between_retries: 3
tasks:
- name: Warte, bis der Webserver erreichbar ist
uri:
url: "{{ healthcheck_url }}"
status_code: 200
return_content: yes
register: healthcheck_response
retries: "{{ max_retries }}"
delay: "{{ delay_between_retries }}"
until: >
healthcheck_response is defined and
healthcheck_response.status is defined and
healthcheck_response.status == 200
failed_when: healthcheck_response.status != 200
ignore_errors: false
- name: Ausgabe des Healthcheck-Resultats
debug:
msg: "Healthcheck erfolgreich: {{ healthcheck_response.content }}"

View File

@@ -0,0 +1,24 @@
- name: Deployment in jeweilige Umgebung
hosts: all
become: true
gather_facts: false
vars:
docker_compose_project_path: "/var/www/michaelschiemer/"
env_file_path: "/var/www/michaelschiemer/.env"
deploy_root: /var/www/michaelschiemer
deploy_public: "{{ deploy_root }}/public"
deploy_user: deploy
app_domain: "example.com" # Passe ggf. an
project_root: "{{ playbook_dir }}/../.."
roles:
- app
- nginx
- php
- redis

View File

@@ -0,0 +1,26 @@
- name: Deployment für DEV (localhost)
hosts: localhost
become: true
gather_facts: false
vars:
docker_compose_project_path: "/home/michael/dev/michaelschiemer"
env_file_path: "/var/www/michaelschiemer/.env"
deploy_root: /var/www/michaelschiemer
deploy_public: "{{ deploy_root }}/public"
deploy_user: deploy
app_domain: "localhost" # Passe ggf. an
project_root: "/home/michael/dev/michaelschiemer"
roles:
#- app
- nginx
- php
- redis
tasks:
- name: Common Deployment Tasks
import_tasks: ../deploy/includes/deploy_common.yml

View File

@@ -0,0 +1,29 @@
- name: Docker Compose Files & Konfigurationen synchronisieren
ansible.builtin.copy:
src: "{{ item.src }}"
dest: "{{ docker_compose_project_path }}/{{ item.dest }}"
owner: root
group: root
mode: 0644
loop:
- { src: '{{ project_root }}/docker-compose.yml', dest: 'docker-compose.yml' }
- { src: '{{ project_root }}/.env', dest: '.env' }
# Weitere Konfigdateien nach Bedarf (z.B. nginx.conf, redis.conf, ...)
- { src: '{{ project_root }}/docker/nginx/nginx.conf', dest: 'nginx.conf' }
- name: "Docker Compose: Container hochfahren (Build & Start)"
ansible.builtin.command: |
docker-compose -f {{ docker_compose_project_path }}/docker-compose.yml up -d --build
args:
chdir: "{{ docker_compose_project_path }}"
- name: Status prüfen
ansible.builtin.command: |
docker-compose -f {{ docker_compose_project_path }}/docker-compose.yml ps
args:
chdir: "{{ docker_compose_project_path }}"
register: compose_ps
- name: Ergebnis anzeigen
ansible.builtin.debug:
var: compose_ps.stdout_lines

View File

@@ -0,0 +1,23 @@
- name: Deployment für PRODUCTION
hosts: production
become: true
gather_facts: false
vars:
docker_compose_project_path: "/var/www/www.michaelschiemer.de/"
env_file_path: "/var/www/www.michaelschiemer.de/.env"
deploy_root: /var/www/www.michaelschiemer.de
deploy_public: "{{ deploy_root }}/public"
deploy_user: deploy
app_domain: "michaelschiemer.de"
project_root: "{{ playbook_dir }}/../.."
roles:
- app
- nginx
- php
- redis
tasks:
- name: Common Deployment Tasks
import_tasks: ../deploy/includes/deploy_common.yml

View File

@@ -0,0 +1,23 @@
- name: Deployment für STAGING
hosts: staging
become: true
gather_facts: false
vars:
docker_compose_project_path: "/var/www/stage.michaelschiemer/"
env_file_path: "/var/www/stage.michaelschiemer/.env"
deploy_root: /var/www/stage.michaelschiemer
deploy_public: "{{ deploy_root }}/public"
deploy_user: deploy
app_domain: "stage.example.com"
project_root: "{{ playbook_dir }}/../.."
roles:
- app
- nginx
- php
- redis
tasks:
- name: Common Deployment Tasks
import_tasks: ../deploy/includes/deploy_common.yml

View File

@@ -0,0 +1,11 @@
---
- name: Basis Setup für alle Zielsysteme
hosts: all
become: true
#gather_facts: true
roles:
#- common
- docker
#- webserver
#- app

View File

@@ -0,0 +1,6 @@
- hosts: web
become: true
gather_facts: true
roles:
- console

View File

@@ -0,0 +1,7 @@
# ansible/wireguard.yml
- hosts: vpn
become: false
gather_facts: false
roles:
- wireguard

View File

@@ -0,0 +1,134 @@
- name: Zielverzeichnis erstellen
file:
path: "{{ deploy_root }}"
state: directory
owner: "{{ deploy_user }}"
group: "{{ deploy_user }}"
mode: '0755'
- name: SSL-Verzeichnis sicherstellen
file:
path: "{{ deploy_root }}/ssl"
state: directory
owner: "{{ deploy_user }}"
group: "{{ deploy_user }}"
mode: '0755'
- name: SSL-Zertifikate prüfen
stat:
path: "/etc/letsencrypt/live/{{ app_domain }}/fullchain.pem"
register: ssl_certs
- name: SSL-Zertifikate kopieren (falls vorhanden)
copy:
src: "{{ item.src }}"
dest: "{{ item.dest }}"
remote_src: yes
owner: "{{ deploy_user }}"
group: "{{ deploy_user }}"
mode: '0644'
loop:
- { src: "/etc/letsencrypt/live/{{ app_domain }}/fullchain.pem", dest: "{{ deploy_root }}/ssl/fullchain.pem" }
- { src: "/etc/letsencrypt/live/{{ app_domain }}/privkey.pem", dest: "{{ deploy_root }}/ssl/privkey.pem" }
when: ssl_certs.stat.exists
- name: public-Verzeichnis synchronisieren
synchronize:
src: "{{ playbook_dir }}/../../public/"
dest: "{{ deploy_public }}/"
delete: yes
recursive: yes
- name: Projekt-Stammdaten kopieren
copy:
src: "{{ playbook_dir }}/../../docker-compose.yml"
dest: "{{ deploy_root }}/docker-compose.yml"
owner: "{{ deploy_user }}"
group: "{{ deploy_user }}"
mode: '0644'
- name: .env-Datei prüfen
stat:
path: "{{ project_root }}/.env"
register: env_file
- name: .env kopieren (falls vorhanden)
copy:
src: "{{ project_root }}/.env"
dest: "{{ deploy_root }}/.env"
mode: '0644'
when: env_file.stat.exists
- name: Quellcode synchronisieren
synchronize:
src: "{{ playbook_dir }}/../../src/"
dest: "{{ deploy_root }}/src/"
delete: yes
recursive: yes
- name: Docker-Verzeichnis prüfen
stat:
path: "{{ project_root }}/docker"
register: docker_dir
delegate_to: localhost
become: false
- name: Docker-Configs synchronisieren (falls vorhanden)
synchronize:
src: "{{ project_root }}/docker/"
dest: "{{ deploy_root }}/docker/"
delete: yes
recursive: yes
when: docker_dir.stat.exists
- name: Rechte im Zielverzeichnis korrigieren
file:
path: "{{ deploy_root }}"
state: directory
owner: "{{ deploy_user }}"
group: "{{ deploy_user }}"
mode: '0755'
recurse: yes
# Cache-Verzeichnis für UID/GID 1000 (z.B. appuser im Container)
- name: Stelle Schreibrechte für Cache-Verzeichnis her
file:
path: "{{ deploy_root }}/cache"
state: directory
owner: 1000
group: 1000
mode: '0775'
recurse: yes
- name: Docker Compose neu bauen und starten
shell: |
docker compose down
docker compose up -d --build
args:
chdir: "{{ deploy_root }}"
- name: PHP-Container für Composer starten
shell: docker compose up -d php
args:
chdir: "{{ deploy_root }}"
- name: Kurze Wartezeit bis PHP-Container bereit
wait_for:
timeout: 5
- name: Composer Abhängigkeiten installieren
shell: docker compose exec -T php composer install --no-interaction
args:
chdir: "{{ deploy_root }}"
register: composer_result
ignore_errors: yes
- name: Composer-Ergebnis anzeigen
debug:
var: composer_result.stdout_lines
when: composer_result.stdout is defined
- name: Composer-Fehler anzeigen
debug:
var: composer_result.stderr_lines
when: composer_result.stderr is defined

View File

@@ -0,0 +1,26 @@
# Grundlegende Systemkonfiguration
- name: Basis-Pakete aktualisieren und installieren
apt:
name:
- sudo
- vim
- htop
- git
- zip
- unzip
- curl
- wget
state: present
update_cache: yes
become: true
# Passwordless sudo für den deploy-Benutzer einrichten
- name: Konfiguriere passwordless sudo für deploy-Benutzer
lineinfile:
path: "/etc/sudoers.d/{{ deploy_user }}"
line: "{{ deploy_user }} ALL=(ALL) NOPASSWD: ALL"
state: present
create: yes
mode: '0440'
validate: 'visudo -cf %s'
become: true

View File

@@ -0,0 +1,9 @@
- name: Füge Funktion für ms (mit Argumenten) hinzu
blockinfile:
path: "/home/{{ ansible_user }}/.bashrc"
marker: "# {mark} ms docker alias"
block: |
ms() {
docker compose exec php php ms "$@"
}
become: false

View File

@@ -0,0 +1,9 @@
# Rolle: Docker
Diese Rolle installiert Docker Engine, CLI, Compose-Plugin sowie (optional) Docker Compose V1 als Fallback.
- Fügt den gewünschten User zur Docker-Gruppe hinzu.
- Startet und aktiviert den Docker-Dienst.
## Variablen
- `docker_compose_version`: Version von Docker Compose V1 für Fallback (Standard: 1.29.2).
- `docker_user`: Benutzer, der in die Gruppe `docker` aufgenommen werden soll (Standard: aktueller Ansible-User).

View File

@@ -0,0 +1,3 @@
docker_compose_version: "v2.29.2"
docker_install_compose: true
docker_user: "{{ ansible_user || default('michael' }}"

View File

@@ -0,0 +1,4 @@
- name: restart docker
ansible.builtin.service:
name: docker
state: restarted

View File

@@ -0,0 +1,58 @@
- name: Docker-Abhängigkeiten installieren
apt:
name:
- apt-transport-https
- ca-certificates
- curl
- gnupg
- lsb-release
state: present
update_cache: yes
- name: Docker GPG-Schlüssel hinzufügen
apt_key:
url: https://download.docker.com/linux/{{ ansible_distribution | lower }}/gpg
state: present
- name: Docker Repository hinzufügen
apt_repository:
repo: "deb [arch=amd64] https://download.docker.com/linux/{{ ansible_distribution | lower }} {{ ansible_distribution_release }} stable"
state: present
- name: Docker Engine installieren
apt:
name:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-compose-plugin
state: present
update_cache: yes
- name: Docker Compose installieren (V1 als Fallback)
get_url:
url: "https://github.com/docker/compose/releases/download/v{{ docker_compose_version }}/docker-compose-linux-x86_64"
dest: /usr/local/bin/docker-compose
mode: '0755'
- name: Benutzer zur Docker-Gruppe hinzufügen
user:
name: "{{ ansible_user }}"
groups: docker
append: yes
- name: Docker-Service starten und aktivieren
service:
name: docker
state: started
enabled: yes
notify: restart docker
- name: Starte Docker-Container via Compose
community.docker.docker_compose_v2:
#project_src: "{{ playbook_dir | dirname }}/../" # ggf. anpassen auf deinen Compose-Pfad!
project_src: "{{ app_root }}"
build: always
recreate: always

View File

@@ -0,0 +1,5 @@
nginx_conf_template: nginx.conf.j2
nginx_default_site_template: default.conf.j2
nginx_ssl_src_dir: "{{ app_root }}/ssl"
nginx_ssl_dest_dir: "/var/www/michaelschiemer/ssl"
nginx_target_dir: "/var/www/michaelschiemer/docker/nginx"

View File

@@ -0,0 +1,2 @@
- name: reload nginx
ansible.builtin.command: docker exec <nginx_container_name> nginx -s reload

View File

@@ -0,0 +1,37 @@
- name: Stelle das nginx-Verzeichnis sicher
ansible.builtin.file:
path: "{{ nginx_target_dir }}"
state: directory
recurse: yes
mode: '0755'
- name: Kopiere nginx-Konfiguration (nginx.conf)
ansible.builtin.template:
src: "{{ nginx_conf_template }}"
dest: "{{ nginx_target_dir }}/nginx.conf"
mode: '0644'
- name: Kopiere default site conf
ansible.builtin.template:
src: "{{ nginx_default_site_template }}"
dest: "{{ nginx_target_dir }}/default.conf"
mode: '0644'
- name: Kopiere docker-entrypoint Skript
ansible.builtin.copy:
src: docker-entrypoint.sh
dest: "{{ nginx_target_dir }}/docker-entrypoint.sh"
mode: '0755'
- name: Baue und starte Nginx-Container (optional, wenn Compose separat genutzt wird, dann hier nicht nötig)
ansible.builtin.command: docker-compose up -d --build web
args:
chdir: "{{ docker_compose_project_path }}"
when: nginx_target_dir is defined
register: nginx_compose_result
ignore_errors: true
- name: Zeige Compose-Resultat
ansible.builtin.debug:
var: nginx_compose_result.stdout_lines
when: nginx_compose_result is defined

View File

@@ -0,0 +1,4 @@
- name: Reload nginx
service:
name: nginx
state: reloaded

View File

@@ -1,3 +1,47 @@
- name: Test-Task Setup-Rolle lokal
debug:
msg: "Setup-Rolle ist vorbereitet echte Installation folgt auf Server."
- name: Docker installieren
apt:
name:
- docker.io
- docker-compose
state: present
update_cache: yes
- name: Certbot + Plugin installieren
apt:
name:
- certbot
- python3-certbot-nginx
state: present
update_cache: yes
- name: Challenge-Verzeichnis für Let's Encrypt anlegen
file:
path: /var/www/html/.well-known/acme-challenge
state: directory
owner: www-data
group: www-data
mode: '0755'
recurse: yes
- name: Füge Let's Encrypt Challenge-Pfad in den Nginx-Vhost ein
blockinfile:
path: /etc/nginx/sites-available/default
marker: "# {mark} ANSIBLE LETSENCRYPT"
insertafter: "^\\s*server\\s*{"
block: |
location ^~ /.well-known/acme-challenge/ {
root /var/www/html;
allow all;
default_type "text/plain";
}
notify: Reload nginx
- name: Let's Encrypt Zertifikat anfordern
command: >
certbot --nginx -n --agree-tos --redirect
-m kontakt@michaelschiemer.de
-d test.michaelschiemer.de
args:
creates: /etc/letsencrypt/live/test.michaelschiemer.de/fullchain.pem

View File

@@ -0,0 +1,18 @@
- name: Systempakete aktualisieren
apt:
update_cache: yes
upgrade: safe
autoremove: yes
autoclean: yes
register: upgrade_result
become: true
- name: Zeige ggf. Anzahl aktualisierter Pakete
debug:
msg: "Anzahl aktualisierter Pakete: {{ upgrade_result.stdout_lines | default([]) | length }}"
- name: Reboot durchführen, wenn notwendig
reboot:
msg: "Reboot wegen Kernel-/System-Update erforderlich"
pre_reboot_delay: 30
when: upgrade_result.changed

View File

@@ -0,0 +1,50 @@
- name: Certbot + Plugin installieren
apt:
name:
- certbot
- python3-certbot-nginx
state: present
update_cache: yes
when: letsencrypt_enabled
- name: Challenge-Verzeichnis für Let's Encrypt anlegen
file:
path: "{{ app_public }}/.well-known/acme-challenge"
state: directory
owner: www-data
group: www-data
mode: '0755'
recurse: yes
when: letsencrypt_enabled and letsencrypt_certbot_method == 'webroot'
- name: Stoppe Nginx für Standalone-Methode
service:
name: nginx
state: stopped
when: letsencrypt_enabled and letsencrypt_certbot_method == 'standalone'
- name: Let's Encrypt Zertifikat anfordern (Standalone)
command: >
certbot certonly --standalone -n --agree-tos
-m {{ app_email }}
-d {{ app_domain }}
args:
creates: /etc/letsencrypt/live/{{ app_domain }}/fullchain.pem
when: letsencrypt_enabled and letsencrypt_certbot_method == 'standalone'
- name: Let's Encrypt Zertifikat anfordern (Webroot)
command: >
certbot certonly --webroot -w {{ app_public }} -n --agree-tos
-m {{ app_email }}
-d {{ app_domain }}
args:
creates: /etc/letsencrypt/live/{{ app_domain }}/fullchain.pem
when: letsencrypt_enabled and letsencrypt_certbot_method == 'webroot'
- name: Kopiere SSL-Zertifikate für Docker
copy:
src: "/etc/letsencrypt/live/{{ app_domain }}/"
dest: "{{ app_root }}/ssl/"
remote_src: yes
mode: '0644'
when: letsencrypt_enabled

View File

@@ -0,0 +1,6 @@
wireguard_interface: wg0
wireguard_port: 51820
wireguard_address: 10.8.0.1/24
wireguard_server_ip: 94.16.110.151 # oder deine Domain
wireguard_network: "10.8.0.0/24"

View File

@@ -0,0 +1,133 @@
# --------------------------------------------------------
# WireGuard installieren
# --------------------------------------------------------
- name: Stelle sicher, dass WireGuard installiert ist
apt:
name: wireguard
state: present
update_cache: yes
become: true
when: ansible_connection != "local"
# --------------------------------------------------------
# Server-Schlüssel erzeugen und speichern
# --------------------------------------------------------
- name: Prüfe ob privater Server-Schlüssel existiert
stat:
path: /etc/wireguard/privatekey
register: privkey_file
become: true
when: ansible_connection != "local"
- name: Erstelle Schlüsselpaar für Server (wenn nicht vorhanden)
command: wg genkey
register: server_private_key
when: ansible_connection != "local" and (not privkey_file.stat.exists | default(true))
- name: Speichere privaten Schlüssel
copy:
content: "{{ server_private_key.stdout }}"
dest: /etc/wireguard/privatekey
mode: "0600"
when: server_private_key.stdout is defined and server_private_key.stdout is defined
- name: Lies privaten Schlüssel ein
slurp:
src: /etc/wireguard/privatekey
become: true
when: ansible_connection != "local"
- name: Erzeuge öffentlichen Server-Schlüssel
command: "echo '{{ wg_privkey }}' | wg pubkey"
register: wg_pubkey
when: ansible_connection != "local"
- name: Privaten Server-Schlüssel anzeigen
debug:
msg: "{{ server_private_key }}"
when: ansible_connection != "local"
# --------------------------------------------------------
# Client-Key-Erzeugung lokal (einmalig pro Client)
# --------------------------------------------------------
- name: Generiere privaten Schlüssel für Clients (auf dem Server)
command: wg genkey
args:
creates: "/etc/wireguard/client-{{ item.name }}.key"
loop: "{{ wireguard_clients }}"
loop_control:
label: "{{ item.name }}"
register: client_private_keys
when: ansible_connection != "local"
- name: Erzeuge öffentlichen Schlüssel für Clients
command: "echo '{{ client_privkey_result.stdout }}' | wg pubkey"
register: client_pubkey_result
when:
- ansible_connection != "local"
- client_privkey_result is defined
- client_privkey_result.stdout is defined
- name: wireguard_clients mit public_key anreichern
set_fact:
wireguard_clients: "{{ wireguard_clients_with_pubkey | default([]) + [ item.0 | combine({'public_key': item.1.stdout|trim }) ] }}"
loop: "{{ wireguard_clients | zip(client_public_keys.results) | list }}"
when: client_public_keys is defined
- name: Aktuelles wireguard_clients-Set überschreiben
set_fact:
wireguard_clients: "{{ wireguard_clients_with_pubkey }}"
when: wireguard_clients_with_pubkey is defined
# --------------------------------------------------------
# Konfigurationsdatei erzeugen
# --------------------------------------------------------
#- debug:
# var: wireguard_clients
- name: Render wg0.conf
template:
src: wg0.conf.j2
dest: /etc/wireguard/wg0.conf
when: wg_privkey is defined and wg_privkey != ""
# --------------------------------------------------------
# IP Forwarding & WireGuard aktivieren
# --------------------------------------------------------
- name: Aktiviere IP-Forwarding
sysctl:
name: net.ipv4.ip_forward
value: 1
state: present
sysctl_set: yes
reload: yes
become: true
when: ansible_connection != "local"
- name: Starte und aktiviere WireGuard
systemd:
name: wg-quick@wg0
enabled: true
state: started
daemon_reload: yes
become: true
when: ansible_connection != "local"
- name: Verteilt für jeden Client die Client-Config
template:
src: client.conf.j2
dest: "/etc/wireguard/clients/{{ item.name }}.conf"
owner: root
group: root
mode: 0600
loop: "{{ wireguard_clients }}"
#delegate_to: localhost
run_once: true
become: true
when: ansible_connection != "local"

View File

@@ -0,0 +1,54 @@
---
# roles/wireguard/tasks/failsafe.yml
# Sicherstellt, dass SSH über VPN funktioniert und ein Fallback vorhanden ist
- name: Stelle sicher, dass wireguard_network gesetzt ist
assert:
that:
- wireguard_network is defined
fail_msg: "wireguard_network muss gesetzt sein (z.B. 10.8.0.0/24)"
- name: Automatisch externe IP als fallback_ip setzen (nur wenn nicht gesetzt)
shell: curl -s ifconfig.me
register: detected_fallback_ip
when: fallback_ip is not defined
changed_when: false
- name: Setze fallback_ip dynamisch als Ansible-Fact (wenn nicht gesetzt)
set_fact:
fallback_ip: "{{ detected_fallback_ip.stdout }}"
when: fallback_ip is not defined
- name: (Optional) Erlaube temporär Fallback-SSH von aktueller IP
ufw:
rule: allow
port: 22
proto: tcp
from_ip: "{{ fallback_ip }}"
- name: Erlaube SSH-Zugriff über VPN
ufw:
rule: allow
port: 22
proto: tcp
from_ip: "{{ wireguard_network }}"
- name: (Warnung) Prüfe ob VPN-Interface aktiv ist
shell: ip a show dev wg0
register: vpn_interface_check
failed_when: false
- name: Hinweis, wenn VPN-Interface nicht aktiv ist
debug:
msg: "⚠️ VPN-Interface wg0 scheint nicht aktiv zu sein. SSH über VPN wird nicht funktionieren."
when: vpn_interface_check.rc != 0
- name: (Optional) SSH von überall blockieren nur wenn VPN aktiv
when:
- ssh_lockdown | default(false)
- vpn_interface_check.rc == 0
ufw:
rule: deny
port: 22
proto: tcp
from_ip: 0.0.0.0/0

View File

@@ -0,0 +1,83 @@
# Beispiel: Passe jeden Task in dieser Datei so an:
- name: Aktiviere Firewall-Regeln für WireGuard
ufw:
rule: allow
port: "{{ wireguard_port }}"
proto: udp
become: true
when: ansible_connection != "local"
- name: Prüfe, ob UFW installiert ist
command: which ufw
register: ufw_installed
ignore_errors: true
changed_when: false
- name: Installiere UFW (falls nicht vorhanden)
apt:
name: ufw
state: present
update_cache: yes
when: ufw_installed.rc != 0
# Setze Standardrichtlinien (erst Konfiguration, dann am Ende aktivieren)
- name: Setze Policy für eingehenden Traffic auf "deny"
ufw:
direction: incoming
policy: deny
- name: Setze Policy für ausgehenden Traffic auf "allow"
ufw:
direction: outgoing
policy: allow
# WireGuard-Port freigeben (UDP)
- name: WireGuard-Port erlauben
ufw:
rule: allow
port: "{{ wireguard_port | default(51820) }}"
proto: udp
# SSH von bestimmter IP erlauben
- name: SSH von deiner IP erlauben (empfohlen)
ufw:
rule: allow
port: 22
proto: tcp
from_ip: "{{ fallback_ip }}"
when: fallback_ip is defined and fallback_ip | length > 0
# Temporär für Tests: SSH für alle erlauben (nur bei Bedarf!)
- name: SSH von überall erlauben (fail-safe, NUR während Setup/Test)
ufw:
rule: allow
port: 22
proto: tcp
when: (not (fallback_ip is defined and fallback_ip | length > 0)) or (enable_ssh_from_anywhere | default(false))
# Masquerading für WireGuard
- name: NAT für WireGuard aktivieren
iptables:
table: nat
chain: POSTROUTING
out_interface: "{{ wireguard_exit_interface | default('eth0') }}"
source: "{{ wireguard_network }}"
jump: MASQUERADE
- name: WireGuard Kernel-Modul laden
modprobe:
name: wireguard
state: present
# UFW ganz am Schluss aktivieren
- name: UFW aktivieren
ufw:
state: enabled
- name: Aktive UFW-Regeln anzeigen (zum Debuggen)
command: ufw status verbose
register: ufw_status
changed_when: false
- name: Zeige UFW-Regeln im Ansible-Output
debug:
var: ufw_status.stdout

View File

@@ -0,0 +1,60 @@
- name: Key-Verzeichnis für Client anlegen
file:
path: "{{ role_path }}/client-keys/{{ client.name }}"
state: directory
mode: "0700"
become: true
- name: Existenz des privaten Schlüssels prüfen
stat:
path: "{{ role_path }}/client-keys/{{ client.name }}/private.key"
register: client_private_key_stat
- name: Privaten Schlüssel generieren (nur falls nicht vorhanden)
command: wg genkey
register: genpriv
args:
chdir: "{{ role_path }}/client-keys/{{ client.name }}"
when: not client_private_key_stat.stat.exists
- name: Privaten Schlüssel speichern (nur falls nicht vorhanden)
copy:
content: "{{ genpriv.stdout }}"
dest: "{{ role_path }}/client-keys/{{ client.name }}/private.key"
mode: "0600"
when: not client_private_key_stat.stat.exists
- name: Public Key aus privaten Schlüssel generieren (bei Neuerstellung)
command: wg pubkey
args:
stdin: "{{ genpriv.stdout }}"
chdir: "{{ role_path }}/client-keys/{{ client.name }}"
register: genpub
when: not client_private_key_stat.stat.exists
- name: Bestehenden privaten Schlüssel laden (falls vorhanden)
slurp:
src: "{{ role_path }}/client-keys/{{ client.name }}/private.key"
register: loaded_private
when: client_private_key_stat.stat.exists
- name: Public Key aus gespeichertem Private Key erzeugen (falls vorhanden)
command: wg pubkey
args:
stdin: "{{ loaded_private.content | b64decode }}"
chdir: "{{ role_path }}/client-keys/{{ client.name }}"
register: genpub_existing
when: client_private_key_stat.stat.exists
- name: Public Key für Client in Datei schreiben
copy:
content: >
{{ (genpub.stdout if not client_private_key_stat.stat.exists else genpub_existing.stdout) }}
dest: "{{ role_path }}/client-keys/{{ client.name }}/public.key"
mode: "0644"
- name: Variablen für Client setzen (private/public key, Adresse)
set_fact:
"wg_{{ client.name }}_private_key": "{{ (genpriv.stdout if not client_private_key_stat.stat.exists else loaded_private.content | b64decode) }}"
"wg_{{ client.name }}_public_key": "{{ (genpub.stdout if not client_private_key_stat.stat.exists else genpub_existing.stdout) }}"
"wg_{{ client.name }}_address": "{{ client.address }}"

View File

@@ -0,0 +1,39 @@
- name: Schleife über alle WireGuard-Clients
include_tasks: generate_client_single.yml
loop: "{{ wireguard_clients }}"
loop_control:
loop_var: client
- name: Generiere privaten Schlüssel für jeden Client
shell: "wg genkey"
register: wg_client_private_keys
loop: "{{ wireguard_clients }}"
loop_control:
label: "{{ item.name }}"
# kein delegate_to mehr!
run_once: true # ggf. auch entfernen, siehe Anmerkung unten
- name: Setze globale Client-Key-Facts für alle Clients
set_fact:
wg_all_clients_private_keys: >-
{{
wg_all_clients_private_keys | default({}) | combine({
item.1.name: item.0.stdout
})
}}
loop: "{{ wg_client_private_keys.results | zip(wireguard_clients) | list }}"
delegate_to: localhost
run_once: true
- name: Generiere Private Keys für Clients
command: "wg genkey"
register: client_keys_raw
loop: "{{ wireguard_clients }}"
loop_control:
loop_var: client
changed_when: false
- name: Mappe Keys nach Namen
set_fact:
wg_all_clients_private_keys: "{{ dict(wireguard_clients | map(attribute='name') | list | zip(client_keys_raw.results | map(attribute='stdout') | list)) }}"

View File

@@ -0,0 +1,7 @@
- name: Stelle sicher, dass WireGuard installiert ist
apt:
name: wireguard
state: present
update_cache: yes
become: true
when: ansible_connection != "local"

View File

@@ -0,0 +1,22 @@
#- include_tasks: install.yml
#- include_tasks: configure.yml
#- include_tasks: generate_clients.yml
#- include_tasks: firewall.yml
- name: Installiere WireGuard
import_tasks: install.yml
when: ansible_connection != "local"
- name: Konfiguriere WireGuard
import_tasks: configure.yml
- name: Generiert .conf Dateien
import_tasks: generate_clients.yml
- name: Setze Firewall-Regeln
import_tasks: firewall.yml
when: ansible_connection != "local"
- name: Wende VPN-Failsafe-Regeln an
import_tasks: failsafe.yml

View File

@@ -0,0 +1,10 @@
[Interface]
PrivateKey = {{ wg_all_clients_private_keys[item.name] }}
Address = {{ item.address }}/32
DNS = 1.1.1.1
[Peer]
PublicKey = {{ item.public_key }}
Endpoint = {{ wireguard_server_ip }}:{{ wireguard_port }}
AllowedIPs = {{ wireguard_network }}, {{ wireguard_server_ip }}/32
PersistentKeepalive = 25

View File

@@ -0,0 +1,12 @@
[Interface]
Address = {{ wireguard_address }}
PrivateKey = {{ wg_privkey | b64decode | trim }}
ListenPort = {{ wireguard_port }}
PostUp = iptables -A FORWARD -i {{ wireguard_interface }} -j ACCEPT; iptables -A FORWARD -o {{ wireguard_interface }} -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i {{ wireguard_interface }} -j ACCEPT; iptables -D FORWARD -o {{ wireguard_interface }} -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
{% for client in wireguard_clients %}
[Peer]
PublicKey = {{ client.public_key }}
AllowedIPs = {{ client.address }}/32
{% endfor %}

View File

@@ -1,4 +0,0 @@
- hosts: web
become: false
roles:
- setup

View File

@@ -0,0 +1,7 @@
# ansible/wireguard-create-config.yml
- hosts: vpn
gather_facts: false
roles:
- role: wireguard
tasks_from: generate_clients # Zum Beispiel, je nach Task
# tasks_from: generate_client_single # Oder für einzelne Clients

View File

@@ -0,0 +1,9 @@
# ansible/wireguard-install-server.yml
- hosts: vpn
become: true
gather_facts: true
roles:
- role: wireguard
tasks_from: install # z.B., je nach Namensschema deiner Rolle
- role: wireguard
tasks_from: configure # Für Config/Firewall usw.