3 คะแนน โดย GN⁺ 2023-12-25 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • แม้พอร์ต SSH จะถูกบล็อกในเครือข่ายที่มีข้อจำกัด ก็สามารถสร้างเส้นทางเชื่อมต่อระยะไกลได้ด้วย พอร์ต HTTPS 443 และเมธอด CONNECT
  • ฝั่งเซิร์ฟเวอร์เปิด CONNECT ด้วย mod_proxy_connect ของ apache2 แต่จำกัดขอบเขตพร็อกซีให้แคบลงโดยอนุญาตเฉพาะเป้าหมาย ssh-server:22
  • ฝั่งไคลเอนต์เชื่อม ProxyCommand ของ OpenSSH เข้ากับ socat และในกรณี HTTPS สคริปต์จะส่งเฮดเดอร์ CONNECT โดยตรง
  • ไฟร์วอลล์หรือ httpd บางตัวอาจปิดการเชื่อมต่อที่ไม่ได้ใช้งาน จึงใช้ ServerAliveInterval 30 เพื่อช่วย รักษาเซสชัน
  • วิธีนี้ห่อหุ้ม SSH ไว้ใน TLS ทำให้หลบเลี่ยงการบล็อกได้ง่ายขึ้น แต่ยังมีภาระเรื่อง การเข้ารหัสซ้อนสองชั้น และการตั้งค่าฝั่งไคลเอนต์

การสร้างเส้นทาง SSH ในเครือข่ายที่ถูกจำกัด

  • ในสภาพแวดล้อม Wi-Fi ของโรงพยาบาล การเชื่อมต่อส่วนใหญ่ถูกบล็อก และโดยหลักแล้วอนุญาตเฉพาะ TCP 80 ของ HTTP และ TCP 443 ของ HTTPS
  • UDP 53 ของ DNS และ TCP 853 ของ DoT ดูเหมือนจะใช้งานได้กับผู้ให้บริการ DNS ที่เป็นที่รู้จัก
  • SSH ถูกบล็อกทั้งหมดทั้งบน TCP 22 และพอร์ตแบบกำหนดเองส่วนใหญ่
  • มีการเชื่อมต่อสำรองผ่าน GSM ที่สามารถใช้ตั้งค่าเซิร์ฟเวอร์ระยะไกลใหม่ได้ จึงสามารถทดสอบวิธีเลี่ยงข้อจำกัดหลายแบบได้

การใช้พอร์ตร่วมกันและการห่อหุ้มโปรโตคอล

  • มีวิธีรับ SSH และ HTTPS พร้อมกันบนพอร์ตเดียว แล้วแยกเส้นทางอย่างโปร่งใส
    • โปรเจกต์ sslh ใช้ฮิวริสติกในการคาดเดาโปรโตคอล แล้วรีไดเรกต์ไปยังแบ็กเอนด์จริง เช่น SSH, HTTPS หรือโปรโตคอลที่รองรับ
    • ข้อดีคือไม่ต้องตั้งค่า ssh ไคลเอนต์เป็นพิเศษ เพียงระบุพอร์ตที่ไม่ใช่พอร์ตเริ่มต้นก็พอ
    • ข้อเสียคือต้องตั้งค่าบริการเพิ่มเติม, HTTPS ก็ต้องผ่าน sslh ด้วย และที่แบ็กเอนด์ ที่อยู่ต้นทาง ของการเชื่อมต่ออาจถูกซ่อนไว้ ทำให้การบันทึกล็อกไม่สะดวก
  • อีกทางเลือกหนึ่งคือการ ห่อหุ้ม โปรโตคอลหนึ่งไว้ภายในอีกโปรโตคอลหนึ่งอย่างสมบูรณ์
    • ProxyCommand ของ OpenSSH ช่วยให้ผู้ใช้กำหนดตัวกลางส่งข้อมูลที่โปรโตคอล ssh จะใช้ได้
    • เพราะห่อ SSH ไว้ในสตรีมอื่นโดยไม่ต้องใช้ฮิวริสติก จึงอาจทำให้ซอฟต์แวร์ DPI บล็อกได้ยากขึ้น
    • HTTPS เองจะไม่ถูกรีไดเรกต์หรือได้รับผลกระทบ
    • แต่ประสิทธิภาพอาจลดลงจาก การเข้ารหัสซ้อนสองชั้น และต้องมีการตั้งค่าฝั่งไคลเอนต์

การตั้งค่า SSH over HTTP

  • เริ่มจากทดลองวิธีส่ง SSH ผ่าน HTTP
  • การตั้งค่า apache2 ฝั่งเซิร์ฟเวอร์โหลด mod_proxy_connect และใช้ AllowCONNECT 22 เพื่ออนุญาต CONNECT ไปยังพอร์ต 22
  • <Proxy *> จะปฏิเสธทั้งหมดโดยค่าเริ่มต้น และอนุญาตเฉพาะ <Proxy ssh-server> เพื่อจำกัดเป้าหมายไว้ที่ ssh-server:22
  • .ssh/config ฝั่งไคลเอนต์ใช้ PROXY:http-server:%h:%p,proxyport=80 ของ socat ใน ProxyCommand
  • ด้วยการตั้งค่านี้ เมื่อรัน $ ssh ssh-via-http จะสามารถเชื่อมต่อ SSH ผ่าน พอร์ต 80 ได้
  • ServerAliveInterval 30 จะส่งโพรบเป็นระยะ เพื่อหลีกเลี่ยงกรณีที่การเชื่อมต่อ HTTP ที่ไม่ได้ใช้งานถูกปิดเงียบ ๆ ระหว่างทาง
  • httpd ค่าเริ่มต้นใช้ TimeOut 60 ซึ่งจะปิดทันเนลเมื่อไม่มีอินพุต/เอาต์พุตต่อเนื่องเป็นเวลา 60 วินาที

การตั้งค่า SSH over HTTPS

  • พบว่า socat ไม่รองรับพร็อกซี HTTPS ผ่านเมธอด CONNECT และรองรับเฉพาะ HTTP
  • จึงใช้ความสามารถ TLS encapsulation ของ socat แทน และให้สคริปต์เป็นผู้สร้างคำขอแบบ CONNECT โดยตรง
  • ~/.ssh/https-tunnel.bash ทำงานตามลำดับดังนี้
    • พิมพ์เฮดเดอร์ CONNECT ssh-server:22 HTTP/1.1 และ Host: ssh-server ก่อน
    • จากนั้นต่อข้อมูลจาก standard input เข้าไปตามเดิม
    • ส่งสตรีมทั้งหมดไปยัง socat - "SSL:$3:$4" เพื่อสร้าง การเชื่อมต่อ TLS
  • ใน .ssh/config กำหนด ProxyCommand ~/.ssh/https-tunnel.bash สำหรับ Host ssh-via-https
  • เมื่อรัน $ ssh ssh-via-https ก็จะเชื่อมต่อไปยัง ssh-server ผ่านทันเนล HTTPS ได้ตามที่คาดไว้

ข้อควรระวังในการใช้งานจริง

  • ในสภาพแวดล้อมที่อนุญาต HTTPS อย่างแพร่หลาย วิธีนี้สามารถส่งข้อมูลผ่านอุปกรณ์กลางที่มีข้อจำกัดเข้มงวดมากได้
  • เมธอด CONNECT อาจดูเหมือนร่องรอยจากยุคเก่า แต่มีประโยชน์ในการห่อ สตรีมเพย์โหลด TCP ใด ๆ ไว้ในสตรีมของโฮสต์ TLS
  • ServerAliveInterval ช่วยรักษาการเชื่อมต่อ OpenSSH เมื่อชั้นขนส่งด้านล่างไม่เป็นมิตรกับการเชื่อมต่อที่ไม่ได้ใช้งาน
  • ตัวอย่างการตั้งค่าแบบ nginx อยู่ที่ CONNECT passthrough on nginx

1 ความคิดเห็น

 
GN⁺ 2023-12-25
ความคิดเห็นจาก Hacker News
  • ประเด็นในบทความที่ว่า “HTTPS มีอยู่ทุกที่ จึงทำให้อุปกรณ์ตัวกลางที่จำกัดมาก ๆ ก็ยังปล่อยให้ข้อมูลผ่านได้” นั่นแหละคือเหตุผลที่แทบทุกโปรโตคอล VPN แบบ proprietary หรือที่เรียกกันว่า “SSL VPN” ต่างก็มีโหมดที่เปิด tunnel ผ่าน HTTPS
    แม้จะไม่ใช่พฤติกรรมเริ่มต้น อย่างน้อยก็จะมีไว้เป็นเส้นทางสำรอง โดยมีเป้าหมายเพื่อให้มีโหมดที่ใช้งานได้กับแทบทุกการเชื่อมต่อบนอินเทอร์เน็ตทั่วโลก
    ผมเป็นหนึ่งในนักพัฒนาหลักของ https://gitlab.com/openconnect/openconnect ซึ่ง implement โปรโตคอล VPN ที่อิง TLS หลายแบบ และยังสร้าง https://github.com/dlenski/what-vpn สำหรับตรวจจับและระบุชนิดของเซิร์ฟเวอร์ VPN ที่อิง TLS ได้มากขึ้นด้วย

    • น่าเสียดายที่ความพยายามในการจำกัดประโยชน์ของการเชื่อมต่อแบบหยาบ ๆ ทำให้ใช้งานพอร์ตตามวัตถุประสงค์ดั้งเดิมของพอร์ตได้ยากขึ้น
  • เมื่อก่อนผู้ให้บริการอินเทอร์เน็ตในเนเธอร์แลนด์ชื่อ XS4ALL เคยให้ฟีเจอร์แบบนี้พอดี
    เขาเปิดให้เชื่อมต่อ SSH ทางพอร์ต 80 และเคยช่วยได้หลายครั้งตอนเดินทางแล้ว WiFi โรงแรมบล็อกทุกอย่างยกเว้นพอร์ต 80

    • XS4ALL เยี่ยมมาก และน่าเสียดายจริง ๆ ที่ KPN ตัดสินใจยุบแบรนด์นั้น
      แต่ในมุมของ KPN ก็คงไม่ค่อยสบายใจกับวัฒนธรรมแฮกเกอร์ของ XS4ALL มาตั้งแต่แรกแล้ว
    • ผมเคยเป็นผู้ใช้ XS4ALL มันยอดเยี่ยมจริง ๆ และยังมีจิตวิญญาณอินเทอร์เน็ตยุคแรกอยู่
      ให้ความรู้สึกเหมือนการกลับมาของวัฒนธรรมวิทยุโจรสลัดในทศวรรษ 1960
      https://en.wikipedia.org/wiki/Pirate_radio_in_Europe
    • ในบรรดาพอร์ตที่ไม่ใช่มาตรฐานของ SSH นั้น พอร์ต 443 ถือว่าอยู่ในอันดับต้น ๆ
      https://www.shodan.io/search/facet?query=ssh&facet=port
      https://www.shodan.io/search/facet.png?query=ssh&facet=port
      ขณะที่พอร์ต 80 พบได้น้อยกว่ามาก
    • ไม่รู้มาก่อนว่า XS4ALL เป็นผู้ให้บริการอินเทอร์เน็ตแบบเต็มรูปแบบ
      จำได้ว่าเมื่อก่อนเคยใช้แค่ในฐานะผู้ให้บริการ newsgroup
    • XS4ALL ยังคงสืบต่อมาในรูปแบบของ Freedom อยู่บ้าง: https://freedom.nl/en
  • มักมีตัวเลือกที่สามด้วย: เปิด SSH ไว้บนพอร์ต 80 หรือ 443 ของโฮสต์อื่น แล้วใช้ ProxyJump จากตรงนั้นไปยังปลายทาง
    หรือจะเปิด SOCKS ไปยังโฮสต์นั้น เพื่อให้ได้การเชื่อมต่ออินเทอร์เน็ตที่โดยรวมถูกกรองน้อยลงก็ได้
    ผมใช้ SOCKS และส่ง DNS ที่อิง TLS ผ่าน port forwarding ของการเชื่อมต่อ SSH ด้วย
    เมื่อหลายปีก่อน ตอนตั้งค่า SOCKS proxy ครั้งแรกและใช้ WiFi ค่อนข้างมาก ผมยังไม่เคยเจอที่ไหนตรวจเกินกว่าระดับดูพอร์ต แต่เครื่องมือแบบนั้นมีอยู่จริง และตอนนี้อาจพบได้บ่อยขึ้นแล้ว

    • ตอน ม.4 ผมเพิ่งเริ่ม self-hosting โรงเรียนบล็อกเว็บไซต์ แต่ไม่ได้ทำการบล็อกพอร์ตเลย
      ดังนั้นผมก็แค่ SSH เข้าเซิร์ฟเวอร์ของตัวเองแล้วใช้งานตามปกติ
      ต่อมาผมทำ archive ไฟล์ Windows .iso แล้วดาวน์โหลดลงแล็ปท็อปและอัปโหลดขึ้นเซิร์ฟเวอร์ด้วย scp ปรากฏว่าการใช้ทราฟฟิกรวมอัปโหลด/ดาวน์โหลดหลายสิบ GB บนพอร์ตที่ไม่ใช่ 80/443 คงมากพอให้เกิดการตรวจสอบทราฟฟิก ช่วงเที่ยงฝ่าย IT เลยบล็อกพอร์ต 22
      แต่เพราะเขาไม่ได้บล็อกพอร์ตอื่น ผมจึงย้าย SSH ไปที่ 443 ด้วย port forwarding แล้วใช้ต่อ
      หลังจากนั้นฝ่าย IT ของโรงเรียนจับได้ว่ามีการใช้ SSH บน 443 จึงใส่การวิเคราะห์ทราฟฟิกพื้นฐานเพื่อบล็อก SSH บน 80/443 แต่พอร์ตอื่นทั้งหมดก็ยังเปิดอยู่
      สุดท้ายพวกเขาบล็อกเซิร์ฟเวอร์ของผมตาม IP แต่ IP อื่นทั้งหมดยังเปิดอยู่
      ถ้าใช้ ProxyJump ผ่าน VPS ก็น่าจะเลี่ยงได้ แต่เพราะเป็นหลักสูตรมัธยมปลายแบบ early college หลังขึ้นปี 2 ผมแทบไม่ได้ไปโรงเรียนแล้ว เลยไม่ค่อยคุ้มที่จะลงแรง
      ครั้งหน้าที่ไป ผมคิดว่าจะลองทำเพื่อพิสูจน์ว่าทำได้ และถ้าบล็อก SSH ทุกพอร์ต ก็แค่ตั้ง SSH over HTTPS ไว้บน VPS
      หลังเรียนจบ ผมคิดว่าจะกลับไปดูอีกทีว่า guest WiFi ทำอะไรได้ถึงไหน
  • อาจเป็นการโปรโมตที่ค่อนข้างตรงไปหน่อย แต่ที่ Adaptive [1] เรากำลังสร้างโครงสร้างพื้นฐานความปลอดภัยของข้อมูลอยู่
    หนึ่งในผลิตภัณฑ์ของเราส่ง SSH และโปรโตคอลอื่น ๆ หลายแบบผ่าน HTTP/3 ทำให้ผู้ใช้เชื่อมต่อกับฐานข้อมูล เซิร์ฟเวอร์ และทรัพยากรอื่น ๆ ได้ผ่าน outbound port เดียว
    คล้าย ๆ พวก Ngrok แต่ self-host ได้ และสามารถเข้าถึงแบบไม่ใช้รหัสผ่าน หรือใส่ credential ชั่วคราวกับการป้องกันแบบ maker-checker ได้
    [1] https://adaptive.live/

  • ในทางปฏิบัติ แค่ให้ openssh listen ที่พอร์ต 443 ก็สามารถเลี่ยง firewall ส่วนใหญ่ได้แล้ว

  • ดีเลย แปลกที่ผมไม่เคยคิดถึงเมธอด CONNECT ในแบบreverse proxy แทนที่จะเป็น forward proxy มาก่อน
    แต่ในกรณีของผม CONNECT อย่างเดียวไม่พอ จึงใช้ SSH บน WebSocket เพื่อทะลุ proxy ของบริษัท
    proxy นั้นตรวจสอบการเชื่อมต่อ HTTPS ด้วย CA ที่กำหนดเอง
    ผมแก้ socat เพื่อให้บริการเซิร์ฟเวอร์ SSH ของผมผ่าน Apache เป็น WebSocket และฝั่ง client ใช้ร่วมกับ ProxyCommand ของ OpenSSH
    คิดอยู่ว่าควรอัปโหลด patch นั้นสักที แต่ก็มีตัวเลือกอื่นอย่าง websocat ด้วย

    • ดังนั้นดูเหมือนว่า proxy หรือ firewall ของบริษัทจำนวนมากจะบล็อก WebSocket เป็นค่าเริ่มต้น
    • ฟังดูเหมือนสร้าง huproxy ขึ้นมาใหม่โดยไม่ได้ตั้งใจ
  • เกือบ 20 ปีก่อน ผมเคยใช้เครื่องมือ corkscrew[0] ที่ทำสิ่งนี้พอดี เพื่อเจาะรูผ่านไฟร์วอลล์ของบริษัท
    ทั้งการทำเป็นโปรแกรมเดี่ยวและบทความเขียนไว้เรียบร้อยดี
    0: https://github.com/bryanpkc/corkscrew

    • เท่าที่ผมเข้าใจ corkscrew ใช้ได้เฉพาะภายใต้เงื่อนไขบางอย่าง: ต้องอยู่หลัง HTTP proxy และ HTTP proxy นั้นต้องรองรับเมธอด CONNECT
      ตอนที่ผมรับงานสัญญาระยะสั้นเมื่อราว 20 ปีก่อน มีเงื่อนไขแรก แต่ไม่มีเงื่อนไขที่สอง
      โชคดีที่มีเครื่องมือที่ใช้ได้แม้ในกรณีแบบนี้ และแน่นอนว่าต้องตั้งค่าฝั่งเซิร์ฟเวอร์ด้วย
      https://github.com/larsbrinkhoff/httptunnel
  • โดยรวมแล้ว การทำ tunneling ผ่าน HTTP/2 พิสูจน์แล้วว่าเป็นตัวเลือกที่ดี
    มี gRPC[1] ซึ่งเป็นโปรโตคอล remote procedure call ที่สร้างอยู่บน HTTP/2
    เพราะ HTTP/2 เด่นเรื่อง multiplexing ที่ใช้การเชื่อมต่อ TCP เดียวเพื่อรับส่งโครงสร้างข้อมูลหลายชุดพร้อมกัน
    อย่างไรก็ตาม QUIC เองก็มี multiplexing อยู่แล้ว จึงอาจไม่มีเหตุผลต้องใช้ HTTP/3 ก็ได้
    ต่อไปการสื่อสารส่วนใหญ่น่าจะย้ายไปอยู่บน HTTP/2 และ QUIC ที่เข้ารหัส เพราะผู้ผลิตอุปกรณ์ตัวกลางคงเลิกเลือกปฏิบัติไม่ได้
    เพื่อบรรเทา active probing[2] อาจต้องเสิร์ฟคอนเทนต์ HTTP/2·HTTP/3 แบบสุ่ม หรืออาจเป็นคอนเทนต์ที่ AI สร้างขึ้น
    [1] <https://grpc.io>
    [2] <https://blog.torproject.org/learning-more-about-gfws-active-...>

    • ผมไม่ค่อยเข้าใจว่าทำไมต้องวางโปรโตคอล remote procedure call อีกชั้นบน HTTP
      ตัว HTTP เองก็เป็นโปรโตคอล request/response อยู่แล้ว และโดยพื้นฐานก็ใช้ทำ remote procedure call ได้
      ไม่ว่าจะเป็น HTTP 1, 2 หรือ 3 ก็ดูไม่ต่างกันมาก
      วิวัฒนาการของโปรโตคอลนั้นเองก็น่าสงสัยอยู่บ้าง และมีส่วนที่ออกแบบมาให้ใช้ประโยชน์จากสิ่งที่ไม่จำเป็นในสภาพแวดล้อม remote procedure call
      โดยแก่นแล้วมันเหมือนถูกออกแบบมาเพื่อ อินเทอร์เน็ตสาธารณะ มากกว่าบริการภายในเครื่อง
    • ในกรณีของ SSH มีแค่การเชื่อมต่อเดียว และจริง ๆ แล้ว SSH ก็ทำ multiplexing เองอยู่แล้ว จึงไม่ค่อยเห็นข้อดีของ HTTP/2
    • HTTP/2 ยังเป็น TCP อยู่ จึงเจอ TCP head-of-line blocking
      HTTP/3 ทำงานบน QUIC
  • ส่วนผมกลับชอบ HTTP บน SSH มากกว่า
    พูดกึ่งล้อเล่นนะ แต่ถ้าเบราว์เซอร์มีการจัดการตัวตนระดับเดียวกับ SSH ในตัวก็คงดี
    ตอนอ่าน hobo ซึ่งเป็นข้อเสนอการยืนยันตัวตน HTTP ด้วยกุญแจสาธารณะครั้งแรก ผมตื่นเต้นมาก แต่ก็พบว่าไม่มีทั้ง implementation ฝั่งเซิร์ฟเวอร์และ implementation ฝั่งไคลเอนต์เบราว์เซอร์
    มี implementation แบบ JavaScript อยู่ แต่ไม่ใช่รูปแบบที่ผมต้องการ

    • ssh -D “*:8080” host
      คำสั่งนี้จะเปิด SOCKS proxy ที่พอร์ต 8080
      ผมใช้ใน Firefox ตลอด และมันทำงานเหมือน VPN รุ่นดั้งเดิม
      ใช้กับเรื่องน่าสงสัยก็สะดวก แต่ผมก็ใช้ตอนเข้าถึงแดชบอร์ด rmq ที่อยู่ในเครือข่ายส่วนตัวของ AWS ด้วย
    • แล้ว HTTPS client certificate ล่ะ?
      https://techcommunity.microsoft.com/t5/iis-support-blog/clie...
      ผมไม่รู้ละเอียดพอว่าตรงกับข้อกำหนดหรือไม่ แต่เคยใช้ยืนยันตัวตนกับเซิร์ฟเวอร์ตอนเรียนมหาวิทยาลัย
    • ที่พูดถึงนี่คือ passkey ใช่ไหม?
    • ถ้าในเบราว์เซอร์ใช้ ssh://google.com ได้ก็คงดี
    • ถ้าพลาดไปเมื่อสัปดาห์ก่อน มีบทความทิศทางคล้ายกัน: SSH3, SSHv2 ที่ใช้ HTTP/3 และ QUIC
      https://news.ycombinator.com/item?id=38664729
  • ทำไม sslh ถึงไม่ได้รับความรักบ้างนะ?
    มันตรวจจับ HTTP, TLS/SSL (รวม SNI และ ALPN), SSH, OpenVPN, tinc, XMPP, SOCKS5 ได้ และยังรู้จักโปรโตคอลอื่น ๆ ที่ตรวจด้วย regex ได้ด้วย
    การใช้งานทั่วไปคือให้บริการหลายอย่างร่วมกันบนพอร์ต 443
    เช่น สามารถเชื่อมต่อ SSH จากหลังไฟร์วอลล์บริษัทได้ ขณะเดียวกันก็ยังให้บริการ HTTPS บนพอร์ตเดียวกันต่อไป
    https://www.rutschle.net/tech/sslh/README.html

    • ครึ่งแรกของบทความเป็นเรื่องว่าจะเลือก sslh หรือ การ encapsulation แบบสมบูรณ์
      เหตุผลที่เลือก encapsulation แบบสมบูรณ์ดูเหมือนจะเป็นเพราะมีบริการให้ตั้งค่าน้อยลงหนึ่งตัว, HTTP server เป็นตัวจัดการการเชื่อมต่อจึงได้ที่อยู่รีโมตในล็อกอย่างถูกต้อง, และอาจเพราะตามจิตวิญญาณแฮ็กเกอร์แล้วชอบวิธีใหม่มากกว่าวิธีเดิม