added new secret logic for vms
This commit is contained in:
88
VAULT_RESET.md
Normal file
88
VAULT_RESET.md
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
# Vault Reset Guide
|
||||||
|
|
||||||
|
Dieses Dokument beschreibt, wie du HashiCorp Vault komplett zurücksetzt, falls das **Root Token** verloren gegangen ist oder die Instanz irreparabel beschädigt scheint.
|
||||||
|
|
||||||
|
**⚠️ ACHTUNG: DATENVERLUST!**
|
||||||
|
Dieser Vorgang löscht unwiderruflich:
|
||||||
|
* Alle gespeicherten Secrets (Passwörter, Tokens).
|
||||||
|
* Alle konfigurierten User, Policies und Engines.
|
||||||
|
* Das Zertifikat (CA).
|
||||||
|
|
||||||
|
Du musst anschließend alle Secrets neu einspielen (via `setup_vault_secrets.sh`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Schritt 1: Vault Container & Daten löschen
|
||||||
|
|
||||||
|
Führe dies auf deinem Management-Host (`vm-management-200`) aus. Wir nutzen SSH, um die Daten auf dem Vault-Server (`vm-docker-apps-301`) zu löschen.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Container stoppen und entfernen
|
||||||
|
ssh -i ~/.ssh/id_ed25519_ansible_prod ansible@10.100.30.11 "docker stop vault-prod && docker rm vault-prod"
|
||||||
|
|
||||||
|
# 2. Persistente Daten löschen (File Storage & Zertifikate)
|
||||||
|
ssh -i ~/.ssh/id_ed25519_ansible_prod ansible@10.100.30.11 "sudo rm -rf /opt/vault/file/* /opt/vault/certs/*"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Schritt 2: Neu-Deployment
|
||||||
|
|
||||||
|
Jetzt lassen wir Ansible den Container neu starten. Da die Verzeichnisse leer sind, wird das `entrypoint.sh` Skript:
|
||||||
|
1. Neue Zertifikate generieren.
|
||||||
|
2. Vault neu initialisieren.
|
||||||
|
3. Neue Unseal-Keys und ein neues Root-Token erstellen.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Wechsel in das Ansible Verzeichnis
|
||||||
|
cd infrastructure/ansible
|
||||||
|
|
||||||
|
# Playbook ausführen (ignoriere 'Permission denied' Fehler bei Secrets, da Vault leer ist)
|
||||||
|
ansible-playbook -i inventory.ini deploy.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Schritt 3: Neue Zugangsdaten abholen
|
||||||
|
|
||||||
|
Der neue Vault ist nun gestartet. Wir müssen uns die neuen "Schlüssel zum Königreich" holen.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Neues Root Token auslesen
|
||||||
|
# (ACHTUNG: Speichere dieses Token sofort in deinem Passwort-Manager!)
|
||||||
|
ssh -i ~/.ssh/id_ed25519_ansible_prod ansible@10.100.30.11 "sudo cat /opt/vault/file/init_keys.json"
|
||||||
|
|
||||||
|
# 2. Neues CA Zertifikat holen
|
||||||
|
# (Sonst vertraut dein PC dem neuen Vault nicht)
|
||||||
|
scp -i ~/.ssh/id_ed25519_ansible_prod ansible@10.100.30.11:/opt/vault/certs/ca.crt ../../vault-ca.crt
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Schritt 4: Secrets wiederherstellen
|
||||||
|
|
||||||
|
Nun nutzen wir unser Setup-Skript, um die Secrets aus deiner (hoffentlich noch vorhandenen oder neu erstellten) `bootstrap.tfvars` wieder einzuspielen.
|
||||||
|
|
||||||
|
1. **Vorbereitung:**
|
||||||
|
Falls du `terraform/bootstrap.tfvars` gelöscht hattest, musst du sie neu erstellen (siehe `README.md` -> Phase 1).
|
||||||
|
|
||||||
|
2. **Import:**
|
||||||
|
```bash
|
||||||
|
cd ../.. # Ins Root des Repos
|
||||||
|
./setup_vault_secrets.sh
|
||||||
|
```
|
||||||
|
* Gib das **neue** Root-Token ein (aus Schritt 3).
|
||||||
|
* Das Skript aktiviert die Engines und schreibt die Secrets neu.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Schritt 5: Clients aktualisieren
|
||||||
|
|
||||||
|
Da wir eine neue CA haben, musst du das Zertifikat ggf. neu verteilen:
|
||||||
|
|
||||||
|
* **Browser:** Importiere das neue `vault-ca.crt` in Firefox/Chrome.
|
||||||
|
* **Linux Desktop:** Kopiere es nach `/usr/local/share/ca-certificates/` und führe `update-ca-certificates` aus.
|
||||||
|
* **Terraform:** Das Environment (`VAULT_CACERT`) zeigt meist eh auf die Datei im Repo (`./vault-ca.crt`), die wir in Schritt 3 überschrieben haben -> Sollte direkt gehen.
|
||||||
|
|
||||||
|
🎉 **Fertig!** Dein Vault ist frisch aufgesetzt.
|
||||||
|
|
||||||
@@ -54,4 +54,4 @@
|
|||||||
include_tasks: deploy_logic_push.yml
|
include_tasks: deploy_logic_push.yml
|
||||||
loop: "{{ host_config.apps }}"
|
loop: "{{ host_config.apps }}"
|
||||||
loop_control:
|
loop_control:
|
||||||
loop_var: app_name
|
loop_var: app_item
|
||||||
|
|||||||
@@ -4,19 +4,19 @@
|
|||||||
# 1. Validierung
|
# 1. Validierung
|
||||||
- name: "Prüfe App im Katalog"
|
- name: "Prüfe App im Katalog"
|
||||||
stat:
|
stat:
|
||||||
path: "{{ apps_catalog_path }}/{{ app_name }}"
|
path: "{{ apps_catalog_path }}/{{ app_item.name }}"
|
||||||
register: catalog_entry
|
register: catalog_entry
|
||||||
|
|
||||||
- name: "Skip if missing"
|
- name: "Skip if missing"
|
||||||
debug:
|
debug:
|
||||||
msg: "App {{ app_name }} nicht gefunden."
|
msg: "App {{ app_item.name }} nicht gefunden."
|
||||||
when: not catalog_entry.stat.exists
|
when: not catalog_entry.stat.exists
|
||||||
|
|
||||||
# 2. Setup
|
# 2. Setup
|
||||||
- name: "Setze Pfade"
|
- name: "Setze Pfade"
|
||||||
set_fact:
|
set_fact:
|
||||||
source_dir: "{{ apps_catalog_path }}/{{ app_name }}"
|
source_dir: "{{ apps_catalog_path }}/{{ app_item.name }}"
|
||||||
target_dir: "{{ base_deploy_path }}/{{ app_name }}"
|
target_dir: "{{ base_deploy_path }}/{{ app_item.name }}"
|
||||||
when: catalog_entry.stat.exists
|
when: catalog_entry.stat.exists
|
||||||
|
|
||||||
- name: "Erstelle Zielverzeichnis"
|
- name: "Erstelle Zielverzeichnis"
|
||||||
@@ -30,9 +30,9 @@
|
|||||||
# Im Pull-Mode brauchen wir ein Token. Wir lesen es aus /root/.vault-token oder ENV
|
# Im Pull-Mode brauchen wir ein Token. Wir lesen es aus /root/.vault-token oder ENV
|
||||||
- name: "Lade Secrets (Lokal)"
|
- name: "Lade Secrets (Lokal)"
|
||||||
set_fact:
|
set_fact:
|
||||||
app_secrets: "{{ lookup('community.hashi_vault.vault_kv2_get', 'apps/' + app_name, engine_mount_point='secret', url=vault_addr, token_path='/root/.vault-token') | default({}) }}"
|
app_secrets: "{{ lookup('community.hashi_vault.vault_kv2_get', 'apps/' + app_item.name, engine_mount_point='secret', url=vault_addr, token_path='/root/.vault-token') | default({}) }}"
|
||||||
ignore_errors: true
|
ignore_errors: true
|
||||||
when: catalog_entry.stat.exists
|
when: catalog_entry.stat.exists and app_item.has_secrets | default(false)
|
||||||
|
|
||||||
- name: "Erstelle .env"
|
- name: "Erstelle .env"
|
||||||
copy:
|
copy:
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
{{ key }}={{ value }}
|
{{ key }}={{ value }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
mode: '0600'
|
mode: '0600'
|
||||||
when: catalog_entry.stat.exists and app_secrets is defined and app_secrets | length > 0
|
when: catalog_entry.stat.exists and app_item.has_secrets | default(false) and app_secrets is defined and app_secrets | length > 0
|
||||||
|
|
||||||
# 4. Sync Files (Local Copy)
|
# 4. Sync Files (Local Copy)
|
||||||
- name: "Sync Files"
|
- name: "Sync Files"
|
||||||
@@ -64,4 +64,3 @@
|
|||||||
environment:
|
environment:
|
||||||
PATH: "/usr/bin:/usr/local/bin:/snap/bin:{{ ansible_env.PATH }}"
|
PATH: "/usr/bin:/usr/local/bin:/snap/bin:{{ ansible_env.PATH }}"
|
||||||
when: catalog_entry.stat.exists
|
when: catalog_entry.stat.exists
|
||||||
|
|
||||||
|
|||||||
@@ -2,22 +2,22 @@
|
|||||||
# Push-Logik: Wir kopieren von Localhost -> Remote Host
|
# Push-Logik: Wir kopieren von Localhost -> Remote Host
|
||||||
|
|
||||||
# 1. Validierung (Lokal)
|
# 1. Validierung (Lokal)
|
||||||
- name: "Prüfe ob App '{{ app_name }}' im Katalog existiert (Lokal)"
|
- name: "Prüfe ob App '{{ app_item.name }}' im Katalog existiert (Lokal)"
|
||||||
stat:
|
stat:
|
||||||
path: "{{ apps_catalog_path }}/{{ app_name }}"
|
path: "{{ apps_catalog_path }}/{{ app_item.name }}"
|
||||||
delegate_to: localhost
|
delegate_to: localhost
|
||||||
register: catalog_entry
|
register: catalog_entry
|
||||||
|
|
||||||
- name: "Fehler: App fehlt im Katalog"
|
- name: "Fehler: App fehlt im Katalog"
|
||||||
fail:
|
fail:
|
||||||
msg: "App '{{ app_name }}' nicht gefunden in {{ apps_catalog_path }}"
|
msg: "App '{{ app_item.name }}' nicht gefunden in {{ apps_catalog_path }}"
|
||||||
when: not catalog_entry.stat.exists
|
when: not catalog_entry.stat.exists
|
||||||
|
|
||||||
# 2. Setup Pfade (Remote)
|
# 2. Setup Pfade (Remote)
|
||||||
- name: "Setze Zielpfad"
|
- name: "Setze Zielpfad"
|
||||||
set_fact:
|
set_fact:
|
||||||
source_dir: "{{ apps_catalog_path }}/{{ app_name }}"
|
source_dir: "{{ apps_catalog_path }}/{{ app_item.name }}"
|
||||||
target_dir: "{{ base_deploy_path }}/{{ app_name }}"
|
target_dir: "{{ base_deploy_path }}/{{ app_item.name }}"
|
||||||
|
|
||||||
- name: "Erstelle Zielverzeichnis auf Remote"
|
- name: "Erstelle Zielverzeichnis auf Remote"
|
||||||
file:
|
file:
|
||||||
@@ -26,18 +26,19 @@
|
|||||||
mode: '0755'
|
mode: '0755'
|
||||||
|
|
||||||
# 3. Secrets aus Vault (Lokal lookup, Remote copy)
|
# 3. Secrets aus Vault (Lokal lookup, Remote copy)
|
||||||
|
# Nur ausführen, wenn has_secrets: true
|
||||||
- name: "Lade Secrets aus Vault (Lokal lookup)"
|
- name: "Lade Secrets aus Vault (Lokal lookup)"
|
||||||
set_fact:
|
set_fact:
|
||||||
app_secrets: "{{ lookup('community.hashi_vault.vault_kv2_get', 'apps/' + app_name, engine_mount_point='secret', url=lookup('env', 'VAULT_ADDR') | default('https://10.100.30.11:8200'), token=lookup('env', 'VAULT_TOKEN')) | default({}) }}"
|
app_secrets: "{{ lookup('community.hashi_vault.vault_kv2_get', 'apps/' + app_item.name, engine_mount_point='secret', url=lookup('env', 'VAULT_ADDR') | default('https://10.100.30.11:8200'), token=lookup('env', 'VAULT_TOKEN'), validate_certs=false) | default({}) }}"
|
||||||
delegate_to: localhost
|
delegate_to: localhost
|
||||||
ignore_errors: true
|
when: app_item.has_secrets | default(false)
|
||||||
|
ignore_errors: true # Trotzdem ignorieren, falls Vault down ist oder Secret fehlt
|
||||||
|
|
||||||
- name: "Setze app_secrets default wenn leer"
|
- name: "Setze app_secrets default wenn leer oder skipped"
|
||||||
set_fact:
|
set_fact:
|
||||||
app_secrets: {}
|
app_secrets: {}
|
||||||
when: app_secrets is undefined
|
when: app_secrets is undefined
|
||||||
|
|
||||||
|
|
||||||
- name: "Erstelle .env Datei auf Remote"
|
- name: "Erstelle .env Datei auf Remote"
|
||||||
copy:
|
copy:
|
||||||
dest: "{{ target_dir }}/.env"
|
dest: "{{ target_dir }}/.env"
|
||||||
@@ -46,11 +47,10 @@
|
|||||||
{{ key }}={{ value }}
|
{{ key }}={{ value }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
mode: '0600'
|
mode: '0600'
|
||||||
when: app_secrets | length > 0
|
when: app_item.has_secrets | default(false) and app_secrets | length > 0
|
||||||
|
|
||||||
# 4. Sync Dateien (Lokal -> Remote)
|
# 4. Sync Dateien (Lokal -> Remote)
|
||||||
# Hinweis: 'copy' Modul unterstützt kein 'exclude'. Für Excludes brauchen wir 'synchronize' (rsync)
|
# Hinweis: 'copy' Modul unterstützt kein 'exclude'.
|
||||||
# oder wir kopieren alles und ignorieren .env Konflikte (da copy sowieso überschreibt)
|
|
||||||
- name: "Synchronisiere App-Dateien (Push)"
|
- name: "Synchronisiere App-Dateien (Push)"
|
||||||
copy:
|
copy:
|
||||||
src: "{{ source_dir }}/"
|
src: "{{ source_dir }}/"
|
||||||
@@ -59,9 +59,8 @@
|
|||||||
directory_mode: '0755'
|
directory_mode: '0755'
|
||||||
# .env im Source wird überschrieben falls existent
|
# .env im Source wird überschrieben falls existent
|
||||||
|
|
||||||
|
|
||||||
# 5. Docker Compose Deployment (Remote)
|
# 5. Docker Compose Deployment (Remote)
|
||||||
- name: "Deploy {{ app_name }} mit Docker Compose"
|
- name: "Deploy {{ app_item.name }} mit Docker Compose"
|
||||||
community.docker.docker_compose_v2:
|
community.docker.docker_compose_v2:
|
||||||
project_src: "{{ target_dir }}"
|
project_src: "{{ target_dir }}"
|
||||||
state: present
|
state: present
|
||||||
@@ -71,5 +70,3 @@
|
|||||||
environment:
|
environment:
|
||||||
PATH: "/usr/bin:/usr/local/bin:/snap/bin:{{ ansible_env.PATH }}"
|
PATH: "/usr/bin:/usr/local/bin:/snap/bin:{{ ansible_env.PATH }}"
|
||||||
register: compose_result
|
register: compose_result
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -59,10 +59,9 @@
|
|||||||
# 4. Bereinigung (Pruning)
|
# 4. Bereinigung (Pruning)
|
||||||
- name: "Ermittle zu löschende Apps"
|
- name: "Ermittle zu löschende Apps"
|
||||||
set_fact:
|
set_fact:
|
||||||
# Apps die installiert sind, aber nicht in wanted_apps stehen
|
# wanted_apps ist jetzt eine Liste von Objekten. Wir brauchen nur die Namen.
|
||||||
# ACHTUNG: 'vault' sollte ggf. geschützt werden, wenn es manuell läuft?
|
# installed_apps | difference(wanted_apps | map(attribute='name') | list)
|
||||||
# Da wir Vault aber auch via GitOps managen (in der Liste), ist das ok.
|
apps_to_remove: "{{ installed_apps | difference(wanted_apps | map(attribute='name') | list) }}"
|
||||||
apps_to_remove: "{{ installed_apps | difference(wanted_apps) }}"
|
|
||||||
|
|
||||||
- name: "Pruning Loop"
|
- name: "Pruning Loop"
|
||||||
include_tasks: prune_logic.yml
|
include_tasks: prune_logic.yml
|
||||||
@@ -85,4 +84,4 @@
|
|||||||
include_tasks: deploy_logic_pull.yml
|
include_tasks: deploy_logic_pull.yml
|
||||||
loop: "{{ wanted_apps }}"
|
loop: "{{ wanted_apps }}"
|
||||||
loop_control:
|
loop_control:
|
||||||
loop_var: app_name
|
loop_var: app_item
|
||||||
|
|||||||
@@ -13,4 +13,3 @@ services:
|
|||||||
|
|
||||||
networks:
|
networks:
|
||||||
proxy-sub:
|
proxy-sub:
|
||||||
external: true
|
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
apps:
|
apps:
|
||||||
- vault
|
- name: vault
|
||||||
- traefik-sub
|
has_secrets: false # Vault verwaltet sich selbst (initial)
|
||||||
- whoami
|
- name: traefik-sub
|
||||||
# Hier einfach weitere Apps aus dem Katalog hinzufügen:
|
has_secrets: false # Aktuell keine Secrets nötig
|
||||||
# - nextcloud
|
- name: whoami
|
||||||
# - monitoring
|
has_secrets: false
|
||||||
|
# Beispiel für später:
|
||||||
|
# - name: nextcloud
|
||||||
|
# has_secrets: true
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
apps:
|
apps:
|
||||||
- traefik-edge
|
- name: traefik-edge
|
||||||
- whoami
|
has_secrets: true # Benötigt Cloudflare Token
|
||||||
|
|||||||
@@ -7,9 +7,36 @@ VAULT_CA_LOCAL="./vault-ca.crt"
|
|||||||
|
|
||||||
# Check if bootstrap vars exist
|
# Check if bootstrap vars exist
|
||||||
if [ ! -f "$BOOTSTRAP_VARS" ]; then
|
if [ ! -f "$BOOTSTRAP_VARS" ]; then
|
||||||
echo "Fehler: $BOOTSTRAP_VARS nicht gefunden."
|
echo "⚠️ Warnung: $BOOTSTRAP_VARS nicht gefunden."
|
||||||
echo "Bitte stelle sicher, dass du im Root des Repos bist und die Datei existiert."
|
read -p "Soll eine neue leere Bootstrap-Datei erstellt werden? (y/n) " -n 1 -r
|
||||||
|
echo ""
|
||||||
|
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||||
|
cat > "$BOOTSTRAP_VARS" << EOF
|
||||||
|
# terraform/bootstrap.tfvars
|
||||||
|
use_vault = false
|
||||||
|
|
||||||
|
# Proxmox Credentials
|
||||||
|
proxmox_api_url = "https://10.100.0.2:8006/api2/json"
|
||||||
|
proxmox_api_token_id = "root@pam!terraform"
|
||||||
|
proxmox_api_token_secret = "CHANGE_ME"
|
||||||
|
|
||||||
|
# OPNsense Credentials
|
||||||
|
opnsense_uri = "https://10.100.0.1:4443"
|
||||||
|
opnsense_api_key = "CHANGE_ME"
|
||||||
|
opnsense_api_secret = "CHANGE_ME"
|
||||||
|
|
||||||
|
# VM User Config
|
||||||
|
ci_user = "ansible"
|
||||||
|
ci_password = "InitialPassword123!"
|
||||||
|
ssh_public_key = "ssh-ed25519 CHANGE_ME"
|
||||||
|
EOF
|
||||||
|
echo "✅ Datei erstellt. Bitte editiere '$BOOTSTRAP_VARS' und trage deine Secrets ein."
|
||||||
|
echo "Führe das Skript danach erneut aus."
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
echo "Abbruch."
|
||||||
exit 1
|
exit 1
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check for Vault CA
|
# Check for Vault CA
|
||||||
|
|||||||
54
vault-ca.crt
54
vault-ca.crt
@@ -1,33 +1,33 @@
|
|||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
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
|
||||||
EEUWC8tQVPJ1frfcPDKjuwI=
|
QvPra+e4ulgvnKYRngLwp9w=
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
|
|||||||
Reference in New Issue
Block a user