Zugangsdaten und Proxy-Handling ohne Keepass
This commit is contained in:
@@ -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/
|
||||||
|
|||||||
@@ -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
@@ -1,3 +1,3 @@
|
|||||||
10 gruppe1
|
10 Münster
|
||||||
20 gruppe2
|
20 Hamm
|
||||||
30 gruppe3
|
30 Kreis_Warendof
|
||||||
|
|||||||
+10
-1
@@ -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
|
||||||
|
|||||||
+65
-40
@@ -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:
|
def get_creds():
|
||||||
# Master-Passwort über stdin an keepassxc-cli durchreichen.
|
"""Holt Benutzername und Passwort für die INWX-API.
|
||||||
result = subprocess.run(
|
|
||||||
cmd, input=master_pw + "\n",
|
Siehe Modul-Docstring für die Reihenfolge der Quellen (ENV, dann Datei).
|
||||||
capture_output=True, text=True,
|
"""
|
||||||
)
|
env_user = os.environ.get("INWX_USER")
|
||||||
if result.returncode != 0:
|
env_password = os.environ.get("INWX_PASSWORD")
|
||||||
print(f"❌ KeePass-Fehler: {result.stderr.strip()}")
|
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
|
return None, None
|
||||||
output = result.stdout
|
|
||||||
else:
|
|
||||||
# Kein KP_PW gesetzt -> keepassxc-cli fragt interaktiv nach.
|
|
||||||
output = subprocess.check_output(cmd, text=True)
|
|
||||||
|
|
||||||
|
_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)
|
||||||
|
|||||||
Reference in New Issue
Block a user