Le proxy SSH Socks à la demande via les unités utilisateur systemd avec activation de socket ne redémarre pas comme souhaité

14

Pour atteindre un réseau isolé, j'utilise un -D .

Afin d'éviter d'avoir à taper les détails chaque fois que je les ai ajoutés à ~/.ssh/config:

$ awk '/Host socks-proxy/' RS= ~/.ssh/config
Host socks-proxy
  Hostname pcit
  BatchMode yes
  RequestTTY no
  Compression yes
  DynamicForward localhost:9118

J'ai ensuite créé un fichier de définition d'unité de service :

$ cat ~/.config/systemd/user/SocksProxy.service 
[Unit]
Description=SocksProxy Over Bridge Host

[Service]
ExecStart=/usr/bin/ssh -Nk socks-proxy

[Install]
WantedBy=default.target

Je laisse le démon recharger les nouvelles définitions de service, j'active le nouveau service, le démarre, vérifie son état et vérifie qu'il écoute:

$ systemctl --user daemon-reload
$ systemctl --user list-unit-files | grep SocksP
SocksProxy.service   disabled

$ systemctl --user enable SocksProxy.service
Created symlink from ~/.config/systemd/user/default.target.wants/SocksProxy.service to ~/.config/systemd/user/SocksProxy.service.

$ systemctl --user start SocksProxy.service 
$ systemctl --user status SocksProxy.service 
● SocksProxy.service - SocksProxy Over Bridge Host
   Loaded: loaded (/home/alex/.config/systemd/user/SocksProxy.service; enabled)
   Active: active (running) since Thu 2017-08-03 10:45:29 CEST; 2s ago
 Main PID: 26490 (ssh)
   CGroup: /user.slice/user-1000.slice/[email protected]/SocksProxy.service
           └─26490 /usr/bin/ssh -Nk socks-proxy

$ netstat -tnlp | grep 118
tcp     0    0 127.0.0.1:9118        0.0.0.0:*             LISTEN     
tcp6    0    0 ::1:9118              :::*                  LISTEN

Cela fonctionne comme prévu. Ensuite, je voulais éviter d'avoir à démarrer manuellement le service ou à l'exécuter de manière permanente avec , en utilisant l' de pour la (re) génération à la demande. Cela n'a pas fonctionné, je pense que (ma version de) ne peut pas recevoir les descripteurs de fichiers socket.ssh

J'ai trouvé la documentation ( 1 , 2 ), et un exemple pour l' utilisation du systemd-socket-proxyd-Outil pour créer 2 services « wrapper », un « service » et une « prise »:

$ cat ~/.config/systemd/user/SocksProxyHelper.socket 
[Unit]
Description=On Demand Socks proxy into Work

[Socket]
ListenStream=8118
#BindToDevice=lo
#Accept=yes

[Install]
WantedBy=sockets.target

$ cat ~/.config/systemd/user/SocksProxyHelper.service 
[Unit]
Description=On demand Work Socks tunnel
After=network.target SocksProxyHelper.socket
Requires=SocksProxyHelper.socket SocksProxy.service
After=SocksProxy.service

[Service]
#Type=simple
#Accept=false
ExecStart=/lib/systemd/systemd-socket-proxyd 127.0.0.1:9118
TimeoutStopSec=5

[Install]
WantedBy=multi-user.target

$ systemctl --user daemon-reload

Cela semble fonctionner jusqu'à ce qu'il sshmeure ou soit tué. Ensuite, il ne réapparaîtra pas lors de la prochaine tentative de connexion.

Des questions:

  1. / Usr / bin / ssh ne peut-il vraiment pas accepter les sockets passés par systemd? Ou seulement des versions plus récentes? Le mien est celui de up2date Debian 8.9 .
  2. Seules les unités de racine peuvent-elles utiliser l' BindTodeviceoption?
  3. Pourquoi mon service proxy ne réapparaît-il pas correctement lors de la première nouvelle connexion après la mort de l'ancien tunnel?
  4. Est-ce la bonne façon de configurer un "proxy ssh socks à la demande"? Si non, comment procédez-vous?
Alex Stragies
la source
autosshdevrait prendre soin de se reconnecter en cas d'échec de la connexion (bien que ce ne soit pas le système).
Jakuje
@Jakuje: Thx pour le commentaire, mais je ne veux pas que la connexion soit permanente. Je veux qu'il soit généré lorsque je l'utilise, puis (idéalement après un délai d'attente sans données envoyées en x minutes) se termine automatiquement. Aussi, ma solution précédente a été utilisée autossh.
Alex Stragies

Réponses:

4
  • / Usr / bin / ssh ne peut-il vraiment pas accepter les sockets passés par systemd?

Je pense que ce n'est pas trop surprenant, compte tenu:

  • OpenSSH est un projet OpenBSD
  • systemd ne prend en charge que le noyau Linux
  • La prise en charge de systemd devrait être explicitement ajoutée à OpenSSH, en tant que dépendance facultative / au moment de la création, donc ce serait probablement difficile à vendre.

  • Seules les unités de racine peuvent-elles utiliser l' BindTodeviceoption?

Les instances utilisateur systemd sont généralement assez isolées et ne peuvent par exemple pas communiquer avec l'instance pid-0 principale. Des choses comme dépendre des unités système des fichiers d'unité utilisateur ne sont pas possibles.

La documentation des BindToDevicementions:

Notez que la définition de ce paramètre peut entraîner l'ajout de dépendances supplémentaires à l'unité (voir ci-dessus).

En raison de la restriction susmentionnée, nous pouvons impliquer que l'option ne fonctionne pas à partir des instances utilisateur de systemd.


  • Pourquoi mon service proxy ne réapparaît-il pas correctement lors de la première nouvelle connexion après la mort de l'ancien tunnel?

Si je comprends bien, la chaîne des événements est la suivante:

  • SocksProxyHelper.socket a démarré.
  • Un client SOCKS se connecte à localhost: 8118.
  • systemd démarre SocksProxyHelper.service.
  • En tant que dépendance de SocksProxyHelper.service, systemd démarre également SocksProxy.service.
  • systemd-socket-proxydaccepte le socket systemd et transmet ses données à ssh.
  • ssh meurt ou est tué.
  • systemd le remarque et le place SocksProxy.servicedans un état inactif, mais ne fait rien.
  • SocksProxyHelper.servicecontinue de fonctionner et d'accepter les connexions, mais ne parvient pas à s'y connecter ssh, car il n'est plus en cours d'exécution.

La solution est d'ajouter BindsTo=SocksProxy.serviceà SocksProxyHelper.service. Citant sa documentation (soulignement ajouté):

Configure les dépendances des exigences, dont le style est très similaire à Requires=. Cependant, ce type de dépendance est plus fort: en plus de son effet, Requires=il déclare que si l'unité liée à est arrêtée, cette unité sera également arrêtée . Cela signifie qu'une unité liée à une autre unité qui entre soudainement en état inactif sera également arrêtée. Les unités peuvent soudainement et de manière inattendue entrer dans un état inactif pour différentes raisons: le processus principal d'une unité de service peut se terminer de son propre choix , le dispositif de support d'une unité peut être débranché ou le point de montage d'une unité de montage peut être démonté sans intervention de le gestionnaire du système et des services.

Lorsqu'il est utilisé en conjonction avec After=sur la même unité, le comportement deBindsTo= est encore plus fort. Dans ce cas, l'unité liée à strictement doit être en état actif pour que cette unité soit également en état actif . Cela signifie non seulement une unité liée à une autre unité qui pénètre soudainement l' état inactif, mais aussi celui qui est lié à une autre unité qui obtient sauté à cause d'une vérification de l' état a échoué ( par exemple ConditionPathExists=, ConditionPathIsSymbolicLink=... - voir ci - dessous) sera arrêtée, devrait - il être en cours d'exécution. Par conséquent, dans de nombreux cas, il est préférable de combiner BindsTo=avec After=.


  • Est-ce la bonne façon de configurer un "proxy ssh socks à la demande"? Si non, comment procédez-vous?

Il n'y a probablement pas de "bonne façon". Cette méthode a ses avantages (tout étant "à la demande") et ses inconvénients (dépendance à systemd, la première connexion ne passe pas car ssh n'a pas encore commencé à écouter). Peut-être que l'implémentation de la prise en charge de l'activation de socket systemd dans autossh serait une meilleure solution.

Vladimir Panteleev
la source
J'ai ajouté BindsTo=SocksProxy.serviceà la section Unité du fichier ~/.config/systemd/user/SocksProxyHelper.service, après la After=SocksProxy.serviceligne. Le redémarrage manuel du service SocksProxy n'est plus nécessaire, lorsque SSH meurt / obtient_killed. Existe-t-il un moyen pour systemd de "maintenir" la connexion initiale, afin qu'elle n'obtienne pas de réinitialisation TCP?
Alex Stragies
@Vladimir Panteleev un point que je voudrais clarifier: la prise en charge de l'activation du socket systemd peut être implémentée relativement facilement en analysant $LISTEN_FDSsans ajouter de dépendance sd_listen_fds(), il peut donc être difficile à vendre, mais pas trop difficile.
Amir