Dynamisch zugewiesenen Port für OpenSSH RemoteForward bestimmen

Dynamisch zugewiesenen Port für OpenSSH RemoteForward bestimmen

Frage (TL;DR)

-RWie kann ein Skript auf dem Remote-Computer (beispielsweise aus ) beim dynamischen Zuweisen von Ports für die Remote-Weiterleitung (auch als Option bezeichnet .bashrc) ermitteln, welche Ports von OpenSSH ausgewählt wurden?


Hintergrund

Ich verwende OpenSSH (an beiden Enden), um mich mit unserem zentralen Server zu verbinden, den ich mit mehreren anderen Benutzern teile. Für meine Remote-Sitzung (vorerst) möchte ich X, Cups und Pulseaudio weiterleiten.

Am trivialsten ist die Weiterleitung von X mithilfe der -XOption. Die zugewiesene X-Adresse wird in der Umgebungsvariable gespeichert DISPLAYund daraus kann ich in den meisten Fällen den entsprechenden TCP-Port ermitteln. Das muss ich aber kaum jemals tun, da Xlib dies berücksichtigt DISPLAY.

Ich brauche einen ähnlichen Mechanismus für cups und pulseaudio. Die Grundlagen für beide Dienste sind in Form der Umgebungsvariablen CUPS_SERVERbzw. vorhanden PULSE_SERVER. Hier sind Anwendungsbeispiele:

ssh -X -R12345:localhost:631 -R54321:localhost:4713 datserver

export CUPS_SERVER=localhost:12345
lowriter #and I can print using my local printer
lpr -P default -o Duplex=DuplexNoTumble minutes.pdf #printing through the tunnel
lpr -H localhost:631 -P default -o Duplex=DuplexNoTumble minutes.pdf #printing remotely

mpg123 mp3s/van_halen/jump.mp3 #annoy co-workers
PULSE_SERVER=localhost:54321 mpg123 mp3s/van_halen/jump.mp3 #listen to music through the tunnel

Das Problem liegt CUPS_SERVERin PULSE_SERVERder korrekten Einstellung.

Wir verwenden häufig Portweiterleitungen und daher benötige ich dynamische Portzuweisungen. Statische Portzuweisungen sind keine Option.

OpenSSH verfügt über einen Mechanismus zur dynamischen Portzuweisung auf dem Remote-Server, indem dieser 0als Bind-Port für die Remote-Weiterleitung angegeben wird (die -ROption). Mit dem folgenden Befehl weist OpenSSH Ports für die CUPS- und Pulsweiterleitung dynamisch zu.

ssh -X -R0:localhost:631 -R0:localhost:4713 datserver

Wenn ich diesen Befehl verwende, sshwird Folgendes gedruckt STDERR:

Allocated port 55710 for remote forward to 127.0.0.1:4713
Allocated port 41273 for remote forward to 127.0.0.1:631

Das sind die Informationen, die ich will! Letztendlich will ich Folgendes generieren:

export CUPS_SERVER=localhost:41273
export PULSE_SERVER=localhost:55710

Allerdings werden die Meldungen „Zugewiesener Port …“ auf meinem lokalen Computer erstellt und an gesendet STDERR, auf die ich auf dem Remotecomputer nicht zugreifen kann. Seltsamerweise scheint OpenSSH keine Möglichkeit zu haben, Informationen zu Portweiterleitungen abzurufen.

CUPS_SERVERWie rufe ich diese Informationen ab, um sie in ein Shell-Skript einzufügen und sie auf dem Remote-Host entsprechend einzurichten PULSE_SERVER?


Sackgassen

Die einzige einfache Lösung, die ich finden konnte, war, die Ausführlichkeit zu erhöhen, sshdbis diese Informationen aus den Protokollen gelesen werden können. Dies ist nicht praktikabel, da diese Informationen viel mehr Informationen preisgeben, als es sinnvoll ist, Nicht-Root-Benutzern zugänglich zu machen.

Ich habe darüber nachgedacht, OpenSSH so zu patchen, dass eine zusätzliche Escape-Sequenz unterstützt wird, die eine schöne Darstellung der internen Struktur druckt permitted_opens, aber selbst wenn ich das möchte, kann ich trotzdem nicht per Skript auf die Client-Escape-Sequenzen von der Serverseite aus zugreifen.


Es muss einen besseren Weg geben

Der folgende Ansatz scheint sehr instabil und ist auf eine solche SSH-Sitzung pro Benutzer beschränkt. Ich benötige jedoch mindestens zwei gleichzeitige solcher Sitzungen und andere Benutzer sogar noch mehr. Aber ich habe es versucht ...

Wenn die Sterne richtig stehen und ich das eine oder andere Huhn geopfert habe, kann ich die Tatsache, dass sshdnicht als mein Benutzer gestartet wird, aber die Berechtigungen nach erfolgreicher Anmeldung verliert, ausnutzen, um Folgendes zu tun:

  • Holen Sie sich eine Liste der Portnummern für alle Listening-Sockets, die meinem Benutzer gehören

    netstat -tlpen | grep ${UID} | sed -e 's/^.*:\([0-9]\+\) .*$/\1/'

  • Holen Sie sich eine Liste der Portnummern für alle Listening-Sockets, die zu Prozessen gehören, die mein Benutzer gestartet hat

    lsof -u ${UID} 2>/dev/null | grep LISTEN | sed -e 's/.*:\([0-9]\+\) (LISTEN).*$/\1/'

  • Bei allen Ports, die im ersten Set, aber nicht im zweiten Set sind, handelt es sich mit hoher Wahrscheinlichkeit um meine Weiterleitungsports. Und tatsächlich erhält man durch Subtrahieren der Sets 41273, 55710und 6010; Cups, Pulse bzw. X.

  • 6010wird mit als X-Port identifiziert DISPLAY.

  • 41273ist der CUPS-Port, weil lpstat -h localhost:41273 -agibt zurück 0.
  • 55710ist der Pulse-Port, weil pactl -s localhost:55710 stater zurückgibt 0. (Es druckt sogar den Hostnamen meines Clients!)

(Um die Subtraktion durchzuführen, sort -uspeichere ich die Ausgabe der obigen Befehlszeilen und verwende sie, commum die Subtraktion durchzuführen.)

Mit Pulseaudio kann ich den Client identifizieren, und das kann im Grunde als Anker dienen, um SSH-Sitzungen zu trennen, die getrennt werden müssen. Ich habe jedoch keine Möglichkeit gefunden 41273, 55710und 6010mit demselben sshdProzess zu verknüpfen. netstatwird diese Informationen nicht an Nicht-Root-Benutzer weitergeben. Ich erhalte nur ein -in der PID/Program nameSpalte, in der ich lesen möchte 2339/54(in diesem speziellen Fall). So nah dran ...

Antwort1

Leider habe ich deine Frage nicht früher gefunden, aber ich habe gerade eine wirklich gute Antwort von Kamil-Maciorowski bekommen:

https://unix.stackexchange.com/a/584505/251179

Zusammenfassend lässt sich sagen, dass Sie zunächst eine Master-Verbindung herstellen und diese im Hintergrund behalten müssen. Anschließend geben Sie einen zweiten Befehl mit dem -O *ctl_cmd*Befehl „set to“ ein forward, um die Portweiterleitung anzufordern/einzurichten:

ssh -fNMS /path/to/socket user@server

port="$(ssh -S /path/to/socket -O forward -R 0:localhost:22 placeholder)"

Dadurch wird eine Verbindung auf Ihrem lokalen Computer hergestellt $portund im Hintergrund ausgeführt.

Sie können es dann $portlokal verwenden oder es ssherneut verwenden, um einen Befehl auf dem Remote-Server auszuführen, wo Sie denselben Steuersocket verwenden können.

Die Flaggen sind:

  • -F= Fordert SSH auf, in den Hintergrund zu gehen.
  • -N= Keinen Remote-Befehl ausführen.
  • -M= Versetzt den Client in den „Master“-Modus zur gemeinsamen Nutzung von Verbindungen.
  • -S= Standort eines Steuer-Sockets für die gemeinsame Nutzung von Verbindungen.
  • = Steuern Sie einen aktiven Verbindungsmultiplex-Masterprozess.

In meinem Fall habe ich noch etwas hinzugefügt, um die Verbindung ständig zu überprüfen:

#!/bin/bash

#--------------------------------------------------
# Setup
#--------------------------------------------------

  set -u;

  tunnel_user="user";
  tunnel_host="1.1.1.1";

  local_port="22";
  local_name="my-name";

  path_key="$HOME/.ssh/tunnel_ed25519";

  path_lock="/tmp/tunnel.${tunnel_host}.pid"
  path_port="/tmp/tunnel.${tunnel_host}.port"
  path_log="/tmp/tunnel.${tunnel_host}.log"
  path_socket="/tmp/tunnel.${tunnel_host}.socket"

#--------------------------------------------------
# Key file
#--------------------------------------------------

  if [ ! -f "${path_key}" ]; then

    ssh-keygen -q -t ed25519 -f "${path_key}" -N "";

    /usr/local/bin/tunnel-client-key.sh
      # Sends the public key to a central server, also run via cron, so it can be added to ~/.ssh/authorized_keys
      # curl -s --form-string "pass=${pass}" --form-string "name=$(local_name)" -F "key=@${path_key}.pub" "https://example.com/key/";

  fi

#--------------------------------------------------
# Lock
#--------------------------------------------------

  if [ -e "${path_lock}" ]; then
    c=$(pgrep -F "${path_lock}" 2>/dev/null | wc -l);
      # MacOS 10.15.4 does not support "-c" to count processes, or the full "--pidfile" flag.
  else
    c=0;
  fi

  if [[ "${c}" -gt 0 ]]; then
    if tty -s; then
      echo "Already running";
    fi;
    exit;
  fi;

  echo "$$" > "${path_lock}";

#--------------------------------------------------
# Port forward
#--------------------------------------------------

  retry=0;

  while true; do

    #--------------------------------------------------
    # Log cleanup
    #--------------------------------------------------

      if [ ! -f "${path_log}" ]; then
        touch "${path_log}";
      fi

      tail -n 30 "${path_log}" > "${path_log}.tmp";

      mv "${path_log}.tmp" "${path_log}";

    #--------------------------------------------------
    # Exit old sockets
    #--------------------------------------------------

      if [ -S "${path_socket}" ]; then

        echo "$(date) : Exit" >> "${path_log}";

        ssh -S "${path_socket}" -O exit placeholder;

      fi

    #--------------------------------------------------
    # Lost lock
    #--------------------------------------------------

      if [ ! -e "${path_lock}" ] || ! grep -q "$$" "${path_lock}"; then

        echo "$(date) : Lost Lock" >> "${path_log}";

        exit;

      fi

    #--------------------------------------------------
    # Master connection
    #--------------------------------------------------

      echo "$(date) : Connect ${retry}" >> "${path_log}";

      ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 -o ServerAliveInterval=30 -o ExitOnForwardFailure=yes -fNTMS "${path_socket}" -i "${path_key}" "${tunnel_user}@${tunnel_host}" >> "${path_log}" 2>&1;

    #--------------------------------------------------
    # Setup and keep checking the port forwarding
    #--------------------------------------------------

      old_port=0;

      while ssh -S "${path_socket}" -O check placeholder 2>/dev/null; do

        new_port=$(ssh -S "${path_socket}" -O forward -R "0:localhost:${local_port}" placeholder 2>&1);

        if [[ "${new_port}" -gt 0 ]]; then

          retry=0;

          if [[ "${new_port}" -ne "${old_port}" ]]; then

            ssh -i "${path_key}" "${tunnel_user}@${tunnel_host}" "tunnel.port.sh '${new_port}' '${local_name}'" >> "${path_log}" 2>&1;
              # Tell remote server what the port is, and local_name.
              # Don't use socket, it used "-N"; if done, a lost connection keeps sshd running on the remote host, even with ClientAliveInterval/ClientAliveCountMax.

            echo "$(date) : ${new_port}" >> "${path_log}";

            echo "${new_port}" > "${path_port}";

            old_port="${new_port}";

            sleep 1;

          else

            sleep 300; # Looks good, check again in 5 minutes.

          fi

        else # Not a valid port number (0, empty string, number followed by an error message, etc?)

          ssh -S "${path_socket}" -O exit placeholder 2>/dev/null;

        fi

      done

    #--------------------------------------------------
    # Cleanup
    #--------------------------------------------------

      if [ ! -f "${path_port}" ]; then
        rm "${path_port}";
      fi

      echo "$(date) : Disconnected" >> "${path_log}";

    #--------------------------------------------------
    # Delay before next re-try
    #--------------------------------------------------

      retry=$((retry+1));

      if [[ $retry -gt 10 ]]; then
        sleep 180; # Too many connection failures, try again in 3 minutes
      else
        sleep 5;
      fi

  done

Antwort2

Ich habe dasselbe erreicht, indem ich auf dem lokalen Client eine Pipe erstellt und dann stderr auf die Pipe umgeleitet habe, die wiederum auf den Eingang von ssh umgeleitet wurde. Es sind nicht mehrere SSH-Verbindungen erforderlich, um einen freien bekannten Port anzunehmen, der fehlschlagen könnte. Auf diese Weise werden das Anmeldebanner und der Text „Zugewiesener Port ### ...“ auf den Remote-Host umgeleitet.

Ich habe ein einfaches Skript auf dem Host getsshport.sh, das auf dem Remote-Host ausgeführt wird, die umgeleitete Eingabe liest und den Port analysiert. Solange dieses Skript nicht endet, bleibt die SSH-Remote-Weiterleitung geöffnet.

lokale Seite

mkfifo pipe
ssh -R "*:0:localhost:22" user@remotehost "~/getsshport.sh" 3>&1 1>&2 2>&3 < pipe | cat > pipe

3>&1 1>&2 2>&3ist ein kleiner Trick, um stderr und stdout zu vertauschen, sodass stderr an cat weitergeleitet wird und die gesamte normale Ausgabe von SSH auf stderr angezeigt wird.

Remote-Seite ~/getsshport.sh

#!/bin/sh
echo "Connection from $SSH_CLIENT"
while read line
do
    echo "$line" # echos everything sent back to the client
    echo "$line" | sed -n "s/Allocated port \([0-9]*\) for remote forward to \(.*\)\:\([0-9]*\).*/client port \3 is on local port \1/p" >> /tmp/allocatedports
done

Ich habe versucht, grepdie Meldung „Port zugewiesen“ zuerst auf der lokalen Seite anzuzeigen, bevor ich sie über SSH gesendet habe. Es scheint jedoch, dass SSH blockiert, während darauf gewartet wird, dass die Pipe auf Stdin geöffnet wird. Grep öffnet die Pipe zum Schreiben erst, wenn es etwas empfängt, sodass es grundsätzlich zu einem Deadlock kommt. catDieses Verhalten scheint jedoch nicht zu bestehen und öffnet die Pipe zum Schreiben sofort, sodass SSH die Verbindung öffnen kann.

Dies ist das gleiche Problem auf der Remote-Seite, und warum readZeile für Zeile statt einfach grep von stdin - andernfalls wird `/tmp/allocatedports' nicht ausgegeben, bis der SSH-Tunnel geschlossen wird, was den gesamten Zweck zunichte macht

Es ist besser , den Standardfehler von SSH in einen Befehl wie diesen umzuleiten ~/getsshport.sh, da ohne Angabe eines Befehls der Bannertext oder was auch immer sich sonst in der Pipe befindet, auf der Remote-Shell ausgeführt wird.

Antwort3

Nehmen Sie zwei (siehe Verlauf für eine Version, diescpvon der Serverseite und ist etwas einfacher), das sollte es tun. Der Kern davon ist dieser:

  1. Übergeben Sie eine Umgebungsvariable vom Client an den Server, die dem Server mitteilt, wie er erkennen kann, wann Portinformationen verfügbar sind, und wie er diese dann abrufen und verwenden kann.
  2. Sobald die Port-Informationen verfügbar sind, kopieren Sie sie vom Client zum Server, sodass der Server sie abrufen kann (mithilfe von Teil 1 oben) und verwenden Sie sie

Zuerst müssen Sie auf der Remote-Seite das Senden einer Umgebungsvariable aktivieren insshdAufbau:

sudo yourfavouriteeditor /etc/ssh/sshd_config

Suchen Sie die Zeile mit AcceptEnvund fügen Sie MY_PORT_FILEsie hinzu (oder fügen Sie die Zeile unter dem rechten HostAbschnitt hinzu, falls noch keine vorhanden ist). Für mich lautete die Zeile folgendermaßen:

AcceptEnv LANG LC_* MY_PORT_FILE

Denken Sie auch daran, neu zu startensshddamit dies wirksam wird.

Damit die folgenden Skripte funktionieren, müssen Sie sie außerdem mkdir ~/portfilesauf der Remote-Seite ausführen!


Dann auf der lokalen Seite ein Skript-Schnipsel, der

  1. Erstellen Sie einen temporären Dateinamen für die Stderr-Umleitung
  2. Lassen Sie einen Hintergrundjob laufen, um zu warten, bis die Datei Inhalt hat.
  3. Übergeben Sie den Dateinamen beim Umleiten als Umgebungsvariable an den Serversshstderr zur Datei
  4. Der Hintergrundjob kopiert die temporäre stderr-Datei auf die Serverseite unter Verwendung eines separatenscp
  5. Der Hintergrundjob kopiert außerdem eine Flag-Datei auf den Server, um anzuzeigen, dass die Stderr-Datei bereit ist

Der Skriptausschnitt:

REMOTE=$USER@datserver

PORTFILE=`mktemp /tmp/sshdataserverports-$(hostname)-XXXXX`
test -e $PORTFILE && rm -v $PORTFILE

# EMPTYFLAG servers both as empty flag file for remote side,
# and safeguard for background job termination on this side
EMPTYFLAG=$PORTFILE-empty
cp /dev/null $EMPTYFLAG

# this variable has the file name sent over ssh connection
export MY_PORT_FILE=$(basename $PORTFILE)

# background job loop to wait for the temp file to have data
( while [ -f $EMPTYFLAG -a \! -s $PORTFILE ] ; do
     sleep 1 # check once per sec
  done
  sleep 1 # make sure temp file gets the port data

  # first copy temp file, ...
  scp  $PORTFILE $REMOTE:portfiles/$MY_PORT_FILE

  # ...then copy flag file telling temp file contents are up to date
  scp  $EMPTYFLAG $REMOTE:portfiles/$MY_PORT_FILE.flag
) &

# actual ssh terminal connection    
ssh -X -o "SendEnv MY_PORT_FILE" -R0:localhost:631 -R0:localhost:4713 $REMOTE 2> $PORTFILE

# remove files after connection is over
rm -v $PORTFILE $EMPTYFLAG

Dann ein Snippet für die Remote-Seite, passend für.bashrc:

# only do this if subdir has been created and env variable set
if [ -d ~/portfiles -a "$MY_PORT_FILE" ] ; then

       PORTFILE=~/portfiles/$(basename "$MY_PORT_FILE")
       FLAGFILE=$PORTFILE.flag
       # wait for FLAGFILE to get copied,
       # after which PORTFILE should be complete
       while [ \! -f "$FLAGFILE" ] ; do 
           echo "Waiting for $FLAGFILE..."
           sleep 1
       done

       # use quite exact regexps and head to make this robust
       export CUPS_SERVER=localhost:$(grep '^Allocated port [0-9]\+ .* localhost:631[[:space:]]*$' "$PORTFILE" | head -1 | cut -d" " -f3)
       export PULSE_SERVER=localhost:$(grep '^Allocated port [0-9]\+ .* localhost:4713[[:space:]]*$' "$PORTFILE" | head -1 | cut -d" " -f3)
       echo "Set CUPS_SERVER and PULSE_SERVER"

       # copied files served their purpose, and can be removed right away
       rm -v -- "$PORTFILE" "$FLAGFILE"
fi

Notiz: Der obige Code ist natürlich nicht sehr gründlich getestet und könnte alle möglichen Fehler, Copy-Paste-Fehler usw. enthalten. Jeder, der ihn verwendet, sollte ihn auch verstehen,Benutzung auf eigene Gefahr!Ich habe es nur mit einer Localhost-Verbindung getestet und in meiner Testumgebung hat es funktioniert. Ihre Ergebnisse können abweichen.

Antwort4

Dies ist eine knifflige Angelegenheit, eine zusätzliche serverseitige Handhabung nach dem Vorbild von SSH_CONNECTIONoder DISPLAYwäre toll, lässt sich aber nicht so einfach hinzufügen: Ein Teil des Problems besteht darin, dass nur der sshClient das lokale Ziel kennt, das Anforderungspaket (an den Server) enthält hingegen nur die Remote-Adresse und den Port.

Die anderen Antworten hier enthalten verschiedene unschöne Lösungen, um diese Clientseite zu erfassen und an den Server zu senden. Hier ist ein alternativer Ansatz, der ehrlich gesagt nicht viel schöner ist, aber zumindest bleibt dieser hässliche Teil auf der Clientseite ;-)

  • Clientseitig hinzufügen/ändern, SendEnvdamit wir einige Umgebungsvariablen nativ über SSH senden können (wahrscheinlich nicht Standard)
  • Serverseitig hinzufügen/ändern, AcceptEnvum dasselbe zu akzeptieren (wahrscheinlich nicht standardmäßig aktiviert)
  • Überwachen Sie die sshClient-STDERR-Ausgabe mit einer dynamisch geladenen Bibliothek und aktualisieren Sie die SSH-Clientumgebung.während des Verbindungsaufbaus
  • Abrufen der Umgebungsvariablen auf der Serverseite im Profil-/Anmeldeskript

Dies funktioniert (zumindest im Moment glücklicherweise), da die Remote-Weiterleitungen eingerichtet und aufgezeichnet werden, bevor die Umgebung ausgetauscht wird (mit bestätigen ssh -vv ...). Die dynamisch geladene Bibliothek muss die write()libc-Funktion erfassen ( ssh_confirm_remote_forward()logit()do_log()write()). Das Umleiten oder Verpacken von Funktionen in eine ELF-Binärdatei (ohne Neukompilierung) ist um Größenordnungen komplexer, als dasselbe für eine Funktion in einer dynamischen Bibliothek zu tun.

Auf dem Client .ssh/config(oder der Kommandozeile -o SendEnv ...)

Host somehost
  user whatever
  SendEnv SSH_RFWD_*

Auf dem Server sshd_config(Root/Administrator-Änderung erforderlich)

AcceptEnv LC_* SSH_RFWD_*

Dieser Ansatz funktioniert für Linux-Clients und erfordert keine besonderen Anforderungen auf dem Server. Mit einigen kleinen Anpassungen sollte er auch für andere *nix-Clients funktionieren. Funktioniert ab mindestens OpenSSH 5.8p1 bis 7.5p1.

Kompilieren gcc -Wall -shared -ldl -Wl,-soname,rfwd -o rfwd.so rfwd.c mit. Aufrufen mit:

LD_PRELOAD=./rfwd.so ssh -R0:127.0.0.1:4713 -R0:localhost:631 somehost

Der Code:

#define _GNU_SOURCE
#include <stdio.h>
#include <dlfcn.h>
#include <string.h>
#include <stdlib.h>

// gcc -Wall -shared  -ldl -Wl,-soname,rfwd -o rfwd.so rfwd.c

#define DEBUG 0
#define dfprintf(fmt, ...) \
    do { if (DEBUG) fprintf(stderr, "[%14s#%04d:%8s()] " fmt, \
          __FILE__, __LINE__, __func__,##__VA_ARGS__); } while (0)

typedef ssize_t write_fp(int fd, const void *buf, size_t count);
static write_fp *real_write;

void myinit(void) __attribute__((constructor));
void myinit(void)
{
    void *dl;
    dfprintf("It's alive!\n");
    if ((dl=dlopen(NULL,RTLD_NOW))) {
        real_write=dlsym(RTLD_NEXT,"write");
        if (!real_write) dfprintf("error: %s\n",dlerror());
        dfprintf("found %p write()\n", (void *)real_write);
    } else {
        dfprintf(stderr,"dlopen() failed\n");
    }
}

ssize_t write(int fd, const void *buf, size_t count)
{
     static int nenv=0;

     // debug1: Remote connections from 192.168.0.1:0 forwarded to local address 127.0.0.1:1000
     //  Allocated port 44284 for remote forward to 127.0.0.1:1000
     // debug1: All remote forwarding requests processed
     if ( (fd==2) && (!strncmp(buf,"Allocated port ",15)) ) {
         char envbuf1[256],envbuf2[256];
         unsigned int rport;
         char lspec[256];
         int rc;

         rc=sscanf(buf,"Allocated port %u for remote forward to %256s",
          &rport,lspec);

         if ( (rc==2) && (nenv<32) ) {
             snprintf(envbuf1,sizeof(envbuf1),"SSH_RFWD_%i",nenv++);
             snprintf(envbuf2,sizeof(envbuf2),"%u %s",rport,lspec);
             setenv(envbuf1,envbuf2,1);
             dfprintf("setenv(%s,%s,1)\n",envbuf1,envbuf2);
         }
     }
     return real_write(fd,buf,count);
}

(Bei diesem Ansatz gibt es einige Bärenfallen der Glibc im Zusammenhang mit der Symbolversionierung, write()dieses Problem tritt hier jedoch nicht auf.)

Wenn Sie mutig sind, können Sie den setenv()zugehörigen Code nehmen und ihn in ssh.c ssh_confirm_remote_forward()eine Rückruffunktion einfügen.

Dadurch werden Umgebungsvariablen mit dem Namen gesetzt SSH_RFWD_nnn. Überprüfen Sie diese in Ihrem Profil, z. B. inbash

for fwd in ${!SSH_RFWD_*}; do
    IFS=" :" read lport rip rport <<< ${!fwd}
    [[ $rport -eq "631" ]] && export CUPS_SERVER=localhost:$lport
    # ...
done

Vorbehalte:

  • Es gibt nicht viele Fehlerprüfungen im Code
  • Veränderung der UmgebungMaiThread-bezogene Probleme verursachen, PAM verwendet Threads, ich erwarte keine Probleme, aber ich habe das nicht getestet
  • sshprotokolliert derzeit nicht eindeutig die vollständige Weiterleitung der Form * local:port:remote:port* (bei Bedarf wäre eine weitere Analyse der debug1Nachrichten ssh -verforderlich), aber das ist für Ihren Anwendungsfall nicht erforderlich

Seltsamerweise scheint OpenSSH keine Möglichkeit zu haben, Informationen zu Portweiterleitungen abzurufen.

Sie können dies (teilweise) interaktiv mit dem Escape-Befehl tun ~#. Merkwürdigerweise überspringt die Implementierung die Kanäle, die zuhören, listet nur offene (d. h. TCP ESTABLISHED) Kanäle auf und druckt die nützlichen Felder in keinem Fall aus. Siehechannels.c channel_open_message()

Sie können diese Funktion patchen, um die Details für Slots auszudrucken SSH_CHANNEL_PORT_LISTENER, aber damit erhalten Sie nur die lokalen Weiterleitungen (Kanälesind nicht dasselbe wie tatsächlichevorwärts). Oder Sie können es patchen, um die beiden Weiterleitungstabellen aus der globalen optionsStruktur zu dumpen:

#include "readconf.h"
Options options;  /* extern */
[...]
snprintf(buf, sizeof buf, "Local forwards:\r\n");
buffer_append(&buffer, buf, strlen(buf));
for (i = 0; i < options.num_local_forwards; i++) {
     snprintf(buf, sizeof buf, "  #%d listen %s:%d connect %s:%d\r\n",i,
       options.local_forwards[i].listen_host,
       options.local_forwards[i].listen_port,
       options.local_forwards[i].connect_host,
       options.local_forwards[i].connect_port);
     buffer_append(&buffer, buf, strlen(buf));
}
snprintf(buf, sizeof buf, "Remote forwards:\r\n");
buffer_append(&buffer, buf, strlen(buf));
for (i = 0; i < options.num_remote_forwards; i++) {
     snprintf(buf, sizeof buf, "  #%d listen %s:%d connect %s:%d\r\n",i,
       options.remote_forwards[i].listen_host,
       options.remote_forwards[i].listen_port,
       options.remote_forwards[i].connect_host,
       options.remote_forwards[i].connect_port);
     buffer_append(&buffer, buf, strlen(buf));
}

Dies funktioniert gut, obwohl es keine „programmgesteuerte“ Lösung ist, mit der Einschränkung, dass der Client-Code die Liste nicht aktualisiert (noch nicht, da er im Quellcode mit XXX gekennzeichnet ist), wenn Sie Weiterleitungen spontan hinzufügen/entfernen ( ~C).


Wenn die Server Linux sind, haben Sie eine weitere Option, die ich im Allgemeinen verwende, allerdings für die lokale Weiterleitung und nicht für die Remote-Weiterleitung. loist 127.0.0.1/8, unter Linux können Sietransparente Bindung an jede Adresse in 127/8, Sie können also feste Ports verwenden, wenn Sie eindeutige 127.xyz-Adressen verwenden, zB:

mr@local:~$ ssh -R127.53.50.55:44284:127.0.0.1:44284 remote
[...]
mr@remote:~$ ss -atnp src 127.53.50.55
State      Recv-Q Send-Q        Local Address:Port          Peer Address:Port 
LISTEN     0      128            127.53.50.55:44284                    *:*    

Dies unterliegt der Bindung privilegierter Ports <1024, OpenSSH unterstützt keine Linux-Funktionen und verfügt auf den meisten Plattformen über eine fest codierte UID-Prüfung.

Mit Bedacht gewählte Oktette (in meinem Fall ASCII-Ordinal-Mnemonik) helfen, das Durcheinander am Ende des Tages zu entwirren.

verwandte Informationen