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)
.inwx_session
# Anmeldedaten
.inwx_credentials
# Python-venv und Cache
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.
### 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"
+3 -3
View File
@@ -1,3 +1,3 @@
10 gruppe1
20 gruppe2
30 gruppe3
10 Münster
20 Hamm
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
"""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)