Autoinstall-Seed und Repo-Struktur
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
commit
f5da6fd3b3
25
README.md
Normal file
25
README.md
Normal file
@ -0,0 +1,25 @@
|
||||
# espo-deploy
|
||||
|
||||
Öffentliche, **geheimnisfreie** Artefakte für EspoCRM-Kunden-VMs. Alles hier wird von den VMs ohne Anmeldung abgerufen –
|
||||
es steht deshalb **kein Passwort, kein Token und keine Kundenangabe** in diesem Repository.
|
||||
|
||||
| Verzeichnis | Inhalt |
|
||||
|---|---|
|
||||
| `autoinstall/` | NoCloud-Seed für die Basis-VM (`user-data`, `meta-data`) |
|
||||
| `rmm-tasks/` | Skripte, die als Task im ConnectWise RMM hinterlegt sind (Quelle für Änderungen) |
|
||||
| `bin/` | Skripte, die auf der VM unter `/srv/espo/bin/` liegen und von Tasks aufgerufen werden |
|
||||
|
||||
Konzept, Entscheidungen und Runbooks liegen im internen Repository `espoCRM/espo-fleet-ops`.
|
||||
|
||||
## Autoinstall
|
||||
|
||||
```
|
||||
autoinstall ds=nocloud-net\;s=https://gitea.subcura.net/EspoCRM_Public/espo-deploy/raw/branch/main/autoinstall/
|
||||
```
|
||||
|
||||
Das Semikolon muss maskiert werden – in GRUB trennt ein unmaskiertes `;` zwei Befehle.
|
||||
|
||||
## Kundenspezifisches
|
||||
|
||||
Gehört **nicht** hierher, sondern in die Custom Fields des RMM (FQDN, Ring, Wartungsfenster) und in die `.env` auf der
|
||||
jeweiligen VM. Geheimnisse entstehen auf der VM und verlassen sie nicht.
|
||||
85
autoinstall/README.md
Normal file
85
autoinstall/README.md
Normal file
@ -0,0 +1,85 @@
|
||||
# Autoinstall-Seed für die EspoCRM-Kunden-VM
|
||||
|
||||
`user-data` und `meta-data` bilden eine NoCloud-Quelle. Das Verzeichnis ist öffentlich abrufbar – es enthält deshalb
|
||||
**keine Geheimnisse** (kein benutzbares Passwort, kein RMM-Token; siehe [ADR 0004](../docs/decisions/0004-bootstrap-der-kunden-vm.md)).
|
||||
|
||||
## Ablageort
|
||||
|
||||
**Für den Testbetrieb (Entscheidung 22.09.2026, Variante 1):** Der Seed liegt zusätzlich in
|
||||
`gordon.zimm/ubuntu-autoinstall` unter `espocrm/`, weil der Installer ohne Anmeldung abruft und die Organisation
|
||||
`espoCRM` anonym nicht lesbar ist. Boot-Zeile:
|
||||
|
||||
```
|
||||
autoinstall ds=nocloud-net\;s=https://gitea.subcura.net/EspoCRM_Public/espo-deploy/raw/branch/main/autoinstall/
|
||||
```
|
||||
|
||||
**Dieses Verzeichnis ist die Quelle.** Änderungen hier vornehmen und anschließend nach `ubuntu-autoinstall/espocrm/`
|
||||
kopieren (`user-data`, `meta-data`). Der endgültige Ablageort bleibt offen → O-26.
|
||||
|
||||
## Hintergrund zur Wahl (O-26)
|
||||
|
||||
Der Installer ruft die Dateien **ohne Anmeldung** ab. Dieses Repo liegt in der Organisation `espoCRM`, die nicht öffentlich
|
||||
sichtbar ist: Ein anonymer Abruf von
|
||||
`https://gitea.subcura.net/espoCRM/espo-fleet-ops/raw/branch/main/autoinstall/user-data` liefert **404**, obwohl das Repo
|
||||
selbst als „nicht privat“ geführt wird (geprüft am 22.09.2026; dasselbe gilt für `espoCRM/espo-dev`).
|
||||
Anonym erreichbar ist dagegen `gordon.zimm/ubuntu-autoinstall`.
|
||||
|
||||
Solange das nicht entschieden ist, kommt der Seed dort hin, wo er abrufbar ist. Kandidaten:
|
||||
|
||||
| Variante | Boot-Zeile | Bemerkung |
|
||||
|---|---|---|
|
||||
| Unterverzeichnis im bestehenden öffentlichen Repo | `autoinstall ds=nocloud-net\;s=https://gitea.subcura.net/EspoCRM_Public/espo-deploy/raw/branch/main/autoinstall/` | schnellster Weg, stört den bestehenden Seed im Wurzelverzeichnis nicht |
|
||||
| eigenes öffentliches Repo, z. B. `gordon.zimm/espo-autoinstall` | `… ds=nocloud-net\;s=…/gordon.zimm/espo-autoinstall/raw/branch/main/` | klar abgegrenzt, ein Zweck |
|
||||
| Organisation `espoCRM` auf „öffentlich“ stellen | `… ds=nocloud-net\;s=…/espoCRM/espo-fleet-ops/raw/branch/main/autoinstall/` | macht auch `espoCRM/espo-dev` für jeden lesbar – nicht empfohlen |
|
||||
| Seed auf der Installationsmedium-Seite (zweites CIDATA-Image) | keine URL nötig | für die Flotte die sauberste Lösung, in Phase 3 zu prüfen |
|
||||
|
||||
### Das Semikolon muss maskiert werden
|
||||
|
||||
In der GRUB-Kommandozeile trennt `;` zwei GRUB-Befehle. Unmaskiert bekommt der Kernel nur `autoinstall ds=nocloud-net`,
|
||||
die Seed-URL fällt weg – der Installer startet dann ganz normal interaktiv. Deshalb **`\;`** schreiben (so steht es auch
|
||||
in der README von `ubuntu-autoinstall`). Gleichwertig: die ganze Angabe in Anführungszeichen setzen,
|
||||
`autoinstall "ds=nocloud-net;s=https://…/espocrm/"`.
|
||||
|
||||
Nach dem Booten der Ubuntu-Server-24.04-ISO im GRUB-Menü `e` drücken, die gewählte Zeile vor `---` einfügen, Strg+X.
|
||||
Die Installation läuft ohne Rückfragen durch und **schaltet die VM am Ende aus**. Vor dem nächsten Start das ISO trennen,
|
||||
sonst startet die Installation erneut.
|
||||
|
||||
## Nach dem ersten Start (einmal je VM)
|
||||
|
||||
Die Schritte liegen auch als `/root/ERSTINBETRIEBNAHME.txt` auf der VM:
|
||||
|
||||
1. An der Hyper-V-Konsole anmelden. Das **Initialpasswort** steht über dem Login-Prompt (`/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*:
|
||||
|
||||
```bash
|
||||
cd /tmp && curl -fsSLOJ "<Download-Link aus dem Portal>" && chmod +x *_ITSPlatform_TKN*.run && sudo ./*_ITSPlatform_TKN*.run; rm -f *_ITSPlatform_TKN*.run
|
||||
```
|
||||
|
||||
`-O -J` behält den vom Server vorgegebenen Dateinamen samt `_TKN<uuid>` – daraus liest der Installer den Token.
|
||||
Das abschließende `rm` hängt an `;`, damit die Datei mit dem Token auch nach einem Abbruch verschwindet.
|
||||
|
||||
Der Token steckt im Dateinamen; der Installer liest ihn aus seiner eigenen Aufrufzeile. Wird die Datei umbenannt
|
||||
(`-o name.run`) oder nur mit `-O` geladen (dann heißt sie `setup`), fehlt der Token und muss gesetzt werden:
|
||||
`sudo env TOKEN=<uuid> ./datei.run`.
|
||||
|
||||
Prüfen: `systemctl status ITSPlatform` und `tail /var/log/ITSPlatform-install.log`; das Gerät erscheint nach
|
||||
wenigen Minuten in der Site. Einzelheiten zur Token-Übergabe:
|
||||
[../docs/quellen/connectwise-rmm-notizen.md](../docs/quellen/connectwise-rmm-notizen.md)
|
||||
|
||||
3. Hinweiszeile mit dem Initialpasswort aus `/etc/issue` entfernen.
|
||||
4. Prüfen, dass das Gerät binnen weniger Minuten in der richtigen Site erscheint.
|
||||
|
||||
## Was der Seed einrichtet
|
||||
|
||||
| Bereich | Wert |
|
||||
|---|---|
|
||||
| Sprache/Tastatur/Zeitzone | de_DE.UTF-8, de, Europe/Berlin |
|
||||
| Netz | erste Ethernet-Schnittstelle (`match: e*`), DHCPv4, kein DHCPv6 |
|
||||
| Konto | `administrator`, Passwort je VM zufällig, Wechsel beim ersten Anmelden erzwungen |
|
||||
| SSH | Server installiert, Passwortanmeldung erlaubt ([ADR 0003](../docs/decisions/0003-zugangsmodell-ssh.md)) |
|
||||
| Pakete | `linux-cloud-tools-virtual` (Hyper-V-Integrationsdienste), `curl`, `bzip2` |
|
||||
| Platten | `/` 20 GiB, swap 4 GiB, `/var/lib/docker` 20 GiB, `/srv` 80 % des Rests, ~20 % frei in der VG |
|
||||
| Zum Schluss | vollständiges `apt full-upgrade`, `autoremove`, `clean`, dann `poweroff` |
|
||||
|
||||
Hintergrund zur Aufteilung: [plattenlayout-erlaeuterung.md](../docs/entwuerfe/plattenlayout-erlaeuterung.md).
|
||||
0
autoinstall/meta-data
Normal file
0
autoinstall/meta-data
Normal file
128
autoinstall/user-data
Normal file
128
autoinstall/user-data
Normal file
@ -0,0 +1,128 @@
|
||||
#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 # setzt der RMM-Baseline-Task auf crm-<site>
|
||||
# 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 80 % des verbliebenen Platzes; ~20 % bleiben unbelegt als Reserve und für LVM-Snapshots.
|
||||
# Beispiel 128-GiB-Platte: / 20, swap 4, docker 20, /srv ~65, frei ~16 GiB.
|
||||
- lvcreate -y -l 80%FREE -n srv-lv ubuntu-vg
|
||||
- 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 (Entscheidung 2026-09-22: Registrierung im RMM von Hand)'
|
||||
'# 1. Initialpasswort steht am Konsolen-Login (/etc/issue), 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. Zeile mit dem Initialpasswort aus /etc/issue entfernen.'
|
||||
'# Alles Weitere erledigt der Baseline-Task, den der Trigger "First check-in" anstoesst.'
|
||||
> /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
|
||||
Loading…
x
Reference in New Issue
Block a user