diff --git a/pve1/ansible/README.md b/pve1/ansible/README.md new file mode 100644 index 0000000..0ec6143 --- /dev/null +++ b/pve1/ansible/README.md @@ -0,0 +1,27 @@ +# Ansible auf pve1 + +Kleine Playbooks, die **von pve1** aus laufen (nicht die pve2 CT-Wartung). + +| Datei | Zweck | +|-------|--------| +| [fish-setup.yml](fish-setup.yml) | Fish + bobthefish auf VM 101 (`jean`) | +| [inventory.ini](inventory.ini) | Inventory für fish-setup | + +## Ausführen + +```bash +cd /root/docu/pve1/ansible # oder: /root/ansible → Symlink +ansible-playbook -i inventory.ini fish-setup.yml +``` + +## Deploy-Pfad auf dem Host + +Empfohlen: + +```bash +ln -sfn /root/docu/pve1/ansible /root/ansible +``` + +## SSH-Keys + +Inventory nutzt `ansible_user=jean` auf `192.168.10.10` — Keys siehe [../../shared/ssh/README.md](../../shared/ssh/README.md). diff --git a/pve1/ansible/fish-setup.yml b/pve1/ansible/fish-setup.yml new file mode 100644 index 0000000..5465645 --- /dev/null +++ b/pve1/ansible/fish-setup.yml @@ -0,0 +1,109 @@ +--- +- name: Install and configure fish shell with bobthefish + hosts: servers + become: true + + vars: + target_user: jean + fisher_url: https://raw.githubusercontent.com/jorgebucaran/fisher/main/functions/fisher.fish + + tasks: + - name: Install fish shell + apt: + name: fish + state: present + update_cache: true + + - name: Set fish as default shell for {{ target_user }} + user: + name: "{{ target_user }}" + shell: /usr/bin/fish + + - name: Create fish config directory + file: + path: /home/{{ target_user }}/.config/fish/functions + state: directory + owner: "{{ target_user }}" + group: "{{ target_user }}" + mode: "0755" + + - name: Install Fisher plugin manager + become_user: "{{ target_user }}" + shell: | + curl -sL {{ fisher_url }} | source && fisher install jorgebucaran/fisher + args: + executable: /usr/bin/fish + creates: /home/{{ target_user }}/.config/fish/functions/fisher.fish + + - name: Install bobthefish theme + become_user: "{{ target_user }}" + shell: | + fisher install oh-my-fish/theme-bobthefish + args: + executable: /usr/bin/fish + register: bobthefish_install + changed_when: "'Installing' in bobthefish_install.stdout" + + - name: Configure bobthefish in config.fish + become_user: "{{ target_user }}" + blockinfile: + path: /home/{{ target_user }}/.config/fish/config.fish + create: true + owner: "{{ target_user }}" + group: "{{ target_user }}" + mode: "0644" + marker: "# {mark} ANSIBLE MANAGED - bobthefish config" + block: | + set -g theme_color_scheme nord + set -g theme_display_git yes + set -g theme_display_git_dirty yes + set -g theme_display_git_untracked yes + set -g theme_git_worktree_support no + set -g theme_display_vagrant no + set -g theme_display_docker_machine no + set -g theme_display_k8s_context no + set -g theme_display_user ssh + set -g theme_display_hostname ssh + set -g theme_show_exit_status yes + set -g theme_title_use_abbreviated_path yes + set -g fish_greeting "" + + - name: Install Nerd Font (JetBrainsMono) for powerline glyphs + block: + - name: Create fonts directory + file: + path: /home/{{ target_user }}/.local/share/fonts + state: directory + owner: "{{ target_user }}" + group: "{{ target_user }}" + mode: "0755" + + - name: Download JetBrainsMono Nerd Font + become_user: "{{ target_user }}" + get_url: + url: https://github.com/ryanoasis/nerd-fonts/releases/download/v3.2.1/JetBrainsMono.zip + dest: /tmp/JetBrainsMono.zip + timeout: 60 + + - name: Install unzip + apt: + name: unzip + state: present + + - name: Unzip Nerd Font + become_user: "{{ target_user }}" + unarchive: + src: /tmp/JetBrainsMono.zip + dest: /home/{{ target_user }}/.local/share/fonts/ + remote_src: true + creates: /home/{{ target_user }}/.local/share/fonts/JetBrainsMonoNerdFont-Regular.ttf + + - name: Install fontconfig + apt: + name: fontconfig + state: present + + - name: Rebuild font cache + become_user: "{{ target_user }}" + command: fc-cache -f + changed_when: true diff --git a/pve1/ansible/inventory.ini b/pve1/ansible/inventory.ini new file mode 100644 index 0000000..7dcb8d8 --- /dev/null +++ b/pve1/ansible/inventory.ini @@ -0,0 +1,2 @@ +[servers] +192.168.10.10 ansible_user=jean ansible_become=true diff --git a/pve2/06_Ansible-Automatisierung.md b/pve2/06_Ansible-Automatisierung.md index 7c664d5..01e4fb1 100644 --- a/pve2/06_Ansible-Automatisierung.md +++ b/pve2/06_Ansible-Automatisierung.md @@ -14,17 +14,25 @@ Stattdessen: ``` /etc/cron.weekly/pve-lxc-disk-maintenance ↓ (Symlink) -/root/ansible/run-disk-maintenance.sh +/root/ansible/run-disk-maintenance.sh ← Symlink nach /root/docu/pve2/ansible ↓ ansible-playbook playbooks/disk-maintenance.yml ↓ SSH docker (101) · media (109) · AIDEV (110) ``` -## Verzeichnisstruktur +## Verzeichnisstruktur (Git) + +Quelle im Repo **`docu`**, auf pve2 deployen: + +```bash +cd /root/docu && git pull +ln -sfn /root/docu/pve2/ansible /root/ansible +``` ``` -/root/ansible/ +/root/docu/pve2/ansible/ # (= /root/ansible nach Symlink) +├── README.md ├── ansible.cfg ├── run-disk-maintenance.sh → von cron.weekly aufgerufen ├── inventory/ @@ -39,6 +47,8 @@ ansible-playbook playbooks/disk-maintenance.yml └── handlers/main.yml ``` +SSH-Keys für Ansible → [../shared/ssh/README.md](../shared/ssh/README.md) + ## Verwaltete Hosts | Ansible-Host | VMID | IP | Besonderheiten | @@ -47,7 +57,7 @@ ansible-playbook playbooks/disk-maintenance.yml | media | 109 | 192.168.20.6 | Jellyfin-Cache-Pfad | | aidev | 110 | 10.100.2.13 | Dev-Tooling optional | -SSH als `root` vom Proxmox-Host — Key-Auth war bereits eingerichtet. +SSH als `root` vom Proxmox-Host — Public Key `root@pve2` muss in den CTs stehen ([shared/ssh](../shared/ssh/README.md)). ## Was das Playbook macht @@ -101,7 +111,7 @@ echo '0 3 * * * root /root/ansible/run-disk-maintenance.sh' > /etc/cron.d/pve-lx ## Konfiguration anpassen -Globale Werte: `/root/ansible/inventory/group_vars/all.yml` +Globale Werte: `/root/docu/pve2/ansible/inventory/group_vars/all.yml` (oder `/root/ansible/…` via Symlink) ```yaml journal_max_size: 200M diff --git a/pve2/ansible/README.md b/pve2/ansible/README.md new file mode 100644 index 0000000..4d41937 --- /dev/null +++ b/pve2/ansible/README.md @@ -0,0 +1,42 @@ +# Ansible auf pve2 — LXC Disk Maintenance + +Wöchentliche Wartung für CTs **101 docker**, **109 media**, **110 AIDEV** per SSH vom Proxmox-Host. + +| Pfad | Inhalt | +|------|--------| +| [ansible.cfg](ansible.cfg) | Defaults | +| [inventory/hosts.yml](inventory/hosts.yml) | Hosts + CT-Variablen | +| [inventory/group_vars/all.yml](inventory/group_vars/all.yml) | Schwellwerte | +| [playbooks/disk-maintenance.yml](playbooks/disk-maintenance.yml) | Playbook | +| [roles/disk_cleanup/](roles/disk_cleanup/) | Tasks (Journal, Docker, fstrim, …) | +| [run-disk-maintenance.sh](run-disk-maintenance.sh) | Cron-Einstieg | + +Doku: [../06_Ansible-Automatisierung.md](../06_Ansible-Automatisierung.md) + +## Ausführen + +```bash +cd /root/docu/pve2/ansible # oder: /root/ansible → Symlink +./run-disk-maintenance.sh +# oder +ansible-playbook playbooks/disk-maintenance.yml +``` + +## Cron (pve2) + +```text +/etc/cron.weekly/pve-lxc-disk-maintenance → /root/ansible/run-disk-maintenance.sh +``` + +Nach Symlink auf dieses Verzeichnis bleibt der Cron gültig. + +## Deploy + +```bash +cd /root/docu && git pull +ln -sfn /root/docu/pve2/ansible /root/ansible +``` + +## SSH + +Ansible verbindet als **root** zu den CTs — Host-Key `root@pve2` muss in CT `authorized_keys` stehen → [../../shared/ssh/README.md](../../shared/ssh/README.md). diff --git a/pve2/ansible/ansible.cfg b/pve2/ansible/ansible.cfg new file mode 100644 index 0000000..4f1e26d --- /dev/null +++ b/pve2/ansible/ansible.cfg @@ -0,0 +1,12 @@ +[defaults] +inventory = inventory/hosts.yml +roles_path = roles +remote_user = root +host_key_checking = False +retry_files_enabled = False +gathering = implicit +stdout_callback = yaml +interpreter_python = auto_silent + +[privilege_escaping] +paramiko = ansible.paramiko_ssh.paramiko_ssh diff --git a/pve2/ansible/inventory/group_vars/all.yml b/pve2/ansible/inventory/group_vars/all.yml new file mode 100644 index 0000000..850db22 --- /dev/null +++ b/pve2/ansible/inventory/group_vars/all.yml @@ -0,0 +1,33 @@ +--- +# Disk maintenance defaults — tune per host in inventory if needed +disk_maintenance_enabled: true + +# systemd journal +journal_max_size: 200M + +# Docker +docker_prune_stopped_containers_older_than: 168h # 7 days +docker_prune_dangling_images: true +docker_prune_unused_images_older_than: 336h # 14 days (aggressive tag) +docker_prune_build_cache_older_than: 336h +docker_prune_dangling_volumes: true +docker_log_truncate_threshold: 50M +docker_log_truncate_target: 10M + +# LVM thin provisioning — critical on Proxmox local-lvm / nvme_second +fstrim_enabled: true + +# Frigate recordings on docker CT (matches config.yaml retain.days: 30) +frigate_recordings_retain_days: 30 +frigate_clips_retain_days: 14 + +# Jellyfin transcode/image cache (not metadata — that is library artwork) +jellyfin_cache_max_age_days: 30 + +# Optional dev tooling (AIDEV) +npm_cache_clean: false +apt_clean: true + +# Alert thresholds for summary output +disk_warn_percent: 80 +thin_pool_warn_percent: 85 diff --git a/pve2/ansible/inventory/hosts.yml b/pve2/ansible/inventory/hosts.yml new file mode 100644 index 0000000..a42b2db --- /dev/null +++ b/pve2/ansible/inventory/hosts.yml @@ -0,0 +1,17 @@ +all: + children: + lxc_containers: + hosts: + docker: + ansible_host: 192.168.10.101 + proxmox_vmid: 101 + frigate_recordings_path: /mnt/records/recordings + frigate_clips_path: /mnt/records/clips + media: + ansible_host: 192.168.20.6 + proxmox_vmid: 109 + jellyfin_cache_path: /opt/stacks/jellyfin/config/cache + aidev: + ansible_host: 10.100.2.13 + proxmox_vmid: 110 + dev_tooling_cleanup: true diff --git a/pve2/ansible/playbooks/disk-maintenance.yml b/pve2/ansible/playbooks/disk-maintenance.yml new file mode 100644 index 0000000..a02122a --- /dev/null +++ b/pve2/ansible/playbooks/disk-maintenance.yml @@ -0,0 +1,37 @@ +--- +# Weekly disk maintenance for Proxmox LXC containers +# Run from the Proxmox host: ansible-playbook playbooks/disk-maintenance.yml +# +# Tags: +# aggressive — also prune unused images older than 14 days +# frigate — enforce recording/clip retention on docker CT +# jellyfin — clean stale transcode/image cache on media CT +# dev-tooling — npm cache clean on AIDEV (off by default) + +- name: LXC disk maintenance + hosts: lxc_containers + become: true + gather_facts: true + vars: + disk_maintenance_enabled: true + roles: + - role: disk_cleanup + when: disk_maintenance_enabled | bool + +- name: Report Proxmox thin pool usage + hosts: localhost + connection: local + gather_facts: false + tasks: + - name: Get LVM thin pool stats + ansible.builtin.shell: lvs pve/data nvme_second/nvme_second -o vg_name,lv_name,data_percent 2>/dev/null --noheadings + register: thin_pools + changed_when: false + + - name: Thin pool summary + ansible.builtin.debug: + msg: | + Proxmox thin pools after maintenance: + {{ thin_pools.stdout }} + + Schedule: see /etc/cron.weekly/pve-lxc-disk-maintenance diff --git a/pve2/ansible/roles/disk_cleanup/defaults/main.yml b/pve2/ansible/roles/disk_cleanup/defaults/main.yml new file mode 100644 index 0000000..f271846 --- /dev/null +++ b/pve2/ansible/roles/disk_cleanup/defaults/main.yml @@ -0,0 +1,17 @@ +--- +journal_max_size: 200M +docker_prune_stopped_containers_older_than: 168h +docker_prune_dangling_images: true +docker_prune_unused_images_older_than: 336h +docker_prune_build_cache_older_than: 336h +docker_prune_dangling_volumes: true +docker_log_truncate_threshold: 50M +docker_log_truncate_target: 10M +fstrim_enabled: true +frigate_recordings_retain_days: 30 +frigate_clips_retain_days: 14 +jellyfin_cache_max_age_days: 30 +npm_cache_clean: false +apt_clean: true +disk_warn_percent: 80 +thin_pool_warn_percent: 85 diff --git a/pve2/ansible/roles/disk_cleanup/handlers/main.yml b/pve2/ansible/roles/disk_cleanup/handlers/main.yml new file mode 100644 index 0000000..07aa0eb --- /dev/null +++ b/pve2/ansible/roles/disk_cleanup/handlers/main.yml @@ -0,0 +1,5 @@ +--- +- name: Restart docker + ansible.builtin.service: + name: docker + state: restarted diff --git a/pve2/ansible/roles/disk_cleanup/tasks/main.yml b/pve2/ansible/roles/disk_cleanup/tasks/main.yml new file mode 100644 index 0000000..6b7e7f8 --- /dev/null +++ b/pve2/ansible/roles/disk_cleanup/tasks/main.yml @@ -0,0 +1,224 @@ +--- +- name: Disk usage before maintenance + ansible.builtin.shell: df -hT / | tail -1 + register: disk_before + changed_when: false + +- name: Show disk before + ansible.builtin.debug: + msg: "{{ inventory_hostname }} before: {{ disk_before.stdout }}" + +- name: Vacuum systemd journal + ansible.builtin.command: "journalctl --vacuum-size={{ journal_max_size }}" + register: journal_vacuum + changed_when: "'Vacuuming done' in journal_vacuum.stdout" + failed_when: false + +- name: Clean apt cache + ansible.builtin.apt: + autoclean: true + autoremove: true + clean: true + when: apt_clean | bool + +- name: Check if docker is available + ansible.builtin.command: docker info + register: docker_info + changed_when: false + failed_when: false + tags: [always, docker] + +- name: Truncate oversized Docker container logs + ansible.builtin.shell: | + set -o pipefail + find /var/lib/docker/containers -name '*-json.log' -size +{{ docker_log_truncate_threshold }} \ + -exec truncate -s {{ docker_log_truncate_target }} {} \; + echo done + args: + executable: /bin/bash + register: log_truncate + changed_when: log_truncate.stdout is search('done') + when: docker_info is defined and docker_info.rc == 0 + +- name: Prune stopped containers + ansible.builtin.command: >- + docker container prune -f --filter until={{ docker_prune_stopped_containers_older_than }} + register: container_prune + changed_when: "'Total reclaimed space' in container_prune.stdout and '0B' not in container_prune.stdout.split('Total reclaimed space')[1].split('\n')[0]" + when: docker_info is defined and docker_info.rc == 0 + +- name: Prune dangling images + ansible.builtin.command: docker image prune -f + register: image_prune_dangling + changed_when: "'Total reclaimed space' in image_prune_dangling.stdout and '0B' not in image_prune_dangling.stdout.split('Total reclaimed space')[1].split('\n')[0]" + when: + - docker_info is defined + - docker_info.rc == 0 + - docker_prune_dangling_images | bool + +- name: Prune unused images older than threshold + ansible.builtin.command: >- + docker image prune -af --filter until={{ docker_prune_unused_images_older_than }} + register: image_prune_old + changed_when: "'Total reclaimed space' in image_prune_old.stdout and '0B' not in image_prune_old.stdout.split('Total reclaimed space')[1].split('\n')[0]" + when: + - docker_info is defined + - docker_info.rc == 0 + - docker_prune_unused_images_older_than | length > 0 + tags: + - aggressive + +- name: Prune docker build cache + ansible.builtin.command: >- + docker builder prune -af --filter until={{ docker_prune_build_cache_older_than }} + register: builder_prune + changed_when: "'Total:' in builder_prune.stdout" + failed_when: false + when: docker_info is defined and docker_info.rc == 0 + +- name: Prune dangling docker volumes + ansible.builtin.command: docker volume prune -f + register: volume_prune + changed_when: "'Total reclaimed space' in volume_prune.stdout and '0B' not in volume_prune.stdout.split('Total reclaimed space')[1].split('\n')[0]" + when: + - docker_info is defined + - docker_info.rc == 0 + - docker_prune_dangling_volumes | bool + +- name: Check for existing Docker daemon.json + ansible.builtin.stat: + path: /etc/docker/daemon.json + register: docker_daemon_json + when: docker_info is defined and docker_info.rc == 0 + +- name: Ensure Docker log rotation defaults + ansible.builtin.copy: + dest: /etc/docker/daemon.json + owner: root + group: root + mode: "0644" + force: false + content: | + { + "log-driver": "json-file", + "log-opts": { + "max-size": "10m", + "max-file": "3" + } + } + notify: Restart docker + when: + - docker_info is defined + - docker_info.rc == 0 + - not docker_daemon_json.stat.exists + +- name: Remove old Frigate recording day folders + ansible.builtin.shell: | + set -euo pipefail + retain={{ frigate_recordings_retain_days }} + cutoff=$(date -d "-${retain} days" +%Y-%m-%d) + removed=0 + for d in "{{ frigate_recordings_path }}"/20??-??-??; do + [ -d "$d" ] || continue + day=$(basename "$d") + if [[ "$day" < "$cutoff" ]]; then + rm -rf "$d" + echo "removed $day" + removed=1 + fi + done + [ "$removed" -eq 0 ] || true + args: + executable: /bin/bash + register: frigate_recording_cleanup + changed_when: frigate_recording_cleanup.stdout | length > 0 + when: + - frigate_recordings_path is defined + - frigate_recordings_path | length > 0 + tags: + - frigate + +- name: Remove old Frigate clip previews + ansible.builtin.find: + paths: "{{ frigate_clips_path | default('') }}/previews" + age: "{{ frigate_clips_retain_days }}d" + file_type: any + recurse: true + register: old_frigate_clips + when: + - frigate_clips_path is defined + - frigate_clips_path | length > 0 + +- name: Delete old Frigate clip files + ansible.builtin.file: + path: "{{ item.path }}" + state: absent + loop: "{{ old_frigate_clips.files | default([]) }}" + when: + - frigate_clips_path is defined + - frigate_clips_path | length > 0 + loop_control: + label: "{{ item.path }}" + tags: + - frigate + +- name: Clean stale Jellyfin cache files + ansible.builtin.find: + paths: "{{ jellyfin_cache_path | default('') }}" + age: "{{ jellyfin_cache_max_age_days }}d" + file_type: file + recurse: true + register: old_jellyfin_cache + when: + - jellyfin_cache_path is defined + - jellyfin_cache_path | length > 0 + +- name: Delete stale Jellyfin cache + ansible.builtin.file: + path: "{{ item.path }}" + state: absent + loop: "{{ old_jellyfin_cache.files | default([]) }}" + when: + - jellyfin_cache_path is defined + - jellyfin_cache_path | length > 0 + loop_control: + label: "{{ item.path }}" + tags: + - jellyfin + +- name: Clean npm cache on dev hosts + ansible.builtin.command: npm cache clean --force + when: + - dev_tooling_cleanup | default(false) | bool + - npm_cache_clean | bool + changed_when: true + failed_when: false + tags: + - dev-tooling + +- name: Run fstrim on root filesystem + ansible.builtin.command: fstrim -v / + register: fstrim_result + changed_when: "'trimmed' in fstrim_result.stdout and '0 B' not in fstrim_result.stdout" + when: fstrim_enabled | bool + +- name: Docker disk summary + ansible.builtin.command: docker system df + register: docker_df + changed_when: false + failed_when: false + when: docker_info is defined and docker_info.rc == 0 + +- name: Disk usage after maintenance + ansible.builtin.shell: df -hT / | tail -1 + register: disk_after + changed_when: false + +- name: Maintenance summary + ansible.builtin.debug: + msg: | + {{ inventory_hostname }}: + before: {{ disk_before.stdout }} + after: {{ disk_after.stdout }} + fstrim: {{ fstrim_result.stdout | default('skipped') }} + docker: {{ docker_df.stdout | default('n/a') }} diff --git a/pve2/ansible/run-disk-maintenance.sh b/pve2/ansible/run-disk-maintenance.sh new file mode 100755 index 0000000..099ed24 --- /dev/null +++ b/pve2/ansible/run-disk-maintenance.sh @@ -0,0 +1,9 @@ +#!/bin/bash +# Weekly disk maintenance — runs Ansible playbook from Proxmox host +set -euo pipefail +export ANSIBLE_CONFIG=/root/ansible/ansible.cfg +LOG=/var/log/pve-lxc-disk-maintenance.log +exec >>"$LOG" 2>&1 +echo "=== $(date -Is) disk maintenance start ===" +ansible-playbook /root/ansible/playbooks/disk-maintenance.yml +echo "=== $(date -Is) disk maintenance done ===" diff --git a/shared/git-und-repos.md b/shared/git-und-repos.md index b9c2b00..e24ad93 100644 --- a/shared/git-und-repos.md +++ b/shared/git-und-repos.md @@ -63,6 +63,16 @@ Pfad: `/usr/local/go/bin/go` — in `~/.bashrc`: export PATH="/usr/local/go/bin:$PATH" ``` +## Weitere Pfade in `docu` + +| Pfad | Inhalt | +|------|--------| +| [shared/ssh/](ssh/README.md) | Gemeinsame `authorized_keys`-Fragmente + Deploy-Skript | +| `pve1/ansible/` | Fish-Setup für VM 101 | +| `pve2/ansible/` | LXC Disk-Maintenance (Cron auf pve2) | + +Symlinks auf den Hosts: `ln -sfn /root/docu/pve{1,2}/ansible /root/ansible` + ## Workflow ```bash diff --git a/shared/ssh/README.md b/shared/ssh/README.md new file mode 100644 index 0000000..c650754 --- /dev/null +++ b/shared/ssh/README.md @@ -0,0 +1,85 @@ +# SSH — gemeinsame authorized_keys + +Zentraler Katalog **öffentlicher** SSH-Keys (keine Private Keys). Stand aus den Hosts exportiert — bei neuem Laptop Key in `fragments/` ergänzen, `rebuild-assembled.sh` laufen lassen. + +## Struktur + +``` +shared/ssh/ +├── fragments/ # Einzelne Key-Gruppen (editierbar) +├── assembled/ # Fertige Sets pro Ziel (generiert) +├── rebuild-assembled.sh # fragments → assembled +└── install-authorized-keys.sh +``` + +| Fragment | Inhalt | +|----------|--------| +| [fragments/admin-workstations.pub](fragments/admin-workstations.pub) | DESKTOP-H9797I1, DESKTOP-J08NPU2 | +| [fragments/admin-laptops-extra.pub](fragments/admin-laptops-extra.pub) | L7L1S5V, OJIEMRE, x380, Security Keys | +| [fragments/admin-mobile.pub](fragments/admin-mobile.pub) | JuiceSSH, server6, WEBTOP | +| [fragments/host-pve1.pub](fragments/host-pve1.pub) | root@pve1 ed25519 | +| [fragments/host-pve2.pub](fragments/host-pve2.pub) | root@pve2 (Ansible / Host-SSH) | +| [fragments/legacy-pve1-rsa.pub](fragments/legacy-pve1-rsa.pub) | Altes RSA auf pve1 (Kommentar „root@pve2“) | + +## Assembled Sets → Ziel + +| Datei | Deploy auf | User | +|-------|------------|------| +| [assembled/proxmox-root.pub](assembled/proxmox-root.pub) | pve1 `192.168.10.5`, pve2 `192.168.10.4` | root | +| [assembled/vm101-jean.pub](assembled/vm101-jean.pub) | VM 101 Ubuntu `192.168.10.10` | jean | +| [assembled/pve2-lxc-root.pub](assembled/pve2-lxc-root.pub) | CT 101 docker, 109 media, 110 AIDEV (pve2) | root | +| [assembled/authorized_keys.all.pub](assembled/authorized_keys.all.pub) | Referenz — alle Keys vereint | — | + +### Matrix (Ist-Zustand) + +| Host | Empfohlenes Set | Anmerkung | +|------|-----------------|-----------| +| root@pve1 | proxmox-root | + legacy RSA noch enthalten | +| root@pve2 | proxmox-root | ohne legacy RSA (reicht host-pve2) | +| jean@192.168.10.10 | vm101-jean | Ansible fish-setup nutzt jean + SSH | +| CT 101 (docker) | pve2-lxc-root | Ansible disk-maintenance | +| CT 109 (media) | subset: admin + OJIEMRE | aktuell nur OJIEMRE — bei Bedarf volles Set | +| CT 110 (aidev) | pve2-lxc-root | **aktuell leer** — Keys fehlen für Ansible | + +## Neuen Key hinzufügen + +1. Passendes Fragment in `fragments/*.pub` editieren (eine Zeile pro Key) +2. `./rebuild-assembled.sh` +3. `./install-authorized-keys.sh ` oder manuell `cat assembled/….pub >> ~/.ssh/authorized_keys` + +## Deploy + +```bash +cd /root/docu/shared/ssh + +# Proxmox lokal +./install-authorized-keys.sh proxmox-root + +# Remote +./install-authorized-keys.sh --remote root@192.168.10.4 proxmox-root + +# VM 101 +./install-authorized-keys.sh vm101-jean + +# CTs auf pve2 (muss auf pve2 laufen) +./install-authorized-keys.sh pve2-lxc-root +./install-authorized-keys.sh pve2-lxc-root --ct 101 + +# Vorschau +./install-authorized-keys.sh --dry-run proxmox-root +``` + +## Ansible + +| Host | Ansible-Pfad | Keys | +|------|--------------|------| +| pve1 | [pve1/ansible/](../../pve1/ansible/) | jean → VM 101 | +| pve2 | [pve2/ansible/](../../pve2/ansible/) | root@pve2 → CTs | + +Symlink auf dem Host: `ln -sfn /root/docu/pve{1,2}/ansible /root/ansible` + +## Hinweise + +- **Nur Public Keys** ins Repo — niemals `id_*` ohne `.pub` oder `.git-credentials-*` +- Zwei verschiedene `ssh-rsa … root@pve2`: aktueller Key auf pve2 vs. Legacy auf pve1 (`legacy-pve1-rsa.pub`) +- CT 110: vor Ansible-Wartung Keys deployen (`pve2-lxc-root`) diff --git a/shared/ssh/assembled/authorized_keys.all.pub b/shared/ssh/assembled/authorized_keys.all.pub new file mode 100644 index 0000000..4aac2af --- /dev/null +++ b/shared/ssh/assembled/authorized_keys.all.pub @@ -0,0 +1,14 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAj1SFdqGjsIrF644ywWANqDMrsrlSBAQiM1HWEfwOIF jean@DESKTOP-L7L1S5V +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII8YFIl6gZ6taPAoDPJtkwkcfEpas9MbVgdkuQuBOJvh jean@DESKTOP-OJIEMRE +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID/3NRXevRiFpmLGkrZTA1Fp2FigYtDvvpG8Ta60U28p jean@x380 +sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIEohWH3Rqh0+h5sYmi921rf3l2mZ0RXebCS8hR9pmHIiAAAABHNzaDo= jean@DESKTOP-DA5D3IG +sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIEGR43JnbBQNZ3U9onHM1XoFiJStBUmGTf2yr9p/haYuAAAABHNzaDo= jean@DESKTOP-2N4HRBF +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIY8YOBNfgjm9AVLycI0V5So2FZFjSk5BTH/K+SLajnw JuiceSSH +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCTs4xGQ9bW9eB3gfPx10Ddi7rxqnYFM+BFT7+DuUeoIiKtOGQLJWO+4ShJt6uNbD1Sk/VRtHxC2zF5h0oe4ZrDLOpVwwD+3e9IkFmtUXcTdVOfL0SDfjeZ8EeW25pTz10pXuwckiKoGGh42a1giATPf9jT63p9qa+hM51DpMMbZPN3k2ZJLzjpwhA4n2xcBbcFGkj3Brp6Tsn3x2T/kDwwuaOAfD8elAwM+etnDE8RhztdFXmWWJqxkTane6nInp0JPeKe/Uq/MEGDNiOhGH5BUC8FFNJQiHF9JspHitL2zbmT7zev29VWJgzMFrD2avn7gVyo9T7VnRF0gxh2ZzeRuDob9tHYYXCg8UG9q4V7uDJBEWoidCKAzn44DDWA4zeBfbwKdh1Muff6LNGwoRtyKgpTf5VsA+zjw/SkKS9xASJZKD4IN11Q1bN4BHhLkwfB0bKFIfDyQlCf+IVb64+uQhICe9qwZPG6SLl0ok2DU3CbL3wdLsQWTrdiOPXtPT0= jean@server6 +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJSVmBVrCmUuCgKS4L3w6jRq2Efi/28ghElDSs22Hu2G abc@bdad197f6631 +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOfSnbZKfHpVI9w8ogdfsA7XnYA28goelOfq+w3X02Bx jean@DESKTOP-H9797I1 +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK0ToemBZ+/ibr9k0LHsn2J0JuLalXw//TLmC0ydE7vr jean@DESKTOP-J08NPU2 +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJJvDmBpuduGeVdN92I/tr5YkfmQo5fQ4lI5ZgakRQef root@pve1 +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICXDMnLAJlxd56f6BCJAjVFbaTDcI2lLMOQf1OWCGzaA root@pve2 +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC6NPVyFfE1hUimqs18TxjIB72+o59CVVQbJzK0cGmvzROnoR4iGPelwvGFnRUOdUbnGXK5TkQQB6dCnLKHEdULyQ5yXimI37Ywerr5cMorUF3QtWp4WHs2ewPgvdjc0gL0GLlSq7mHQ649PZbhpHo8GlOudsdEVqoxOKcvI/V572huuItTYj0AMnSHglQ1NR0jjeo6ItDblEpU6Anj4knqQYYyOD1CGAJZaKt+2UXsIV3xhkDFhq6Xv83VIj3T4Cju9VsIqFb5eOUUu7er0WpF0rqEqcVliPlq4Ja+eJ1Wl9HnD/5tUeM5yWDHynXTwSlMUCVvnaBtrFbIFhDR9pxWGEnNy6UEjiLRIrYkNMKr+QnzTesB8N8jvfPJoAMcESZAAi675PawbqYxK59ZG+sa/sX83G7GFl5MtB0lUhiyCJPdGUa63QfQw0J8X0dvkZCNcpsDWhbq9B+uu1GL1JS2Rr2uoYSwfIFUiyaQY8KniYlzYb2TyImEQZ7UYkTurIYVutjGJwNqr5KhrZb6flkt/t7fHe/rAScbhm/4lVQFYZYGggitnR6rh262CBl2ML53V1crhLzPjOQWu770y64mZBjf+NwWK84ikPsA0ei2/ph+oWnAkYZVbWVR47AOnLqDed95jJBL5rbAkeSe32MDTPG638pfiBRl/mvPdabZcQ== root@pve2 +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCvZFYXzxAFFa6aDpAOE/SlhU6yDYTxcP3h1NUyZvW2z/pXg05bQ1ZclYwk3RWjBRj4LQeYqClGyiXDo3bTJFusFq+UWVoYr0OIzyEu+5o+rNavErJxXRyiCFb61sSpxwanlec4MqTTNv8SvRHmmZPUjnfHRKjbHbj4LxMZT4cu3PSYAH98ANdgYw5ufZWuCQHxKN9LvQ8K/1JQAbxOGisnovIMtYo96NBk49FWHHQ29O0A5qDMW8HU9jXfWq5GTYplRdr7pnWDJBjuAUudmtG9vcMGZep5ExA2v9nfsbji4jemntBViDwk3mKcYn5NwIWrot89CON5Qe62QRSJnja5c7fSEPs+I2ltJ3ExLWwIMrQQua+yNJlGSkjLw8y1McuSUIk1FNRxLh4S1TDJOZ4zgHwHQFn1CV3+ZCCD0IM0VNKFpOgNmdQoziHIz96dCeZaQRFXl1Cf0YfhRwhuUqI8ifhgy32GfC5HlR82KRWYFNofZymRCMWaN8jMjfZZ3K2RkiAQUfjc9iojzY0NSO9kbM8RorHXNMgNkQVozgE//baULBCAYqT0q9jHd8mdqf4cfZ+Oj/EDqlnX6YNk+AC6VGmp4LlqWZGRdM9ovXoDe9g82RPypJI8fF0Ie0ws9rOQoVEzCmG9d3EXIQXn5M6JF660QgfcUrKTwUW/mKs0eQ== root@pve2 diff --git a/shared/ssh/assembled/proxmox-root.pub b/shared/ssh/assembled/proxmox-root.pub new file mode 100644 index 0000000..3c35356 --- /dev/null +++ b/shared/ssh/assembled/proxmox-root.pub @@ -0,0 +1,6 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOfSnbZKfHpVI9w8ogdfsA7XnYA28goelOfq+w3X02Bx jean@DESKTOP-H9797I1 +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK0ToemBZ+/ibr9k0LHsn2J0JuLalXw//TLmC0ydE7vr jean@DESKTOP-J08NPU2 +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJJvDmBpuduGeVdN92I/tr5YkfmQo5fQ4lI5ZgakRQef root@pve1 +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICXDMnLAJlxd56f6BCJAjVFbaTDcI2lLMOQf1OWCGzaA root@pve2 +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC6NPVyFfE1hUimqs18TxjIB72+o59CVVQbJzK0cGmvzROnoR4iGPelwvGFnRUOdUbnGXK5TkQQB6dCnLKHEdULyQ5yXimI37Ywerr5cMorUF3QtWp4WHs2ewPgvdjc0gL0GLlSq7mHQ649PZbhpHo8GlOudsdEVqoxOKcvI/V572huuItTYj0AMnSHglQ1NR0jjeo6ItDblEpU6Anj4knqQYYyOD1CGAJZaKt+2UXsIV3xhkDFhq6Xv83VIj3T4Cju9VsIqFb5eOUUu7er0WpF0rqEqcVliPlq4Ja+eJ1Wl9HnD/5tUeM5yWDHynXTwSlMUCVvnaBtrFbIFhDR9pxWGEnNy6UEjiLRIrYkNMKr+QnzTesB8N8jvfPJoAMcESZAAi675PawbqYxK59ZG+sa/sX83G7GFl5MtB0lUhiyCJPdGUa63QfQw0J8X0dvkZCNcpsDWhbq9B+uu1GL1JS2Rr2uoYSwfIFUiyaQY8KniYlzYb2TyImEQZ7UYkTurIYVutjGJwNqr5KhrZb6flkt/t7fHe/rAScbhm/4lVQFYZYGggitnR6rh262CBl2ML53V1crhLzPjOQWu770y64mZBjf+NwWK84ikPsA0ei2/ph+oWnAkYZVbWVR47AOnLqDed95jJBL5rbAkeSe32MDTPG638pfiBRl/mvPdabZcQ== root@pve2 +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCvZFYXzxAFFa6aDpAOE/SlhU6yDYTxcP3h1NUyZvW2z/pXg05bQ1ZclYwk3RWjBRj4LQeYqClGyiXDo3bTJFusFq+UWVoYr0OIzyEu+5o+rNavErJxXRyiCFb61sSpxwanlec4MqTTNv8SvRHmmZPUjnfHRKjbHbj4LxMZT4cu3PSYAH98ANdgYw5ufZWuCQHxKN9LvQ8K/1JQAbxOGisnovIMtYo96NBk49FWHHQ29O0A5qDMW8HU9jXfWq5GTYplRdr7pnWDJBjuAUudmtG9vcMGZep5ExA2v9nfsbji4jemntBViDwk3mKcYn5NwIWrot89CON5Qe62QRSJnja5c7fSEPs+I2ltJ3ExLWwIMrQQua+yNJlGSkjLw8y1McuSUIk1FNRxLh4S1TDJOZ4zgHwHQFn1CV3+ZCCD0IM0VNKFpOgNmdQoziHIz96dCeZaQRFXl1Cf0YfhRwhuUqI8ifhgy32GfC5HlR82KRWYFNofZymRCMWaN8jMjfZZ3K2RkiAQUfjc9iojzY0NSO9kbM8RorHXNMgNkQVozgE//baULBCAYqT0q9jHd8mdqf4cfZ+Oj/EDqlnX6YNk+AC6VGmp4LlqWZGRdM9ovXoDe9g82RPypJI8fF0Ie0ws9rOQoVEzCmG9d3EXIQXn5M6JF660QgfcUrKTwUW/mKs0eQ== root@pve2 diff --git a/shared/ssh/assembled/pve2-lxc-root.pub b/shared/ssh/assembled/pve2-lxc-root.pub new file mode 100644 index 0000000..cfeb4e8 --- /dev/null +++ b/shared/ssh/assembled/pve2-lxc-root.pub @@ -0,0 +1,12 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOfSnbZKfHpVI9w8ogdfsA7XnYA28goelOfq+w3X02Bx jean@DESKTOP-H9797I1 +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK0ToemBZ+/ibr9k0LHsn2J0JuLalXw//TLmC0ydE7vr jean@DESKTOP-J08NPU2 +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICXDMnLAJlxd56f6BCJAjVFbaTDcI2lLMOQf1OWCGzaA root@pve2 +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC6NPVyFfE1hUimqs18TxjIB72+o59CVVQbJzK0cGmvzROnoR4iGPelwvGFnRUOdUbnGXK5TkQQB6dCnLKHEdULyQ5yXimI37Ywerr5cMorUF3QtWp4WHs2ewPgvdjc0gL0GLlSq7mHQ649PZbhpHo8GlOudsdEVqoxOKcvI/V572huuItTYj0AMnSHglQ1NR0jjeo6ItDblEpU6Anj4knqQYYyOD1CGAJZaKt+2UXsIV3xhkDFhq6Xv83VIj3T4Cju9VsIqFb5eOUUu7er0WpF0rqEqcVliPlq4Ja+eJ1Wl9HnD/5tUeM5yWDHynXTwSlMUCVvnaBtrFbIFhDR9pxWGEnNy6UEjiLRIrYkNMKr+QnzTesB8N8jvfPJoAMcESZAAi675PawbqYxK59ZG+sa/sX83G7GFl5MtB0lUhiyCJPdGUa63QfQw0J8X0dvkZCNcpsDWhbq9B+uu1GL1JS2Rr2uoYSwfIFUiyaQY8KniYlzYb2TyImEQZ7UYkTurIYVutjGJwNqr5KhrZb6flkt/t7fHe/rAScbhm/4lVQFYZYGggitnR6rh262CBl2ML53V1crhLzPjOQWu770y64mZBjf+NwWK84ikPsA0ei2/ph+oWnAkYZVbWVR47AOnLqDed95jJBL5rbAkeSe32MDTPG638pfiBRl/mvPdabZcQ== root@pve2 +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAj1SFdqGjsIrF644ywWANqDMrsrlSBAQiM1HWEfwOIF jean@DESKTOP-L7L1S5V +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII8YFIl6gZ6taPAoDPJtkwkcfEpas9MbVgdkuQuBOJvh jean@DESKTOP-OJIEMRE +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID/3NRXevRiFpmLGkrZTA1Fp2FigYtDvvpG8Ta60U28p jean@x380 +sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIEohWH3Rqh0+h5sYmi921rf3l2mZ0RXebCS8hR9pmHIiAAAABHNzaDo= jean@DESKTOP-DA5D3IG +sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIEGR43JnbBQNZ3U9onHM1XoFiJStBUmGTf2yr9p/haYuAAAABHNzaDo= jean@DESKTOP-2N4HRBF +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIY8YOBNfgjm9AVLycI0V5So2FZFjSk5BTH/K+SLajnw JuiceSSH +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCTs4xGQ9bW9eB3gfPx10Ddi7rxqnYFM+BFT7+DuUeoIiKtOGQLJWO+4ShJt6uNbD1Sk/VRtHxC2zF5h0oe4ZrDLOpVwwD+3e9IkFmtUXcTdVOfL0SDfjeZ8EeW25pTz10pXuwckiKoGGh42a1giATPf9jT63p9qa+hM51DpMMbZPN3k2ZJLzjpwhA4n2xcBbcFGkj3Brp6Tsn3x2T/kDwwuaOAfD8elAwM+etnDE8RhztdFXmWWJqxkTane6nInp0JPeKe/Uq/MEGDNiOhGH5BUC8FFNJQiHF9JspHitL2zbmT7zev29VWJgzMFrD2avn7gVyo9T7VnRF0gxh2ZzeRuDob9tHYYXCg8UG9q4V7uDJBEWoidCKAzn44DDWA4zeBfbwKdh1Muff6LNGwoRtyKgpTf5VsA+zjw/SkKS9xASJZKD4IN11Q1bN4BHhLkwfB0bKFIfDyQlCf+IVb64+uQhICe9qwZPG6SLl0ok2DU3CbL3wdLsQWTrdiOPXtPT0= jean@server6 +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJSVmBVrCmUuCgKS4L3w6jRq2Efi/28ghElDSs22Hu2G abc@bdad197f6631 diff --git a/shared/ssh/assembled/vm101-jean.pub b/shared/ssh/assembled/vm101-jean.pub new file mode 100644 index 0000000..a72759b --- /dev/null +++ b/shared/ssh/assembled/vm101-jean.pub @@ -0,0 +1,11 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOfSnbZKfHpVI9w8ogdfsA7XnYA28goelOfq+w3X02Bx jean@DESKTOP-H9797I1 +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK0ToemBZ+/ibr9k0LHsn2J0JuLalXw//TLmC0ydE7vr jean@DESKTOP-J08NPU2 +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAj1SFdqGjsIrF644ywWANqDMrsrlSBAQiM1HWEfwOIF jean@DESKTOP-L7L1S5V +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII8YFIl6gZ6taPAoDPJtkwkcfEpas9MbVgdkuQuBOJvh jean@DESKTOP-OJIEMRE +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID/3NRXevRiFpmLGkrZTA1Fp2FigYtDvvpG8Ta60U28p jean@x380 +sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIEohWH3Rqh0+h5sYmi921rf3l2mZ0RXebCS8hR9pmHIiAAAABHNzaDo= jean@DESKTOP-DA5D3IG +sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIEGR43JnbBQNZ3U9onHM1XoFiJStBUmGTf2yr9p/haYuAAAABHNzaDo= jean@DESKTOP-2N4HRBF +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIY8YOBNfgjm9AVLycI0V5So2FZFjSk5BTH/K+SLajnw JuiceSSH +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCTs4xGQ9bW9eB3gfPx10Ddi7rxqnYFM+BFT7+DuUeoIiKtOGQLJWO+4ShJt6uNbD1Sk/VRtHxC2zF5h0oe4ZrDLOpVwwD+3e9IkFmtUXcTdVOfL0SDfjeZ8EeW25pTz10pXuwckiKoGGh42a1giATPf9jT63p9qa+hM51DpMMbZPN3k2ZJLzjpwhA4n2xcBbcFGkj3Brp6Tsn3x2T/kDwwuaOAfD8elAwM+etnDE8RhztdFXmWWJqxkTane6nInp0JPeKe/Uq/MEGDNiOhGH5BUC8FFNJQiHF9JspHitL2zbmT7zev29VWJgzMFrD2avn7gVyo9T7VnRF0gxh2ZzeRuDob9tHYYXCg8UG9q4V7uDJBEWoidCKAzn44DDWA4zeBfbwKdh1Muff6LNGwoRtyKgpTf5VsA+zjw/SkKS9xASJZKD4IN11Q1bN4BHhLkwfB0bKFIfDyQlCf+IVb64+uQhICe9qwZPG6SLl0ok2DU3CbL3wdLsQWTrdiOPXtPT0= jean@server6 +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJSVmBVrCmUuCgKS4L3w6jRq2Efi/28ghElDSs22Hu2G abc@bdad197f6631 +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJJvDmBpuduGeVdN92I/tr5YkfmQo5fQ4lI5ZgakRQef root@pve1 diff --git a/shared/ssh/fragments/admin-laptops-extra.pub b/shared/ssh/fragments/admin-laptops-extra.pub new file mode 100644 index 0000000..bf46463 --- /dev/null +++ b/shared/ssh/fragments/admin-laptops-extra.pub @@ -0,0 +1,6 @@ +# Weitere Laptops / Geräte (VM 101, CTs) +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAj1SFdqGjsIrF644ywWANqDMrsrlSBAQiM1HWEfwOIF jean@DESKTOP-L7L1S5V +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII8YFIl6gZ6taPAoDPJtkwkcfEpas9MbVgdkuQuBOJvh jean@DESKTOP-OJIEMRE +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID/3NRXevRiFpmLGkrZTA1Fp2FigYtDvvpG8Ta60U28p jean@x380 +sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIEohWH3Rqh0+h5sYmi921rf3l2mZ0RXebCS8hR9pmHIiAAAABHNzaDo= jean@DESKTOP-DA5D3IG +sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIEGR43JnbBQNZ3U9onHM1XoFiJStBUmGTf2yr9p/haYuAAAABHNzaDo= jean@DESKTOP-2N4HRBF diff --git a/shared/ssh/fragments/admin-mobile.pub b/shared/ssh/fragments/admin-mobile.pub new file mode 100644 index 0000000..948f7b8 --- /dev/null +++ b/shared/ssh/fragments/admin-mobile.pub @@ -0,0 +1,4 @@ +# Mobil / Sonstiges +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIY8YOBNfgjm9AVLycI0V5So2FZFjSk5BTH/K+SLajnw JuiceSSH +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCTs4xGQ9bW9eB3gfPx10Ddi7rxqnYFM+BFT7+DuUeoIiKtOGQLJWO+4ShJt6uNbD1Sk/VRtHxC2zF5h0oe4ZrDLOpVwwD+3e9IkFmtUXcTdVOfL0SDfjeZ8EeW25pTz10pXuwckiKoGGh42a1giATPf9jT63p9qa+hM51DpMMbZPN3k2ZJLzjpwhA4n2xcBbcFGkj3Brp6Tsn3x2T/kDwwuaOAfD8elAwM+etnDE8RhztdFXmWWJqxkTane6nInp0JPeKe/Uq/MEGDNiOhGH5BUC8FFNJQiHF9JspHitL2zbmT7zev29VWJgzMFrD2avn7gVyo9T7VnRF0gxh2ZzeRuDob9tHYYXCg8UG9q4V7uDJBEWoidCKAzn44DDWA4zeBfbwKdh1Muff6LNGwoRtyKgpTf5VsA+zjw/SkKS9xASJZKD4IN11Q1bN4BHhLkwfB0bKFIfDyQlCf+IVb64+uQhICe9qwZPG6SLl0ok2DU3CbL3wdLsQWTrdiOPXtPT0= jean@server6 +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJSVmBVrCmUuCgKS4L3w6jRq2Efi/28ghElDSs22Hu2G abc@bdad197f6631 diff --git a/shared/ssh/fragments/admin-workstations.pub b/shared/ssh/fragments/admin-workstations.pub new file mode 100644 index 0000000..b9acb4c --- /dev/null +++ b/shared/ssh/fragments/admin-workstations.pub @@ -0,0 +1,3 @@ +# Jean — Desktop/Laptop (Hauptzugang Proxmox) +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOfSnbZKfHpVI9w8ogdfsA7XnYA28goelOfq+w3X02Bx jean@DESKTOP-H9797I1 +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK0ToemBZ+/ibr9k0LHsn2J0JuLalXw//TLmC0ydE7vr jean@DESKTOP-J08NPU2 diff --git a/shared/ssh/fragments/host-pve1.pub b/shared/ssh/fragments/host-pve1.pub new file mode 100644 index 0000000..dab6b68 --- /dev/null +++ b/shared/ssh/fragments/host-pve1.pub @@ -0,0 +1,2 @@ +# Host-Key pve1 (192.168.10.5) — für SSH von pve2 → pve1 +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJJvDmBpuduGeVdN92I/tr5YkfmQo5fQ4lI5ZgakRQef root@pve1 diff --git a/shared/ssh/fragments/host-pve2.pub b/shared/ssh/fragments/host-pve2.pub new file mode 100644 index 0000000..bc30e34 --- /dev/null +++ b/shared/ssh/fragments/host-pve2.pub @@ -0,0 +1,3 @@ +# Host-Keys pve2 (192.168.10.4) — Ansible + Host-zu-Host +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICXDMnLAJlxd56f6BCJAjVFbaTDcI2lLMOQf1OWCGzaA root@pve2 +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC6NPVyFfE1hUimqs18TxjIB72+o59CVVQbJzK0cGmvzROnoR4iGPelwvGFnRUOdUbnGXK5TkQQB6dCnLKHEdULyQ5yXimI37Ywerr5cMorUF3QtWp4WHs2ewPgvdjc0gL0GLlSq7mHQ649PZbhpHo8GlOudsdEVqoxOKcvI/V572huuItTYj0AMnSHglQ1NR0jjeo6ItDblEpU6Anj4knqQYYyOD1CGAJZaKt+2UXsIV3xhkDFhq6Xv83VIj3T4Cju9VsIqFb5eOUUu7er0WpF0rqEqcVliPlq4Ja+eJ1Wl9HnD/5tUeM5yWDHynXTwSlMUCVvnaBtrFbIFhDR9pxWGEnNy6UEjiLRIrYkNMKr+QnzTesB8N8jvfPJoAMcESZAAi675PawbqYxK59ZG+sa/sX83G7GFl5MtB0lUhiyCJPdGUa63QfQw0J8X0dvkZCNcpsDWhbq9B+uu1GL1JS2Rr2uoYSwfIFUiyaQY8KniYlzYb2TyImEQZ7UYkTurIYVutjGJwNqr5KhrZb6flkt/t7fHe/rAScbhm/4lVQFYZYGggitnR6rh262CBl2ML53V1crhLzPjOQWu770y64mZBjf+NwWK84ikPsA0ei2/ph+oWnAkYZVbWVR47AOnLqDed95jJBL5rbAkeSe32MDTPG638pfiBRl/mvPdabZcQ== root@pve2 diff --git a/shared/ssh/fragments/legacy-pve1-rsa.pub b/shared/ssh/fragments/legacy-pve1-rsa.pub new file mode 100644 index 0000000..844b2ba --- /dev/null +++ b/shared/ssh/fragments/legacy-pve1-rsa.pub @@ -0,0 +1,2 @@ +# Legacy: RSA auf pve1, Kommentar irreführend „root@pve2“ — steht noch auf pve1 authorized_keys +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCvZFYXzxAFFa6aDpAOE/SlhU6yDYTxcP3h1NUyZvW2z/pXg05bQ1ZclYwk3RWjBRj4LQeYqClGyiXDo3bTJFusFq+UWVoYr0OIzyEu+5o+rNavErJxXRyiCFb61sSpxwanlec4MqTTNv8SvRHmmZPUjnfHRKjbHbj4LxMZT4cu3PSYAH98ANdgYw5ufZWuCQHxKN9LvQ8K/1JQAbxOGisnovIMtYo96NBk49FWHHQ29O0A5qDMW8HU9jXfWq5GTYplRdr7pnWDJBjuAUudmtG9vcMGZep5ExA2v9nfsbji4jemntBViDwk3mKcYn5NwIWrot89CON5Qe62QRSJnja5c7fSEPs+I2ltJ3ExLWwIMrQQua+yNJlGSkjLw8y1McuSUIk1FNRxLh4S1TDJOZ4zgHwHQFn1CV3+ZCCD0IM0VNKFpOgNmdQoziHIz96dCeZaQRFXl1Cf0YfhRwhuUqI8ifhgy32GfC5HlR82KRWYFNofZymRCMWaN8jMjfZZ3K2RkiAQUfjc9iojzY0NSO9kbM8RorHXNMgNkQVozgE//baULBCAYqT0q9jHd8mdqf4cfZ+Oj/EDqlnX6YNk+AC6VGmp4LlqWZGRdM9ovXoDe9g82RPypJI8fF0Ie0ws9rOQoVEzCmG9d3EXIQXn5M6JF660QgfcUrKTwUW/mKs0eQ== root@pve2 diff --git a/shared/ssh/install-authorized-keys.sh b/shared/ssh/install-authorized-keys.sh new file mode 100755 index 0000000..1ac8fed --- /dev/null +++ b/shared/ssh/install-authorized-keys.sh @@ -0,0 +1,131 @@ +#!/usr/bin/env bash +# authorized_keys aus docu/shared/ssh deployen +set -euo pipefail + +DOCU_ROOT="${DOCU_ROOT:-/root/docu}" +SSH_DIR="$DOCU_ROOT/shared/ssh" +DRY_RUN=0 +TARGET="" +DEST="" +REMOTE="" +CT_IDS=() + +usage() { + cat <<'EOF' +Usage: install-authorized-keys.sh [options] + +Targets: + proxmox-root → /root/.ssh/authorized_keys auf Proxmox-Hosts + vm101-jean → jean@192.168.10.10 ~/.ssh/authorized_keys + pve2-lxc-root → root in CTs auf pve2 (101 docker, 109 media, 110 aidev) + +Options: + --dest PATH Lokale Zieldatei (nur proxmox-root, default: /root/.ssh/authorized_keys) + --remote USER@HOST Auf Remote-Host installieren (proxmox-root / vm101-jean) + --ct VMID Nur einen CT (pve2-lxc-root, mehrfach möglich) + --dry-run Nur anzeigen, nicht schreiben + -h Hilfe + +Beispiele: + ./install-authorized-keys.sh proxmox-root + ./install-authorized-keys.sh --remote root@192.168.10.5 proxmox-root + ./install-authorized-keys.sh vm101-jean + ./install-authorized-keys.sh pve2-lxc-root --ct 101 +EOF +} + +log() { printf '%s\n' "$*"; } + +run() { + if (( DRY_RUN )); then + log "[dry-run] $*" + else + "$@" + fi +} + +install_local_file() { + local src="$1" dest="$2" + run mkdir -p "$(dirname "$dest")" + run chmod 700 "$(dirname "$dest")" + if (( DRY_RUN )); then + log "[dry-run] cp $src → $dest" + head -3 "$src" + log "… ($(wc -l <"$src") Zeilen)" + else + install -m 600 -o root -g root "$src" "$dest" + log "Installiert: $dest ($(wc -l <"$dest") Keys)" + fi +} + +install_remote() { + local src="$1" remote="$2" dest="$3" + if (( DRY_RUN )); then + log "[dry-run] ssh $remote install -m 600 … ← $src" + return + fi + ssh "$remote" "mkdir -p $(dirname "$dest") && chmod 700 $(dirname "$dest")" + scp -q "$src" "$remote:/tmp/authorized_keys.new" + ssh "$remote" "install -m 600 -o \$(id -un) -g \$(id -gn) /tmp/authorized_keys.new '$dest' && rm -f /tmp/authorized_keys.new" + log "Installiert auf $remote:$dest" +} + +install_pve2_ct() { + local src="$1" vmid="$2" + if (( DRY_RUN )); then + log "[dry-run] pct exec $vmid → /root/.ssh/authorized_keys" + return + fi + pct exec "$vmid" -- mkdir -p /root/.ssh + pct exec "$vmid" -- chmod 700 /root/.ssh + pct push "$vmid" "$src" /root/.ssh/authorized_keys + pct exec "$vmid" -- chmod 600 /root/.ssh/authorized_keys + log "CT $vmid: /root/.ssh/authorized_keys ($(wc -l <"$src") Keys)" +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --dest) DEST="$2"; shift 2 ;; + --remote) REMOTE="$2"; shift 2 ;; + --ct) CT_IDS+=("$2"); shift 2 ;; + --dry-run) DRY_RUN=1; shift ;; + -h|--help) usage; exit 0 ;; + -*) echo "Unbekannte Option: $1" >&2; usage >&2; exit 1 ;; + *) TARGET="$1"; shift ;; + esac +done + +[[ -n "$TARGET" ]] || { usage >&2; exit 1; } +[[ -d "$SSH_DIR/assembled" ]] || { echo "Fehlt: $SSH_DIR (git pull?)" >&2; exit 1; } + +case "$TARGET" in + proxmox-root) + SRC="$SSH_DIR/assembled/proxmox-root.pub" + DEST="${DEST:-/root/.ssh/authorized_keys}" + if [[ -n "$REMOTE" ]]; then + install_remote "$SRC" "$REMOTE" "$DEST" + else + install_local_file "$SRC" "$DEST" + fi + ;; + vm101-jean) + SRC="$SSH_DIR/assembled/vm101-jean.pub" + DEST="${DEST:-/home/jean/.ssh/authorized_keys}" + REMOTE="${REMOTE:-jean@192.168.10.10}" + install_remote "$SRC" "$REMOTE" "$DEST" + ;; + pve2-lxc-root) + SRC="$SSH_DIR/assembled/pve2-lxc-root.pub" + if [[ ${#CT_IDS[@]} -eq 0 ]]; then + CT_IDS=(101 109 110) + fi + for vmid in "${CT_IDS[@]}"; do + install_pve2_ct "$SRC" "$vmid" + done + ;; + *) + echo "Unbekanntes Target: $TARGET" >&2 + usage >&2 + exit 1 + ;; +esac diff --git a/shared/ssh/rebuild-assembled.sh b/shared/ssh/rebuild-assembled.sh new file mode 100755 index 0000000..f3b47ae --- /dev/null +++ b/shared/ssh/rebuild-assembled.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash +# Regeneriert assembled/*.pub aus fragments/ (Dedupe nach Key-Material, Feld 2) +set -euo pipefail +ROOT="$(cd "$(dirname "$0")" && pwd)" +FRAG="$ROOT/fragments" +ASM="$ROOT/assembled" + +dedupe() { awk '!seen[$2]++'; } + +mkdir -p "$ASM" + +build() { + local out="$1"; shift + { for f in "$@"; do cat "$f"; done; } | grep -v '^#' | grep -v '^$' | dedupe > "$ASM/$out" +} + +build proxmox-root.pub \ + "$FRAG/admin-workstations.pub" \ + "$FRAG/host-pve1.pub" \ + "$FRAG/host-pve2.pub" \ + "$FRAG/legacy-pve1-rsa.pub" + +build vm101-jean.pub \ + "$FRAG/admin-workstations.pub" \ + "$FRAG/admin-laptops-extra.pub" \ + "$FRAG/admin-mobile.pub" \ + "$FRAG/host-pve1.pub" + +build pve2-lxc-root.pub \ + "$FRAG/admin-workstations.pub" \ + "$FRAG/host-pve2.pub" \ + "$FRAG/admin-laptops-extra.pub" \ + "$FRAG/admin-mobile.pub" + +build authorized_keys.all.pub \ + "$FRAG"/*.pub + +echo "OK: $(wc -l "$ASM"/*.pub | tail -1)"