From fda247376d7d879763ef1e4e89b80b19ad8c6aee Mon Sep 17 00:00:00 2001 From: C Maier Date: Tue, 18 Aug 2026 13:49:31 +0200 Subject: [PATCH] Zugangsdaten und Proxy-Handling ohne Keepass --- .gitignore | 2 + README.md | 41 ++++++++++++++++--- domain_owner | 6 +-- domain_price | 11 ++++- inwx_common.py | 107 ++++++++++++++++++++++++++++++------------------- 5 files changed, 117 insertions(+), 50 deletions(-) diff --git a/.gitignore b/.gitignore index af8a49c..0af142e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ hosting.kdbx # Zwischengespeicherte INWX-Session (enthält ein kurzlebiges Session-Cookie) .inwx_session +# Anmeldedaten +.inwx_credentials # Python-venv und Cache venv/ diff --git a/README.md b/README.md index d96d5eb..ee9ae7c 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,31 @@ +### Skripte und Funktionen +immer zuerst die python Umgebung aktivieren und Proxy setzen +`source venv/bin/activate` +`export INWX_PROXY="http://proxy.stadt-muenster.de:8080` + +## allgemeine Funktonen, nicht aufrufbar +inwx_common.py +## Eintrag hinzufügen +inwx_add_record.py +# Bsp python3 inwx_add_record.py stadt-muenster.eu TXT @ citeq:10 + +## Gesamtliste fuer die citeq +inwx_domains.py +# Bsp pyhton3 inwx_domains.py + +## Abrechnungsliste mit Verwaltungen und Kosten +inwx_domains_owner.py +# Bsp pyhton3 inwx_domains_owner.py + +## Inhalt einer Domain auflisten +inwx_list.py +# Bsp python3 inwx_list.py stadt-muenster.eu + +## Listen um Inhaber und Preise der Abrechnung aufzulisten +domain_owner +domain_price + +### Neuinstallation Um die Library sauber in einer isolierten Umgebung auf deinem neuen Server zu installieren, gehst du in dein Projektverzeichnis und führst diese drei Schritte aus. Das verhindert, dass sich verschiedene Python-Projekte in die Quere kommen. ### 1. Die Umgebung erstellen @@ -19,7 +47,9 @@ source venv/bin/activate Jetzt kannst du `pip` nutzen. Da die Umgebung aktiv ist, wird die Library **nur** in diesem Ordner installiert und nicht systemweit: ```bash -pip install inwx-domrobot +### pip install inwx-domrobot +pip install --proxy http://proxy.stadt-muenster.de:8080 inwx-domrobot + ``` --- @@ -30,9 +60,10 @@ Du kannst sofort prüfen, ob alles an der richtigen Stelle gelandet ist: * **Paket-Check:** `pip list` (Hier sollte `inwx-domrobot` auftauchen). * **Pfad-Check:** `which python3` (Sollte den Pfad zu deinem Projektordner anzeigen, nicht `/usr/bin/python3`). -### Wie geht es weiter? -Wenn du fertig bist oder das Terminal schließt, kannst du die Umgebung mit dem Befehl `deactivate` wieder verlassen. Wenn du das Skript später erneut ausführen willst, musst du vorher nur wieder den `source`-Befehl aus Schritt 2 eingeben. -**Tipp für den neuen Server:** Falls `python3 -m venv` eine Fehlermeldung ausgibt ("venv not found"), musst du das entsprechende System-Paket unter Ubuntu/Debian einmalig nachinstallieren: -`sudo apt update && sudo apt install python3-venv -y` +### Anmeldedaten +siehe .inwx.credentials + +### Proxy Nutzung +export INWX_PROXY="http://proxy.stadt-muenster.de:8080" diff --git a/domain_owner b/domain_owner index 6510e33..c6bf9ff 100644 --- a/domain_owner +++ b/domain_owner @@ -1,3 +1,3 @@ -10 gruppe1 -20 gruppe2 -30 gruppe3 +10 Münster +20 Hamm +30 Kreis_Warendof diff --git a/domain_price b/domain_price index af413fd..72b00bf 100644 --- a/domain_price +++ b/domain_price @@ -1 +1,10 @@ -de 5,00 +de 12,90 +com 13,20 +ms 19,50 +eu 13,50 +schule 16,00 +digital 17,60 +info 15,50 +nrw 16,70 +net 14,10 +org 14,00 diff --git a/inwx_common.py b/inwx_common.py index 027898a..da55597 100644 --- a/inwx_common.py +++ b/inwx_common.py @@ -1,15 +1,21 @@ #!/usr/bin/env python3 """Gemeinsame Helfer für die INWX-Skripte. -Kapselt das Laden der Zugangsdaten aus KeePassXC sowie den Login mit -Session-Caching: Nach dem ersten Login wird das Session-Cookie von INWX -lokal zwischengespeichert. Folgeaufrufe nutzen diese Session wieder und -benötigen daher weder das KeePass-Master-Passwort noch einen neuen Login, -solange die Session bei INWX noch gültig ist. +Kapselt das Laden der Zugangsdaten sowie den Login mit Session-Caching: +Nach dem ersten Login wird das Session-Cookie von INWX lokal zwischen- +gespeichert. Folgeaufrufe nutzen diese Session wieder und benötigen daher +keinen neuen Login, solange die Session bei INWX noch gültig ist. + +Zugangsdaten werden in dieser Reihenfolge gesucht: + 1. Umgebungsvariablen INWX_USER / INWX_PASSWORD + (z.B. gesetzt über eine systemd EnvironmentFile=) + 2. Credentials-Datei CREDS_FILE_PATH im Format: + username=dein_inwx_benutzername + password=dein_inwx_passwort + Die Datei muss chmod 600 und dem ausführenden Benutzer gehören. """ import json import os -import subprocess import sys import requests @@ -23,50 +29,64 @@ except ImportError: # --- KONFIGURATION --- API_URL = 'https://api.domrobot.com' BASE_DIR = os.path.dirname(os.path.abspath(__file__)) -KP_DB_PATH = os.path.join(BASE_DIR, "hosting.kdbx") -KP_ENTRY_NAME = "inwx" +# HTTP-Proxy für die Verbindung zur INWX-API (optional). +# Format: http://proxyhost:port oder mit Auth: http://user:passwort@proxyhost:port +# Kann per Umgebungsvariable INWX_PROXY gesetzt werden. +PROXY_URL = os.environ.get("INWX_PROXY") +# Datei mit den INWX-Zugangsdaten (username=... / password=...). +# WICHTIG: chmod 600, nur für den ausführenden Benutzer lesbar! +CREDS_FILE_PATH = os.path.join(BASE_DIR, ".inwx_credentials") # Datei mit der zwischengespeicherten Session (enthält nur ein kurzlebiges # Session-Cookie, niemals das Passwort). SESSION_CACHE_PATH = os.path.join(BASE_DIR, ".inwx_session") -def get_creds(): - """Holt Benutzername und Passwort aus KeePassXC. - - Das KeePass-Master-Passwort wird interaktiv abgefragt. Ist die - Umgebungsvariable KP_PW gesetzt, wird deren Wert stattdessen automatisch - an keepassxc-cli übergeben (praktisch in nicht-interaktiven Umgebungen). - """ +def _check_perms(path): + """Warnt, falls eine Secrets-Datei für Gruppe/Andere lesbar ist.""" try: - # -s sorgt dafür, dass das Passwort nicht als 'PROTECTED' ausgegeben wird - cmd = ["keepassxc-cli", "show", "-s", KP_DB_PATH, KP_ENTRY_NAME] + mode = os.stat(path).st_mode + if mode & 0o077: + print(f"⚠️ {path} ist für andere Benutzer les-/schreibbar! " + f"Bitte 'chmod 600 {path}' ausführen.") + except OSError: + pass - master_pw = os.environ.get("KP_PW") - if master_pw: - # Master-Passwort über stdin an keepassxc-cli durchreichen. - result = subprocess.run( - cmd, input=master_pw + "\n", - capture_output=True, text=True, - ) - if result.returncode != 0: - print(f"❌ KeePass-Fehler: {result.stderr.strip()}") - return None, None - output = result.stdout - else: - # Kein KP_PW gesetzt -> keepassxc-cli fragt interaktiv nach. - output = subprocess.check_output(cmd, text=True) +def get_creds(): + """Holt Benutzername und Passwort für die INWX-API. + + Siehe Modul-Docstring für die Reihenfolge der Quellen (ENV, dann Datei). + """ + env_user = os.environ.get("INWX_USER") + env_password = os.environ.get("INWX_PASSWORD") + if env_user and env_password: + return env_user, env_password + + if not os.path.exists(CREDS_FILE_PATH): + print(f"❌ Keine Zugangsdaten gefunden: weder INWX_USER/INWX_PASSWORD " + f"gesetzt, noch {CREDS_FILE_PATH} vorhanden.") + return None, None + + _check_perms(CREDS_FILE_PATH) + + try: creds = {} - for line in output.splitlines(): - if ":" in line: - k, v = line.split(":", 1) + with open(CREDS_FILE_PATH, "r") as f: + for line in f: + line = line.strip() + if not line or line.startswith("#") or "=" not in line: + continue + k, v = line.split("=", 1) creds[k.strip().lower()] = v.strip() - user = creds.get("username") or creds.get("benutzername") or creds.get("user") - password = creds.get("password") or creds.get("passwort") + user = creds.get("username") or creds.get("user") + password = creds.get("password") + if not user or not password: + print(f"❌ {CREDS_FILE_PATH} enthält keine gültigen username=/password=-Zeilen.") + return None, None return user, password except Exception as e: - print(f"❌ KeePass-Fehler: {e}") + print(f"❌ Fehler beim Lesen von {CREDS_FILE_PATH}: {e}") return None, None @@ -107,7 +127,7 @@ def _clear_cached_session(): def _do_login(api): - """Frischer Login via KeePass-Zugangsdaten; speichert die Session. True bei Erfolg.""" + """Frischer Login mit den konfigurierten Zugangsdaten; speichert die Session. True bei Erfolg.""" user, password = get_creds() if not user or not password: return False @@ -129,9 +149,14 @@ def get_client(): Nutzt eine zwischengespeicherte Session, falls vorhanden. Ob diese noch gültig ist, wird erst beim ersten echten Aufruf via call_api() geprüft – so sparen wir uns eine zusätzliche Anfrage. Ist keine Session vorhanden, - wird sofort via KeePass eingeloggt. + wird sofort neu eingeloggt. + + Ist PROXY_URL gesetzt, laufen alle Requests der Session (Login und + API-Aufrufe) über diesen HTTP-Proxy. """ api = ApiClient(api_url=API_URL) + if PROXY_URL: + api.api_session.proxies = {"http": PROXY_URL, "https": PROXY_URL} if _load_cached_cookies(api): print("ℹ️ Verwende zwischengespeicherte Session.") return api @@ -159,8 +184,8 @@ def call_api(api, method, params=None): """Wie ApiClient.call_api, meldet sich aber bei abgelaufener Session neu an. Deutet der Rückgabecode auf eine fehlende/abgelaufene Session hin (siehe - _is_auth_error), wird die zwischengespeicherte Session verworfen, via - KeePass neu eingeloggt und der Aufruf einmal wiederholt. + _is_auth_error), wird die zwischengespeicherte Session verworfen, neu + eingeloggt und der Aufruf einmal wiederholt. """ params = params or {} res = api.call_api(method, params)