# svc-proxy – Proxy für systemd-Dienste `svc-proxy` legt fest, über welchen Proxy ein systemd-Dienst seine Verbindungen aufbaut. Bedienen lässt es sich auf zwei Wegen: - **ncurses-Menü**: listet alle Dienste auf. Man wählt einen aus und trägt die Daten bequem ein. - **Kommandozeile**: für Skripte, Ansible, Provisionierung usw. Pro Dienst wählt man den **Modus**: | Modus | Wirkung | Geeignet für | |---|---|---| | `env` – Proxy-Variablen (Standard) | setzt `http_proxy`, `https_proxy`, … | Programme, die diese Variablen beachten (curl, apt, Docker, Python, Go, …) | | `proxychains` | leitet **alle** ausgehenden TCP-Verbindungen des Dienstes über den Proxy | Programme ohne Proxy-Unterstützung, z.B. **Kerio Connect** (SMTP, POP3, IMAP) | Es gibt einen **globalen Proxy**. Alle Felder, die man bei einem Dienst leer lässt, übernehmen den globalen Wert. Explizit gesetzte Werte beim Dienst gelten vorrangig. Benutzername und Kennwort sind optional; ohne sie wird der Proxy ohne Anmeldung genutzt. Abhängigkeiten: nur `python3` (≥ 3.6) mit den Standardmodulen. `curses` ist bei Debian/Ubuntu/RHEL im Python-Paket enthalten. Für den Modus `proxychains` zusätzlich `proxychains4` (Debian/Ubuntu) bzw. `proxychains-ng` (RHEL/Fedora). --- ## Installation ```bash sudo install -m 0755 svc-proxy /usr/local/sbin/svc-proxy sudo apt install proxychains4 # nur für den Modus proxychains nötig ``` Danach `sudo svc-proxy` (Menü) oder `svc-proxy --help` (Hilfe). --- ## Funktionsweise Für jeden konfigurierten Dienst werden zwei Dateien erzeugt: | Datei | Inhalt | Rechte | |-------|--------|--------| | `/etc/systemd/system/.d/50-svc-proxy.conf` | Drop-in mit `EnvironmentFile=` | 0644 | | `/etc/svc-proxy/env/.env` | die eigentlichen Proxy-Variablen | 0600 | Im Modus `proxychains` kommt eine dritte Datei dazu: | Datei | Inhalt | Rechte | |-------|--------|--------| | `/etc/svc-proxy/proxychains/.conf` | proxychains-Konfiguration | 0644, mit Kennwort 0600 | Die Konfiguration selbst (global + alle Dienste) liegt in `/etc/svc-proxy/config.json` (0600). Die Drop-ins und Env-Dateien werden daraus generiert und sollten nicht von Hand bearbeitet werden. ### Modus `env` (Proxy-Variablen) Gesetzt werden die Variablen ``` http_proxy https_proxy ftp_proxy all_proxy no_proxy HTTP_PROXY HTTPS_PROXY FTP_PROXY ALL_PROXY NO_PROXY ``` > **Warum `EnvironmentFile=` und nicht `Environment=`?** > Werte aus `Environment=` kann jeder Benutzer mit `systemctl show ` > auslesen, also auch ein Proxy-Kennwort. Die Env-Datei ist nur für root lesbar. Sonderzeichen in Benutzername/Kennwort (z.B. `@`, `:`, `/`) werden automatisch URL-kodiert. ### Wichtige Einschränkung Der Proxy wirkt nur bei Programmen, die die Proxy-Umgebungsvariablen auswerten. Das tun die meisten (curl, wget, apt, pip, Python `requests`/`urllib`, Go, Node mit passenden Libs, Docker-Daemon, containerd, Git, ...). Programme, die die Variablen ignorieren (z.B. viele Java-Anwendungen ohne `-Dhttp.proxyHost`), müssen anders konfiguriert werden. Eine Netzwerk-Sperre ist das nicht: Ein Programm *kann* den Proxy umgehen. SOCKS-Proxys (`socks5://`, `socks5h://`) werden ebenfalls in alle Variablen geschrieben. Das funktioniert z.B. mit curl und Go. Manche Programme verstehen in `http_proxy` aber nur `http://`. Für solche Fälle gibt es den Modus `proxychains`. ### Modus `proxychains` (alle TCP-Verbindungen) [proxychains-ng](https://github.com/rofl0r/proxychains-ng) wird per `LD_PRELOAD` in den Dienst geladen. Die Bibliothek fängt jeden `connect()`-Aufruf ab und baut die Verbindung stattdessen über den Proxy auf. Das Programm selbst muss nichts von einem Proxy wissen. Das ist dasselbe wie `proxychains4 programm`, nur fest im Dienst eingetragen. Die Env-Datei enthält dann: ``` LD_PRELOAD="/usr/lib/x86_64-linux-gnu/libproxychains.so.4" PROXYCHAINS_CONF_FILE="/etc/svc-proxy/proxychains/.conf" PROXYCHAINS_QUIET_MODE="1" ``` **Sicherheitsprüfung:** proxychains liest seine Konfiguration schon beim Start des Dienstes. Ein Fehler darin (z.B. `localnet netmask error`) bricht den Dienst ab. Deshalb lässt svc-proxy jede erzeugte Konfiguration vorher von der installierten Bibliothek einlesen. Lehnt sie die Konfiguration ab, wird für den Dienst nichts installiert und eine Warnung ausgegeben. Der Dienst startet dann normal ohne Proxy. **Direkt (ohne Proxy) verbunden werden immer:** - localhost (`127.0.0.0/8`, `::1`\*) - private Netze: `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`, `169.254.0.0/16`, `fc00::/7`\*, `fe80::/10`\* - DNS (Port 53, jedes Ziel), damit z.B. MX-Abfragen normal funktionieren - alle **IP-Adressen und Netze** aus der No-Proxy-Liste (z.B. `192.0.2.0/24`). Hostnamen/Domains aus No-Proxy kann proxychains nicht berücksichtigen. Hinweise: \* IPv6-Ausnahmen nur mit proxychains-ng **ab 4.16**, siehe nächster Abschnitt. #### Ältere proxychains-Versionen (Debian 11, Ubuntu 20.04) proxychains-ng **4.12/4.14** kennt keine IPv6-Ausnahmen. Mit einer solchen Zeile bricht es mit `localnet netmask error` ab, und Verbindungen zu `::1` schickt es über den Proxy. svc-proxy erkennt das: Die IPv6-Zeilen werden weggelassen, und es gibt eine Warnung (auch in `show` und `test`). IPv4 funktioniert normal. Spricht der Dienst intern aber über `::1`/localhost via IPv6, schlagen diese Verbindungen fehl. Lösung: eine aktuelle Version selbst bauen. svc-proxy bevorzugt automatisch die Bibliothek unter `/usr/local/lib`: ```bash sudo apt install build-essential git git clone https://github.com/rofl0r/proxychains-ng.git cd proxychains-ng git checkout v4.17 ./configure --prefix=/usr/local --sysconfdir=/etc make sudo make install # -> /usr/local/lib/libproxychains4.so sudo svc-proxy apply # Konfigurationen neu erzeugen sudo svc-proxy show kerio-connect # zeigt Bibliothek und "IPv6-Ausnahmen: ja" ``` Weitere Hinweise: - Proxy-Typen: `http` (CONNECT), `socks4`, `socks5`. `socks5h` wird wie `socks5` behandelt. Einen `https`-Proxy kann proxychains nicht. - proxychains braucht für den Proxy eine **IPv4-Adresse**. Ein Hostname wird beim Speichern aufgelöst und die IP fest eingetragen. Ändert sich die IP des Proxys, einmal `svc-proxy apply` ausführen. - Benutzer/Kennwort dürfen hier keine Leerzeichen enthalten. - Läuft der Dienst unter einem eigenen Benutzer (`User=`), bekommt dieser die Datei mit dem Kennwort zugewiesen. Bei `DynamicUser=yes` geht das nicht, dann einen Proxy ohne Anmeldung verwenden. - Nur TCP wird umgeleitet, UDP nicht. Eingehende Verbindungen (z.B. MX-Empfang) sind nicht betroffen. - Statisch gelinkte Programme (z.B. viele Go-Binaries) ignorieren `LD_PRELOAD`. Für die gibt es meist den Modus `env`. Prüfen mit `ldd /pfad/zum/programm`. - Der Modus wird **nicht** vom globalen Proxy geerbt, er gilt immer nur für den jeweiligen Dienst (Standard: `env`). ### Beispiel: Kerio Connect Kerio beachtet keine Proxy-Variablen, und der Proxy in den Kerio-Einstellungen gilt nur für Updates und Antivirus. Mit proxychains laufen auch SMTP-Versand und POP3/IMAP-Abruf über den Proxy: ```bash sudo apt install proxychains4 ldd /opt/kerio/mailserver/mailserver | grep libc # muss dynamisch gelinkt sein sudo svc-proxy global --proxy socks5://192.168.1.50:1080 sudo svc-proxy set kerio-connect --mode proxychains --restart ``` Beim Mailversand über einen Proxy gilt: Empfänger sehen die IP des Proxys. Diese muss im SPF-Eintrag stehen und einen passenden Reverse-DNS haben, und der Proxy muss ausgehend Port 25 erlauben. Alternativ in Kerio einen SMTP-Relay (Smarthost, Port 587 mit Anmeldung) eintragen. Die Verbindung dorthin läuft dann ebenfalls über den Proxy. --- ## Vererbung: Dienst ↔ global Felder: **Typ, Host, Port, Benutzer, Kennwort, No-Proxy** | Feld beim Dienst | Ergebnis | |---|---| | Typ / Host / Port / No-Proxy leer | globaler Wert | | Typ / Host / Port / No-Proxy gesetzt | Wert des Dienstes | | Benutzer leer **und** Host leer | globale Zugangsdaten | | Benutzer leer, **eigener Host** | Proxy **ohne** Anmeldung | | Benutzer gesetzt | Benutzer + Kennwort des Dienstes | Die Sonderregel für Benutzer/Kennwort verhindert, dass globale Zugangsdaten an einen fremden Proxy geschickt werden. Ändert man den globalen Proxy, werden alle Dienste, die ihn ganz oder teilweise erben, automatisch neu geschrieben. Wird der globale Proxy gelöscht, verlieren Dienste ohne eigenen Host ihren Proxy. Ihre Konfiguration bleibt aber gespeichert und wird wieder aktiv, sobald ein globaler Proxy gesetzt ist. --- ## ncurses-Menü ```bash sudo svc-proxy ``` ``` svc-proxy 1.0.0 – Proxy für systemd-Dienste Global: http://max:****@proxy.firma.local:3128 No-Proxy: localhost,127.0.0.1,::1 220 Dienste Dienst Status Proxy G cron.service active global → http://max:****@proxy.firma.local:3128 E* docker.service active eigen → socks5h://10.1.1.1:1080 nginx.service inactive ... ↑↓ Wählen Enter Bearbeiten g Global Entf Proxy entfernen / Filter c Nur konfigurierte ... ``` Markierungen: **G** = nutzt globalen Proxy, **E** = eigene Werte, **\*** = in dieser Sitzung geändert (Neustart noch offen), **[proxychains]** = Modus proxychains. ### Tasten in der Liste | Taste | Funktion | |---|---| | ↑ ↓ / Bild↑ Bild↓ / Pos1 Ende | Dienst wählen | | Enter | Proxy des Dienstes bearbeiten | | g | globalen Proxy bearbeiten | | Entf / d | Proxy vom Dienst entfernen | | / | Filter (Name oder Beschreibung), Esc löscht den Filter | | c | nur konfigurierte Dienste anzeigen (an/aus) | | t | Verbindungstest für den gewählten Dienst (ohne Proxy: globaler Proxy) | | r | gewählten Dienst sofort neu starten | | F5 | Dienstliste neu einlesen | | F1 / ? | Hilfe | | q / F10 | Beenden | ### Tasten im Formular | Taste | Funktion | |---|---| | Tab / ↑ ↓ | Feld wechseln | | ← → / Leertaste | Modus bzw. Proxy-Typ wählen (beim Typ gibt es zusätzlich „global“) | | Enter | nächstes Feld bzw. Schaltfläche auslösen | | F2 | Speichern | | F3 / „Testen“ | Verbindungstest mit den eingegebenen, noch nicht gespeicherten Werten | | Strg+U | Feld leeren | | Esc | Abbrechen | Leere Felder zeigen grau den globalen Wert an. Unter dem Formular steht live die Proxy-URL, die sich ergibt. Ungültige Eingaben werden nicht gespeichert. Nach jedem Speichern läuft `systemctl daemon-reload`. Beim Beenden erscheint eine Liste der geänderten Dienste: Diese kann man an- und abwählen und neu starten lassen. --- ## Kommandozeile ``` svc-proxy [BEFEHL] [OPTIONEN] ``` | Befehl | Beschreibung | |---|---| | *(keiner)* / `tui` | Menü starten | | `list [--all]` | konfigurierte Dienste (mit `--all`: alle Dienste) | | `show [DIENST]` | effektive Werte eines Dienstes inkl. Herkunft; ohne DIENST: global | | `global [FELDER]` | globalen Proxy setzen/ändern; ohne FELDER: anzeigen | | `global --clear` | globalen Proxy löschen | | `set DIENST... [FELDER] [--reset]` | Proxy für Dienst(e) setzen/ändern | | `unset DIENST...` / `unset --all` | Proxy entfernen | | `apply` | alle Drop-ins aus der Konfiguration neu erzeugen | | `test [DIENST] [--target H:P] [--mail]` | Verbindungstest über den Proxy (siehe unten) | | `help`, `-h`, `--help` | Hilfe mit Beispielen | ### Felder | Option | Bedeutung | |---|---| | `--proxy URL` | komplette URL, z.B. `http://user:pass@proxy:3128`; setzt Typ, Host, Port, Benutzer, Kennwort | | `--type TYP` | `http`, `https`, `socks5`, `socks5h`, `socks4` | | `--host HOST` | Hostname / IP | | `--port PORT` | 1–65535 | | `--user NAME` | Benutzername | | `--password PW` | Kennwort (in `ps` sichtbar!) | | `--password-stdin` | Kennwort aus der Standardeingabe lesen (empfohlen) | | `--no-proxy LISTE` | Ausnahmen, kommagetrennt | | `--mode MODUS` | nur bei `set`: `env` (Standard) oder `proxychains` | - Bei `set` und `global` werden **nur die angegebenen Felder** geändert, der Rest bleibt. - Leerer Wert leert ein Feld: `--user ""`. - `set DIENST --reset` leert vorher alle Felder, der Dienst nutzt danach komplett den globalen Proxy. - Ein neuer Dienst ohne Felder (`set DIENST`) nutzt den globalen Proxy. - Dienstnamen ohne Endung bekommen automatisch `.service` angehängt. - Beim ersten Setzen des globalen Proxys ist No-Proxy standardmäßig `localhost,127.0.0.1,::1`. ### Neustart und Reload | Option | Wirkung | |---|---| | *(nichts)* | im Terminal Nachfrage `[j/N]`; ohne Terminal (Cron, Skript) kein Neustart | | `--restart` | geänderte Dienste ohne Nachfrage neu starten | | `--no-restart` | nicht neu starten, nicht fragen | | `--no-reload` | kein `systemctl daemon-reload` | Neu gestartet wird mit `systemctl try-restart`, also nur Dienste, die gerade laufen. Gestoppte Dienste werden nicht gestartet. ### Weitere Optionen | Option | Bedeutung | |---|---| | `--config-dir DIR` | anderes Konfigurationsverzeichnis (Standard `/etc/svc-proxy`) | | `--systemd-dir DIR` | anderes Ziel für Drop-ins (Standard `/etc/systemd/system`) | | `-V`, `--version` | Version | Umgebungsvariable `SVC_PROXY_PROXYCHAINS_LIB`: Pfad zur proxychains-Bibliothek, falls sie nicht automatisch gefunden wird. Mit `--config-dir`/`--systemd-dir` läuft das Skript auch ohne root. Das ist praktisch zum Testen, z.B. zusammen mit `--no-reload`. --- ## Verbindungstest ```bash sudo svc-proxy test kerio-connect --mail ``` ``` Verbindungstest: Dienst kerio-connect.service Proxy: socks5://max:****@192.168.1.50:1080 [proxychains] [ OK ] Proxy erreichbar: 192.168.1.50:1080 (1 ms) [FEHLER] gmail-smtp-in.l.google.com:25 → Proxy meldet: vom Regelwerk des Proxys nicht erlaubt (Port/Ziel gesperrt?) [ OK ] smtp.gmail.com:587 → Antwort: 220 smtp.gmail.com ESMTP ... (46 ms) [ OK ] smtp.gmail.com:465 → TLS ok, Antwort: 220 smtp.gmail.com ESMTP ... (61 ms) [ OK ] pop.gmail.com:995 → TLS ok, Antwort: +OK Gpop ready for requests ... (67 ms) [ OK ] imap.gmail.com:993 → TLS ok, Antwort: * OK Gimap ready for requests ... (105 ms) Test über proxychains (so verbindet der Dienst): [FEHLER] proxychains: gmail-smtp-in.l.google.com:25 → Verbindung abgelehnt [ OK ] proxychains: smtp.gmail.com:587 → Antwort: 220 smtp.gmail.com ESMTP ... ... Ergebnis: 2 Test(s) fehlgeschlagen. ``` Ablauf: 1. **Proxy erreichbar?** TCP-Verbindung zum Proxy. 2. **Handshake und Anmeldung:** SOCKS5, SOCKS4 oder HTTP CONNECT (auch `https`-Proxy). Verständliche Meldungen z.B. bei falschem Kennwort, fehlender Anmeldung, falschem Proxy-Typ (socks/http vertauscht) oder einem vom Proxy gesperrten Port. 3. **Ziele:** Tunnel zu jedem Ziel aufbauen und die Begrüßung lesen. Das zeigt, dass wirklich Daten fließen (SMTP `220`, POP3 `+OK`, IMAP `* OK`, HTTP-Status). Die Ports 443, 465, 993 und 995 werden per TLS angesprochen (ohne Zertifikatsprüfung, es geht nur um die Verbindung). 4. **Nur im Modus proxychains:** Derselbe Test läuft noch einmal als normales Programm über die proxychains-Bibliothek mit der Konfiguration des Dienstes. Damit ist der komplette Weg geprüft, den auch der Dienst nimmt. | Option | Bedeutung | |---|---| | `--target HOST:PORT` | Testziel, mehrfach möglich (Standard: `example.com:80`, `example.com:443`) | | `--mail` | Mail-Ziele: SMTP 25/587/465, POP3S 995, IMAPS 993 (Gmail) | | `--timeout SEK` | Zeitlimit pro Schritt (Standard 6 s) | | `--proxy`, `--host`, … | Werte nur für den Test überschreiben, nichts wird gespeichert | Ohne `DIENST` wird der globale Proxy getestet. Mit `--proxy URL` (ohne DIENST) läuft der Test auch ohne root und ohne gespeicherte Konfiguration. So lässt sich ein Proxy prüfen, bevor man ihn einträgt. Rückgabewert: `0` = alles ok, `1` = mindestens ein Test fehlgeschlagen, praktisch für Monitoring-Skripte. Hinweis zu Port 25: Viele Proxys und Provider sperren ausgehend Port 25. Der Test zeigt genau das. Abhilfe ist ein SMTP-Relay (Smarthost) auf Port 587 oder 465. --- ## Beispiele ```bash # Menü sudo svc-proxy # Globaler Proxy ohne Anmeldung sudo svc-proxy global --proxy http://proxy.firma.local:3128 # Globaler Proxy mit Anmeldung, Kennwort nicht in der Prozessliste echo 'geheim' | sudo svc-proxy global --proxy http://proxy:3128 --user max --password-stdin # oder aus einer Datei / einem Secret-Store sudo svc-proxy global --user max --password-stdin < /root/proxy.pw # Ausnahmen für den globalen Proxy sudo svc-proxy global --no-proxy localhost,127.0.0.1,::1,.firma.local,10.0.0.0/8 # Mehrere Dienste nutzen den globalen Proxy, sofort neu starten sudo svc-proxy set docker containerd nginx --restart # Eigener SOCKS-Proxy ohne Anmeldung für einen Dienst sudo svc-proxy set myapp --proxy socks5h://10.1.1.1:1080 # Nur anderer Host, Port/Typ/No-Proxy kommen vom globalen Proxy, # keine Anmeldung (eigener Host ohne Benutzer) sudo svc-proxy set backup --host proxy2.firma.local # Kerio Connect: alle Mail-Verbindungen über den (globalen) SOCKS5-Proxy sudo svc-proxy set kerio-connect --mode proxychains --restart # Eigener Proxy mit Anmeldung im proxychains-Modus, Netz 192.0.2.0/24 direkt echo 'geheim' | sudo svc-proxy 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 svc-proxy set kerio-connect --mode env # Nur den Port ändern sudo svc-proxy set myapp --port 1081 --no-restart # Zurück auf den globalen Proxy sudo svc-proxy set myapp --reset # Entfernen sudo svc-proxy unset myapp --restart sudo svc-proxy unset --all --no-restart # Verbindungstests sudo svc-proxy test # globaler Proxy sudo svc-proxy test kerio-connect --mail # Mail-Ports über Kerios Proxy sudo svc-proxy test kerio-connect --target mx.example.com:25 --target pop.example.com:995 svc-proxy test --proxy socks5://192.0.2.10:1080 --mail # ad hoc, ohne Speichern, ohne root # Anzeigen sudo svc-proxy list sudo svc-proxy list --all sudo svc-proxy show docker sudo svc-proxy show # globaler Proxy ``` Beispielausgabe von `show`: ``` Dienst: ssh.service Status: active Herkunft: eigene Werte (leere Felder = global) Modus: Proxy-Variablen Proxy-Typ http (global) Host other.proxy (dienst) Port 3128 (global) Benutzer - (dienst) Kennwort - (dienst) No-Proxy localhost,127.0.0.1,::1 (global) Proxy-URL: http://other.proxy:3128 ``` ### Automatisierung (z.B. Ansible) ```yaml - name: Proxy für Docker setzen ansible.builtin.shell: > svc-proxy global --proxy http://proxy.firma.local:3128 --no-restart && svc-proxy set docker.service --restart ``` Rückgabewert: `0` bei Erfolg, `1` bei Fehler (ungültige Eingabe, fehlende Rechte, fehlgeschlagener Neustart). --- ## Prüfen, ob es wirkt ```bash systemctl cat docker.service # zeigt das Drop-in sudo cat /proc/$(systemctl show -p MainPID --value docker)/environ | tr '\0' '\n' | grep -i proxy # proxychains-Modus: ist die Bibliothek im Prozess geladen? sudo grep proxychains /proc/$(systemctl show -p MainPID --value kerio-connect)/maps | head -1 # laufende Verbindungen des Dienstes: Ziel sollte der Proxy sein sudo ss -tnp | grep mailserver ``` ## Deinstallation ```bash sudo svc-proxy unset --all --restart sudo rm -rf /etc/svc-proxy /usr/local/sbin/svc-proxy ```