Zugangsdaten und Proxy-Handling ohne Keepass

This commit is contained in:
C Maier
2026-08-18 13:49:31 +02:00
parent a9b6d00fca
commit fda247376d
5 changed files with 117 additions and 50 deletions
+2
View File
@@ -3,6 +3,8 @@ hosting.kdbx
# Zwischengespeicherte INWX-Session (enthält ein kurzlebiges Session-Cookie) # Zwischengespeicherte INWX-Session (enthält ein kurzlebiges Session-Cookie)
.inwx_session .inwx_session
# Anmeldedaten
.inwx_credentials
# Python-venv und Cache # Python-venv und Cache
venv/ venv/
+36 -5
View File
@@ -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. 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 ### 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: Jetzt kannst du `pip` nutzen. Da die Umgebung aktiv ist, wird die Library **nur** in diesem Ordner installiert und nicht systemweit:
```bash ```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). * **Paket-Check:** `pip list` (Hier sollte `inwx-domrobot` auftauchen).
* **Pfad-Check:** `which python3` (Sollte den Pfad zu deinem Projektordner anzeigen, nicht `/usr/bin/python3`). * **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: ### Anmeldedaten
`sudo apt update && sudo apt install python3-venv -y` siehe .inwx.credentials
### Proxy Nutzung
export INWX_PROXY="http://proxy.stadt-muenster.de:8080"
+3 -3
View File
@@ -1,3 +1,3 @@
10 gruppe1 10 Münster
20 gruppe2 20 Hamm
30 gruppe3 30 Kreis_Warendof
+10 -1
View File
@@ -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
+66 -41
View File
@@ -1,15 +1,21 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""Gemeinsame Helfer für die INWX-Skripte. """Gemeinsame Helfer für die INWX-Skripte.
Kapselt das Laden der Zugangsdaten aus KeePassXC sowie den Login mit Kapselt das Laden der Zugangsdaten sowie den Login mit Session-Caching:
Session-Caching: Nach dem ersten Login wird das Session-Cookie von INWX Nach dem ersten Login wird das Session-Cookie von INWX lokal zwischen-
lokal zwischengespeichert. Folgeaufrufe nutzen diese Session wieder und gespeichert. Folgeaufrufe nutzen diese Session wieder und benötigen daher
benötigen daher weder das KeePass-Master-Passwort noch einen neuen Login, keinen neuen Login, solange die Session bei INWX noch gültig ist.
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 json
import os import os
import subprocess
import sys import sys
import requests import requests
@@ -23,50 +29,64 @@ except ImportError:
# --- KONFIGURATION --- # --- KONFIGURATION ---
API_URL = 'https://api.domrobot.com' API_URL = 'https://api.domrobot.com'
BASE_DIR = os.path.dirname(os.path.abspath(__file__)) BASE_DIR = os.path.dirname(os.path.abspath(__file__))
KP_DB_PATH = os.path.join(BASE_DIR, "hosting.kdbx") # HTTP-Proxy für die Verbindung zur INWX-API (optional).
KP_ENTRY_NAME = "inwx" # 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 # Datei mit der zwischengespeicherten Session (enthält nur ein kurzlebiges
# Session-Cookie, niemals das Passwort). # Session-Cookie, niemals das Passwort).
SESSION_CACHE_PATH = os.path.join(BASE_DIR, ".inwx_session") SESSION_CACHE_PATH = os.path.join(BASE_DIR, ".inwx_session")
def get_creds(): def _check_perms(path):
"""Holt Benutzername und Passwort aus KeePassXC. """Warnt, falls eine Secrets-Datei für Gruppe/Andere lesbar ist."""
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).
"""
try: try:
# -s sorgt dafür, dass das Passwort nicht als 'PROTECTED' ausgegeben wird mode = os.stat(path).st_mode
cmd = ["keepassxc-cli", "show", "-s", KP_DB_PATH, KP_ENTRY_NAME] 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 = {} creds = {}
for line in output.splitlines(): with open(CREDS_FILE_PATH, "r") as f:
if ":" in line: for line in f:
k, v = line.split(":", 1) 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() creds[k.strip().lower()] = v.strip()
user = creds.get("username") or creds.get("benutzername") or creds.get("user") user = creds.get("username") or creds.get("user")
password = creds.get("password") or creds.get("passwort") 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 return user, password
except Exception as e: except Exception as e:
print(f"KeePass-Fehler: {e}") print(f"Fehler beim Lesen von {CREDS_FILE_PATH}: {e}")
return None, None return None, None
@@ -107,7 +127,7 @@ def _clear_cached_session():
def _do_login(api): 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() user, password = get_creds()
if not user or not password: if not user or not password:
return False return False
@@ -129,9 +149,14 @@ def get_client():
Nutzt eine zwischengespeicherte Session, falls vorhanden. Ob diese noch Nutzt eine zwischengespeicherte Session, falls vorhanden. Ob diese noch
gültig ist, wird erst beim ersten echten Aufruf via call_api() geprüft 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, 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) api = ApiClient(api_url=API_URL)
if PROXY_URL:
api.api_session.proxies = {"http": PROXY_URL, "https": PROXY_URL}
if _load_cached_cookies(api): if _load_cached_cookies(api):
print("️ Verwende zwischengespeicherte Session.") print("️ Verwende zwischengespeicherte Session.")
return api 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. """Wie ApiClient.call_api, meldet sich aber bei abgelaufener Session neu an.
Deutet der Rückgabecode auf eine fehlende/abgelaufene Session hin (siehe Deutet der Rückgabecode auf eine fehlende/abgelaufene Session hin (siehe
_is_auth_error), wird die zwischengespeicherte Session verworfen, via _is_auth_error), wird die zwischengespeicherte Session verworfen, neu
KeePass neu eingeloggt und der Aufruf einmal wiederholt. eingeloggt und der Aufruf einmal wiederholt.
""" """
params = params or {} params = params or {}
res = api.call_api(method, params) res = api.call_api(method, params)