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

สองรูปแบบ จุดนัดพบเดียว

ในรูปแบบแรก ไคลเอนต์เปิดเซสชัน SSH ออกไปและขอให้เซิร์ฟเวอร์ฟังบนพอร์ตที่เลือก โดยส่งทุกการเชื่อมต่อกลับมาผ่านเซสชันนั้น บนเซิร์ฟเวอร์ไม่มีการส่งต่อใด ๆ และไม่มีการแปลแพ็กเก็ต ดังนั้นข้อกำหนดเดียวคือบัญชี SSH การส่งต่อที่ได้รับอนุญาต และกฎไฟร์วอลล์สำหรับพอร์ตนั้น บริการยังคงผูกกับลูปแบ็กบนไคลเอนต์ ซึ่งโดยปกติแล้วเป็นสิ่งที่คุณต้องการ

ssh -N -R 8081:127.0.0.1:8080 -o ExitOnForwardFailure=yes -o ServerAliveInterval=30 [email protected]

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

ไม่มีรูปแบบใดปลอดภัยกว่าโดยธรรมชาติ ทั้งสองแบบเปิดพอร์ตบริการหนึ่งพอร์ตให้ทุกคนที่เข้าถึงที่อยู่สาธารณะของเซิร์ฟเวอร์ได้ และทั้งสองแบบยังพึ่งการพิสูจน์ตัวตนของบริการเอง

เงื่อนไขแรก: พอร์ตต้องมีอายุยืนกว่าเซสชันหรือไม่

การส่งต่อย้อนกลับมีอายุเท่ากับเซสชัน SSH ที่สร้างมันขึ้นมาเท่านั้น เมื่อลิงก์ขาด ตัวฟังจะหายไป และเพื่อนร่วมงานเห็นการเชื่อมต่อถูกปฏิเสธแทนเส้นทางที่ช้า การกำกับคำสั่งด้วยยูนิต systemd และนโยบายรีสตาร์ตแก้ปัญหานี้ได้ และ ExitOnForwardFailure=yes ทำให้การผูกที่ล้มเหลวปรากฏทันที แทนที่จะปล่อยเซสชันที่ทำงานโดยไม่มีการส่งต่อ

พอร์ตที่แปลแล้วจะรอดจากการรีบูตหากกฎถูกบันทึกไว้ แต่ต้องอาศัยไคลเอนต์รักษาที่อยู่ของอุโมงค์เดิมไว้ พูลแบบไดนามิกที่ให้ที่อยู่ใหม่กับแล็ปท็อปหลังเชื่อมต่อใหม่จะชี้กฎไปที่ความว่างเปล่าอย่างเงียบ ๆ จองที่อยู่ของอุโมงค์ก่อน จากนั้นเขียนกฎ และถือว่าทั้งสองอย่างเป็นการเปลี่ยนแปลงเดียวกัน

หากบริการต้องตอบตอนตีสามโดยไม่มีใครเฝ้าดู รูปแบบที่เซิร์ฟเวอร์ประกาศพอร์ตมีชิ้นส่วนที่เคลื่อนไหวน้อยกว่า เพราะโพรเซสเดียวที่ต้องคงอยู่คืออุโมงค์เอง

เงื่อนไขที่สอง: ไฟร์วอลล์และบันทึกเป็นของใคร

รูปแบบย้อนกลับเก็บนโยบายทั้งหมดไว้สองที่ sshd ตัดสินผ่าน AllowTcpForwarding ว่าอนุญาตให้ส่งต่อระยะไกลหรือไม่เลย และ GatewayPorts ตัดสินว่าตัวฟังผูกกับลูปแบ็กหรือทุกที่อยู่ ด้วยค่าเริ่มต้น no เพื่อนร่วมงานนอกเซิร์ฟเวอร์เข้าถึงพอร์ตไม่ได้แม้การส่งต่อจะสำเร็จ และดูเหมือนอุโมงค์เสียทั้งที่จริงคือที่อยู่ผูก

sudo ss -ltn '( sport = :8081 )'
sudo grep -E '^(GatewayPorts|AllowTcpForwarding)' /etc/ssh/sshd_config

รูปแบบที่แปลแล้วต้องเปิดใช้การส่งต่อและมีสองกฎ และกฎข้อที่สองคือจุดที่สูตรส่วนใหญ่ผิดพลาด การจำกัดการแปลต้นทางด้วยสถานะ conntrack DNAT หมายความว่าเฉพาะการเชื่อมต่อที่กฎปลายทางสร้างขึ้นเท่านั้นที่ถูกเขียนใหม่ ไม่ใช่ทุกแพ็กเก็ตที่เซิร์ฟเวอร์ส่งเข้าอุโมงค์

sudo sysctl -w net.ipv4.ip_forward=1
sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 8081 -j DNAT --to-destination 10.88.0.6:8080
sudo iptables -t nat -A POSTROUTING -o tun0 -m conntrack --ctstate DNAT -j SNAT --to-source 10.88.0.1

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

เงื่อนไขที่สาม: พอร์ตเดียวหรือหลายพอร์ต และชื่อเป็นของใคร

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

ชื่อทำให้ซับซ้อนขึ้นอีก การส่งต่อย้อนกลับสามารถอยู่หลังพร็อกซี TCP ที่กำหนดเส้นทางตาม SNI ขณะที่พอร์ตที่แปลแล้วมักเป็นหนึ่งพอร์ตต่อบริการ หากบริการสิ้นสุด TLS เองและต้องการชื่อโฮสต์ดั้งเดิม วิธีพร็อกซีรักษาไว้ได้ และวิธี DNAT รักษาไว้ได้เฉพาะเมื่อบริการยอมรับชื่อนั้นบนพอร์ตนั้น

จดบันทึกว่าเงื่อนไขใดในสามข้อเป็นตัวกำหนดรูปแบบ มิฉะนั้นคนถัดไปที่เพิ่มบริการที่สองจะเปิดการเปรียบเทียบเดิมอีกครั้งด้วยบริบทที่น้อยลง

ตัดสินใจ แล้วทดสอบจากภายนอก

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

sudo iptables -t nat -L PREROUTING -n -v
sudo tcpdump -ni any 'tcp port 8081' -c 20

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

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

บทความที่เกี่ยวข้อง