การส่งต่อพอร์ตระยะไกล SSH ล้มเหลว


26

การติดตามผล:ดูเหมือนว่าชุดการตัดการเชื่อมต่ออย่างรวดเร็วที่เกิดขึ้นพร้อมกับการใช้งานเซิร์ฟเวอร์เพียงไม่กี่เดือนนั้นอาจเป็นเรื่องบังเอิญและเพิ่งจะเปิดเผยปัญหาที่แท้จริง เหตุผลที่ไม่สามารถเชื่อมต่อใหม่ได้เกือบจะแน่นอนเนื่องจากค่า AliveInterval (คำตอบของ kasperd) การใช้ตัวเลือก ExitOnForwardFailure ควรอนุญาตให้การหมดเวลาเกิดขึ้นอย่างถูกต้องก่อนทำการเชื่อมต่ออีกครั้งซึ่งควรแก้ปัญหาในกรณีส่วนใหญ่ คำแนะนำของ MadHatter (สคริปต์การฆ่า) น่าจะเป็นวิธีที่ดีที่สุดเพื่อให้แน่ใจว่าอุโมงค์สามารถเชื่อมต่อได้แม้ว่าทุกอย่างจะล้มเหลว

ฉันมีเซิร์ฟเวอร์ (A) อยู่หลังไฟร์วอลล์ที่เริ่มต้นช่องสัญญาณย้อนกลับในหลายพอร์ตไปยัง DigitalOcean VPS (B) ขนาดเล็กเพื่อให้ฉันสามารถเชื่อมต่อกับ A ผ่านทางที่อยู่ IP ของ B อุโมงค์ทำงานอย่างต่อเนื่องเป็นเวลาประมาณ 3 เดือน แต่ก็ล้มเหลวทันทีสี่ครั้งใน 24 ชั่วโมงที่ผ่านมา สิ่งเดียวกันเกิดขึ้นชั่วขณะหนึ่งกลับไปที่ผู้ให้บริการ VPS รายอื่น - ใช้งานได้หลายเดือนจากนั้นก็ล้มเหลวอย่างรวดเร็วหลายครั้ง

ฉันมีสคริปต์บนเครื่องที่จะรันคำสั่งอุโมงค์ ( ssh -R *:X:localhost:X address_of_Bสำหรับพอร์ตแต่ละ X) Warning: remote port forwarding failed for listen port Xแต่เมื่อมันรันก็กล่าวว่า

การเข้าไปที่ sshd /var/log/secureบนเซิร์ฟเวอร์จะแสดงข้อผิดพลาดเหล่านี้:

bind: Address already in use
error: bind: Address already in use
error: channel_setup_fwd_listener: cannot listen to port: X

การแก้ปัญหาต้องเริ่มต้น VPS ใหม่ จนกว่าจะถึงตอนนั้นความพยายามในการเชื่อมต่อทั้งหมดจะให้ข้อความ "การส่งต่อพอร์ตระยะไกลล้มเหลว" และจะไม่ทำงาน ตอนนี้มาถึงจุดที่อุโมงค์ใช้เวลาประมาณ 4 ชั่วโมงก่อนหยุด

ไม่มีการเปลี่ยนแปลงอะไรใน VPS และเป็นเครื่องผู้ใช้แบบใช้ครั้งเดียวที่ทำหน้าที่เป็นจุดสิ้นสุดอุโมงค์ย้อนกลับเท่านั้น กำลังใช้งาน OpenSSH_5.3p1 บน CentOS 6.5 ดูเหมือนว่า sshd จะไม่ปิดพอร์ตที่ส่วนท้ายเมื่อการเชื่อมต่อขาดหายไป ฉันกำลังสูญเสียที่จะอธิบายว่าทำไมหรือทำไมมันจะเกิดขึ้นทันทีหลังจากเดือนของการดำเนินงานเกือบสมบูรณ์แบบ

ในการชี้แจงฉันต้องคิดก่อนว่าทำไม sshd ปฏิเสธที่จะฟังพอร์ตหลังจากที่อุโมงค์ล้มเหลวซึ่งดูเหมือนว่าเกิดจาก sshd เปิดพอร์ตทิ้งไว้และไม่เคยปิดพอร์ตเลย นั่นน่าจะเป็นปัญหาหลัก ฉันไม่แน่ใจว่าสิ่งใดที่จะทำให้เกิดพฤติกรรมนี้หลังจากผ่านไปหลายเดือนตามที่ฉันคาดหวัง (เช่นปิดพอร์ตทันทีและอนุญาตให้สคริปต์เชื่อมต่อใหม่)


คำถามของคุณคืออะไร? วิธีการระบุข้อผิดพลาดการผูกพอร์ตหรือวิธีการค้นหาสาเหตุที่ ssh กำลังจะตายหรืออย่างอื่นอีกครั้ง?
MadHatter สนับสนุนโมนิก้า

ฉันต้องคิดออกว่าทำไม sshd ปฏิเสธที่จะเปิดพอร์ตบน VPS (ข้อผิดพลาดการผูก) ดูเหมือนว่าข้อผิดพลาดการเชื่อมพอร์ตจะเป็นรากของปัญหาและทุกอย่างจะทำงานได้หากฉันสามารถแก้ไขได้
Justin Mrkva

2
สำหรับผู้แฝงตัวคนอื่น ๆ แทนที่จะสร้างสคริปต์ด้วยตนเองเพื่อให้การเชื่อมต่อเปิดอยู่ให้ใช้ autossh แทนซึ่งจะทำสิ่งนี้ให้คุณ serverfault.com/questions/598210/…
oligofren

คำตอบ:


27

ฉันเห็นด้วยกับ MadHatter ว่ามีโอกาสที่จะส่งต่อพอร์ตจากการเชื่อมต่อ ssh ที่หมดอายุแล้ว แม้ว่าปัญหาปัจจุบันของคุณจะกลายเป็นสิ่งอื่นคุณสามารถคาดหวังว่าจะพบกับการเชื่อมต่อ ssh ที่หมดอายุดังกล่าวไม่ช้าก็เร็ว

การเชื่อมต่อที่หมดอายุสามารถเกิดขึ้นได้สามวิธี:

  • หนึ่งในสองปลายทางถูกรีบูทในขณะที่ปลายอีกด้านหนึ่งของการเชื่อมต่อไม่ได้ทำงานอย่างสมบูรณ์
  • หนึ่งในสองปลายทางปิดการเชื่อมต่อ แต่ในเวลาที่การเชื่อมต่อถูกปิดมีการดับชั่วคราวในการเชื่อมต่อ ไฟดับเป็นเวลาสองสามนาทีหลังจากการเชื่อมต่อถูกปิดและอีกปลายหนึ่งไม่เคยเรียนรู้เกี่ยวกับการเชื่อมต่อที่ปิด
  • การเชื่อมต่อยังคงทำงานได้อย่างสมบูรณ์ที่จุดปลายทั้งสองของการเชื่อมต่อ ssh แต่บางคนได้วางอุปกรณ์ stateful ระหว่างพวกเขาซึ่งหมดเวลาการเชื่อมต่อเนื่องจากความเกียจคร้าน อุปกรณ์ stateful นี้จะเป็น NAT หรือไฟร์วอลล์ไฟร์วอลล์ที่คุณกล่าวถึงแล้วเป็นผู้ต้องสงสัยหลัก

การพิจารณาว่าสิ่งใดในสามข้อที่เกิดขึ้นนั้นไม่สำคัญอย่างยิ่งเพราะมีวิธีการซึ่งจะจัดการกับทั้งสามอย่าง นั่นคือการใช้ข้อความแบบ keepalive

คุณควรจะดูเป็นClientAliveIntervalคำหลักสำหรับsshd_configและServerAliveIntervalช่วงเวลาสำหรับการหรือssh_config~/.ssh/config

การรันsshคำสั่งในลูปสามารถทำงานได้ดี มันเป็นความคิดที่ดีที่จะแทรกสลีปในลูปด้วยเช่นกันที่คุณจะไม่ทำให้เซิร์ฟเวอร์ท่วมเมื่อการเชื่อมต่อด้วยเหตุผลบางประการล้มเหลว

หากไคลเอนต์เชื่อมต่อใหม่ก่อนที่การเชื่อมต่อจะสิ้นสุดลงบนเซิร์ฟเวอร์คุณสามารถสิ้นสุดในสถานการณ์ที่การเชื่อมต่อ ssh ใหม่นั้นใช้งานได้ แต่ไม่มีการส่งต่อพอร์ต เพื่อหลีกเลี่ยงปัญหานี้คุณต้องใช้ExitOnForwardFailureคำหลักในฝั่งไคลเอ็นต์


ฉันคิดว่านี่อาจเป็นปัญหา โดยเฉพาะอย่างยิ่งสคริปต์ของฉันใน A จะพยายามเชื่อมต่อกับ B อีกครั้งหากกระบวนการ ssh ตาย (แน่นอนเนื่องจากข้อความเตือนไม่ฆ่ากระบวนการ ssh ที่เพิ่งหยุดทำงานเมื่อเกิดเหตุการณ์นี้ขึ้น แต่นั่นก็เป็นปัญหาสำหรับวันอื่น) แต่ถ้า A พยายามเชื่อมต่อกับ B เร็วเกินไป B อาจกำลังรอให้ A เชื่อมต่อใหม่ ฉันอาจต้องตรวจสอบให้แน่ใจว่า B หมดเวลาก่อนที่จะเชื่อมต่ออีกครั้ง เมื่อรวมกับคำแนะนำของ MadHatter ในการฆ่ากระบวนการ sshd ก่อนที่จะเชื่อมต่อใหม่อาจครอบคลุม 95% ของกรณีที่เป็นไปได้
Justin Mrkva

1
และการพูดถึงข้อความเตือนไม่ได้ฆ่า SSH นั่นทำให้ฉันคิดว่า ... และดู manpages ปรากฎ-o ExitOnForwardFailure yesว่าเป็นสิ่งที่ฉันต้องการ นั่นคือสิ่งที่ฉันต้องคิดออก ฉันคิดว่าฉันจะเขียนสคริปต์ Python เพื่อแยกวิเคราะห์ข้อความเตือนเหล่านั้น ง่ายกว่านี้มาก : D
Justin Mrkva

ขออภัยที่ลืมเกี่ยวกับExitOnForwardFailureการเขียนคำตอบของฉัน ฉันได้เพิ่มเข้าไปในคำตอบทันที
kasperd

4
ไม่มีปัญหาและจริง ๆ แล้ว-o ExitOnForwardFailure=yes(สังเกตเครื่องหมายเท่ากับ) ดังนั้นหากใครเจอสิ่งนี้อย่าคัดลอกและวางจากความคิดเห็นก่อนหน้าของฉันมันจะไม่ทำงาน : P
Justin Mrkva

ดังนั้นฉันได้รับการตรวจสอบเซิร์ฟเวอร์ประมาณ 10 ชั่วโมงและดูเหมือนว่ามันทำงานได้ดี; ฉันสมมติว่า ณ จุดนี้ว่าคำตอบนี้ถูกต้อง (ฉันประมาณ 99% ขึ้นอยู่กับสิ่งที่ฉันเคยเห็น) และชุดของการยกเลิกการเชื่อมต่ออย่างรวดเร็วนั้นเป็นเรื่องบังเอิญที่เกี่ยวข้องกับปัญหาเครือข่ายที่เพิ่งปรากฏขึ้นหลังจากนั้นไม่กี่เดือน เริ่มแต่ละบริการ ขอบคุณทุกคนสำหรับความช่วยเหลือของคุณ ;)
Justin Mrkva

4

คุณสามารถค้นหากระบวนการที่เชื่อมพอร์ตบนเซิร์ฟเวอร์นั้นได้

sudo netstat -apn|grep -w X

ดูเหมือนว่าน่าจะเป็นครึ่งตายsshdแต่ทำไมต้องตั้งสมมติฐานเมื่อคุณมีข้อมูล นอกจากนี้ยังเป็นวิธีที่ดีสำหรับสคริปต์ในการค้นหา PID เพื่อส่งสัญญาณ 9 ถึงก่อนที่จะพยายามนำอุโมงค์ขึ้นมาอีกครั้ง


ฉันจำได้ว่าตรวจสอบกับผู้ให้บริการ VPS ก่อนหน้านี้และฉันยืนยันว่า sshd เป็นกระบวนการรับฟังพอร์ตเหล่านั้น ครั้งต่อไปมันจะเกิดขึ้นฉันจะตรวจสอบที่นี่ แต่เนื่องจากพฤติกรรมและการตั้งค่าเหมือนกันทุกประการฉันไม่คิดว่ามันจะแตกต่างกัน
Justin Mrkva

เยี่ยมมากให้สคริปต์ของคุณที่เปิดอุโมงค์อีกครั้งฆ่าอุโมงค์เก่าก่อนที่จะลองทำ
MadHatter สนับสนุนโมนิก้า

ไม่เคยมีสคริปต์ช่องสัญญาณ (A) มากกว่าหนึ่งสคริปต์ทำงานพร้อมกันหากนั่นคือสิ่งที่คุณกำลังพูด ในทางตรงกันข้ามถ้าคุณต้องการให้สคริปต์รันคำสั่งบน B จากระยะไกลเพื่อฆ่ากระบวนการหลงทาง ... นั่นไม่ใช่ความคิดที่เลวร้าย แต่ข้อกังวลอย่างหนึ่งคือการปิดการเชื่อมต่อ SSH ทั้งหมดซ้ำ ๆ ถ้าฉันพยายามที่จะดีบั๊ก หากสคริปต์ใน A นั้นมักจะฆ่า B เนื่องจากความผิดพลาดดังนั้นฉันจะไม่สามารถถูกไล่ออกจาก B โดยสคริปต์อันธพาล : P ฉันจะต้องทดสอบเพื่อให้แน่ใจว่าไม่ได้ทำเช่นนั้น แต่อย่างที่ฉันพูดไม่ใช่ความคิดที่ไม่ดีครึ่งหนึ่ง ;)
Justin Mrkva

ฉันไม่คิดว่าจะมี คุณบอกว่ามีสคริปต์ที่ทำงานบนเซิร์ฟเวอร์ระยะไกลที่พยายามทำให้เกิดช่องสัญญาณและล้มเหลวเนื่องจากข้อผิดพลาดในการผูกและฉันสมมติว่ามันจะทำงานเมื่อคุณต้องการ (เช่นเมื่ออุโมงค์ที่มีอยู่ไม่ดี) เพราะคุณไม่ได้พูดอย่างอื่น ทั้งหมดที่ฉันแนะนำคือมันฆ่ากระบวนการเฉพาะที่เปิดพอร์ตไว้ก่อนที่จะพยายามเปิดช่องอุโมงค์ใหม่
MadHatter สนับสนุนโมนิก้า

สคริปต์ที่รัน ssh อยู่บนเซิร์ฟเวอร์ A เท่านั้นเซิร์ฟเวอร์ B เป็นเซิร์ฟเวอร์วานิลลาธรรมดาที่ไม่มีสคริปต์เพิ่มเติม สิ่งที่ฉันน่าจะทำคือเขียนสคริปต์เพื่อวางเซิร์ฟเวอร์ B จากนั้นเรียกมันจากระยะไกลจาก A หากไม่สามารถเชื่อมต่อจำนวนครั้งในแถวหนึ่งได้ วิธีนี้มีโอกาสน้อยที่จะรบกวนการเชื่อมต่อ SSH อื่น ๆ และฉันอาจจะมีบันทึกสคริปต์การฆ่าทุกครั้งที่เรียกใช้และออกโดยไม่ทำอะไรเลยหากมีการเรียกใช้หลายครั้งเร็วเกินไป โดยส่วนตัวดูเหมือนว่าการ จำกัด อัตราสคริปต์ใด ๆ ที่ฆ่า sshd น่าจะรอบคอบ : P
Justin Mrkva

3

สำหรับฉันเมื่อsshอุโมงค์ยกเลิกการเชื่อมต่อจะใช้เวลาสักครู่สำหรับการเชื่อมต่อเพื่อรีเซ็ตดังนั้นsshกระบวนการยังคงบล็อกทำให้ฉันไม่มีอุโมงค์ที่ใช้งานอยู่และฉันไม่รู้ว่าทำไม วิธีแก้ไขปัญหาคือใส่sshพื้นหลังด้วย-fและวางไข่การเชื่อมต่อใหม่โดยไม่ต้องรอการเชื่อมต่อเก่าเพื่อรีเซ็ต -o ExitOnForwardFailure=yesสามารถใช้ในการ limt จำนวนของกระบวนการใหม่ การ-o ServerAliveInterval=60ปรับปรุงความน่าเชื่อถือของการเชื่อมต่อปัจจุบันของคุณ

คุณสามารถทำซ้ำsshคำสั่งบ่อยๆพูด a cronหรือในลูปในสคริปต์ของคุณเช่นในตัวอย่างต่อไปนี้เราจะรันsshคำสั่งทุก 3 นาที:

while (1)
do
    ssh -f user@hostname -Rport:host:hostport -N -o ExitOnForwardFailure=yes -o ServerAliveInterval=60
    sleep 180
done

วิธีแก้ปัญหาที่แข็งแกร่งกว่านั้นคือการใช้การตรวจสอบอัตโนมัติ
Marco Lavagnino

-o ExitOnForwardFailure=yesเป็นสิ่งที่ฉันกำลังมองหาขอบคุณมาก!
vadipp

1

จากประสบการณ์ของฉัน ssh มีนิสัยที่น่ากลัวเล็กน้อยที่จะไม่ออกอย่างหมดจดหาก 'บางสิ่ง' ยังคงทำงานบนระบบรีโมต เช่นเริ่มต้นในพื้นหลัง คุณสามารถทำซ้ำสิ่งนี้โดย:

ssh <server>
while true; do  sleep 60; done&
exit

ssh ของคุณจะออกจากระบบ แต่จะไม่ปิดเซสชัน - จนกว่ากระบวนการรีโมตจะออก (ซึ่งจะไม่เกิดขึ้นเนื่องจากเป็นลูป 'ในขณะที่ใช้งานจริง') มันอาจจะเป็นสิ่งที่คล้ายกันที่เกิดขึ้น - เซสชั่นของคุณมีกระบวนการ 'ติด' ที่ถูกวางไข่โดย ssh พอร์ตยังคงมีการใช้งานอยู่ดังนั้นจึงไม่สามารถนำกลับมาใช้ใหม่ได้โดยกระบวนการโลคัลของคุณ


คำสั่ง SSH แบบสมบูรณ์ที่เรียกใช้งานบนเครื่อง A นั้นssh -o ConnectTimeout=10 -o BatchMode=yes -gnN -R *:X:localhost:X root@$TUNSRV 1>>tunnel.log 2>&1 &จึงไม่มีสิ่งใดถูกดำเนินการโดย SSH ยกเว้นช่องสัญญาณของตัวเองโดยเฉพาะเนื่องจากตัวเลือก -N สิ่งที่จะถูกเก็บไว้เปิดจะถูกทำบนเซิร์ฟเวอร์ระยะไกล B โดยใช้ sshd ตัวเอง
Justin Mrkva
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.