Gordon Zimm a643d64172 Seed: Erstinbetriebnahme-Anleitung auf den jetzigen Ablauf gebracht
Die Datei /root/ERSTINBETRIEBNAHME.txt ist das Erste, was ein Techniker nach
dem ersten Start liest. Sie nannte noch einen Trigger, den es nicht gibt, und
liess die Voraussetzungen offen.

Jetzt steht drin, was vor dem ersten Task im RMM gesetzt sein muss (Company
Unique Id, Site, Device Group, Friendly Name, espo_fqdn, espo_ring,
espo_zustand), in welcher Reihenfolge ESPO-01 bis ESPO-03 laufen, und dass das
Admin-Passwort vor DNS und Portfreigabe zu wechseln ist. Dazu der veraltete
Kommentar zum Hostnamen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-25 09:32:57 +02:00

144 lines
8.4 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#cloud-config
# Autoinstall-Seed für die EspoCRM-Kunden-VM (Ubuntu Server 24.04 LTS auf Hyper-V).
# Stand 2026-09-22, abgeleitet aus gitea gordon.zimm/ubuntu-autoinstall (Commit 0cd9fc6).
# Auf einer Test-VM am 22.09.2026 vollständig durchgelaufen (Ubuntu 24.04.5, Hyper-V, 127-GiB-Platte).
#
# Grundsätze:
# * Der Seed wird per NoCloud über HTTP geladen und kann sich nicht authentisieren
# -> er enthält KEIN Geheimnis: kein benutzbares Passwort, kein RMM-Token.
# * Die ISO richtet nur das Minimum ein. Docker, Härtung, Logrotation, Firewall, Hostname und
# Instanz kommen reproduzierbar über RMM-Tasks ("alles Weitere erfolgt über ConnectWise RMM").
autoinstall:
version: 1
source:
id: ubuntu-server
updates: all
locale: de_DE.UTF-8
keyboard:
layout: de
timezone: Europe/Berlin
# Schnittstellennamen nicht festnageln: Hyper-V meldet je nach Kernel eth0 oder enp*.
network:
version: 2
ethernets:
primary:
match:
name: "e*"
dhcp4: true
dhcp6: false
identity:
realname: administrator
username: administrator
hostname: unkonfiguriert # ESPO-01 setzt ihn auf espo-<kuerzel> aus %companyuniqueid%
# Hash eines einmalig erzeugten 48-Zeichen-Zufallspassworts; der Klartext wurde nirgends abgelegt.
# Das Konto ist damit faktisch gesperrt, bis die late-commands je VM ein Initialpasswort setzen.
password: '$6$KDLfMzLPV6wWtwA4$9VnUET9kFWUVp07z2hkN9Kv9.WjgdIPGLmm7zb4Te47Ms7lyrw/9Ewpqf759xVfXcOfAkWJSFrxUClfeyxeuI0'
ssh:
install-server: true
allow-pw: true
authorized-keys: []
packages:
- linux-cloud-tools-virtual # hv_kvp_daemon, hv_vss_daemon, hv_fcopy_daemon
- curl # Voraussetzung des RMM-Agenten (libcurl4 kommt mit)
- bzip2 # der .run-Installer entpackt sich damit
# kein linux-azure: der Azure-Kernel ist für Azure gebaut. Soll er trotzdem bleiben,
# gehört linux-cloud-tools-azure dazu, sonst fehlen die Hyper-V-Integrationsdienste.
# Explizite Aufteilung: klein für das Betriebssystem, groß für die Nutzdaten.
# `layout: {name: lvm, sizing-policy: scaled}` wäre kürzer, gibt aber rund die Hälfte der Platte an "/" –
# auf einer reinen Docker-VM ist das an der falschen Stelle.
storage:
config:
- {type: disk, id: disk0, ptable: gpt, match: {size: largest}, wipe: superblock-recursive, preserve: false, grub_device: false}
- {type: partition, id: part-esp, device: disk0, size: 1G, flag: boot, grub_device: true, preserve: false}
- {type: format, id: fmt-esp, volume: part-esp, fstype: fat32, preserve: false}
- {type: mount, id: mnt-esp, device: fmt-esp, path: /boot/efi}
- {type: partition, id: part-boot, device: disk0, size: 2G, preserve: false}
- {type: format, id: fmt-boot, volume: part-boot, fstype: ext4, preserve: false}
- {type: mount, id: mnt-boot, device: fmt-boot, path: /boot}
- {type: partition, id: part-pv, device: disk0, size: -1, flag: lvm, preserve: false}
- {type: lvm_volgroup, id: vg0, name: ubuntu-vg, devices: [part-pv], preserve: false}
# Betriebssystem, RMM-Agent, Logs – 20 GiB sind reichlich (Bedarf real 8–12 GiB)
- {type: lvm_partition, id: lv-root, name: root-lv, volgroup: vg0, size: 20G, preserve: false}
- {type: format, id: fmt-root, volume: lv-root, fstype: ext4, preserve: false}
- {type: mount, id: mnt-root, device: fmt-root, path: /}
# `path: none` für Swap ist am 22.09.2026 auf einer Test-VM bestätigt worden.
- {type: lvm_partition, id: lv-swap, name: swap-lv, volgroup: vg0, size: 4G, preserve: false}
- {type: format, id: fmt-swap, volume: lv-swap, fstype: swap, preserve: false}
- {type: mount, id: mnt-swap, device: fmt-swap, path: none}
# Images, Container, Container-Logs – reproduzierbar, wird nicht gesichert
- {type: lvm_partition, id: lv-docker, name: docker-lv, volgroup: vg0, size: 20G, preserve: false}
- {type: format, id: fmt-docker, volume: lv-docker, fstype: ext4, preserve: false}
- {type: mount, id: mnt-docker, device: fmt-docker, path: /var/lib/docker}
# /srv (Nutzdaten) wird in late-commands anteilig angelegt, damit der Seed für jede Plattengröße passt.
late-commands:
# 1. /srv bekommt den gesamten Rest bis auf 10 GiB. Die bleiben als Platz für einen LVM-Snapshot
# vor einem Update frei: Ein Snapshot hält nur die während seiner Lebensdauer geänderten Blöcke,
# dafür sind 10 GiB reichlich. Eine grössere Reserve lohnt nicht – die VHDX ist dynamisch und
# jederzeit erweiterbar. Das lvreduce läuft vor dem mkfs und ist deshalb risikolos.
# Beispiel 128-GiB-Platte: / 20, swap 4, docker 20, /srv ~71, frei 10 GiB.
- lvcreate -y -l 100%FREE -n srv-lv ubuntu-vg
- lvreduce -y -L -10G /dev/ubuntu-vg/srv-lv
- mkfs.ext4 -q -L srv /dev/ubuntu-vg/srv-lv
- mkdir -p /target/srv
# Eintrag über UUID, wie curtin es für die übrigen Dateisysteme tut.
- >-
SRVUUID=$(blkid -s UUID -o value /dev/ubuntu-vg/srv-lv);
printf 'UUID=%s /srv ext4 defaults 0 2\n' "$SRVUUID" >> /target/etc/fstab
# 2. Je VM zufälliges Initialpasswort, am Konsolen-Login sichtbar, Wechsel beim ersten Anmelden erzwungen.
- >-
PW=$(tr -dc 'A-Za-z0-9' </dev/urandom | head -c 20);
echo "administrator:$PW" | curtin in-target -- chpasswd;
curtin in-target -- chage -d 0 administrator;
printf '\n Initialpasswort administrator: %s\n (beim ersten Anmelden ändern, danach diese Zeile aus /etc/issue entfernen)\n' "$PW" >> /target/etc/issue
# 3. Hyper-V-Integrationsdienste (Zeit/IP-Meldung, Dateisystem-Quiesce für Veeam).
# Normalerweise startet udev sie selbst; enable schadet nicht und darf fehlschlagen.
- curtin in-target -- systemctl enable hv-kvp-daemon.service hv-vss-daemon.service || true
# 4. Kurze Anleitung für den einen Handgriff nach dem ersten Start (Passwort + RMM-Agent).
- >-
printf '%s\n'
'# Erstinbetriebnahme dieser VM - Registrierung im RMM von Hand (ADR 0004)'
'#'
'# 1. Initialpasswort steht am Konsolen-Login (/etc/issue), der Wechsel wird erzwungen.'
'# Neues Passwort im Secure Information Store der Site hinterlegen.'
'# 2. RMM-Agent installieren (Token aus: Endpoints > Devices > Manage > Download Agent):'
'# cd /tmp && curl -fsSLOJ "<Download-Link aus dem Portal>" && chmod +x *_ITSPlatform_TKN*.run && sudo ./*_ITSPlatform_TKN*.run; rm -f *_ITSPlatform_TKN*.run'
'# Pruefen: systemctl status ITSPlatform ; tail /var/log/ITSPlatform-install.log'
'# 3. Im RMM am Geraet einstellen, BEVOR ein Task laeuft:'
'# - Company Unique Id der Company gesetzt (liefert den Hostnamen espo-<kuerzel>)'
'# - Geraet der richtigen Site und der Device Group EspoCRM zugeordnet'
'# - Friendly Name setzen (folgt dem Hostnamen nicht von selbst)'
'# - Custom Fields: espo_fqdn, espo_ring (0 intern, 1 Pilotkunde, 2 Produktivkunde), espo_zustand = NEU'
'# 4. Tasks in dieser Reihenfolge von Hand starten:'
'# ESPO-01-Grundeinrichtung -> Hostname, SSH-Haertung, Journal, Zeitzone'
'# ESPO-02-Docker -> Docker in gepinnter Version, Logrotation, DOCKER-USER'
'# ESPO-03-First-Deploy -> Geheimnisse, Compose aus dem Ring, EspoCRM starten'
'# ESPO-01 entfernt die Zeile mit dem Initialpasswort aus /etc/issue selbst.'
'# 5. Nach ESPO-03: Admin-Passwort in EspoCRM aendern (admin/admin) und im Safe ablegen.'
'# ERST DANACH DNS-Eintrag und Portfreigabe an der OPNsense setzen.'
> /target/root/ERSTINBETRIEBNAHME.txt
# 5. Zum Schluss alles auf den neuesten Stand bringen. `updates: all` deckt den Stand zu Beginn der
# Installation ab; dieser Durchgang holt, was währenddessen dazugekommen ist, und räumt auf.
# NEEDRESTART_SUSPEND verhindert Dienst-Neustarts im chroot.
- curtin in-target -- sh -c 'DEBIAN_FRONTEND=noninteractive NEEDRESTART_SUSPEND=1 apt-get update'
- curtin in-target -- sh -c 'DEBIAN_FRONTEND=noninteractive NEEDRESTART_SUSPEND=1 apt-get -y -o Dpkg::Options::=--force-confold -o Dpkg::Options::=--force-confdef full-upgrade'
- curtin in-target -- sh -c 'DEBIAN_FRONTEND=noninteractive apt-get -y autoremove --purge'
- curtin in-target -- sh -c 'apt-get clean'
shutdown: poweroff