Modus proxychains pro Dienst hinzufügen

Neben den Proxy-Variablen (env) kann pro Dienst jetzt der Modus
proxychains gewählt werden: proxychains-ng wird per LD_PRELOAD geladen
und leitet alle ausgehenden TCP-Verbindungen über den Proxy, auch bei
Programmen ohne Proxy-Unterstützung (z.B. Kerio Connect SMTP/POP3).

- eigene proxychains-Konfiguration je Dienst unter /etc/svc-proxy/proxychains/
- localhost, private Netze, DNS und IP-Einträge aus No-Proxy gehen direkt
- Proxy-Hostname wird zu IPv4 aufgelöst, Kennwort-Datei an User= des Dienstes
- Modus-Auswahl im ncurses-Formular, --mode für set, Anzeige in list/show
- README und --help ergänzt
- __pycache__ aus dem Repo entfernt, .gitignore angelegt

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
duffyduck
2026-10-05 14:11:32 +02:00
co-authored by Claude Opus 5.5
parent 2d786a0270
commit 3968c1a7b8
4 changed files with 375 additions and 39 deletions
+272 -34
View File
@@ -9,16 +9,20 @@ Ausführliche Hilfe: svc-proxy --help
import argparse
import curses
import ipaddress
import json
import locale
import os
import pwd
import re
import socket
import subprocess
import sys
import tempfile
from glob import glob as glob_paths
from urllib.parse import quote, unquote, urlsplit
VERSION = "1.0.0"
VERSION = "1.1.0"
PROG = "svc-proxy"
DEFAULT_CONFIG_DIR = "/etc/svc-proxy"
@@ -38,6 +42,34 @@ FIELD_LABELS = {
}
PROXY_ENV_VARS = ["http_proxy", "https_proxy", "ftp_proxy", "all_proxy"]
# Modus pro Dienst: Proxy-Variablen oder proxychains (LD_PRELOAD, alle TCP-Verbindungen)
MODES = ["env", "proxychains"]
MODE_LABELS = {
"env": "Proxy-Variablen",
"proxychains": "proxychains (alle TCP-Verbindungen)",
}
PROXYCHAINS_TYPES = {"http": "http", "socks4": "socks4", "socks5": "socks5", "socks5h": "socks5"}
PROXYCHAINS_LIB_GLOBS = [
"/usr/lib/*/libproxychains.so.4",
"/usr/lib/libproxychains.so.4",
"/usr/lib*/libproxychains4.so",
"/usr/lib*/proxychains-ng/libproxychains4.so",
"/usr/local/lib/libproxychains4.so",
]
# Ziele, die im proxychains-Modus immer direkt (ohne Proxy) erreicht werden
PROXYCHAINS_LOCALNETS = [
"127.0.0.0/255.0.0.0",
"::1/128",
"10.0.0.0/255.0.0.0",
"172.16.0.0/255.240.0.0",
"192.168.0.0/255.255.0.0",
"169.254.0.0/255.255.0.0",
"fc00::/7",
"fe80::/10",
"0.0.0.0:53/0.0.0.0",
"[::]:53/0",
]
UNIT_RE = re.compile(r"^[A-Za-z0-9:_.@\\-]+$")
HOST_RE = re.compile(r"^[A-Za-z0-9._:-]+$")
NO_PROXY_RE = re.compile(r"^[A-Za-z0-9.,:*/_\[\]-]*$")
@@ -45,10 +77,19 @@ NO_PROXY_RE = re.compile(r"^[A-Za-z0-9.,:*/_\[\]-]*$")
HELP_TEXT = """\
{prog} {version} – Proxy für systemd-Dienste setzen
Hängt an beliebige systemd-Dienste ein Drop-in an, das die Umgebungsvariablen
http_proxy, https_proxy, ftp_proxy, all_proxy und no_proxy (jeweils auch in
GROSSBUCHSTABEN) setzt. Alle Programme, die diese Variablen beachten (curl,
wget, apt, Python, Go, Node, ...), laufen dann über den angegebenen Proxy.
Hängt an beliebige systemd-Dienste ein Drop-in an, das den Dienst über einen
Proxy laufen lässt. Pro Dienst gibt es zwei Modi:
env (Standard) Setzt http_proxy, https_proxy, ftp_proxy, all_proxy und
no_proxy (auch in GROSSBUCHSTABEN). Wirkt bei allen Programmen, die
diese Variablen beachten (curl, wget, apt, Python, Go, Docker, ...).
proxychains Lädt proxychains-ng per LD_PRELOAD in den Dienst. Dadurch laufen ALLE
ausgehenden TCP-Verbindungen über den Proxy, auch bei Programmen, die
keine Proxy-Variablen kennen (z.B. Kerio Connect: SMTP, POP3, IMAP).
Direkt (ohne Proxy) bleiben: localhost, private Netze (10/8, 172.16/12,
192.168/16, fc00::/7, Link-Local), DNS (Port 53) und alle IP-/Netz-
Einträge aus der No-Proxy-Liste. Benötigt: apt install proxychains4
Proxy-Typen: http, socks4, socks5 (socks5h wird wie socks5 behandelt).
AUFRUF
{prog} ncurses-Menü starten (wie '{prog} tui')
@@ -79,6 +120,8 @@ FELDER (für 'global' und 'set')
--password KENNWORT Kennwort (Achtung: in der Prozessliste sichtbar!)
--password-stdin Kennwort von der Standardeingabe lesen (empfohlen)
--no-proxy LISTE Kommagetrennte Ausnahmen, z.B. localhost,10.0.0.0/8,.intern
--mode MODUS (nur 'set') env | proxychains (Standard: env, wird nicht
vom globalen Proxy geerbt)
Ein Feld mit leerem Wert (z.B. --user "") wird geleert. Bei 'set' werden nur
die angegebenen Felder geändert, die übrigen bleiben erhalten.
@@ -131,6 +174,17 @@ BEISPIELE
# Eigener SOCKS-Proxy ohne Anmeldung für einen Dienst
sudo {prog} set myapp --proxy socks5h://10.1.1.1:1080
# Kerio Connect: alle Mailverbindungen über SOCKS5, Proxy-Daten vom globalen Proxy
sudo apt install proxychains4
sudo {prog} set kerio-connect --mode proxychains --restart
# Kerio mit eigenem SOCKS5-Proxy inkl. Anmeldung, LAN-Netz 192.0.2.0/24 direkt
sudo {prog} set kerio-connect --mode proxychains --proxy socks5://192.0.2.10:1080 \\
--user max --password-stdin --no-proxy 192.0.2.0/24
# Zurück zu den Proxy-Variablen
sudo {prog} set kerio-connect --mode env
# Nur den Port eines Dienstes ändern (Rest bleibt)
sudo {prog} set myapp --port 1081 --no-restart
@@ -147,6 +201,7 @@ BEISPIELE
DATEIEN
{cfg}/config.json Konfiguration (global + Dienste, 0600)
{cfg}/env/DIENST.env erzeugte Umgebungsdatei (0600)
{cfg}/proxychains/DIENST.conf proxychains-Konfiguration (Modus proxychains)
{sysd}/DIENST.d/{dropin} erzeugtes Drop-in
Dienstnamen ohne Endung erhalten automatisch '.service'.
@@ -163,7 +218,9 @@ class ProxyError(Exception):
# --------------------------------------------------------------------------
def empty_settings():
return {k: "" for k in FIELDS}
s = {k: "" for k in FIELDS}
s["mode"] = "" # nur bei Diensten benutzt, wird nicht vererbt
return s
def normalize_unit(name):
@@ -245,7 +302,13 @@ def validate_settings(raw, is_global):
if not NO_PROXY_RE.match(s["no_proxy"]):
raise ProxyError("Ungültige No-Proxy-Liste: %s" % s["no_proxy"])
s["mode"] = (raw.get("mode") or "").strip().lower()
if s["mode"] and s["mode"] not in MODES:
raise ProxyError("Unbekannter Modus '%s' (erlaubt: %s)" % (s["mode"], ", ".join(MODES)))
if is_global:
if s["mode"]:
raise ProxyError("Den Modus gibt es nur pro Dienst, nicht global")
if any(s[k] for k in ("host", "port", "user", "password")):
if not s["host"]:
raise ProxyError("Globaler Proxy: Host fehlt")
@@ -283,13 +346,85 @@ def resolve(svc, glob):
eff["user"], eff["password"] = glob["user"], glob["password"]
src["user"] = src["password"] = "global"
eff["mode"] = svc.get("mode") or "env"
src["mode"] = "dienst" if svc.get("mode") else "standard"
if not eff["host"]:
raise ProxyError("kein Proxy-Host (weder beim Dienst noch global gesetzt)")
if not eff["port"]:
raise ProxyError("kein Proxy-Port (weder beim Dienst noch global gesetzt)")
if eff["mode"] == "proxychains":
if eff["type"] not in PROXYCHAINS_TYPES:
raise ProxyError("proxychains kann keinen '%s'-Proxy (nur http, socks4, socks5)" % eff["type"])
if re.search(r"\s", eff["user"] + eff["password"]):
raise ProxyError("proxychains: Benutzer/Kennwort dürfen keine Leerzeichen enthalten")
return eff, src
def find_proxychains_lib():
override = os.environ.get("SVC_PROXY_PROXYCHAINS_LIB")
if override:
return override
for pattern in PROXYCHAINS_LIB_GLOBS:
for path in sorted(glob_paths(pattern)):
if os.path.isfile(path):
return path
raise ProxyError("proxychains ist nicht installiert (Debian/Ubuntu: apt install proxychains4, "
"RHEL/Fedora: dnf install proxychains-ng)")
def proxy_ipv4(host):
"""proxychains akzeptiert in der ProxyList nur IPv4-Adressen."""
try:
addr = ipaddress.ip_address(host)
except ValueError:
addr = None
if addr is not None:
if addr.version != 4:
raise ProxyError("proxychains braucht eine IPv4-Adresse für den Proxy, nicht %s" % host)
return str(addr)
try:
infos = socket.getaddrinfo(host, None, socket.AF_INET, socket.SOCK_STREAM)
except socket.gaierror as e:
raise ProxyError("Proxy-Host %s nicht auflösbar (%s) – proxychains braucht eine IPv4-Adresse"
% (host, e.strerror))
return infos[0][4][0]
def proxychains_localnets(no_proxy):
"""Standard-Ausnahmen plus alle IP-/Netz-Einträge aus der No-Proxy-Liste."""
nets = list(PROXYCHAINS_LOCALNETS)
for entry in no_proxy.split(","):
entry = entry.strip().strip("[]")
try:
net = ipaddress.ip_network(entry, strict=False)
except ValueError:
continue # Hostnamen/Domains kann proxychains nicht ausnehmen
if net.version == 4:
item = "%s/%s" % (net.network_address, net.netmask)
else:
item = "%s/%d" % (net.network_address, net.prefixlen)
if item not in nets:
nets.append(item)
return nets
def check_mode(eff):
"""Prüft vor dem Speichern, ob der gewählte Modus nutzbar ist."""
if eff["mode"] == "proxychains":
find_proxychains_lib()
proxy_ipv4(eff["host"])
def service_user(unit):
"""User= des Dienstes (leer = root)."""
rc, out, _ = run_cmd(["systemctl", "show", "-p", "User", "-p", "DynamicUser", unit])
if rc != 0:
return "", False
vals = dict(line.split("=", 1) for line in out.splitlines() if "=" in line)
return vals.get("User", ""), vals.get("DynamicUser", "no") == "yes"
def proxy_url(eff, mask=False):
auth = ""
if eff.get("user"):
@@ -330,6 +465,10 @@ def is_inheriting(svc):
return not any(svc[k] for k in FIELDS)
def mode_suffix(eff):
return " [proxychains]" if eff["mode"] == "proxychains" else ""
# --------------------------------------------------------------------------
# Konfiguration und Erzeugen der Dateien
# --------------------------------------------------------------------------
@@ -339,13 +478,21 @@ class Config:
self.config_dir = config_dir
self.config_file = os.path.join(config_dir, "config.json")
self.env_dir = os.path.join(config_dir, "env")
self.pc_dir = os.path.join(config_dir, "proxychains")
self.systemd_dir = systemd_dir
self.global_ = empty_settings()
self.services = {}
# Pfade
@staticmethod
def safe_name(unit):
return re.sub(r"[^A-Za-z0-9:_.@-]", "_", unit)
def env_file(self, unit):
return os.path.join(self.env_dir, re.sub(r"[^A-Za-z0-9:_.@-]", "_", unit) + ".env")
return os.path.join(self.env_dir, self.safe_name(unit) + ".env")
def pc_file(self, unit):
return os.path.join(self.pc_dir, self.safe_name(unit) + ".conf")
def dropin_dir(self, unit):
return os.path.join(self.systemd_dir, unit + ".d")
@@ -365,35 +512,46 @@ class Config:
except (OSError, ValueError) as e:
raise ProxyError("Konfiguration %s nicht lesbar: %s" % (self.config_file, e))
def clean(d):
def clean(d, keys):
s = empty_settings()
for k in FIELDS:
for k in keys:
if isinstance(d, dict) and isinstance(d.get(k), str):
s[k] = d[k]
return s
self.global_ = clean(data.get("global"))
self.global_ = clean(data.get("global"), FIELDS)
self.services = {}
for unit, s in (data.get("services") or {}).items():
if UNIT_RE.match(unit):
self.services[unit] = clean(s)
self.services[unit] = clean(s, FIELDS + ["mode"])
def ensure_config_dir(self):
# 0711: Dienste mit eigenem Benutzer müssen ihre proxychains-Datei erreichen,
# dürfen das Verzeichnis aber nicht auflisten. config.json und env/ bleiben 0600/0700.
os.makedirs(self.config_dir, mode=0o711, exist_ok=True)
os.chmod(self.config_dir, 0o711)
def save(self):
os.makedirs(self.config_dir, mode=0o700, exist_ok=True)
self.ensure_config_dir()
data = {
"version": 1,
"global": self.global_,
"global": {k: self.global_[k] for k in FIELDS},
"services": {u: self.services[u] for u in sorted(self.services)},
}
atomic_write(self.config_file, json.dumps(data, indent=2, ensure_ascii=False) + "\n", 0o600)
# Drop-ins
def render_env(self, unit, eff):
url = proxy_url(eff)
lines = [
"# Erzeugt von %s fuer %s - Aenderungen werden ueberschrieben." % (PROG, unit),
"# Bearbeiten mit: %s set %s ..." % (PROG, unit),
]
if eff["mode"] == "proxychains":
lines.append('LD_PRELOAD="%s"' % find_proxychains_lib())
lines.append('PROXYCHAINS_CONF_FILE="%s"' % self.pc_file(unit))
lines.append('PROXYCHAINS_QUIET_MODE="1"')
return "\n".join(lines) + "\n"
url = proxy_url(eff)
for var in PROXY_ENV_VARS:
lines.append('%s="%s"' % (var, url))
lines.append('%s="%s"' % (var.upper(), url))
@@ -402,6 +560,53 @@ class Config:
lines.append('NO_PROXY="%s"' % eff["no_proxy"])
return "\n".join(lines) + "\n"
def render_proxychains(self, unit, eff):
entry = "%s %s %s" % (PROXYCHAINS_TYPES[eff["type"]], proxy_ipv4(eff["host"]), eff["port"])
if eff["user"]:
entry += " %s %s" % (eff["user"], eff["password"])
lines = [
"# Erzeugt von %s fuer %s - Aenderungen werden ueberschrieben." % (PROG, unit),
"# Proxy: %s (%s)" % (proxy_url(eff, mask=True), eff["host"]),
"strict_chain",
"quiet_mode",
"tcp_read_time_out 15000",
"tcp_connect_time_out 8000",
"# Kein proxy_dns: DNS wird normal aufgeloest (wichtig z.B. fuer MX-Abfragen).",
"",
"# Diese Ziele werden direkt (ohne Proxy) verbunden:",
]
lines += ["localnet " + n for n in proxychains_localnets(eff["no_proxy"])]
lines += ["", "[ProxyList]", entry]
return "\n".join(lines) + "\n"
def write_proxychains(self, unit, eff):
"""Schreibt die proxychains-Datei. Liefert (geändert, warnung)."""
self.ensure_config_dir()
os.makedirs(self.pc_dir, mode=0o755, exist_ok=True)
os.chmod(self.pc_dir, 0o755)
path = self.pc_file(unit)
secret = bool(eff["user"])
changed = write_if_different(path, self.render_proxychains(unit, eff), 0o600 if secret else 0o644)
os.chmod(path, 0o600 if secret else 0o644)
warning = None
# Die Bibliothek liest die Datei im Dienst-Prozess, also mit dessen Benutzer.
user, dynamic = service_user(unit)
if secret and os.geteuid() == 0:
uid = gid = 0
if dynamic:
warning = ("%s: nutzt DynamicUser – die proxychains-Datei mit Kennwort ist für den "
"Dienst nicht lesbar. Proxy ohne Anmeldung verwenden." % unit)
elif user and user != "root":
try:
pw = pwd.getpwnam(user)
uid, gid = pw.pw_uid, pw.pw_gid
except KeyError:
warning = "%s: Benutzer '%s' unbekannt – proxychains-Datei evtl. nicht lesbar" % (unit, user)
st = os.stat(path)
if (st.st_uid, st.st_gid) != (uid, gid):
os.chown(path, uid, gid)
return changed, warning
def render_dropin(self, unit):
return (
"# Erzeugt von %s - nicht manuell bearbeiten.\n"
@@ -413,6 +618,7 @@ class Config:
def remove_unit_files(self, unit):
changed = remove_file(self.dropin_file(unit))
changed |= remove_file(self.env_file(unit))
changed |= remove_file(self.pc_file(unit))
try:
os.rmdir(self.dropin_dir(unit))
except OSError:
@@ -424,14 +630,22 @@ class Config:
svc = self.services.get(unit)
if svc is None:
return self.remove_unit_files(unit), None
warning = None
try:
eff, _ = resolve(svc, self.global_)
changed = False
if eff["mode"] == "proxychains":
changed, warning = self.write_proxychains(unit, eff)
else:
changed = remove_file(self.pc_file(unit))
env = self.render_env(unit, eff)
except ProxyError as e:
return self.remove_unit_files(unit), "%s: %s – Proxy für diesen Dienst deaktiviert" % (unit, e)
self.ensure_config_dir()
os.makedirs(self.env_dir, mode=0o700, exist_ok=True)
changed = write_if_different(self.env_file(unit), self.render_env(unit, eff), 0o600)
changed |= write_if_different(self.env_file(unit), env, 0o600)
changed |= write_if_different(self.dropin_file(unit), self.render_dropin(unit), 0o644)
return changed, None
return changed, warning
def managed_dropins(self):
"""Units, für die ein Drop-in von svc-proxy existiert."""
@@ -549,12 +763,14 @@ def is_backspace(k):
class FormField:
def __init__(self, key, label, value, kind="text", choices=None, secret=False, placeholder=""):
def __init__(self, key, label, value, kind="text", choices=None, secret=False, placeholder="",
display=None):
self.key = key
self.label = label
self.kind = kind # text | choice | button
self.value = value
self.choices = choices or []
self.display = display or {}
self.secret = secret
self.placeholder = placeholder
self.pos = len(value)
@@ -659,8 +875,8 @@ class TUI:
except ProxyError as e:
return "FEHLER: " + str(e), self.c_err
if is_inheriting(svc):
return "global → " + proxy_url(eff, mask=True), self.c_global
return "eigen → " + proxy_url(eff, mask=True), self.c_own
return "global → " + proxy_url(eff, mask=True) + mode_suffix(eff), self.c_global
return "eigen → " + proxy_url(eff, mask=True) + mode_suffix(eff), self.c_own
# ---------- Hauptliste ----------
def draw_main(self):
@@ -959,6 +1175,11 @@ class TUI:
"Benutzer/Kennwort werden nur geerbt, wenn auch der Host global ist.",
"Eigener Host ohne Benutzer = Proxy ohne Anmeldung.",
"",
"Modus (pro Dienst):",
" Proxy-Variablen setzt http_proxy & Co. – nur für Programme, die sie beachten",
" proxychains leitet ALLE TCP-Verbindungen des Dienstes über den Proxy",
" (z.B. Kerio SMTP/POP3). Lokale/private Netze und DNS gehen direkt.",
"",
"Tasten: Enter bearbeiten, g global, Entf/d entfernen, / filtern,",
"c nur konfigurierte, r Dienst neu starten, F5 Liste neu laden, q Ende.",
"Nach jeder Änderung wird 'systemctl daemon-reload' ausgeführt.",
@@ -983,7 +1204,11 @@ class TUI:
else:
type_choices = [""] + PROXY_TYPES
tval = settings["type"]
fields = [FormField("type", "Proxy-Typ", tval, kind="choice", choices=type_choices)]
fields = []
if not is_global:
fields.append(FormField("mode", "Modus", settings["mode"] or "env", kind="choice",
choices=MODES, display=MODE_LABELS))
fields.append(FormField("type", "Proxy-Typ", tval, kind="choice", choices=type_choices))
for key in ("host", "port", "user", "password", "no_proxy"):
fields.append(FormField(key, FIELD_LABELS[key], settings[key],
secret=(key == "password"), placeholder=ph(key)))
@@ -993,7 +1218,7 @@ class TUI:
buttons.append(FormField("remove", "Proxy entfernen" if not is_global else "Global löschen",
"", kind="button"))
items = fields + buttons
cur = 1 # Host
cur = [f.key for f in fields].index("host")
error = ""
def collect():
@@ -1016,13 +1241,17 @@ class TUI:
input_x = 2 + label_w + 3
input_w = max(10, min(w - input_x - 2, 60))
cursor_pos = None
step = 2 if h >= 4 + len(fields) * 2 + 9 else 1
for i, f in enumerate(fields):
y = 4 + i * 2
y = 4 + i * step
focused = (i == cur)
put(scr, y, 2, f.label.rjust(label_w), curses.A_BOLD if focused else 0)
put(scr, y, 2 + label_w, " : ")
if f.kind == "choice":
shown = f.value if f.value else "(global: %s)" % (g["type"] or "http")
if f.display:
shown = f.display.get(f.value, f.value)
else:
shown = f.value if f.value else "(global: %s)" % (g["type"] or "http")
text = "◀ %s ▶" % shown
put(scr, y, input_x, text.ljust(input_w), self.c_sel if focused else curses.A_UNDERLINE)
else:
@@ -1039,7 +1268,7 @@ class TUI:
if focused:
cursor_pos = (y, input_x + f.pos - f.off)
by = 4 + len(fields) * 2
by = 4 + len(fields) * step
bx = input_x
for j, b in enumerate(buttons):
focused = (len(fields) + j == cur)
@@ -1055,7 +1284,7 @@ class TUI:
preview = proxy_url(cand, mask=True) if cand["host"] else "(kein globaler Proxy)"
else:
eff, _ = resolve(cand, g)
preview = proxy_url(eff, mask=True)
preview = proxy_url(eff, mask=True) + mode_suffix(eff)
if eff["no_proxy"]:
preview += " No-Proxy: " + eff["no_proxy"]
put(scr, py, 2, "Ergebnis: ", curses.A_BOLD)
@@ -1066,12 +1295,15 @@ class TUI:
if error:
put(scr, py + 1, 2, error, self.c_err)
if not is_global:
put(scr, py + 3, 2, "Leere Felder = globaler Wert. Benutzer/Kennwort werden nur geerbt,", self.c_dim)
put(scr, py + 4, 2, "wenn auch der Host leer ist; eigener Host ohne Benutzer = ohne Anmeldung.", self.c_dim)
put(scr, py + 2, 2, "Leere Felder = globaler Wert. Benutzer/Kennwort werden nur geerbt,", self.c_dim)
put(scr, py + 3, 2, "wenn auch der Host leer ist; eigener Host ohne Benutzer = ohne Anmeldung.", self.c_dim)
if fields[0].value == "proxychains":
put(scr, py + 4, 2, "proxychains: alle TCP-Verbindungen über den Proxy; direkt bleiben localhost,", self.c_dim)
put(scr, py + 5, 2, "private Netze, DNS (Port 53) und IP-/Netz-Einträge aus No-Proxy.", self.c_dim)
else:
put(scr, py + 3, 2, "Benutzer/Kennwort leer = Proxy ohne Anmeldung.", self.c_dim)
self.key_bar("Tab/↑↓ Feld ←→ Typ Enter weiter F2 Speichern Strg+U Feld leeren Esc Abbrechen")
self.key_bar("Tab/↑↓ Feld ←→ Auswahl Enter weiter F2 Speichern Strg+U Feld leeren Esc Abbrechen")
if cursor_pos and items[cur].kind == "text":
self.cursor(True)
try:
@@ -1148,7 +1380,8 @@ class TUI:
try:
cand = validate_settings(collect(), is_global)
if not is_global:
resolve(cand, g)
eff, _ = resolve(cand, g)
check_mode(eff)
except ProxyError as e:
error = "Nicht gespeichert: %s" % e
continue
@@ -1210,7 +1443,7 @@ def fields_from_args(args):
vals = {}
if args.proxy is not None:
vals.update(parse_proxy_url(args.proxy))
for key in ("type", "host", "port", "user", "password", "no_proxy"):
for key in ("type", "host", "port", "user", "password", "no_proxy", "mode"):
v = getattr(args, key)
if v is not None:
vals[key] = v
@@ -1248,11 +1481,12 @@ def cmd_set(cfg, args):
svc = validate_settings(base, is_global=False)
try:
eff, _ = resolve(svc, cfg.global_)
check_mode(eff)
except ProxyError as e:
raise ProxyError("%s: %s" % (unit, e))
staged[unit] = svc
print("%s -> %s%s" % (unit, proxy_url(eff, mask=True),
" (global)" if is_inheriting(svc) else ""))
print("%s -> %s%s%s" % (unit, proxy_url(eff, mask=True),
" (global)" if is_inheriting(svc) else "", mode_suffix(eff)))
cfg.services.update(staged)
cfg.save()
changed, warnings = set(), []
@@ -1318,7 +1552,7 @@ def cmd_list(cfg, args):
mark = "G" if is_inheriting(svc) else "E"
try:
eff, _ = resolve(svc, cfg.global_)
text = proxy_url(eff, mask=True)
text = proxy_url(eff, mask=True) + mode_suffix(eff)
except ProxyError as e:
text = "FEHLER: %s" % e
print(" %s %-*s %-10s %s" % (mark, width, name, state, text))
@@ -1347,12 +1581,13 @@ def cmd_show(cfg, args):
if svc is None:
print("Proxy: keiner")
return 0
print("Modus: %s" % ("nutzt globalen Proxy" if is_inheriting(svc) else "eigene Werte (leere Felder = global)"))
print("Herkunft: %s" % ("nutzt globalen Proxy" if is_inheriting(svc) else "eigene Werte (leere Felder = global)"))
try:
eff, src = resolve(svc, cfg.global_)
except ProxyError as e:
print("FEHLER: %s" % e)
return 1
print("Modus: %s" % MODE_LABELS[eff["mode"]])
for key in FIELDS:
val = eff[key]
if key == "password" and val:
@@ -1361,6 +1596,8 @@ def cmd_show(cfg, args):
print("Proxy-URL: %s" % proxy_url(eff, mask=True))
print("Drop-in: %s" % cfg.dropin_file(unit))
print("Env-Datei: %s" % cfg.env_file(unit))
if eff["mode"] == "proxychains":
print("proxychains-Konfiguration: %s" % cfg.pc_file(unit))
return 0
@@ -1387,6 +1624,7 @@ def build_parser():
fields.add_argument("--password")
fields.add_argument("--password-stdin", action="store_true")
fields.add_argument("--no-proxy", dest="no_proxy")
fields.add_argument("--mode", type=str.lower, choices=MODES)
parser = argparse.ArgumentParser(prog=PROG, add_help=False, parents=[common(False)])
parser.add_argument("-V", "--version", action="version", version="%s %s" % (PROG, VERSION))