ฉันค้นหาวิธีแก้ไขปัญหานี้เป็นระยะเวลา 16 เดือนที่ผ่านมา แต่ทุกครั้งที่ฉันดูมันเป็นไปไม่ได้ที่จะทำเช่นนี้กับโปรโตคอล SSH ตามที่ระบุไว้ใน RFC ที่เกี่ยวข้องและดำเนินการโดยการใช้งานที่สำคัญ
อย่างไรก็ตามหากคุณยินดีที่จะใช้ไคลเอนต์ SSH ที่ปรับเปลี่ยนเล็กน้อยและคุณยินดีที่จะใช้โปรโตคอลในแบบที่ไม่ได้ตั้งใจอย่างแท้จริงเมื่อพวกเขาได้รับการออกแบบก็เป็นไปได้ที่จะบรรลุ เพิ่มเติมเกี่ยวกับเรื่องนี้ด้านล่าง
ทำไมมันเป็นไปไม่ได้
ไคลเอ็นต์ไม่ส่งชื่อโฮสต์เป็นส่วนหนึ่งของโปรโตคอล SSH
มันอาจส่งชื่อโฮสต์เป็นส่วนหนึ่งของการค้นหา DNS แต่อาจถูกแคชและเส้นทางจากไคลเอนต์ผ่านตัวแก้ไขไปยังเซิร์ฟเวอร์ที่มีสิทธิ์อาจไม่ข้ามพร็อกซีและแม้ว่าจะไม่มีวิธีการเชื่อมโยงการค้นหา DNS ที่เจาะจงด้วย ลูกค้า SSH เฉพาะ
คุณไม่สามารถทำอะไรกับโปรโตคอล SSH ได้เช่นกัน คุณต้องเลือกเซิร์ฟเวอร์โดยไม่ต้องเห็นแบนเนอร์รุ่น SSH จากลูกค้า คุณต้องส่งแบนเนอร์ไปยังลูกค้าก่อนที่มันจะส่งอะไรไปยังพร็อกซี่ แบนเนอร์จากเซิร์ฟเวอร์อาจแตกต่างกันและคุณไม่มีโอกาสคาดเดาว่าจะใช้ป้ายไหนถูกต้อง
แม้ว่าแบนเนอร์นี้จะไม่ได้รับการเข้ารหัส แต่คุณไม่สามารถแก้ไขได้ แบนเนอร์ทุกบิตนั้นจะได้รับการตรวจสอบในระหว่างการตั้งค่าการเชื่อมต่อดังนั้นคุณจะทำให้การเชื่อมต่อล้มเหลวเล็กน้อย
ข้อสรุปสำหรับฉันค่อนข้างชัดเจนเราต้องเปลี่ยนบางอย่างในฝั่งไคลเอ็นต์เพื่อให้การเชื่อมต่อนี้ใช้งานได้
การแก้ปัญหาส่วนใหญ่กำลังสรุปการรับส่งข้อมูล SSH ภายในโปรโตคอลอื่น เราอาจจินตนาการได้ว่าการเพิ่มโปรโตคอล SSH นั้นด้วยซึ่งแบนเนอร์รุ่นที่ส่งโดยไคลเอนต์จะมีชื่อโฮสต์ สิ่งนี้สามารถยังคงเข้ากันได้กับ severs ที่มีอยู่เนื่องจากส่วนหนึ่งของแบนเนอร์ถูกระบุในขณะนี้เป็นเขตการระบุรูปแบบอิสระและแม้ว่าลูกค้ามักจะรอแบนเนอร์รุ่นจากเซิร์ฟเวอร์ก่อนที่จะส่งของตัวเองโปรโตคอลจะอนุญาตให้ลูกค้าส่งแบนเนอร์ของพวกเขา เป็นครั้งแรก ไคลเอ็นต์ ssh บางเวอร์ชันล่าสุด (ตัวอย่างหนึ่งบน Ubuntu 14.04) ส่งแบนเนอร์โดยไม่รอแบนเนอร์เซิร์ฟเวอร์
ฉันไม่รู้จักไคลเอนต์ใด ๆ ซึ่งได้ดำเนินการตามขั้นตอนเพื่อรวมชื่อโฮสต์ของเซิร์ฟเวอร์ในแบนเนอร์นี้ ฉันได้ส่งตัวปะแก้ไปยังรายชื่อผู้รับจดหมาย OpenSSHเพื่อเพิ่มคุณสมบัติดังกล่าว แต่มันก็ถูกปฏิเสธตามความปรารถนาที่จะไม่เปิดเผยชื่อโฮสต์ให้ใครก็ตามที่สอดแนมการจราจรของ SSH เนื่องจากชื่อโฮสต์ลับนั้นไม่สามารถใช้งานร่วมกับการดำเนินการของพร็อกซีที่ใช้พื้นฐานได้อย่าคาดหวังว่าจะเห็นส่วนขยาย SNI อย่างเป็นทางการสำหรับโปรโตคอล SSH ได้ทุกเวลาในไม่ช้า
ทางออกที่แท้จริง
ทางออกที่ดีที่สุดสำหรับฉันจริงๆแล้วคือการใช้ IPv6
ด้วย IPv6 ฉันสามารถกำหนดที่อยู่ IP แยกต่างหากให้กับแต่ละเซิร์ฟเวอร์ดังนั้นเกตเวย์สามารถใช้ที่อยู่ IP ปลายทางเพื่อค้นหาเซิร์ฟเวอร์ที่จะส่งแพ็คเก็ตได้ บางครั้งไคลเอ็นต์ SSH อาจทำงานบนเครือข่ายซึ่งวิธีเดียวที่จะได้รับที่อยู่ IPv6 คือการใช้ Teredo Teredo เป็นที่รู้จักกันไม่น่าเชื่อถือ แต่เฉพาะเมื่อสิ้นสุด IPv6 ดั้งเดิมของการเชื่อมต่อใช้รีเลย์สาธารณะ Teredo หนึ่งสามารถใส่รีเลย์ Teredo บนเกตเวย์ที่คุณต้องการเรียกใช้พร็อกซี่ Miredo สามารถติดตั้งและกำหนดค่าเป็นรีเลย์ในเวลาน้อยกว่าห้านาที
วิธีแก้ปัญหา
คุณสามารถใช้กระโดดโฮสต์ / โฮสต์ป้อมปราการ วิธีการนี้มีไว้สำหรับกรณีที่คุณไม่ต้องการเปิดเผยพอร์ต SSH ของเซิร์ฟเวอร์แต่ละเครื่องโดยตรงไปยังอินเทอร์เน็ตสาธารณะ มันมีประโยชน์เพิ่มเติมในการลดจำนวนที่อยู่ IP ภายนอกที่คุณต้องการสำหรับ SSH ซึ่งเป็นเหตุผลว่าทำไมมันจึงสามารถใช้งานได้ในสถานการณ์นี้ ความจริงที่ว่ามันเป็นโซลูชันที่มีวัตถุประสงค์เพื่อเพิ่มการป้องกันอีกชั้นหนึ่งเพื่อเหตุผลด้านความปลอดภัยไม่ได้ป้องกันคุณจากการใช้งานเมื่อคุณไม่ต้องการการรักษาความปลอดภัยที่เพิ่มขึ้น
ssh -o ProxyCommand='ssh -W %h:%p user1@bastion' user2@target
แฮ็กที่สกปรกเพื่อให้ทำงานได้หากโซลูชันจริง (IPv6) อยู่นอกเหนือการเข้าถึงของคุณ
แฮ็คที่ฉันจะอธิบายควรใช้เป็นทางเลือกสุดท้ายเท่านั้น ก่อนที่คุณจะคิดถึงการใช้แฮ็คนี้ฉันขอแนะนำให้รับที่อยู่ IPv6 สำหรับเซิร์ฟเวอร์แต่ละเครื่องที่คุณต้องการเข้าถึงจากภายนอกผ่าน SSH ใช้ IPv6 เป็นวิธีหลักในการเข้าถึงเซิร์ฟเวอร์ SSH ของคุณและใช้แฮ็คนี้เมื่อคุณต้องการใช้งานไคลเอ็นต์ SSH จากเครือข่าย IPv4 เท่านั้นซึ่งคุณไม่มีผลต่อการปรับใช้ IPv6
แนวคิดคือการรับส่งข้อมูลระหว่างไคลเอนต์และเซิร์ฟเวอร์จะต้องเป็นการรับส่งข้อมูล SSH ที่ถูกต้องสมบูรณ์ แต่พร็อกซีจะต้องเข้าใจเพียงพอเกี่ยวกับกระแสของแพ็คเก็ตเพื่อระบุชื่อโฮสต์ เนื่องจาก SSH ไม่ได้กำหนดวิธีในการส่งชื่อโฮสต์คุณสามารถพิจารณาโปรโตคอลอื่นที่ให้ความเป็นไปได้ดังกล่าวแทน
HTTP และ HTTPS อนุญาตให้ไคลเอนต์ส่งชื่อโฮสต์ก่อนที่เซิร์ฟเวอร์จะส่งข้อมูลใด ๆ คำถามตอนนี้คือไม่ว่าจะเป็นไปได้ที่จะสร้างกระแสของไบต์ที่ถูกต้องพร้อมกันเป็นทราฟฟิก SSH และเป็น HTTP และ HTTPS HTTP เป็นค่าเริ่มต้นที่ค่อนข้างมาก แต่เป็นไปได้ HTTP (สำหรับคำจำกัดความที่เป็นอิสระของ HTTP อย่างเพียงพอ)
SSH-2.0-OpenSSH_6.6.1 / HTTP/1.1
$:
Host: example.com
ดูเหมือน SSH หรือ HTTP กับคุณหรือไม่ มันเป็น SSH และเป็นไปตาม RFC อย่างสมบูรณ์ (ยกเว้นบางส่วนของตัวละครไบนารีได้ mangled บิตโดยการแสดงผล SF)
สตริง SSH / HTTP/1.1
รุ่นรวมถึงการแสดงความคิดเห็นฟิลด์ซึ่งในข้างต้นมีค่า หลังจาก newline SSH มีข้อมูลแพ็กเก็ตไบนารีบางส่วน แพ็คเก็ตแรกคือMSG_SSH_IGNORE
ข้อความที่ส่งโดยไคลเอนต์และละเว้นโดยเซิร์ฟเวอร์ เพย์โหลดที่จะถูกละเว้นคือ:
:
Host: example.com
หากพร็อกซี HTTP นั้นเปิดเสรีอย่างเพียงพอในสิ่งที่ยอมรับดังนั้นลำดับไบต์เดียวกันจะถูกตีความว่าเป็นวิธี HTTP ที่เรียกว่าSSH-2.0-OpenSSH_6.6.1
และข้อมูลไบนารีที่จุดเริ่มต้นของข้อความเพิกเฉยจะถูกตีความว่าเป็นชื่อส่วนหัว HTTP
พร็อกซีจะเข้าใจทั้งวิธี HTTP หรือส่วนหัวแรก แต่มันสามารถเข้าใจHost
ส่วนหัวซึ่งเป็นสิ่งที่จำเป็นเพื่อค้นหาแบ็กเอนด์
เพื่อให้สามารถใช้งานพร็อกซีได้จะต้องได้รับการออกแบบบนหลักการที่ว่าจะต้องเข้าใจ HTTP อย่างเพียงพอเพื่อค้นหาแบ็กเอนด์และเมื่อพบแบ็กเอนด์แล้วพร็อกซีก็จะผ่านสตรีมไบต์ดิบและปล่อยให้เลิกจริง ของการเชื่อมต่อ HTTP ที่ต้องทำโดยแบ็กเอนด์
มันอาจฟังดูเหมือนเป็นการยืดเวลาเพื่อสร้างสมมติฐานมากมายเกี่ยวกับพร็อกซี HTTP แต่ถ้าคุณยินดีที่จะติดตั้งซอฟต์แวร์ใหม่ที่พัฒนาขึ้นโดยมีวัตถุประสงค์เพื่อสนับสนุน SSH ข้อกำหนดของพร็อกซี HTTP นั้นจะไม่เลวร้ายนัก
ในกรณีของฉันเองฉันพบวิธีนี้เพื่อทำงานกับพร็อกซีที่ติดตั้งไว้แล้วโดยไม่มีการเปลี่ยนแปลงโค้ดการกำหนดค่าหรืออื่น ๆ และนี่คือสำหรับพร็อกซีที่เขียนด้วย HTTP เท่านั้นและไม่มี SSH ในใจ
หลักฐานของแนวคิดลูกค้าและพร็อกซี่ (ข้อจำกัดความรับผิดชอบว่าพร็อกซีเป็นบริการที่ดำเนินการโดยฉันโปรดแทนที่ลิงก์เมื่อพร็อกซีอื่นได้รับการยืนยันเพื่อรองรับการใช้งานนี้)
คำเตือนของแฮ็คนี้
- อย่าใช้มัน ดีกว่าที่จะใช้โซลูชันจริงซึ่งก็คือ IPv6
- หากพร็อกซีพยายามที่จะเข้าใจทราฟฟิก HTTP มันจะหยุดลงอย่างแน่นอน
- การใช้ไคลเอ็นต์ SSH ที่แก้ไขแล้วนั้นไม่ดี