- SSH tunneling dùng kết nối SSH bảo mật để chở lưu lượng TCP, giúp mở các đường truy cập bị giới hạn một cách an toàn như mã hóa giao thức cũ, truy cập bảng quản trị, hoặc kết nối tới máy chủ nằm sau NAT
- Ở phía máy chủ, cần
AllowTcpForwarding yes; nếu muốn mở cổng trên giao diện không phải loopback thì cần cấu hình GatewayPorts yes và khởi động lại máy chủ SSH
ssh -J, ssh -L, ssh -R, ssh -D lần lượt dùng cho jump host, chuyển tiếp cổng cục bộ, chuyển tiếp cổng từ xa và chuyển tiếp động dựa trên SOCKS5
- Để giữ tunnel chạy nền, dùng
ssh -fN; việc phát hiện ngắt kết nối có thể điều chỉnh bằng các thiết lập heartbeat như ClientAliveInterval và ClientAliveCountMax
- SSH tunneling không hỗ trợ trực tiếp UDP và có thể gặp vấn đề độ trễ, thông lượng khi chạy TCP-over-TCP, nên nó gần với công cụ mở các đường TCP cụ thể hơn là giải pháp thay thế VPN
Khi nào dùng SSH tunneling
- SSH tunneling và port forwarding chuyển lưu lượng TCP qua kết nối SSH từ client tới server, hoặc từ server về client
- Có thể dùng cổng TCP và UNIX socket, nhưng các ví dụ tập trung vào cổng TCP
- Các cách dùng tiêu biểu gồm bảo mật, xử lý sự cố và vượt tuyến kết nối
- Bảo mật
- Mã hóa các kết nối không an toàn hoặc giao thức cũ như FTP
- Truy cập bảng quản trị web qua SSH tunnel dựa trên xác thực khóa công khai
- Có thể chỉ mở cổng 22 mà không cần phơi thêm các cổng như 80/443
- Xử lý sự cố
- Vượt qua tường lửa hoặc bộ lọc nội dung
- Chọn một đường mạng khác
- Kết nối
- Truy cập máy chủ nằm sau NAT
- Đi từ Internet vào máy chủ nội bộ thông qua jump host
- Phơi cổng cục bộ ra Internet
Các thiết lập cần kiểm tra trước khi port forwarding
- Các tùy chọn trong ví dụ có thể kết hợp tùy theo môi trường
- Nếu không chỉ định
bind_address thì giá trị mặc định là localhost
- Người dùng cục bộ và từ xa đều cần quyền mở cổng trên từng máy
- Các cổng
0-1024 cần quyền root nếu không có cấu hình riêng
- Các cổng khác có thể do người dùng thường thiết lập
- Tường lửa phía client và mạng cũng phải mở phù hợp với đường chuyển tiếp
- Trên máy chủ SSH, port forwarding phải được bật
AllowTcpForwarding yes
- Thường mặc định đã bật, nhưng vẫn cần kiểm tra cấu hình máy chủ
- Nếu muốn forward cổng qua giao diện không phải
127.0.0.1, hãy bật GatewayPorts trên máy chủ SSH
GatewayPorts yes
- Sau khi cấu hình cần khởi động lại dịch vụ máy chủ SSH
SSH jump host và tunnel nhiều tầng
ssh -J dùng khi muốn kết nối trong suốt tới máy từ xa thông qua một hoặc nhiều host trung gian
ssh -J user@REMOTE-MACHINE:22 -p 22 user@10.99.99.1
- Khi dùng cổng mặc định
22, có thể bỏ phần chỉ định cổng
- Nếu dùng REMOTE-MACHINE làm jump host thì kết nối có thể được xác nhận như sau
[user@REMOTE-MACHINE]$ ss | grep -i ssh
tcp ESTAB 0 0 167.135.173.108:ssh 192.160.140.207:45960
tcp ESTAB 0 0 10.99.99.2:49770 10.99.99.1:ssh
- Vai trò của các địa chỉ ví dụ như sau
167.135.173.108: IP công khai của REMOTE-MACHINE
192.160.140.207: IP công khai của LOCAL-MACHINE
10.99.99.2: IP nội bộ của REMOTE-MACHINE
10.99.99.1: IP nội bộ của REMOTE-WEBAPP
- Khi dùng nhiều jump host, phân tách bằng dấu phẩy
ssh -J user@REMOTE-MACHINE:22,user@ANOTHER-REMOTE-MACHINE:22 -p 22 user@10.99.99.1
Chuyển tiếp cổng cục bộ
- Chuyển tiếp cổng cục bộ dùng tùy chọn
ssh -L
- Ví dụ đầu tiên chuyển
10.10.10.1:8001 trên LOCAL-MACHINE tới localhost:8000 trên REMOTE-MACHINE
ssh -L 10.10.10.1:8001:localhost:8000 user@REMOTE-MACHINE
- Nhật ký truy cập của web server chỉ bind vào
127.0.0.1 trên REMOTE-MACHINE sẽ trông như sau
127.0.0.1 - - [30/Dec/2022 18:05:15] "GET / HTTP/1.1" 200
- Yêu cầu này bắt đầu từ LOCAL-MACHINE
- Ví dụ thứ hai chuyển cổng cục bộ
8001 tới 10.99.99.1:8000 thông qua REMOTE-MACHINE
ssh -L 8001:10.99.99.1:8000 user@REMOTE-MACHINE
- Trong nhật ký truy cập web server của REMOTE-WEBAPP, yêu cầu sẽ hiện là đến từ IP nội bộ của REMOTE-MACHINE
10.99.99.2 - - [30/Dec/2022 21:28:42] "GET / HTTP/1.1" 200
Chuyển tiếp cổng từ xa
- Chuyển tiếp cổng từ xa dùng tùy chọn
ssh -R
- Ví dụ chuyển
8000 trên REMOTE-MACHINE tới localhost:8001 hoặc 10.10.10.2:8001 ở phía cục bộ
ssh -R 8000:localhost:8001 user@REMOTE-MACHINE
ssh -R 8000:10.10.10.2:8001 user@REMOTE-MACHINE
- Cũng có thể cấu hình để lắng nghe trên giao diện từ xa cụ thể là
10.99.99.2:8000
ssh -R 10.99.99.2:8000:10.10.10.2:8001 user@REMOTE-MACHINE
- Để lắng nghe ngoài giao diện loopback, máy chủ SSH phải bật
GatewayPorts yes
Chuyển tiếp cổng động và SOCKS5
- Khi cần chuyển tiếp nhiều cổng, SSH dùng giao thức SOCKS
- SOCKS là giao thức proxy trong suốt, và SSH dùng phiên bản mới là SOCKS5
- Cổng mặc định của máy chủ SOCKS5 là
1080 như được định nghĩa trong RFC 1928
- Client phải được cấu hình để dùng SOCKS proxy ở tầng ứng dụng hoặc tầng hệ điều hành
- Ví dụ
ssh -D mở một SOCKS proxy tại 10.10.10.1:5555
ssh -D 10.10.10.1:5555 user@REMOTE-MACHINE
- Trên client LOCAL, có thể dùng
curl để kiểm tra đường kết nối
curl -L -x socks5://10.10.10.1:5555 brrl.net/ip
- Nếu hoạt động bình thường, IP công khai của REMOTE-MACHINE sẽ được trả về
SSH TUN/TAP tunneling
- Có thể tạo tunnel hai chiều bằng cờ
-w
- Giao diện phải được tạo sẵn từ trước
- Có lưu ý rằng cách này chưa được kiểm thử
-w local_tun[:remote_tun]
Chạy nền và kết thúc
- Để chạy tunnel ở chế độ nền theo cách native, dùng
-fN
-f: chạy nền
-N: không mở shell
ssh -fN -L 8001:127.0.0.1:8000 user@REMOTE-MACHINE
- Ngoài ra có thể dùng
screen hoặc công cụ khác
- SSH đang chạy nền có thể được tìm theo tiến trình rồi kết thúc bằng PID
user@pleasejustwork:~$ ps -ef | grep ssh
[...]
user 19255 1 0 11:40 ? 00:00:00 ssh -fN -L 8001:127.0.0.1:8000 user@REMOTE-MACHINE
[...]
kill 19255
Giữ kết nối SSH và tự kết nối lại
- Có nhiều cách để giữ kết nối SSH
- Các tùy chọn heartbeat để xử lý timeout có thể đặt ở client, server hoặc cả hai
ClientAliveInterval gửi một yêu cầu sau mỗi n giây để duy trì kết nối
ClientAliveInterval 15
ClientAliveCountMax là số lượng yêu cầu heartbeat được gửi trước khi đóng kết nối sau khi không nhận phản hồi từ phía bên kia
ClientAliveCountMax 3
3 là giá trị mặc định; đặt 0 sẽ vô hiệu hóa việc tự đóng kết nối
- Trong cấu hình ví dụ, nếu không có phản hồi thì kết nối sẽ bị ngắt sau khoảng 45 giây
- Sau khi kết nối bị đóng, có thể dùng
autossh, script hoặc cronjob để tự kết nối lại
Giới hạn và lưu ý bảo mật
-
UDP
- SSH phụ thuộc vào cơ chế truyền tin cậy để giải mã chính xác
- UDP không cung cấp độ tin cậy, nên không được hỗ trợ và cũng không được khuyến nghị trong SSH tunnel
- Có phương pháp đề cập đến UDP over SSH tunneling, nhưng có lưu ý là chưa được kiểm thử
-
TCP-over-TCP
- Do overhead, thông lượng giảm và độ trễ tăng
- Trên các kết nối có mất gói hoặc độ trễ cao, có thể xảy ra TCP meltdown
- Có thể tham khảo bài viết về TCP over TCP
- Khi dùng OpenVPN-over-TCP trong một thời gian, thông lượng thấp hơn UDP nhưng vẫn hoạt động ổn định; kết quả phụ thuộc nhiều vào cấu hình
-
Giới hạn khi dùng như giải pháp thay thế VPN
- Có thể dùng SSH tunneling theo kiểu VPN, nhưng VPN phù hợp hơn nếu cần hiệu năng tốt hơn
-
Rủi ro bảo mật
- Nếu không cần tính năng này thì nên tắt
- Kẻ tấn công có thể dùng SSH tunneling và port forwarding để né tường lửa và các biện pháp bảo mật khác
-
Tài liệu tham khảo
1 bình luận
Ý kiến trên Hacker News
Năm 2024, thay vì tự gõ các lệnh SSH dài, tốt hơn là cấu hình LocalForward, RemoteForward, ProxyJump trong
~/.ssh/configViệc này giúp tiết kiệm rất nhiều thời gian khi truy cập bằng
ssh,scp,rsyncvào các máy chủ từ xa phải đi qua nhiều kết nối SSH trung gianVí dụ, có thể nối
jump-host-1,jump-host-2,jump-host-3bằngProxyJumprồi kết nối tớitarget-serverqua một bí danhLocalForward 0.0.0.0:8080 0.0.0.0:80chuyển tiếp cổng 80 ở phía xa về cổng 8080 cục bộ, cònRemoteForward 0.0.0.0:9022 0.0.0.0:22chuyển tiếp các yêu cầu SSH đi vào cổng 9022 ở phía xa về cổng 22 cục bộVới
LocalForwardvàRemoteForward, phía bên trái là máy chủ đích, bên phải là máy cục bộ; cũng có mẹo là dùng0.0.0.0thay vìlocalhosthoặc127.0.0.1ssh_config: trong mạng gia đình hoặc VPN tự host, cấu hình để SSH trực tiếp tới từng thiết bị; còn khi ở bên ngoài thì tự động rẽ nhánh qua jump host là rất hữu íchCó thể phát hiện trước xem có đang ở nhà/trong VPN hay không bằng địa chỉ router, chẳng hạn
Match host *.example.org exec "getent ahosts router.example.org | grep -q ^10.0.0.1", rồi dùngarpđể nếu địa chỉ MAC khác giá trị kỳ vọng thì dùngProxyJump jump.example.orgThứ tự rất quan trọng: phải phát hiện mạng VPN/mạng nhà trước, sau đó mới áp dụng jump host khi ở bên ngoài
Khi tạo một VPS dùng trong thời gian ngắn để dùng SSH làm SOCKS proxy nhằm vượt giới hạn khu vực, hoặc cần vào một lần một máy chủ bình thường không truy cập để hỗ trợ tạm thời cho đội khác, tôi nghĩ dùng tùy chọn dòng lệnh sẽ tốt hơn
0.0.0.0có thể nguy hiểmNó có thể mở cổng trên mọi interface mạng, và đặc biệt nếu máy chủ từ xa không có tường lửa thì bạn sẽ phơi bày thứ gì đó ra toàn bộ Internet
Với tunneling hoặc port forwarding dùng thường xuyên thì file cấu hình là tốt, nhưng với tác vụ một lần hoặc hiếm khi làm thì tôi nghĩ tùy chọn dòng lệnh tốt hơn
Tôi đang tìm một giải pháp kiểu GitOps phù hợp cho một người dùng/nhiều máy, lấy cấu hình từ nơi như GitHub mà không cần luôn bật một máy chủ riêng
bash/fishcơ bản để chỉ cần nhớ tối thiểu tham sốNếu quên thì xem lại định nghĩa hàm đã ghi trong tài liệu là được
Trong môi trường doanh nghiệp vô lý nơi tôi làm việc, SSH tunneling là thứ bắt buộc
Vì quan liêu, đôi khi phải mất nhiều tuần để được cấp quyền truy cập, mở cổng hoặc xin ngoại lệ tường lửa/VPN
Bài này có nhắc đến
-Dnhưng chưa giải thích đủ sức mạnh của nóChạy
ssh -D 8888 someserverrồi đặt SOCKS proxy của trình duyệt thànhlocalhost:8888thì toàn bộ lưu lượng trình duyệt sẽ được định tuyến quasomeserverFirefox vẫn cho làm việc này mà không cần đổi giá trị mặc định của hệ thống, nên rất hữu ích
Nhưng khi vào công ty, do danh sách cho phép IP, tôi thậm chí không thể kết nối SSH tới một máy chủ tùy ý trên Internet; quá khổ sở nên tôi còn tìm cách tạo HTTP tunnel và đưa máy chủ do tôi kiểm soát vào danh sách cho phép, rồi cuối cùng đổi việc
Những hạn chế như vậy có lý do của chúng, và việc vượt qua có thể bị xem là thiếu chuyên môn và coi thường quy trình cũng như bảo mật của tổ chức
Nếu phải mất vài tuần hay vài tháng để được cấp quyền truy cập thì nên chờ như vậy; chắc bạn không muốn chịu trách nhiệm vì mở backdoor vào thông tin mật hoặc làm suy yếu bảo mật
Màn hack SSH tunneling bẩn nhất tôi từng làm lúc 3 giờ sáng là một việc nối ba trung tâm dữ liệu
Ba cơ sở trong cùng khu vực đô thị đều được kết nối với upstream Internet, nhưng do chính sách định tuyến kỳ quặc, A chỉ kết nối được với B, và chỉ B mới kết nối được với C
Cuối cùng, để chuyển dữ liệu của A qua B sang C, tôi phải trộn lẫn rsync + SSH tunnel + key + mẹo định tuyến; sau vài lần chỉnh lại những câu lệnh như thần chú, thấy toàn bộ hệ thống chạy một mạch đúng là như phép màu
Nhìn lại bây giờ thì cách làm quá rõ ràng, nhưng với tôi lúc đó còn là người mới, đó là một thành tựu lớn, và tôi vẫn nhớ cảm giác phấn khích khi xác nhận đồng bộ đã hoàn tất
~/.ssh/configvà ProxyJump thì về cơ bản có thể nhảy qua bao nhiêu tầng cũng được, như A, B, C, D, ECác chi tiết trong phần trực quan hóa rất hay
Trong networking, đặc biệt ở các kết nối cấp thấp hơn, tôi rất muốn có thêm nhiều công cụ biểu diễn lưu lượng một cách trực quan
Dĩ nhiên đó chỉ là biểu diễn khái niệm tĩnh; hầu hết các nhà cung cấp mạng cũng cung cấp một dạng trực quan hóa lưu lượng nào đó, nhưng thường chỉ dừng ở mức các chỉ số hoặc biểu đồ riêng lẻ
Nếu máy chủ Linux hoặc thiết bị IoT nằm sau tường lửa và không có IP tĩnh nhưng bạn vẫn muốn truy cập bằng SSH, có thể dùng dịch vụ tunneling như https://sshreach.me
Tôi đã dùng tunneling khá nhiều trong vài năm, nhưng không biết tùy chọn
-JThay vì mỗi vài tháng cần tunnel lại mất 30 phút cấu hình, giá mà có một công cụ trực quan để cấu hình tunnel
Có giải thích rằng TCP-over-TCP làm tăng overhead, giảm thông lượng và tăng độ trễ, đồng thời có thể gây ra TCP meltdown trên các kết nối có độ trễ lớn như mất gói hoặc mạng vệ tinh
Tuy nhiên, trong tunnel SSH, thực tế đây không phải là vấn đề trừ khi dùng TAP/TUN
Vì SSH tách luồng TCP ra rồi chuyển tiếp
Tuy vậy, khi dùng nhiều kênh, hiệu năng có thể giảm do head-of-line blocking
Khoảng 15 năm trước, tôi đã học SSH tunnel để vượt qua firewall của mạng đại học, và phải đổi cổng mặc định sang 443
Từ đó về sau tôi vẫn tiếp tục dùng nó cho nhiều mục đích đa dạng hơn nhiều so với việc vượt firewall
Dùng thử
sshuttlethì tunneling tiện hơn rất nhiềuNếu dùng như
sshuttle -r user@host 10.0.0.0/8, dải10/8sẽ tự động được tunnel, và về cơ bản hoạt động như một VPN chạy trên SSHTò mò liệu bản thân SSH có chức năng redirect không
Ví dụ, khi A định SSH vào B, B sẽ báo hãy kết nối tới C, rồi A kết nối trực tiếp tới C một cách trong suốt, còn B không còn nằm trên đường đi dữ liệu chính nữa; không biết có chức năng kiểu đó không
Firewall/router của tôi không chuyển tiếp đúng DHCP option 67 mà luôn gửi địa chỉ của chính nó, nên tôi đã phải thiết lập IP ảo/quy tắc để khi lưu lượng PXE boot trên cổng đó đi tới IP của router thì nó được định tuyến tới server PXE boot thực sự
Nếu không phải vậy thì có thể dùng chức năng jump:
ssh -J B CNếu B không cần nằm trên đường đi và có thể kết nối trực tiếp tới C thì cứ kết nối thẳng tới C; còn nếu không làm được thì dù sao B cũng phải nằm như một hop
Nếu giải thích thêm vấn đề, có thể sẽ có cách phù hợp hơn
Nếu là host được nhúng ở mức nào đó thì có thể để DNS đảm nhận việc “routing”, nhưng trong trường hợp này cần tự xử lý xác minh fingerprint của host key