[Projekt] automatisierte Debian-Installation für Monitoring-Appliance

Smalltalk
Antworten
Benutzeravatar
heisenberg
Beiträge: 3567
Registriert: 04.06.2015 01:17:27
Lizenz eigener Beiträge: MIT Lizenz

[Projekt] automatisierte Debian-Installation für Monitoring-Appliance

Beitrag von heisenberg » 22.08.2023 20:01:49

Hallo zusammen,

ich bin gerade dabei, ein kleines Projekt mit Debian umzusetzen. Hier möchte ich für mich und andere Interessierte dabei dokumentieren, was ich so getan habe, was mir so an Fehlern über den Weg gelaufen kam, ebenso wie Details zur Umsetzung. Dieser erste Beitrag wird im Laufe der Zeit jeweils Updates bekommen zusätzlich zu den angefügten neuen Beiträgen. Falls jemand zu irgend etwas seinen Senf dazu geben möchte oder zu irgend etwas Fragen hat: Ich freue mich über jedwede Beteilligung. Und falls die eine oder andere Anmerkung nicht interessant für mich ist; vielleicht ist sie ja interessant für jemanden anderen.

Beschreibung

Ich möchte kleine Mini-Server vollautomatisch mit Debian installieren. Am Ende sollen fertige Rechner rauskommen, die als Monitoring-Stationen für Remote-Standorte eingesetzt werden. Auf den Rechnern sollen auch diverse Docker-Container laufen, die entweder automatisiert erstellt oder - falls ein Backup vorhanden ist, damit restauriert werden. Als Monitoring-Software wird CheckMK im Docker-Container laufen. Auf jedem Server wird weiterhin ein sekunderärer Nameserver mit PowerDNS laufen, der seine Daten in einer MariaDB mit dem primären Nameserver synchronisiert.

Das sind so die Themen, die mir dabei aktuell vorschweben: automatische Debian-Installation; Monitoring; docker; docker-compose; Pre-Seeding; PowerDNS; CheckMK; dnsmasq; Debian-Paketmanagement; Debian-Paket-Repositories; Shellscripting; MariaDB-Replikation; Backup/Restore

Grüße,
h.
Jede Rohheit hat ihren Ursprung in einer Schwäche.

Benutzeravatar
heisenberg
Beiträge: 3567
Registriert: 04.06.2015 01:17:27
Lizenz eigener Beiträge: MIT Lizenz

Re: [Projekt] automatisierte Debian-Installation für Monitoring-Appliance

Beitrag von heisenberg » 22.08.2023 20:14:48

Installation: Debian-Preseeding

Für die Installation habe ich mir Debian-Preseeding ausgesucht. Beispieldateien für die automatische Debian-Installation mit ausführlichen Kommentaren sind ja im Internet leicht zu finden. Anlaufstelle ist hier die offizielle Debian Dokumentation: https://wiki.debian.org/DebianInstaller/Preseed

Als erstes habe ich mit einem Standard-Debian-ISO-Image mal eine Installation gestartet und dort über den Punkt "Help" am Bootprompt die Preseed-Datei auf meinem Server angegeben. auto url=http://mein.server.name/preseed.cfg. Das hat schon mal funktioniert. Die Installation lief schön sauber automatisch durch.

Jetzt brauche ich noch ein individuelles Netinstaller Image, dem ich sowohl die preseed-URL von meinem Webserver angebe, als auch das Setup-Script mitgebe, dass er nach der Debian-Installation zum weiterführenden Setup aufrufen soll.

Das Verändern des Debian-Images ist hier beschrieben:

https://wiki.debian.org/DebianInstaller/Modify/CD

D. h. ich ziehe mir zunächst mal ein aktuelles Netboot-Image und entpacke das gemäß obiger Anleitung.

Dann entschlacke ich mir die Grub-Konfiguration im Unterverzeichnis $CD/boot/grub/grub.cfg an, dass diese nun ungefähr so aussieht:

if [ x$feature_default_font_path = xy ] ; then
font=unicode
else
font=$prefix/font.pf2
fi

if loadfont $font ; then
set gfxmode=800x600
set gfxpayload=keep
insmod efi_gop
insmod efi_uga
insmod video_bochs
insmod video_cirrus
insmod gfxterm
insmod png
terminal_output gfxterm
fi

set color_normal=light-gray/black
set color_highlight=white/black

set default "0"
set timeout 30

menuentry --hotkey=i 'Install' {
set background_color=black
# TODO: password-protect preseed.cfg: password is contained!
linux /install.amd/vmlinuz vga=788 --- quiet url=http://myserver.domain.tld/preseed.cfg
initrd /install.amd/initrd.gz
}


Die Boot-Konfiguration ist nicht grub, sondern isolinux. Vielleicht brauche ich die Grub-Konfiguration noch, wenn ich später das Image auf einen USB-Stick schreibe? Also jetzt erst einmal isolinux, weil ich als Testumgebung mit Proxmox-VE und VMs arbeite und hier von ISO-Images boote.

Hier habe ich die Datei $DVD/isolinux/menu.cfg etwas reduziert. Die sieht dann so aus:

Code: Alles auswählen

menu hshift 4
menu width 70

menu title Fully Automated Debian Installation
include stdmenu.cfg
include txt.cfg
Den Rest habe ich in txt.cfg angepasst:

Code: Alles auswählen

label install
        menu label ^Install
        kernel /install.amd/vmlinuz
        append vga=788 initrd=/install.amd/initrd.gz priority=critical auto=true url=http://my.server.name/preseed.cfg

timeout 300
ontimeout /install.amd/vmlinuz vga=788 initrd=/install.amd/initrd.gz priority=critical auto=true url=http://my.server.name/preseed.cfg
Mit der obigen Konfiguration komme ich schon mal bis zum Hostnamen-Eingabe Dialog. Mal schauen, warum das nicht geht...

Ok. Der Boot-Parameter priority=critical lässt die Abfrage nach dem Hostnamen verschwinden. Ansonsten ist noch eine Unklarheit, warum "auto url=..." nicht funktioniert, sondern "auto=yes auto url=..." benötigt wird und ob das nicht redundant ist. (Nachtrag: Die korrekte Syntax ist auto=yes url=http://....)

Beim wiederholten neu bauen des Installer-ISOs ist es auch schon mit rsync zu arbeiten. Da ist das jeweils neu gebaute 630 MB Test.iso in <3 Sekunden auf dem Server.
Jede Rohheit hat ihren Ursprung in einer Schwäche.

Benutzeravatar
heisenberg
Beiträge: 3567
Registriert: 04.06.2015 01:17:27
Lizenz eigener Beiträge: MIT Lizenz

Re: [Projekt] automatisierte Debian-Installation für Monitoring-Appliance

Beitrag von heisenberg » 22.08.2023 21:25:16

Hmm. Ich weiss noch nicht, wie ich den Abschluß der Installation erledige:

Problem: Das ISO-Image bzw. der USB-Stick muss nach Ende der Installation ausgeworfen bzw. nicht mehr zum Booten verwendet werden. Das ich die CD vielleicht noch irgendwie über den QEMU-Agenten vielleicht ausgeworfen bekomme, kann ich mir schon vorstellen. Aber wie in das System booten, wenn der USB-Stick noch angesteckt ist. Hmmmm....
  • Ich könnte dem Installations-Stick die Bootbarkeit nehmen. (Bootbereich überschreiben). Schlecht. Der USB-Stick sollte schon immer im funktionsfähigen Modus bleiben.
  • Ich könnte auf dem Installations-Stick per Default von der Platte booten. Dann ist die Installation aber nicht mehr vollautomatisch, sondern braucht zumindest einen Tastendruck: (Installation bestätigen). Ist auch nicht unbedingt das schlechteste, weil es verhindert, dass nicht aus Versehen ein Rechner platt gemacht wird, wenn zufällig irgendwie davon gebootet wird.
  • Mit UEFI ist es natürlich einfacher. Da kann ich das Bootmenu nach der Installation umschreiben.
Jede Rohheit hat ihren Ursprung in einer Schwäche.

Benutzeravatar
heisenberg
Beiträge: 3567
Registriert: 04.06.2015 01:17:27
Lizenz eigener Beiträge: MIT Lizenz

Re: [Projekt] automatisierte Debian-Installation für Monitoring-Appliance

Beitrag von heisenberg » 22.08.2023 23:11:32

Die Preseed-datei ist auch nicht sicherheitsrelevant, also kann ich sie auch hier verlinken. Das dort angegebene Passwort wird nach der Installation direkt neu gesetzt.

https://files.gemeinschaftsbildung.spac ... reseed.cfg
Jede Rohheit hat ihren Ursprung in einer Schwäche.

Benutzeravatar
heisenberg
Beiträge: 3567
Registriert: 04.06.2015 01:17:27
Lizenz eigener Beiträge: MIT Lizenz

Re: [Projekt] automatisierte Debian-Installation für Monitoring-Appliance

Beitrag von heisenberg » 23.08.2023 20:54:44

Boot-methode umsetzen

Ich habe mich jetzt für die zweite Variante entschieden: Der Installer bootet per Vorgabe von der installierten Platte. Dann kann nix schief gehen, falls der Installer-Stick aus Versehen mal in einem Arbeitsplatzrechner landet und davon bootet.

Die isolinux.cfg habe ich in eine Datei zusammengefasst:

Code: Alles auswählen

path
prompt 0

menu hshift 4
menu width 70

menu title Debian Monitoring Appliance Installation

menu color title        #FFFFFFFF #00aa0000 #FFFFFFFF none
menu color border       * #00000000 #00000000 none
menu color sel          * #ff000000 #ffffbf00 *
menu color hotsel       1;7;37;40 #ff000000 #00ffbf00 *
menu vshift 4
menu rows 12
menu background #00aa0000
menu helpmsgrow 14
menu cmdlinerow 16
menu timeoutrow 16

label Normal von der Festplatte starten
        localboot -1

label (Vorgabe, keine Installation)
        localboot -1

MENU SEPARATOR

label install
        menu label Beginne Appliance Installation
        kernel /install.amd/vmlinuz
        append vga=788 initrd=/install.amd/initrd.gz priority=critical auto=true url=http://files.gemeinschaftsbildung.space/downloads/preseed.cfg

label install
        menu label !!! WARNUNG - Alles wird geloescht !!!
        kernel /install.amd/vmlinuz
        append vga=788 initrd=/install.amd/initrd.gz priority=critical auto=true url=http://files.gemeinschaftsbildung.space/downloads/preseed.cfg

default 1
timeout 50

default vesamenu.c32
Sieht dann so aus:

Bild

Nächster Schritt: Test-Umgebung nachbauen

Die Umgebung sieht ungefähr so aus:

Code: Alles auswählen

[zentraler Server]
  |
  | ---- [VPN-Standort-Server-1] ---- [Monitoring-Server-1]
  |
  ...
  |
  | ----  [VPN-Standort-Server-n] ---- [Monitoring-Server-n]
Die einzelnen Schritte:
  • Neue Bridge in Proxmox VE anlegen
  • Test-Standortserver mit Debian installieren
    • openvpn config aufspielen
    • IP-Forwarding aktivieren
    • Masquerading konfigurieren
    • DHCP-Server installieren und eine Netzwerkdeklaration für das interne Interface erstellen.
  • An der Test-Monitoring-VM die Netzwerkschnittstelle tauschen, so dass die Internetverbindung jetzt über den VPN-Server aufgebaut wird.
Zuletzt geändert von heisenberg am 24.08.2023 11:09:16, insgesamt 1-mal geändert.
Jede Rohheit hat ihren Ursprung in einer Schwäche.

Benutzeravatar
heisenberg
Beiträge: 3567
Registriert: 04.06.2015 01:17:27
Lizenz eigener Beiträge: MIT Lizenz

Re: [Projekt] automatisierte Debian-Installation für Monitoring-Appliance

Beitrag von heisenberg » 23.08.2023 21:57:41

Erste Scripte

Jetzt hänge ich das erste eigene Setup-Script ein, das am Ende der Debian Installation aufgerufen wird. Dafür brauche ich noch ein paar Änderungen an der Preseed-Datei:

Code: Alles auswählen

# Mein Script möchte etwas via scp kopieren. Deswegen brauche ich noch das Paket openssh-client
d-i pkgsel/include string openssh-client
 
### Das Unterdrücken der Abschlussmeldung erstmal aufheben, so dass ich im Fehlerfall 
### nochmal nachschauen kann, was schief gelaufen ist.
# d-i finish-install/reboot_in_progress note

# Das Install-Script definieren
d-i preseed/late_command string /cdrom/debian_preseed_latescript
Dann muss das Script auf die CD-Rom, ebenso wie der hier verwendete SSH-Key, der auf den VPN-Server beschränkt Zugriff haben darf und schließlich noch die Known-Hosts-Datei, die den SSH-HostKey des VPN-Servers enthält.

Das ist das erste Script. Dieses zieht sich ein zweites Script vom VPN-Server, was dann schließlich beim nächsten Neustart ausgeführt werden soll.

Code: Alles auswählen

#!/bin/sh

REMOTE_USER=moninstall
REMOTE_SERVER=192.168.255.1
SSH_KEY_FILE=moninstall.key
INSTALL_SCRIPT=scripts/first_boot_install_script
KNOWN_HOSTS_FILE=moninstall.known_hosts

mkdir -p /target/install
cp /cdrom/$SSH_KEY_FILE /target/install
cp /cdrom/$KNOWN_HOSTS_FILE /target/install
chmod go-rwx /target/install/$SSH_KEY_FILE
chmod go-rwx /target/install/$KNOWN_HOSTS_FILE

# copy the ssh servers hostkey to make sure it is trusted
in-target scp -o UserKnownHostsFile=/install/$KNOWN_HOSTS_FILE -i /install/$SSH_KEY_FILE $REMOTE_USER@$REMOTE_SERVER:$INSTALL_SCRIPT /install
Schön. Das hat - nach ca. 10 Fehlern und jeweils neustarten der Installation - geklappt. Das first_boot_install_script ist im /install - Verzeichnis der Debian-Installation. Das ist genug für heute. Noch das Anhalten bei der Meldung, dass der Installer fertig ist wieder raus aus der Preesed, so dass die Installation nun wieder vollautomatisch durchläuft.
Jede Rohheit hat ihren Ursprung in einer Schwäche.

Benutzeravatar
heisenberg
Beiträge: 3567
Registriert: 04.06.2015 01:17:27
Lizenz eigener Beiträge: MIT Lizenz

Re: [Projekt] automatisierte Debian-Installation für Monitoring-Appliance

Beitrag von heisenberg » 24.08.2023 00:21:48

Ich werde wahrscheinlich noch ein Teil der Scripte direkt auf das Installationsmedium verlagern, so dass ich während der Debianinstallation möglichst nichts ausführe, was Fehler werfen könnte. Denn bei der Debianinstallation bin ich etwas beschränkt, was die Handlungsmöglichkeiten und Darstellungsmöglichkeiten von Fehlern angeht. Das funktioniert beim ersten booten in das neue System besser. Da stelle ich mir vor, dass ein Setup beginnt, bei dem mehrere Schritte auf der Konsole zu sehen sind und mit grünem OK und rotem Fehler, dass der Bedienende sieht, wie der Installationsstatus ist, samt einer Erklärung, was da gerade nicht passt.

Ich denke auch über einen Check am Anfang des eigentlichen Individualsetup nach, so dass man da gleich alle Fehler auf einmal präsentiert bekommt.

Beispielsweise:
  • DNS-Einträge fehlen
  • Default-GW hat nicht die erwartete IP-Adresse
  • VPN-Server ist nicht erreichbar
  • ...
Konfigurationsbestimmung

Bzgl. der Konfiguration habe ich darüber nachgedacht, wie das Gerät seine Konfiguration bekommt. Der erste Gedanke war der, dass ich die IP-Adresse des Default-GW nehme, davon den PTR-Eintrag an meinem eigenen DNS-Server (private Zone). Der PTR-Eintrag sollte dann zeigen auf: router.standort.hauptdomain.zz.

Aus der Standort-Domain (standort.hauptdomain.zz) leitet sich alles weitere ab:
  • Hostname (Es gibt nur einen Monitoring-Server pro Standort und der heisst monitoring.standort.hauptdomain.zz)
  • IP-Adresse (Die ist statisch immer die gleiche End-IP im definierten /24-Subnetz für den Standort. Das Subnetz ist auch durch den Standortrouter bekannt.)
  • Paketkonfiguration (Das Paket monitoring-$STANDORT-config zieht alle übrigen Pakete nach).
Nachteil dabei ist, dass der Server nur am Standort installiert werden kann und dort auch einen Monitor benötigt, damit man Installationsfehler überhaupt feststellen kann. Alternativ könnte man noch eine Mail schicken oder eine Datei auf einen Server hochladen für den Installationsbericht.

Gedanke ist dabei: Man könnte ja mehrere Monitoring-Server installieren wollen und das vielleicht an einem zentralen Standort, bevor die Server an die Standorte ausgeliefert werden. Aber mit einer statischen IP wird das natürlich nüscht und auch nicht mit einer Konfiguration, die automatisch an der Router-IP erkannt wird.

Möglichkeit wäre noch, den Server per Auswahlmenue zu konfigurieren: Standort auswählen und fertig. Das wäre auch nett zum neu konfigurieren. Man ruft einfach das setup neu auf, wählt einen anderen Standort aus und durch die Paketabhängigkeiten fliegt z. B. das Paket monitoring-berlin-config raus und monitoring-hamburg-config wird installiert und der Server ist in kürzester Zeit mit einer anderen Konfiguration betankt.

Vielleicht kann man auch ein Installationsnetzwerk speziell definieren und dann wird die Konfiguration nicht abgeschlossen, wenn der Server merkt, dass er dort ist. Dann prüft der Server beim hochfahren, wo er jeweils steht und solange er noch nicht final konfiguriert ist, installiert er sich fertig, sobald er merkt, dass er an einem gültigen Standort ist.
Jede Rohheit hat ihren Ursprung in einer Schwäche.

Benutzeravatar
heisenberg
Beiträge: 3567
Registriert: 04.06.2015 01:17:27
Lizenz eigener Beiträge: MIT Lizenz

Re: [Projekt] automatisierte Debian-Installation für Monitoring-Appliance

Beitrag von heisenberg » 28.08.2023 00:57:11

Gerade hatte ich einen komischen Fehler. Diesmal auf der Serverseite, die ich hier aktuell noch nicht erklärt bzw. angesprochen habe.

Der DNS-Server (dnsmasq) auf dem VPN-Server hat mir von dem einen VPN-Client-Netz aus geantwortet und aus dem anderen nicht. Nach viel schauen mit tcpdump, iptables, host, nmap und queries loggen mit dnsmasq habe ich dann mal den dnsmasq durchgestartet und siehe da: Ich bekomme die Fehlermeldung "ignore query from non-local network". Das ist ja mal eine klare Fehlermeldung. Die Fehlermeldung kommt wohl nicht so oft, dass ich das bisherige auftreten nicht bemerkt habe. Nur: Warum sieht dnsmasq das eine Netzwerk hinter einem VPN-Client als "local" an und das andere hinter einem anderen VPN-Client nicht?

Na wie auch immer. Die Lösung war dann einfach der folgende Eintrag in /etc/dnsmasq.conf :

Code: Alles auswählen

except-interface=nonexisting
WARNUNG: Wenn man das so macht, hat man sich auch gleich einen schönen offenen DNS-Recursor gebastelt. D. h. der Server kann und wird dann auch wunderbar für Attacken im Internet mißbraucht werden, bis die Internetleitung glüht. Besonders schön, wenn der Server im Rechenzentrum steht mit einer schönen Gigabitanbindung. Da kann das richtig ordentlich Schaden anrichten. Strato würde sich bei mir bedanken und mir aus lauter Dankbarkeit bestimmt recht schnell mal den Server abschalten.

D. h. da müssen natürlich noch Firewallregeln rein. Sieht dann bei mir so aus:

Code: Alles auswählen


# iptables -L -v -n

Chain INPUT (policy ACCEPT 39532 packets, 3392K bytes)
 pkts bytes target     prot opt in     out     source               destination         
...
    3   180 DNS        6    --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:53
   98  7447 DNS        17   --  *      *       0.0.0.0/0            0.0.0.0/0            udp dpt:53
....
Chain DNS (2 references)
 pkts bytes target     prot opt in     out     source               destination         
   11   684 ACCEPT     0    --  *      *       10.0.0.0/8           0.0.0.0/0           
    2   144 ACCEPT     0    --  lo     *       0.0.0.0/0            0.0.0.0/0           
    0     0 ACCEPT     0    --  *      *       127.0.0.0/8          0.0.0.0/0           
   13  1058 ACCEPT     0    --  *      *       172.16.0.0/12        0.0.0.0/0           
   86  6652 DROP       0    --  *      *       0.0.0.0/0            0.0.0.0/
Ich erlaube also alles, was über das Interface lo reinkommt und zusätzlich alles von den IP-Netzen 127.0.0.0/8 (localhost), 10.0.0.0/8 und 192.168.255.0/24 (Mein VPN-Bereich) und 172.16.0.0/12 (privater IP-Adressbereich für die Docker-Container). Der Rest, was an Port 53 TCP+UDP reinkommt wird gedropped. Bzgl. Docker muss man da auch aufpassen. Der Docker-Daemon verwendet per Default willkürlich RFC1918-IP-Adressbereiche (D. h. 10.0.0.0/8, 192.168.0.0/16 oder 172.16.0.0/12). Wenn man die selbst verwendet - so wie in diesem Fall - dann muss man ggf. den Docker-Daemon so konfigurieren, dass er nur Teile davon verwendet bzw. welche Teile davon.

Ich könnte den dnsmasq auch an die VPN-Netzwerkschnittstelle (=tun0) binden, aber das scheint mir irgendwie nicht unbedingt sinnvoll bzw. stabil, wenn ich den VPN-Server gelegentlich mal durchstarte und die Schnittstelle dann mal kurz nicht da ist. Andere Variante wäre evtl. noch den dnsmasq an localhost zu binden und eine iptables-dnat-regel zu erstellen, die nur alles vom VPN kommend dorthin weiterleitet.

Schön auch an der obigen iptables-Ausgabe - die 86 Pakete, die mit DROP behandelt wurden - zu sehen, dass da sofort DNS-Requests reinkommen. D. h. das übliche Abklopfen meines Servers, ob da vielleicht ein offener DNS-Recursor da ist, den man direkt mißbrauchen kann, findet quasi sofort und dauerhaft statt.

Nachtrag
Ich hatte den IP-Bereich für die Docker-Container vergessen bei den FW-Regeln. Deswegen hat die Namensauflösung im Check-MK Container nicht mehr funktioniert.
Jede Rohheit hat ihren Ursprung in einer Schwäche.

Benutzeravatar
heisenberg
Beiträge: 3567
Registriert: 04.06.2015 01:17:27
Lizenz eigener Beiträge: MIT Lizenz

Re: [Projekt] automatisierte Debian-Installation für Monitoring-Appliance

Beitrag von heisenberg » 29.08.2023 19:47:53

Das ist jetzt das aktuelle preseed-late-script:

Code: Alles auswählen

#!/bin/sh

{

set -x
CDPATH=/cdrom/mon-app-install
mkdir -p /target/install

cp      $CDPATH/install-script.service  /target/etc/systemd/system/
cp      $CDPATH/install-script          \
        $CDPATH/environment             \
        $CDPATH/moninstall.key          \
        $CDPATH/moninstall.known_hosts  \
        /target/install

in-target systemctl enable install-script
} >/target/late-script.log 2>&1 
und mit folgendem Unit-File wird der Installer auf Textkonsole 1 gestartet:

Code: Alles auswählen

[Unit]
Description=Monitoring Install Script
After=getty.target
After=network-online.target
Conflicts=getty@tty1.service

[Service]
Type=simple
ExecStart=/install/install-script
StandardInput=tty-force
StandardOutput=tty-force
StandardError=tty-force
TTYPath=/dev/tty1

[Install]
WantedBy=multi-user.target
Die Scripte sind auch nochmal in diesem Repository einsehbar:

https://codeberg.org/megabert/debian-mo ... ovisioning
Jede Rohheit hat ihren Ursprung in einer Schwäche.

Benutzeravatar
heisenberg
Beiträge: 3567
Registriert: 04.06.2015 01:17:27
Lizenz eigener Beiträge: MIT Lizenz

Re: [Projekt] automatisierte Debian-Installation für Monitoring-Appliance

Beitrag von heisenberg » 29.08.2023 19:56:43

Bis jetzt ist die Paketstruktur so:

Code: Alles auswählen

su-monapp-config-$LOCATION
# spezifisches Konfigurationspaket für einen bestimmten Standort

    * Abhängigkeit

    	+ su-monapp-base-config

su-monapp-base-config
# Konfiguration aller organisationsweiten Einstellungen + Abhängigkeiten zu allen benötigen Paketen

    - root-pw setzen
    - user-pw setzen
    - ip adresse konfigurieren
    - admin-ssh-key hinterlegen
    
    * Abhängigkeiten
    
        + su-monapp-checkmk-docker
        + su-monapp-powerdns-ns2-docker
        + docker.io
        + docker-compose
        + openssh-server
    
su-monapp-checkmk-docker
# Docker-Container für Check-MK

    f /srv/checkmk/docker-compose.yml

    - erzeugt die notwendigen Verzeichnisse unterhalb von /srv/checkmk
    - erzeugt neue check-mk Monitoring-Instanz als Docker-Container mit dem Namen checkmk
        - Instanz-ID muss Organisationsweit eindeutig sein
    - setzt das globale Check-MK Admin-Passwort für diese Instanz
    - Importiert Backup der Instanz, wenn vorhanden
    - fährt den Docker-Container hoch
    - Verknüpft ggf. den CheckMK-Standort mit der Hauptinstanz
    
su-monapp-powerdns-ns2-docker
# Docker-Container für PowerDNS (+MariaDB)

    f /srv/powerdns-ns2/docker-compose.yml
    
    - erzeugt die notwendigen Verzeichnisse unterhalb von /srv/powerdns-ns2
    - erzeugt eine neue eindeutige ID des lokalen MariaDB-Servers (docker) zur Replikation und schreibt 
      die Information dazu auf den VPN-Server
    - spielt den DB-Dump von der PowerDNS-Masterdatenbank ein
    - fährt die beiden Docker-Container (powerdns+mariadb) hoch
    
su-autossh
# SSH-Remote Portforward für die Entwicklungsarbeit der automatischen Installation
# um auf den Server per SSH zu kommen.
Jede Rohheit hat ihren Ursprung in einer Schwäche.

Benutzeravatar
heisenberg
Beiträge: 3567
Registriert: 04.06.2015 01:17:27
Lizenz eigener Beiträge: MIT Lizenz

Re: [Projekt] automatisierte Debian-Installation für Monitoring-Appliance

Beitrag von heisenberg » 30.08.2023 23:43:58

Jetzt kommen die Debian-Pakete und das wird wohl recht komplex. Das letzte Mal habe ich mit einem Hello-World - Paket vor 10 Jahren gebastelt und dann nichts mehr. Jetzt also wieder bei 0 anfangen.

Es gibt viele Anleitungen für viele Arten von Paketen und alles ist sehr umfangreich. Ich habe mich per Google durchgekämpft und schildere mal den Weg. Insgesamt wird das vermutlich ein langer Weg zu vernünftigen Paketen werden. Aber ich fange einfach mal an rumzustümpern. Im Laufe der Zeit wird dass dann schon besser werden.

Mein erstes Paket

Mein erstes Paket wird heissen su-remote-access. "su" ist dabei ein Projektkennzeichen.

Das Paket soll es ermöglichen, sich von aussen per SSH mit einem Key als user einloggen zu können. Dazu wird ein User angelegt, der einen vordefinierten Public-Key in seine authorized_keys hinterlegt bekommt. Dieser User hat unbeschränkte sudo-Rechte. Weiterhin wird ein systemd-service installiert, der ausgehend eine Verbindung zu einem konfigurierten SSH-Server aufbaut und dabei ein Remote-Portforwarding einrichtet. D. h. es leitet den eigenen SSH-Port an diesen Zielserver weiter, so dass man sich von dort aus auf das System, auf dieses Paket installiert wird, einloggen kann. Das geht auch, wenn das System in einem privaten LAN steht. Das System braucht nur eine nicht per Firewall blockierte Internetverbindung.

Tools für den Paketbau installieren

Zunächst braucht es ein paar Pakete für den Paketbau (werde ich vermutlich im Laufe der Zeit noch ergänzen):

Code: Alles auswählen

apt install dh-make debhelper
Paketstruktur erstellen

Dann braucht es ein paar Verzeichnisse:

Code: Alles auswählen

mkdir $HOME/packages
cd $HOME/packages
mkdir su-remote-access
cd su-remote-access
mkdir su-remote-access-0.1
cd su-remote-access-0.1
Also in $HOME/packages kommen alle Pakete rein, die ich bauen werde. Der Name su-remote-access ist der Name des Paketes.

Im Verzeichnis su-remote-access gibt es dann noch einmal ein Unterverzeichnis su-remote-access-0.1. D. h. das ist die erste Version meines Paketes, dass die Versionsnummer enthält. Debian möchte den Namen genau so haben und nicht anders.

In diesem Verzeichnis kann ich jetzt dh_make aufrufen, mit noch ein paar zu setzenden Variablen vorher:

Code: Alles auswählen

export DEBFULLNAME="Mr. Root"
export DEBEMAIL="nomail@invalid.zz"
dh_make --single -c gpl --yes --native
Damit habe ich jetzt die Vorlage-Dateien für das Debianpaket im Unterverzeichnis debian erzeugen lassen. Das sind so einige. Ich gehe durch die Dateien lösche so einige direkt wieder, andere lasse ich so liegen und editiere die notwendigsten:
  • control - Die Steuerungsdatei für das Paket.
  • changelog - Dort stehen Änderungen drin und die jeweils aktuelle Version, die die Versionsnummer bestimmt.
Mein Paket hat insgesamt recht wenige Komponenten:
  • Eine Abhängigkeit auf autossh, openssh-server, bash und sudo
  • Eine systemd service unit
  • Einen SSH-Private-Key mit dem ich mich zu einem Zielserver verbinden kann und das Portforwarding aufbauen kann
  • Einen SSH-Public-Key den ich an die /home/su-remote-access/.ssh/authorized_keys anhänge, für den keybasierten login des remote-admins
  • Den SSH-Host-Key des Servers zu dem ich mich verbinden möchte.
  • Ein postinst-Script, also das Script, das nach der Installation der Dateien aufgerufen wird. In diesem aktiviere und starte ich den Service und füge den Public-Key in die authorized_keys ein
  • Ein prerm-Script, also das Script, das vor der Deinstallation aufgerufen wird. Dort deaktiviere und beende ich den Service, lösche den User wieder und lösche auch die sudo-Rechte für den User
Zuletzt geändert von heisenberg am 31.08.2023 01:33:53, insgesamt 1-mal geändert.
Jede Rohheit hat ihren Ursprung in einer Schwäche.

Benutzeravatar
heisenberg
Beiträge: 3567
Registriert: 04.06.2015 01:17:27
Lizenz eigener Beiträge: MIT Lizenz

Re: [Projekt] automatisierte Debian-Installation für Monitoring-Appliance

Beitrag von heisenberg » 31.08.2023 00:11:34

Dateiübersicht

Das ist aktuell mein Dateibaum mit den wesentlichen Dateien. Es sind noch einige mehr. Aber dass sind jetzt mal die wichtigen:

Code: Alles auswählen

su-remote-access-0.4
├── data
│   ├── portforward.known_hosts
│   ├── portforward.ssh.key
│   └── ssh.management.public.key
├── debian
│   ├── README
│   ├── changelog
│   ├── control
│   ├── copyright
│   ├── postinst
│   ├── prerm
│   └── su-remote-access.install
├── defaults
│   └── su-remote-access
└── systemd
    └── su-remote-access.service
Daten-Dateien

Jetzt zu den einzelnen Dateien. In den Hauptverzeichnissen "data", "defaults" und "systemd" sind die Dateien drin, die ich ins System kopieren will. SSH-Keys und known_hosts bedürfen keiner Erklärung.

Hier das Service-File für den ausgehenden SSH-Tunnel, su-remote-access.service:

Code: Alles auswählen

[Unit]
Description=Remote Privileged Access
After=network.target network-online.target 

[Service]
EnvironmentFile=/etc/defaults/su-remote-access
ExecStart=/usr/bin/autossh -i ${PORT_FORWARDING_PRIVATE_KEY} -o UserKnownHostsFile=${PORT_FORWARDING_KNOWN_HOSTS} -l ${PORT_FORWARDING_REMOTE_USER} -R ${PORT_FORWARDING_REMOTE_SOCKET}:localhost:22 -o ExitOnForwardFailure=yes -p 22 ${PORT_FORWARDING_REMOTE_SERVER} "bash -c 'while :;do echo -n . ;sleep 30;done'"
KillMode=process
RestartSec=10
Restart=on-failure

[Install]
WantedBy=multi-user.target
Der Service verwendet nur die Variablen aus der Defaults Datei, ruft damit autossh auf und führt eine Bash-Schleife aus.

Das ist die Datei /etc/defaults/su-remote-access:

Code: Alles auswählen

REMOTE_ALLOWED_SSH_KEY=/etc/su-remote-access/ssh.management.key
PORT_FORWARDING_PRIVATE_KEY=/etc/su-remote-access/portforward.ssh.key
PORT_FORWARDING_KNOWN_HOSTS=/etc/su-remote-access/portforward.known_hosts
PORT_FORWARDING_REMOTE_SOCKET=127.0.0.1:60043
PORT_FORWARDING_REMOTE_SERVER=192.168.255.1
PORT_FORWARDING_REMOTE_USER=portforwarder
Steuer-Dateien

debian/control

Code: Alles auswählen

Source: su-remote-access
Section: net
Priority: optional
Maintainer: Mr. Root <nomail@invalid.zz>
Rules-Requires-Root: no
Build-Depends:
 debhelper-compat (= 13),
Standards-Version: 4.6.2

Package: su-remote-access
Architecture: all
Pre-Depends:
 sudo,
Depends:
 autossh,
 openssh-server,
Description: ssh remote port forward for maintenance
 Start a remote ssh tunnel to be able to login from remote
Anmerkungen
  • Grundsätzlich wichtig ist die Architecture: all, weil das hier kein Binärpaket ist.
  • sudo ist in den Pre-Depends aufgeführt, weil ich später in einem Verzeichnis eine Datei anlege, das sudo erst erzeugen muss (/etc/sudoers.d).
  • autossh brauche für den Tunnel
  • openssh-server brauche ich, damit sich jemand einloggen kann.
debian/su-remote-access.install

Code: Alles auswählen

#!/usr/bin/dh-exec

systemd/su-remote-access.service        lib/systemd/system
defaults/su-remote-access               etc/defaults
data/portforward.known_hosts            etc/su-remote-access
data/portforward.ssh.key                etc/su-remote-access
data/ssh.management.key                 etc/su-remote-access
Damit werden anscheinend die Dateien installiert. Ich habe mir das mal aus dem openssh-server - Source-Paket (apt-get source openssh-server) abgeschaut, aber das kann so nicht richtig sein. Auch die Dateirechte kann ich da gar nicht spezifizieren und muss dass dann erst im postinst-Script fixen.

debian/postinst

Code: Alles auswählen

#!/bin/sh
# postinst script for sample-pkg.
#
# See: dh_installdeb(1).

set -e

remote_user=su-remote-access
config_file=/etc/defaults/su-remote-access

export remote_user

if [ -r $config_file ] ; then
        . $config_file
else
        echo "$config_file missing, aborting"
        # exit good - so package maybe cleanly deinstalled, if file is missing
        exit 0
fi

case "$1" in
    configure)
            # create user for remote login
            if id $remote_user >/dev/null 2>&1 ; then
                    echo "NOTE: user $remote_user already exists, skipping user creation. "
            else
                    useradd --create-home --shell /bin/bash $remote_user 
                    homedir=$(getent passwd "$remote_user" | cut -d: -f6 )
                    install -d --mode=0500 --owner=$remote_user --group=$remote_user $homedir/.ssh
                    install    --mode=0400 --owner=$remote_user --group=$remote_user $REMOTE_ALLOWED_SSH_KEY $homedir/.ssh/authorized_keys
            fi

            # definitely not a good way
            chmod 0400 /etc/su-remote-access/ssh.management.key
            chmod 0400 /etc/su-remote-access/portforward.ssh.key

            # create sudo file
            echo "$remote_user ALL=(ALL) NOPASSWD:ALL" >/etc/sudoers.d/remote_admin_access

            # start systemd service now
            systemctl enable su-remote-access
            systemctl start  su-remote-access
;;

    abort-upgrade|abort-remove|abort-deconfigure)
    ;;

    *)
        echo "postinst called with unknown argument '$1'" >&2
        exit 1
    ;;
esac

# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.

#DEBHELPER#

exit 0
Das ist jetzt einfach das von dh_make installierte Beispiel-Script, dass ich um ein paar Elemente erweitert habe:
  • Benutzer erzeugen
  • Public SSH-Key in die authorized_keys des Benutzers legen
  • Rechte richtig setzen
  • sudo-config schreiben
  • systemd dienst starten
debian/prerm

Code: Alles auswählen

#!/bin/sh
# prerm script for sample.
#
# See: dh_installdeb(1).

set -e

remote_user=su-remote-access

export remote_user

case "$1" in
    remove|upgrade|deconfigure)
            if id $remote_user >/dev/null 2>&1 ; then
                userdel -r $remote_user
            fi
            systemctl stop    su-remote-access || true
            systemctl disable su-remote-access || true
            rm -f /etc/sudoers.d/remote_admin_access
    ;;

    failed-upgrade)
    ;;

    *)
        echo "prerm called with unknown argument '$1'" >&2
        exit 1
    ;;
esac

# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.

#DEBHELPER#

exit 0
Hier jetzt wieder die Rolle rückwärts:
  • Dienst stoppen und deaktivieren
  • sudo-config-fragment entfernen
  • user löschen
Wichtig ist hier: Bei den Scripten ist die Shell-Option -e gesetzt. D. h. das Script sollte auf keinen Fall unnötigerweise einen Befehl enthalten, der sich mit einem Exit-Code != 0 beendet. Sonst gibt es Installationsfehler. Deswegen auch das Konstrukt: systemctl <irgendwas> || true. Damit wird ein Fehler-Exitcode entschärft.

debian/changelog

Code: Alles auswählen

su-remote-access (0.4-13) UNRELEASED; urgency=medium

  * cleaned up sudo access: rm etc/sudoers.d/remote_admin_access

 -- root <bla@blub.de>  Tue, 29 Aug 2023 15:28:11 +0000

su-remote-access (0.4-12) UNRELEASED; urgency=medium

  * service file must have variables in ${...}

 -- root <bla@blub.de>  Tue, 29 Aug 2023 15:28:11 +0000

...
Aus dem Changelog wird die Versionsnummer für das Paket genommen. D. h. das Format ist deswegen auch exakt einzuhalten. Ich hätte wahrscheinlich nicht so viele Versionen machen müssen, aber ich habe das Spasses halber mal gemacht. Da habe ich jetzt auch eine Liste mit den ganzen Korrekturen, die ich immer wieder durchführen habe müssen. Ist ja auch eine praktische Arbeitsdokumentation. Wenn ich das noch automatisiere - da gibt's doch bestimmt schon ein Debian-Helper-Script für? - dann wird das bestimmt einfacher.

Paket bauen

Das war's jetzt mit den Dateien. Jetzt kann das Paket gebaut werden. Dazu wechselt man ins Hauptverzeichnis des Paketes und führt den Befehl zum Paketbau aus:

Code: Alles auswählen


# pwd

/root/packages/su-remote-access/su-remote-access-0.4

# dpkg-buildpackage -uc -us

dpkg-buildpackage: info: source package su-remote-access
dpkg-buildpackage: info: source version 0.4-13
dpkg-buildpackage: info: source distribution UNRELEASED
dpkg-buildpackage: info: source changed by root <bla@blub.de>
dpkg-buildpackage: info: host architecture amd64
 dpkg-source --before-build .
 debian/rules clean
dh clean
   dh_clean
 dpkg-source -b .
dpkg-source: warning: no source format specified in debian/source/format, see dpkg-source(1)
dpkg-source: info: using source format '1.0'
dpkg-source: warning: native package version may not have a revision
dpkg-source: info: building su-remote-access in su-remote-access_0.4-13.tar.gz
dpkg-source: info: building su-remote-access in su-remote-access_0.4-13.dsc
 debian/rules binary
dh binary
   dh_update_autotools_config
   dh_autoreconf
   create-stamp debian/debhelper-build-stamp
   dh_prep
   dh_auto_install --destdir=debian/su-remote-access/
   dh_install
   dh_installdocs
   dh_installchangelogs
   dh_installsystemd
   dh_perl
   dh_link
   dh_strip_nondeterminism
   dh_compress
   dh_fixperms
   dh_missing
   dh_installdeb
   dh_gencontrol
   dh_md5sums
   dh_builddeb
dpkg-deb: building package 'su-remote-access' in '../su-remote-access_0.4-13_all.deb'.
 dpkg-genbuildinfo -O../su-remote-access_0.4-13_amd64.buildinfo
 dpkg-genchanges -O../su-remote-access_0.4-13_amd64.changes
dpkg-genchanges: info: including full source code in upload
 dpkg-source --after-build .
dpkg-buildpackage: info: full upload; Debian-native package (full source is included)

Wie Ihr seht, hat sich die Paketnummer bei mir mittlerweile schon deutlich erhöht. Es gab schon diverse Iterationen. ;-)

Das Paket wurde erfolgreich gebaut und liegt jetzt im übergeordneten Verzeichnis von meinem Paketverzeichnis.

Beim Paketbau habe ich noch jede Menge zu lernen. Für Hinweise und Korrekturen freue ich mich deswegen sehr.

Nachtrag:

Installieren tut es gerade noch nicht erfolgreich. Aus irgend einem Grund wird das ~su-remote-acces/.ssh Verzeichnis nicht angelegt.

Fehler gefunden: ~$USER geht nicht. Das musste ich anders lösen ( homedir=$(getent passwd "$remote_user" | cut -d: -f6 ) ). Und nein, ich will kein eval verwenden.
Jede Rohheit hat ihren Ursprung in einer Schwäche.

Benutzeravatar
heisenberg
Beiträge: 3567
Registriert: 04.06.2015 01:17:27
Lizenz eigener Beiträge: MIT Lizenz

Re: [Projekt] automatisierte Debian-Installation für Monitoring-Appliance

Beitrag von heisenberg » 31.08.2023 13:11:15

Weitere Verbesserungsideen für su-remote-access:
  • SSH-Keys werden bei Bedarf automatisch erzeugt, so dass das ein universelles Paket für jedermann ist.
  • Ein Konfigurationsfragment für den SSH-Server, auf den der Login für den Remote-Portforward stattfindet wird erzeugt. Dorthin kann die Shellschleife ausgelagert werden, so dass nur der Portforward und kein interaktiver Login stattfinden kann (authorized_keys):

    Code: Alles auswählen

    ssh-keytype lsfjdlsfjs...lh-dsflsdjfd keyname bash -c 'while :;do echo -n . sleep 30;done'
  • Das Portforwarding wird dynamisch gestaltet, so dass mehrere Server gleichezeitig denTunnel aufbauen können.
Jede Rohheit hat ihren Ursprung in einer Schwäche.

Benutzeravatar
heisenberg
Beiträge: 3567
Registriert: 04.06.2015 01:17:27
Lizenz eigener Beiträge: MIT Lizenz

Automatisierungen für den Paketbau und Repository-Arbeiten

Beitrag von heisenberg » 05.09.2023 22:10:01

Jetzt habe ich ein minimales eigenes Paket-Repository aufgesetzt, so wie Inne das im Wiki sehr nachvollziehbar und einfach erklärt hat (Danke dafür!):

Debianforum.de Wiki: Wiki-Artikel zum Thema Lokales_Repository

Im Übrigen sieht meine Verzeichnisstruktur für den Paketbau wie folgt aus:

Code: Alles auswählen

packages
├── Makefile
└── su-remote-access
    ├── Makefile
    ├── su-remote-access-0.4
    │   ├── data
    │   ├── debian
    │   ├── defaults
    │   └── systemd
    ├── su-remote-access-0.5
    │   ├── data
    │   ├── debian
    │   ├── defaults
    │   └── systemd
    ├── su-remote-access-0.6
    │   ├── data
    │   ├── debian
    │   ├── defaults
    │   └── systemd
    ├── ...
    └── su-remote-access_0.6_all.deb
Man sieht da jetzt ein Hauptverzeichnis "packages". Darunter ist das Verzeichnis su-remote-access - mein einziges Paket für den Moment. In diesem Paketverzeichnisse sind noch weitere versionierte Unterverzeichnisse für jede Paketversion und außerdem die Ergebnisse des Paketbaus. Eines dieser Ergebnisse ist dann das aktuelle Debian Paket der neuesten Version dieses Paketes.

Da hätte ich gerne ein minimale Automatisierung, damit ich automatisch meine Pakete neu bauen kann und ins Repo bringen kann.

Dafür gibt's dann direkt im Verzeichnis "packages" ein Makefile, dass das tut:

Code: Alles auswählen

repo_dir=/srv/repo

all:
	# build all packages
	@for pkg_dir in $(shell ls);do \
		if [ $$pkg_dir != Makefile ] && [ $$pkg_dir != bin ] ; then \
		echo $$pkg_dir ; \
		cd $$pkg_dir ; \
		make ; \
		fi ; \
	done
	# renew repo package list
	cd $(repo_dir) && dpkg-scanpackages ./ | tee Packages | gzip -f >Packages.gz
In jedem Unterverzeichnis mit dem Paketnamen gibt es dann ein weiteres - generisches - Makefile, dass von obigem aufgerufen wird. Das sieht so aus:

Code: Alles auswählen

pkg=$(shell basename $(shell pwd))
pkg_dir=$(shell ls -d $(pkg)-* | tail -n 1)
repo_dir=/srv/repo
all: 
	@echo $(pkg_dir)
	cd $(pkg_dir) && dpkg-buildpackage -uc -us
	rm -f $(repo_dir)/$(pkg)_*.deb
	../bin/copy_package_to_repo $(shell pwd) $(repo_dir)
Ich hatte auch schon mal ein Makefile geschrieben, dass nur dann baut, wenn es tatsächlich auch Änderungen gab. Das habe ich gerade nicht hier, aber so soll das natürlich laufen: Keine unnötigen Bauprozesse - so wie hier im Moment noch.

Nachtrag

Das folgende musste ich aufgrund eines Fehlers ändern:

Code: Alles auswählen

# --- kopiere das neue Paket ins Repository
cp $(shell ls *.deb | tail -n1) $(repo_dir)
In einem Makefile werden wohl alle $(shell ... ) Konstrukte vor der Abarbeitung des Makefiles ersetzt. Das ist sehr wichtig zu wissen! D. h. die Shell wird hier eher als Template-Engine verwendet, die vorher gewisse Ersetzungen im Makefile vornehmen kann und nicht als tatsächliche Shell.

Das bedeutet auch, dass das ls *.deb vor der Ausführung von make durchgeführt wird, wo das neue .deb noch gar nicht existiert. Das wirft dann entweder einen Fehler, weil das .deb File fehlt, oder es kopiert ein altes .deb File. D. h. ich muss das auslagern in eine Datei - ein Shell-Script - die make dann zum richtigen Zeitpunkt auswertet. Wie ich oben bereits korrigiert habe, lautet dass dann:

Code: Alles auswählen

../bin/copy_package_to_repo $(shell pwd) $(repo_dir)
Das zugehörige copy_package_to_repo ist dann dieser Bash-Fetzen:

Code: Alles auswählen

#!/bin/bash

pkg_path="$1"
repo_path="$2"

package=$(ls $pkg_path/*deb | tail -n1)
echo cp "$package" "$repo_path"
cp "$package" "$repo_path"
Jede Rohheit hat ihren Ursprung in einer Schwäche.

Antworten