คู่มือปฏิบัติ SSH Tunnel: Local และ Remote Port Forwarding
(labs.iximiuz.com)ภาพรวมโดยสรุป
- SSH tunnel เป็นเทคนิคสำหรับเชื่อมต่ออย่างปลอดภัยไปยังบริการที่ไม่สามารถเข้าถึงได้โดยตรงจากภายนอก หรือเปิดเผยบริการภายในออกสู่ภายนอกเป็นการชั่วคราว
- SSH port forwarding แบ่งหลัก ๆ ได้เป็น local forwarding, remote forwarding, dynamic local forwarding, dynamic remote forwarding
-Lให้ SSH client ฝั่งโลคัลเป็นผู้เปิดพอร์ต และ-Rให้ SSH server ฝั่งรีโมตเป็นผู้เปิดพอร์ต- หากใช้ Bastion Host จะเชื่อมต่อได้ไม่เพียงแค่ตัว SSH server เอง แต่ยังรวมถึงบริการบนเครือข่ายส่วนตัวอื่น ๆ ที่เซิร์ฟเวอร์นั้นเข้าถึงได้ด้วย
- Dynamic forwarding จะไม่ตรึงปลายทางไว้ล่วงหน้า แต่สร้าง SOCKS proxy เพื่อให้เข้าถึงหลายโฮสต์และหลายพอร์ตได้
- หากต้องการเปิดเผย remote forwarding ออกสู่เครือข่ายภายนอก ควรตรวจสอบการตั้งค่า
GatewayPortsของ SSH server
บทนำ
จุดประสงค์ของ SSH tunnel
-
SSH ไม่ได้มีไว้แค่รันคำสั่งระยะไกลเท่านั้น แต่ยังมีความสามารถแบบ tunnel สำหรับส่งต่อทราฟฟิกเครือข่ายแบบเข้ารหัสอีกด้วย
-
โดยไม่ต้องใช้ VPN แยกต่างหากหรือโปรแกรม proxy เฉพาะทาง ก็สามารถเข้าถึงเครือข่ายส่วนตัวและเผยแพร่บริการได้ด้วยคำสั่ง SSH มาตรฐานเพียงอย่างเดียว
-
ตัวอย่างการใช้งานที่พบบ่อยมีดังนี้
- เข้าถึงบริการภายใน VPC โดยอ้อมผ่าน public server
- เปิดพอร์ตโลคัลของ remote development server ผ่านเบราว์เซอร์บนเครื่องผู้ใช้
- เปิดเผยบริการในบ้านหรือในเครือข่ายส่วนตัวออกสู่ภายนอกชั่วคราว
- เชื่อมพอร์ตดีบักของเบราว์เซอร์ฝั่งโลคัลเข้ากับ remote development agent
-
เวลาทำความเข้าใจ SSH tunnel ควรแยกสองประเด็นนี้ออกจากกัน
- อุปกรณ์ใดเป็นผู้เปิดพอร์ตใหม่และรอฟังการเชื่อมต่อ
- ทราฟฟิกที่ผ่าน tunnel ไปแล้ว จะเชื่อมต่อถึงปลายทางจากมุมมองของอุปกรณ์ใด
เนื้อหา
Local forwarding เชื่อมบริการฝั่งรีโมตมาไว้บนเครื่องโลคัล
- Local port forwarding คือวิธีเชื่อมต่อเซิร์ฟเวอร์ระยะไกล หรือบริการที่เซิร์ฟเวอร์ระยะไกลเข้าถึงได้ เข้ากับพอร์ตโลคัลบนเครื่องผู้ใช้
- โครงสร้างคำสั่งพื้นฐานมีดังนี้
ssh -L [local_addr:]local_port:remote_addr:remote_port [user@]sshd_addr
-
SSH client จะรอรับการเชื่อมต่อที่
local_portบนเครื่องผู้ใช้ -
ทราฟฟิกที่เข้ามายังพอร์ตนั้นจะถูกส่งผ่าน SSH tunnel ไปยัง
remote_addr:remote_port -
การเชื่อมต่อไปยังปลายทางสุดท้ายจะเกิดขึ้นจากมุมมองของเครือข่ายที่ SSH server ตั้งอยู่
-
กรณีใช้งานหลักมีดังนี้
- เชื่อมต่อ MySQL, PostgreSQL, Redis ระยะไกล
- เข้าถึงเว็บแอปพลิเคชันที่เปิดให้ใช้งานเฉพาะในเครือข่ายส่วนตัว
- เข้าถึงพอร์ตของคอนเทนเนอร์ที่ไม่ได้เปิดบน external interface ของเซิร์ฟเวอร์
Bastion Host ทำหน้าที่เป็นตัวกลางในการเข้าถึงเครือข่ายส่วนตัว
-
remote_addrกับsshd_addrซึ่งเป็นปลายทางของการเชื่อมต่อ SSH ไม่จำเป็นต้องเป็นเครื่องเดียวกัน -
หาก SSH server เข้าถึงบริการภายในอื่น ๆ ได้ ก็สามารถใช้เซิร์ฟเวอร์นั้นเป็นจุดส่งต่อทราฟฟิกได้
-
เซิร์ฟเวอร์ตัวกลางลักษณะนี้เรียกว่า Bastion Host หรือ Jump Host
-
โครงสร้างการเชื่อมต่อทั่วไปมีดังนี้
- จากเครื่องผู้ใช้ SSH เข้า public Bastion Host
- Bastion Host ส่งต่อทราฟฟิกไปยังบริการภายใน VPC ที่มันเข้าถึงได้
-
ด้วยวิธีนี้จึงเข้าถึงฐานข้อมูล, search cluster, internal API และบริการอื่น ๆ ที่ไม่ได้เปิดเผยสู่ภายนอกได้
Remote forwarding เปิดเผยบริการฝั่งโลคัลไปยังฝั่งรีโมต
- Remote port forwarding คือวิธีเชื่อมบริการบนเครื่องผู้ใช้หรือในเครือข่ายภายใน เข้ากับพอร์ตบน external SSH server
- โครงสร้างคำสั่งพื้นฐานมีดังนี้
ssh -R [remote_addr:]remote_port:local_addr:local_port [user@]gateway_addr
-
Remote SSH server จะเปิด
remote_portและรอรับการเชื่อมต่อ -
ทราฟฟิกที่เข้ามายังพอร์ตนั้นจะถูกส่งผ่าน SSH tunnel ไปยัง
local_addr:local_portฝั่ง SSH client -
กรณีใช้งานหลักมีดังนี้
- เปิดเผย development server ที่รันอยู่บนโน้ตบุ๊กเพื่อใช้สาธิตจากภายนอก
- ทำให้เข้าถึงบริการ homelab ผ่านอินเทอร์เน็ตได้
- ให้พอร์ตดีบักของเบราว์เซอร์ฝั่งโลคัลใช้งานได้ในสภาพแวดล้อมพัฒนาระยะไกล
การเปิดเผยสู่ภายนอกต้องมีการตั้งค่า GatewayPorts
- ในการตั้งค่าเริ่มต้น พอร์ตของ remote forwarding อาจ bind ได้แค่กับ
localhostของ SSH server เท่านั้น - ในกรณีนี้ บริการนั้นจะเข้าถึงได้จากภายใน remote server แต่จะเข้าถึงจากภายนอกไม่ได้
- หากต้องการให้เชื่อมต่อผ่าน public IP ของ remote server ได้ ควรตรวจสอบการตั้งค่าต่อไปนี้ใน
sshd_config
GatewayPorts yes
- การตั้งค่านี้อาจทำให้พอร์ตถูกเปิดบน external interface ได้ จึงควรใช้ firewall และ access control ร่วมกัน
- หากเปิดบริการพัฒนาออกสู่อินเทอร์เน็ตสาธารณะโดยตรง ความเสี่ยงจากการเข้าถึงโดยไม่ได้รับอนุญาตหรือการโจมตีอาจเพิ่มขึ้น
เครื่องโลคัลสามารถเป็น Jump Host ของเครือข่ายส่วนตัวได้
-
ปลายทางของ remote forwarding ไม่ได้จำกัดอยู่แค่เครื่องที่รัน SSH client เท่านั้น
-
หากเครื่องผู้ใช้เข้าถึงเซิร์ฟเวอร์อื่นในเครือข่ายบ้านได้ ก็สามารถเปิดเผยพอร์ตของเซิร์ฟเวอร์ภายในนั้นผ่าน remote SSH server ได้เช่นกัน
-
ในกรณีนี้ เครื่องผู้ใช้จะกลายเป็นอุปกรณ์ตัวกลางที่เชื่อมสองเครือข่ายต่อไปนี้เข้าด้วยกัน
- เครือข่ายภายในบ้าน หรือเครือข่ายพัฒนาส่วนตัว
- public SSH gateway server
-
วิธีนี้ช่วยให้เข้าถึงบริการของ home server หรือ internal development server ที่ไม่ได้เชื่อมต่ออินเทอร์เน็ตโดยตรงได้จากภายนอก
Dynamic local forwarding สร้าง SOCKS proxy ฝั่งโลคัล
- Local forwarding แบบทั่วไปจะตรึงพอร์ตโลคัลหนึ่งพอร์ตไว้กับปลายทางหนึ่งแห่ง
- Dynamic local forwarding จะสร้าง SOCKS proxy บน SSH client
- โครงสร้างคำสั่งพื้นฐานมีดังนี้
ssh -D [local_addr:]local_port [user@]sshd_addr
-
SOCKS proxy จะทำงานที่พอร์ตที่กำหนดบนเครื่องผู้ใช้
-
แอปพลิเคชันที่รองรับ SOCKS สามารถระบุที่อยู่และพอร์ตปลายทางต่างกันได้ในแต่ละครั้งที่เชื่อมต่อ
-
การเชื่อมต่อไปยังปลายทางจริงจะเกิดขึ้นจากมุมมองของเครือข่ายฝั่ง remote SSH server
-
จุดต่างจาก local forwarding แบบตรึงปลายทางคือ สามารถใช้การเชื่อมต่อ SSH เดียวเพื่อเข้าถึงหลายบริการในเครือข่ายส่วนตัวฝั่งรีโมตได้
-
กรณีใช้งานหลักมีดังนี้
- เรียกใช้ internal API หลายตัวผ่าน Bastion Host
- สำรวจเว็บแอปพลิเคชันภายในเครือข่ายส่วนตัว
- เข้าถึง VPC endpoint หลายจุดผ่าน EC2 instance เดียว
Dynamic remote forwarding สร้าง SOCKS proxy ฝั่งรีโมต
- หากละปลายทางแบบคงที่ในตัวเลือก
-Rก็จะสามารถสร้าง SOCKS proxy บน remote SSH server ได้ - โครงสร้างคำสั่งพื้นฐานมีดังนี้
ssh -R [bind_address:]port [user@]gateway_addr
- Remote gateway server จะรอรับการเชื่อมต่อ SOCKS ที่พอร์ตที่กำหนด
- คำขอที่เข้ามาทาง proxy จะถูกส่งผ่าน SSH tunnel กลับมายังฝั่ง client
- ปลายทางสุดท้ายจะถูกเชื่อมต่อจากมุมมองของเครือข่ายที่ SSH client ตั้งอยู่
- ด้วยวิธีนี้ remote server จะเข้าถึงเครือข่ายบ้านหรือเครือข่ายส่วนตัวทั้งหมดที่เครื่องผู้ใช้เข้าถึงได้
- Dynamic remote forwarding ต้องใช้ OpenSSH 7.6 ขึ้นไปบนฝั่ง client
- หากต้องการเปิดเผย remote SOCKS proxy ออกสู่ external interface ก็ต้องใช้การตั้งค่า
GatewayPortsเช่นเดียวกับ remote forwarding ทั่วไป
ตัวเลือกสำหรับรันเบื้องหลังจะคงไว้เฉพาะ tunnel
- หากต้องการคงไว้เฉพาะ port forwarding โดยไม่รันคำสั่งใดหลังเชื่อมต่อ SSH ให้ใช้ตัวเลือก
-N - หากต้องการส่ง tunnel ไปทำงานเบื้องหลัง สามารถใช้ตัวเลือก
-fร่วมกันได้ - ตัวอย่างตามประเภทมีดังนี้
ssh -f -N -L ...
ssh -f -N -R ...
ssh -f -N -D ...
- ในงานอัตโนมัติหรือสภาพแวดล้อมที่ต้องรันยาวนาน ควรเตรียมแนวทางสำหรับการปิด tunnel, การเชื่อมต่อใหม่, และการตรวจจับความผิดปกติแยกไว้ด้วย
แยกประเภทคำสั่งได้จากตำแหน่งที่เปิดพอร์ต
ssh -Lให้ local SSH client เป็นผู้เปิดพอร์ตใหม่ssh -Rให้ remote SSH server เป็นผู้เปิดพอร์ตใหม่ssh -Dสร้าง SOCKS proxy บน local SSH clientssh -Rที่ละปลายทางไว้จะสร้าง SOCKS proxy บน remote SSH server- กฎพื้นฐานสำหรับจดจำคำสั่งมีดังนี้
-L = local:remote
-R = remote:local
- ที่อยู่และพอร์ตทางซ้ายของเครื่องหมายโคลอนคือจุด listen ที่ถูกเปิดขึ้นใหม่
localอาจหมายถึงตัว SSH client เอง หรือเครื่องภายในที่ client เข้าถึงได้remoteอาจหมายถึงตัว SSH server เอง หรือเครื่องภายในอื่นที่ server เข้าถึงได้
บทสรุป
เลือกใช้ SSH tunnel ตามทิศทางการเข้าถึง
-
หากต้องการใช้บริการฝั่งรีโมตบนเครื่องของตนเอง ให้ใช้ local forwarding
-L -
หากต้องการเปิดเผยเครื่องของตนเองหรือบริการภายในไปยังฝั่ง remote server ให้ใช้ remote forwarding
-R -
หากต้องการเข้าถึงหลายปลายทางในเครือข่ายระยะไกลแบบยืดหยุ่น ให้ใช้ dynamic local forwarding
-D -
หากต้องการให้ remote gateway เข้าถึงทั้งเครือข่ายฝั่ง client ได้อย่างยืดหยุ่น ให้ใช้ dynamic remote forwarding
-Rแบบไม่ระบุปลายทาง -
เมื่อต้องออกแบบ SSH tunnel ควรตรวจสอบสิ่งต่อไปนี้ก่อนเป็นอันดับแรก
- ตำแหน่งที่ต้องเปิดพอร์ตใหม่
- อุปกรณ์ที่สามารถเข้าถึงปลายทางสุดท้ายได้
- การตั้งค่า
GatewayPorts, firewall, และสิทธิ์เข้าถึง SSH - ความเสี่ยงด้านความปลอดภัยที่อาจเกิดจากการเปิดเผยสู่ภายนอก
-
เกณฑ์สำคัญที่สุดในการตัดสินใจคือ ต้องการให้บริการมองเห็นจากฝั่งไหน และอุปกรณ์ใดที่สามารถเข้าถึงปลายทางจริงได้
ยังไม่มีความคิดเห็น