From 7e4ef95c37d29e6b694f78a43f611de6a5ec5692 Mon Sep 17 00:00:00 2001 From: jonnybravo Date: Fri, 26 Sep 2025 15:51:04 +0200 Subject: [PATCH] =?UTF-8?q?Anpassungen=20hinzuf=C3=BCgen=20von=20ssh-key?= =?UTF-8?q?=20und=20anpassung=20der=20out.csv?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + setup_container.py | 14 +++- setup_host.py | 162 +++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 163 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 477bb1f..2f0dc12 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ Führen Sie `setup_host.py` aus. Das Skript akzeptiert verschiedene Argumente, u * `-b`, `--basename`: Der Basisname für die Container, falls die Vorlage `{basename}` enthält. (Standard: `vm`) * `--no-port-forward`: Deaktiviert die SSH-Port-Weiterleitung. * `--port-start`: Der Start-Port für die SSH-Weiterleitung, falls diese aktiv ist. (Standard: `2201`) +* `--ssh-pub-key-file`: Pfad zu einem öffentlichen SSH-Schlüssel, der für den Benutzer `jonnybravo` hinterlegt werden soll. (Standard: `~/.ssh/id_rsa.pub`) --- diff --git a/setup_container.py b/setup_container.py index cda7cb1..4530b0c 100755 --- a/setup_container.py +++ b/setup_container.py @@ -44,6 +44,7 @@ def main(): parser.add_argument("password", help="Das gewünschte root-Passwort.") parser.add_argument("hostname", help="Der Hostname für diesen Container.") parser.add_argument("distro", help="Die Distribution des Containers (z.B. 'arch', 'ubuntu').") + parser.add_argument("ssh_key", nargs='?', default="", help="Der öffentliche SSH-Schlüssel für den Benutzer 'jonnybravo'.") args = parser.parse_args() root_password = args.password @@ -132,7 +133,18 @@ def main(): wheel_group_equivalent = "wheel" if distro in ["arch", "fedora"] else "sudo" run_command(f"useradd -m -G {config['user_groups']} -s /bin/bash jonnybravo", "Benutzer 'jonnybravo' hinzufügen") run_command("passwd jonnybravo", "Passwort für jonnybravo setzen", input_str=password_input) - + + # SSH-Schlüssel für jonnybravo einrichten + if args.ssh_key: + print("--- Richte SSH-Schlüssel für Benutzer 'jonnybravo' ein ---") + ssh_dir = "/home/jonnybravo/.ssh" + authorized_keys_file = os.path.join(ssh_dir, "authorized_keys") + run_command(f"mkdir -p {ssh_dir}", f"Erstelle Verzeichnis {ssh_dir}") + run_command(f"echo '{args.ssh_key}' > {authorized_keys_file}", f"Schreibe Schlüssel nach {authorized_keys_file}") + run_command(f"chown -R jonnybravo:jonnybravo {ssh_dir}", f"Setze Eigentümer für {ssh_dir}") + run_command(f"chmod 700 {ssh_dir}", f"Setze Berechtigungen für {ssh_dir}") + run_command(f"chmod 600 {authorized_keys_file}", f"Setze Berechtigungen für {authorized_keys_file}") + if wheel_group_equivalent == "wheel": run_command("echo '%wheel ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/wheel_nopasswd", "Passwortloses sudo für Gruppe 'wheel' aktivieren") else: # sudo group diff --git a/setup_host.py b/setup_host.py index 19befdf..b2bf7c6 100755 --- a/setup_host.py +++ b/setup_host.py @@ -1,12 +1,13 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- - +# geschrieben vmit der Hilfe von Gemini2.5-pro import subprocess import os import sys import argparse import time import json +import pylxd def run_command(command_str, description, ignore_errors=False): @@ -75,6 +76,9 @@ def get_container_ip(container_name): def main(): + os.environ['PYLXD_WARNINGS'] = 'none' + host_folder = "/home/jonnybravo/lxc_folder" + parser = argparse.ArgumentParser( description="Erstellt und konfiguriert mehrere LXD-Container.") parser.add_argument("-c", "--count", type=int, default=1, @@ -91,6 +95,8 @@ def main(): help="Zu verwendendes Image (z.B. images:archlinux, ubuntu:22.04).") parser.add_argument("--no-port-forward", action="store_true", help="Überspringt die Einrichtung der SSH-Port-Weiterleitung.") + parser.add_argument("--ssh-pub-key-file", default="~/.ssh/ansible-test.pub", + help="Pfad zur öffentlichen SSH-Schlüsseldatei, die für den Benutzer 'jonnybravo' autorisiert werden soll.") args = parser.parse_args() if args.count > 1 and '{i}' not in args.hostname_template: @@ -99,15 +105,139 @@ def main(): sys.exit(1) run_command("command -v lxc >/dev/null", "Prüfe, ob LXD installiert ist") - print("LXD ist verfügbar. WICHTIG: Es wird davon ausgegangen, dass 'lxd init' bereits ausgeführt wurde.") + client = pylxd.Client(project='default') + + # Überprüfe, ob das Profil 'myprofile' existiert, und erstelle es, falls nicht + try: + client.profiles.get("myprofile") + print("\n--- Profil 'myprofile' existiert bereits. Aktualisiere es. ---") + profile = client.profiles.get("myprofile") + profile.config = {"security.nesting": "true", + "security.privileged": "true"} + profile.devices = { + "root": { + "path": "/", + "pool": "default", + "type": "disk", + }, + "eth0": { + "name": "eth0", + "network": "lxdbr0", + "type": "nic", + }, + "public": { + "path": "/public", + "source": host_folder, + "type": "disk", + } + } + profile.save() + except pylxd.exceptions.NotFound: + print("\n--- Profil 'myprofile' nicht gefunden. Erstelle es. ---") + profile_config = {"security.nesting": "true", + "security.privileged": "true"} + profile_devices = { + "root": { + "path": "/", + "pool": "default", + "type": "disk", + }, + "eth0": { + "name": "eth0", + "network": "lxdbr0", + "type": "nic", + }, + "public": { + "path": "/public", + "source": host_folder, + "type": "disk", + } + } + client.profiles.create("myprofile", profile_config, profile_devices) + + # Erstelle den Ordner auf dem Host, falls er nicht existiert + if not os.path.exists(host_folder): + print(f"--- Erstelle Host-Ordner: {host_folder} ---") + os.makedirs(host_folder) + else: + print(f"--- Host-Ordner {host_folder} existiert bereits. ---") + + # Konfiguriere lxdbr0, falls nicht bereits geschehen + try: + run_command("lxc network show lxdbr0", + "Prüfe, ob lxdbr0 existiert", ignore_errors=True) + except subprocess.CalledProcessError: + print("\n--- lxdbr0 nicht gefunden. Konfiguriere es. ---") + run_command("lxc network set lxdbr0 ipv4.address 10.0.4.1/24", + "Setze IPv4-Adresse für lxdbr0") + run_command("lxc network set lxdbr0 ipv4.nat true", + "Aktiviere IPv4-NAT für lxdbr0") + run_command("lxc network set lxdbr0 ipv6.address none", + "Deaktiviere IPv6 für lxdbr0") + else: + print("\n--- lxdbr0 existiert bereits. ---") + + # Lese den öffentlichen SSH-Schlüssel + ssh_key_path = os.path.expanduser(args.ssh_pub_key_file) + ssh_key_content = "" + if os.path.exists(ssh_key_path): + with open(ssh_key_path, 'r') as f: + ssh_key_content = f.read().strip() + print(f"--- SSH-Schlüssel von {ssh_key_path} geladen. ---") + else: + print(f"--- WARNUNG: SSH-Schlüsseldatei nicht gefunden unter { + ssh_key_path}. Der Schlüssel wird nicht kopiert. ---") + + # Importiere pylxd und initialisiere den Client + # client = pylxd.Client() # Dies sollte bereits oben geschehen sein + + # Initialisiere LXD, falls nicht bereits geschehen + # try: + # client.api.get('/') + # print("\n--- LXD ist bereits initialisiert. ---") + # except pylxd.exceptions.ClientConnectionFailed: + # print("\n--- LXD ist nicht initialisiert. Initialisiere es. ---") + # run_command("lxd init --auto", "Initialisiere LXD") + + # Erstelle den 'out'-Ordner, falls er nicht existiert if not os.path.exists("out"): os.makedirs("out") + + # Bereinige die CSV-Datei von alten Einträgen + csv_path = "out/clients.csv" + if os.path.exists(csv_path): + print(f"\n--- Bereinige {csv_path} von alten Einträgen ---") + try: + result = run_command("lxc list --format json", + "Frage alle Container ab", ignore_errors=True) + if result and result.returncode == 0: + existing_containers = {c['name'] + for c in json.loads(result.stdout)} + valid_clients = [] + with open(csv_path, 'r') as f: + lines = f.readlines() + if lines: + header = lines[0] + valid_clients.append(header) + for line in lines[1:]: + try: + name = line.strip().split(',')[0] + if name in existing_containers: + valid_clients.append(line) + except IndexError: + pass # Ignoriere leere oder fehlerhafte Zeilen + + with open(csv_path, 'w') as f: + f.writelines(valid_clients) + print(f"--- {csv_path} erfolgreich bereinigt. ---") + except (json.JSONDecodeError, FileNotFoundError): + print(f"--- WARNUNG: Konnte {csv_path} nicht bereinigen. ---") + + # Stelle sicher, dass die CSV-Datei existiert und einen Header hat + if not os.path.exists(csv_path) or os.path.getsize(csv_path) == 0: with open(csv_path, "w") as f: f.write("name,ip\n") - elif not os.path.isdir("out"): - # os.remove("out") - os.makedirs("out") clients = [] @@ -117,10 +247,17 @@ def main(): print(f"{'=' * 20} ERSTELLE CONTAINER: {vm_name} {'=' * 20}") - run_command(f"lxc delete {vm_name} --force", - f"{vm_name} vorsorglich löschen", ignore_errors=True) + try: + client.containers.get(vm_name) + print( + f"--- HINWEIS: Container '{vm_name}' existiert bereits und wird übersprungen. ---") + continue + except pylxd.exceptions.NotFound: + pass + run_command(f"lxc launch {args.image} { - vm_name}", f"Starte {vm_name} von Image") + vm_name} -p myprofile", f"Starte {vm_name} von Image mit myprofile") + wait_for_network(vm_name) distro_id_command = "lxc exec {} -- cat /etc/os-release | grep '^ID=' | cut -d'=' -f2" @@ -130,11 +267,11 @@ def main(): install_cmd = "" if distro_id == "arch": - install_cmd = "pacman -Sy --noconfirm python" + install_cmd = "pacman -Sy --noconfirm python vim " elif distro_id in ["ubuntu", "debian"]: - install_cmd = "sh -c 'export DEBIAN_FRONTEND=noninteractive; apt-get update && apt-get install -y python3'" + install_cmd = "sh -c 'export DEBIAN_FRONTEND=noninteractive; apt-get update && apt-get install -y python3 vim'" elif distro_id == "fedora": - install_cmd = "dnf install -y python3" + install_cmd = "dnf install -y python3 vim" else: print(f"FEHLER: Nicht unterstützte Distribution: { distro_id}", file=sys.stderr) @@ -146,7 +283,7 @@ def main(): run_command(f"lxc file push setup_container.py { vm_name}/root/", f"Kopiere Setup-Skript nach {vm_name}") run_command(f"lxc exec {vm_name} -- python3 /root/setup_container.py '{ - args.root_password}' '{vm_name}' '{distro_id}'", f"Führe Konfiguration in {vm_name} aus") + args.root_password}' '{vm_name}' '{distro_id}' '{ssh_key_content}'", f"Führe Konfiguration in {vm_name} aus") ssh_info = "" if not args.no_port_forward: @@ -171,7 +308,6 @@ def main(): if clients: csv_path = "out/clients.csv" with open(csv_path, "a") as f: - # f.write("name,ip\n") for client in clients: f.write(f"{client['name']},{client['ip']}\n") print(f"\nAlle Container wurden erstellt und in {