OpenSSH RemoteForward के लिए गतिशील रूप से आवंटित पोर्ट निर्धारित करें


13

प्रश्न (TL, DR)

दूरस्थ अग्रेषण (उर्फ -Rविकल्प) के लिए गतिशील रूप से बंदरगाहों को असाइन करते समय, दूरस्थ मशीन पर एक स्क्रिप्ट (उदाहरण के लिए खट्टे से .bashrc) कैसे निर्धारित कर सकती है कि ओपनएसएसएच द्वारा किन बंदरगाहों को चुना गया था?


पृष्ठभूमि

मैं अपने केंद्रीय सर्वर से कनेक्ट करने के लिए ओपनएसएसएच (दोनों छोर पर) का उपयोग करता हूं, जिसे मैं कई अन्य उपयोगकर्ताओं के साथ साझा करता हूं। अपने दूरस्थ सत्र के लिए (अभी के लिए) मैं एक्स, कप और पल्सेडियो को आगे करना चाहूंगा।

-Xविकल्प का उपयोग करते हुए सबसे तुच्छ X अग्रेषित कर रहा है। आवंटित एक्स पता पर्यावरण चर में संग्रहीत किया जाता है DISPLAYऔर इससे मैं ज्यादातर मामलों में, वैसे भी टीसीपी पोर्ट को निर्धारित कर सकता हूं। लेकिन मुझे शायद ही कभी ज़रूरत पड़े, क्योंकि ज़लीब सम्मान DISPLAY

मुझे कप और पल्सीडियो के लिए एक समान तंत्र की आवश्यकता है। दोनों सेवाओं के लिए मूल बातें, पर्यावरण चर के रूप में CUPS_SERVERऔर PULSE_SERVERक्रमशः मौजूद हैं। यहाँ उपयोग उदाहरण हैं:

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

समस्या सेटिंग CUPS_SERVERऔर PULSE_SERVERसही ढंग से है।

हम पोर्ट फ़ॉरवर्डिंग का बहुत अधिक उपयोग करते हैं और इसलिए मुझे डायनेमिक पोर्ट एलोकेशन की आवश्यकता है। स्थैतिक पोर्ट आवंटन एक विकल्प नहीं हैं।

OpenSSH में 0दूरस्थ अग्रेषण ( -Rविकल्प) के लिए बाइंड-पोर्ट के रूप में निर्दिष्ट करके, दूरस्थ सर्वर पर गतिशील पोर्ट आवंटन के लिए एक तंत्र है । निम्न आदेश का उपयोग करके, OpenSSH गतिशील रूप से कप और पल्स अग्रेषण के लिए पोर्ट आवंटित करेगा।

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

जब मैं उस कमांड का उपयोग करता हूं, sshतो निम्न को प्रिंट करेगा 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

वहाँ जानकारी मैं चाहता हूँ! अंततः मैं उत्पन्न करना चाहता हूँ:

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

हालाँकि "आवंटित पोर्ट ..." संदेश मेरे स्थानीय मशीन पर बनाए जाते हैं और भेजे जाते हैं STDERR, जिन्हें मैं दूरस्थ मशीन पर एक्सेस नहीं कर सकता। अजीब तरह से ओपनएसएसएच के पास पोर्ट फ़ॉरवर्डिंग के बारे में जानकारी प्राप्त करने के साधन नहीं हैं।

मुझे उस जानकारी को कैसे प्राप्त करना है जो इसे शेल स्क्रिप्ट में डालने के लिए पर्याप्त रूप से सेट किया गया है CUPS_SERVERऔर PULSE_SERVERदूरस्थ होस्ट पर?


अंतिम छोर

एकमात्र आसान चीज जो मुझे मिल सकी, sshdजब तक कि लॉग से जानकारी को पढ़ा नहीं जा सकता है , तब तक वर्बोसिटी बढ़ रही थी । यह व्यवहार्य नहीं है क्योंकि यह जानकारी गैर-रूट उपयोगकर्ताओं द्वारा सुलभ बनाने के लिए समझदार होने की तुलना में बहुत अधिक जानकारी का खुलासा करती है।

मैं ओपनएसएसएच को एक अतिरिक्त एस्केप सीक्वेंस का समर्थन करने के बारे में सोच रहा था जो आंतरिक संरचना का एक अच्छा प्रतिनिधित्व करता है permitted_opens, लेकिन यहां तक ​​कि अगर मैं चाहता हूं, तो मैं अभी भी क्लाइंट साइड सीक्वेंस को सर्वर साइड से एक्सेस करने की स्क्रिप्ट नहीं कर सकता।


इसके लिए अवश्य ही एक बेहतर तरीका होना चाहिए

निम्नलिखित दृष्टिकोण बहुत अस्थिर लगता है और प्रति उपयोगकर्ता एक ऐसे SSH सत्र तक सीमित है। हालाँकि, मुझे कम से कम दो समवर्ती ऐसे सत्र और अन्य उपयोगकर्ताओं की आवश्यकता है। लेकिन मैंने कोशिश की ...

जब तारों को ठीक से संरेखित किया जाता है, तो एक चिकन या दो का बलिदान करने के बाद, मैं इस तथ्य का दुरुपयोग कर सकता हूं जो sshdमेरे उपयोगकर्ता के रूप में शुरू नहीं हुआ है, लेकिन ऐसा करने के लिए सफल लॉगिन के बाद विशेषाधिकार छोड़ देता है:

  • सभी सुनने वाले सॉकेट के लिए पोर्ट नंबरों की एक सूची प्राप्त करें जो मेरे उपयोगकर्ता के हैं

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

  • सभी श्रवण सॉकेट के लिए पोर्ट संख्याओं की एक सूची प्राप्त करें जो मेरे उपयोगकर्ता द्वारा शुरू की गई प्रक्रियाओं से संबंधित हैं

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

  • सभी पोर्ट जो पहले सेट में हैं, लेकिन दूसरे सेट में मेरे फॉरवर्डिंग पोर्ट होने की उच्च संभावना नहीं है, और वास्तव में सेट पैदावार घटाना 41273, 55710और 6010; कप, पल्स और एक्स, क्रमशः।

  • 6010एक्स पोर्ट का उपयोग कर के रूप में पहचाना जाता है DISPLAY

  • 41273कप पोर्ट है, क्योंकि lpstat -h localhost:41273 -aरिटर्न 0
  • 55710पल्स पोर्ट है, क्योंकि pactl -s localhost:55710 statरिटर्न 0। (यह मेरे क्लाइंट के होस्टनाम को भी प्रिंट करता है!)

(सेट सबट्रेक्ट I करने के लिए sort -uऔर उपरोक्त कमांड लाइनों से आउटपुट को स्टोर commकरने के लिए और सबस्टेशन को करने के लिए उपयोग करें।)

पल्सीडियो मुझे क्लाइंट को पहचानने की अनुमति देता है और, सभी इरादों और उद्देश्यों के लिए, यह SSH सत्रों को अलग करने के लिए एक लंगर के रूप में काम कर सकता है जिसे अलग करने की आवश्यकता होती है। हालाँकि, मुझे टाई करने का तरीका नहीं मिला है 41273, 55710और 6010उसी sshdप्रक्रिया को। netstatगैर-रूट उपयोगकर्ताओं के लिए उस जानकारी का खुलासा नहीं करेंगे। मुझे केवल -उस PID/Program nameकॉलम में एक जगह मिलती है जहाँ मैं 2339/54(इस विशेष उदाहरण में) पढ़ना चाहूंगा । बहुत करीब ...


fwiw, यह कहना अधिक सटीक है कि netstatआप उन प्रक्रियाओं के लिए PID नहीं दिखाएंगे जो आपके पास नहीं हैं या जो कर्नेल-स्थान हैं। उदाहरण के लिए
ब्राचली

सबसे मजबूत तरीका sshd को पैच करना होगा ... एक त्वरित और गंदी पैच उस जगह पर बस कुछ लाइनें होगी जहां सर्वर को ओएस से अपना स्थानीय पोर्ट मिलता है, पोर्ट नंबर को एक फाइल में लिखना, उपयोगकर्ता से उत्पन्न नाम, रिमोट होस्ट और बंदरगाह। सर्वर मान लेता है कि क्लाइंट की तरफ पोर्ट है, जो निश्चित नहीं है, शायद इसकी संभावना भी नहीं है (अन्यथा फीचर पहले से मौजूद होगा)।
हाइड

@ पट्टी: बिल्कुल। दूरस्थ सर्वर को अग्रेषित पोर्ट के बारे में नहीं पता है। यह सिर्फ कुछ सुनने के सॉकेट बनाता है और डेटा को ssh कनेक्शन के माध्यम से अग्रेषित किया जाता है। यह स्थानीय गंतव्य बंदरगाहों के बारे में नहीं जानता है।
बनगुंगिन

जवाबों:


1

दो ले लो (एक संस्करण के लिए इतिहास देखें जो सर्वर की तरफ से स्केप करता है और थोड़ा सरल है), इसे ऐसा करना चाहिए। इसका सार यह है:

  1. क्लाइंट से सर्वर तक एक पर्यावरण चर पास करें, सर्वर को बताएंगे कि पोर्ट की जानकारी उपलब्ध होने पर यह कैसे पता लगा सकता है और फिर इसे प्राप्त करें और इसका उपयोग करें।
  2. एक बार पोर्ट की जानकारी उपलब्ध होने के बाद, इसे क्लाइंट से सर्वर पर कॉपी करें, सर्वर को इसे प्राप्त करने की अनुमति दें (ऊपर दिए गए भाग 1 की मदद से), और इसका उपयोग करें

सबसे पहले, दूरस्थ पक्ष पर सेटअप, आपको sshd विन्यास में एक एनवी चर भेजने में सक्षम करने की आवश्यकता है :

sudo yourfavouriteeditor /etc/ssh/sshd_config

इसके साथ लाइन ढूंढें AcceptEnvऔर इसमें जोड़ें MY_PORT_FILE(या Hostयदि अभी तक कोई नहीं है तो राइट सेक्शन के तहत लाइन जोड़ें )। मेरे लिए यह रेखा बन गई:

AcceptEnv LANG LC_* MY_PORT_FILE

इसे प्रभावी करने के लिए sshd को पुनरारंभ करना भी याद रखें ।

इसके अतिरिक्त, नीचे स्क्रिप्ट के लिए काम करने के लिए, mkdir ~/portfilesदूरस्थ पक्ष पर करें!


फिर स्थानीय तरफ, एक स्क्रिप्ट स्निपेट जो होगा

  1. Stderr पुनर्निर्देशन के लिए अस्थायी फ़ाइल नाम बनाएँ
  2. सामग्री के लिए फ़ाइल की प्रतीक्षा करने के लिए पृष्ठभूमि की नौकरी छोड़ दें
  3. फ़ाइल का नाम सर्वर को env वेरिएबल के रूप में दें, जबकि ssh stderr को फ़ाइल पर रीडायरेक्ट करता है
  4. बैकग्राउंड जॉब अलग-अलग scp का उपयोग करके stderr temp फाइल को सर्वर साइड में कॉपी करता है
  5. बैकग्राउंड जॉब भी एक फ्लैग फाइल की प्रतिलिपि बनाता है ताकि सर्वर को स्टैडर फाइल तैयार हो सके

स्क्रिप्ट स्निपेट:

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

फिर दूरस्थ पक्ष के लिए एक स्निपेट, .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

नोट : उपरोक्त कोड निश्चित रूप से बहुत अच्छी तरह से परीक्षण नहीं किया गया है और इसमें सभी प्रकार के कीड़े, कॉपी-पेस्ट त्रुटियां आदि हो सकती हैं। कोई भी इसका बेहतर उपयोग करने वाला व्यक्ति भी इसे समझ सकता है, अपने जोखिम पर उपयोग कर सकता है! मैंने इसे केवल लोकलहोस्ट कनेक्शन का उपयोग करके परीक्षण किया, और इसने मेरे लिए, मेरे टेस्ट एनवी में काम किया। YMMV।


बेशक मुझे scpरिमोट साइड से लोकल की तरफ, जो मैं नहीं कर सकता, की आवश्यकता है। मेरे पास एक समान दृष्टिकोण था, लेकिन मैं sshकनेक्शन स्थापित करने के बाद इसे पृष्ठभूमि में लपेटूंगा, फिर उस फ़ाइल को स्थानीय से रिमोट के माध्यम से भेजूंगा scpऔर फिर sshक्लाइंट को अग्रभूमि में खींचूंगा और दूरस्थ तरफ एक स्क्रिप्ट चलाऊंगा। मुझे पता नहीं चला है कि स्थानीय और दूरस्थ प्रक्रियाओं को अच्छी तरह से स्क्रिप्टिंग और अग्रभूमि की स्क्रिप्टिंग कैसे की जाती है। स्थानीय sshक्लाइंट को कुछ दूरस्थ लिपियों के साथ लपेटना और एकीकृत करना एक अच्छा दृष्टिकोण नहीं लगता है।
बनगुंगिन

आह। मुझे लगता है कि आपको क्लाइंट साइड scp को ही बैकग्राउंड करना चाहिए (while [ ... ] ; do sleep 1 ; done ; scp ... )&:। तब सर्वर में अग्रभूमि पर प्रतीक्षा करें .bashrc(यह मानते हुए कि क्लाइंट फ़ाइल को प्रदर्शित करने के लिए सही एन वी चर भेजता है)। मैं कुछ परीक्षण के बाद उत्तर को अपडेट करूंगा (शायद कल तक कोई समय नहीं)।
हाइड

@Bananguin नया संस्करण किया। मेरे लिए काम करने लगता है, इसलिए आपके उपयोग के मामले में अनुकूल होना चाहिए। "अच्छा दृष्टिकोण" के बारे में, हाँ, लेकिन मुझे नहीं लगता कि यहाँ वास्तव में एक अच्छा दृष्टिकोण संभव है। जानकारी को किसी भी तरह से पारित करने की आवश्यकता है, और यह हमेशा एक हैक होने जा रहा है, जब तक कि आप ssh क्लाइंट और सर्वर दोनों को एकल कनेक्शन पर सफाई से करने के लिए पैच न करें।
हाइड

और मैं अधिक से अधिक पैचिंग ओपनश के बारे में सोच रहा हूं। कोई बड़ी बात नहीं लगती। जानकारी पहले से ही उपलब्ध है। मुझे बस इसे सर्वर पर भेजने की आवश्यकता है। जब भी सर्वर को इस तरह की जानकारी मिलती है तो वह इसे लिखता है~/.ssh-${PID}-forwards
बनंगुइन

1

स्थानीय पक्ष के लिए एक स्निपेट, .bashrc के लिए उपयुक्त:

#!/bin/bash

user=$1
host=$2

sshr() {
# 1. connect, get dynamic port, disconnect  
port=`echo "exit" | ssh -R '*:0:127.0.0.1:52698' -t $1 2>&1 | grep 'Allocated port' | awk '/port/ {print $3;}'`
# 2. reconnect with this port and set remote variable
cmds="ssh -R $port:127.0.0.1:52698 -t $1 bash -c \"export RMATE_PORT=$port; bash\""
($cmds)
}

sshr $user@$host

0

मैंने स्थानीय ग्राहक पर एक पाइप बनाकर इसे हासिल किया है, फिर उस पाइप को स्ट्रीडर को रीडायरेक्ट किया जाता है जिसे ssh के इनपुट पर रीडायरेक्ट किया जाता है। यह एक मुक्त ज्ञात बंदरगाह मानने के लिए कई ssh कनेक्शन की आवश्यकता नहीं है जो विफल हो सकता है। इस तरह लॉगऑन बैनर और "आवंटित पोर्ट ### ..." पाठ दूरस्थ होस्ट पर पुनर्निर्देशित किया गया है।

मेरे पास होस्ट पर एक सरल स्क्रिप्ट है getsshport.shजो रिमोट होस्ट पर चलाया जाता है जो रीडायरेक्ट किए गए इनपुट को पढ़ता है और पोर्ट को बाहर करता है। जब तक यह स्क्रिप्ट समाप्त नहीं होती है, तब तक ssh रिमोट फॉरवर्ड खुला रहता है।

स्थानीय पक्ष

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>&3 stderr और stdout को स्वैप करने के लिए एक छोटी सी ट्रिक है, ताकि stderr को कैट से पाइप किया जा सके, और ssh से सभी सामान्य आउटपुट को stderr पर दिखाया जाता है।

दूरस्थ पक्ष ~ / getshport.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

मैंने grepssh के माध्यम से भेजने से पहले पहले स्थानीय ओर "आवंटित पोर्ट" संदेश का प्रयास किया था , लेकिन लगता है कि ssh पाइप को स्टड पर खोलने के इंतजार में ब्लॉक कर देगा। grep लिखने के लिए पाइप तब तक नहीं खोलता है जब तक वह कुछ प्राप्त नहीं करता है, इसलिए यह मूल रूप से गतिरोध है। catहालाँकि ऐसा प्रतीत नहीं होता कि यह समान व्यवहार है, और तुरंत लिखने के लिए पाइप को खोलता है जिससे कनेक्शन को खोलने की अनुमति मिलती है।

यह दूरस्थ पक्ष पर एक ही समस्या है, और क्यों readस्टड से सिर्फ grep के बजाय लाइन द्वारा लाइन - अन्यथा `/ tmp / आवंटित 'तब तक नहीं लिखा जाता है जब तक ssh सुरंग बंद नहीं हो जाता है जो पूरे उद्देश्य को हरा देता है

Ssh के stderr को एक कमांड में डालना पसंद किया ~/getsshport.shजाता है, जैसे कि कमांड को निर्दिष्ट किए बिना, बैनर टेक्स्ट, या पाइप में जो कुछ भी है वह रिमोट शेल पर निष्पादित हो जाता है।


अच्छा। मैंने संसाधनों को बचाने renice +10 $$; exec catसे पहले जोड़ा done
स्पॉन्जमैन

0

यह एक मुश्किल है, अतिरिक्त सर्वर-साइड हैंडलिंग के साथ SSH_CONNECTIONया DISPLAYमहान होगा, लेकिन इसे जोड़ना आसान नहीं है: समस्या का हिस्सा यह है कि केवल sshग्राहक को स्थानीय गंतव्य पता है, अनुरोध पैकेट (सर्वर पर) शामिल है केवल दूरस्थ पता और पोर्ट।

इस ग्राहक पक्ष को कैप्चर करने और सर्वर पर भेजने के लिए यहां अन्य उत्तरों में विभिन्न व्याख्या समाधान हैं। यहाँ एक वैकल्पिक दृष्टिकोण है जो ईमानदार होने के लिए बहुत अधिक सुंदर नहीं है, लेकिन कम से कम इस बदसूरत पार्टी को ग्राहक पक्ष पर रखा जाता है; ;-)

  • ग्राहक पक्ष, जोड़ें / संशोधन SendEnvताकि हम मूल रूप से ssh पर कुछ पर्यावरण चर भेज सकें (शायद डिफ़ॉल्ट नहीं)
  • सर्वर साइड, AcceptEnvएक ही मानने के लिए जोड़ें / संशोधन (शायद डिफ़ॉल्ट रूप से सक्षम नहीं)
  • sshडायनेमिक रूप से भरी हुई लाइब्रेरी के साथ क्लाइंट स्टैडर आउटपुट की निगरानी करें , और सेट किए गए कनेक्शन के दौरान ssh क्लाइंट वातावरण को अपडेट करें
  • प्रोफ़ाइल / लॉगिन स्क्रिप्ट में पर्यावरण चर सर्वर साइड चुनें

यह काम करता है (ख़ुशी से, अभी के लिए वैसे भी) क्योंकि रिमोट फॉरवर्ड की स्थापना की जाती है और पर्यावरण के आदान-प्रदान से पहले रिकॉर्ड की जाती है (पुष्टि के साथ ssh -vv ...)। गतिशील रूप से भरी हुई लाइब्रेरी को write()libc फ़ंक्शन ( ssh_confirm_remote_forward()logit()do_log()write()) पर कब्जा करना होगा । ELF बाइनरी में (पुन: जमा किए बिना) पुनर्निर्देशन या रैपिंग फ़ंक्शंस डायनामिक लाइब्रेरी में फ़ंक्शन के लिए समान कार्य करने की तुलना में अधिक जटिल है।

क्लाइंट पर .ssh/config(या कमांड लाइन -o SendEnv ...)

Host somehost
  user whatever
  SendEnv SSH_RFWD_*

सर्वर पर sshd_config(रूट / प्रशासनिक परिवर्तन आवश्यक)

AcceptEnv LC_* SSH_RFWD_*

यह दृष्टिकोण लिनक्स क्लाइंट्स के लिए काम करता है और सर्वर पर कुछ खास करने की आवश्यकता नहीं होती है, इसे कुछ मामूली ट्विक्स के साथ अन्य * निक्स के लिए काम करना चाहिए। कम से कम ओपनएसएसएच 5.8 पी 1 से 7.5 पी 1 तक काम करता है।

इनवॉइस के साथ संकलित करें gcc -Wall -shared -ldl -Wl,-soname,rfwd -o rfwd.so rfwd.c :

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

कोड:

#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);
}

(इस दृष्टिकोण के साथ प्रतीक संस्करण से संबंधित कुछ शानदार भालू जाल हैं, लेकिन write()यह समस्या नहीं है।)

यदि आप बहादुर महसूस कर रहे हैं, तो आप setenv()संबंधित कोड ले सकते हैं और इसे ssh.c ssh_confirm_remote_forward()कॉलबैक फ़ंक्शन में पैच कर सकते हैं ।

यह नामित पर्यावरण चर सेट करता है SSH_RFWD_nnn, जैसे कि आपकी प्रोफ़ाइल में, इन का निरीक्षण करेंbash

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

चेतावनियां:

  • कोड में बहुत अधिक त्रुटि जाँच नहीं है
  • पर्यावरण बदलने से थ्रेड से संबंधित समस्याएं हो सकती हैं, पीएएम थ्रेड्स का उपयोग करता है, मुझे समस्याओं की उम्मीद नहीं है लेकिन मैंने इसका परीक्षण नहीं किया है
  • sshवर्तमान में फ़ॉर्म * स्थानीय: पोर्ट: रिमोट: पोर्ट * के पूर्ण फ़ॉरवर्डिंग को लॉग नहीं करता है (यदि आवश्यक debug1संदेशों के पार्सिंग की ssh -vआवश्यकता होगी), लेकिन आपको अपने उपयोग के मामले में इसकी आवश्यकता नहीं है

अजीब तरह से ओपनएसएसएच के पास पोर्ट फ़ॉरवर्डिंग के बारे में जानकारी प्राप्त करने के साधन नहीं हैं।

आप (आंशिक रूप से) इसे भागने के साथ अंतःक्रियात्मक रूप से कर सकते हैं ~#, जो सुन रहे हैं उन चैनलों पर क्रियान्वयन रुक जाता है, यह केवल खुले (यानी टीसीपी असंबद्ध) लोगों को सूचीबद्ध करता है, और यह किसी भी मामले में उपयोगी फ़ील्ड को प्रिंट नहीं करता है। देखchannels.c channel_open_message()

आप SSH_CHANNEL_PORT_LISTENERस्लॉट के लिए विवरण मुद्रित करने के लिए उस फ़ंक्शन को पैच कर सकते हैं , लेकिन यह केवल आपको स्थानीय फ़ॉरवर्डिंग प्राप्त करता है ( चैनल वास्तविक रूप से वही चीज़ नहीं हैं जो आगे की ओर हैं )। या, आप इसे वैश्विक optionsसंरचना से दो अग्रेषण तालिकाओं को डंप करने के लिए पैच कर सकते हैं :

#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));
}

यह ठीक काम करता है, हालांकि यह "प्रोग्रामेटिक" समाधान नहीं है, कैविट के साथ जो क्लाइंट कोड (अभी तक, इसे स्रोत में XXX को चिह्नित किया गया है) सूची को अपडेट करता है जब आप फ़ॉर-द-फ़्लाई ( ~C) को जोड़ते / हटाते हैं।


यदि सर्वर (सर्वर) लिनक्स हैं तो आपके पास एक और विकल्प है, यह वह है जिसे मैं आमतौर पर उपयोग करता हूं, हालांकि रिमोट के बजाय स्थानीय अग्रेषण के लिए। lo127.0.0.1/8 है, लिनक्स पर आप पारदर्शी रूप से 127/8 में किसी भी पते पर बाँध सकते हैं , इसलिए यदि आप अद्वितीय 127.xyz पतों का उपयोग करते हैं, तो आप निश्चित पोर्ट का उपयोग कर सकते हैं, जैसे:

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                    *:*    

यह बाइंडिंग विशेषाधिकार प्राप्त बंदरगाहों के अधीन है <1024, ओपनएसएसएच लिनक्स क्षमताओं का समर्थन नहीं करता है और अधिकांश प्लेटफार्मों पर एक हार्ड-कोडित यूआईडी जांच है।

बुद्धिमानी से चुने गए ओकटेट्स (मेरे मामले में एएससीआईआई ऑर्डिनल मेनेमिक्स) दिन के अंत में गड़बड़ को सुलझाने में मदद करते हैं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.