tl; dr
Sử dụng các sshd
tùy chọn này trên máy chủ (trong sshd_config
tệp):
ClientAliveCountMax 3
ClientAliveInterval 15
Câu chuyện phía khách hàng
Làm cách nào tôi có thể hủy chuyển tiếp cổng từ thiết bị đã yêu cầu từ xa, thay vì giết quá trình trên máy chủ?
Câu trả lời đơn giản là: chỉ cần chấm dứt ssh
trên máy khách. Ngay cả khi bạn buộc phải giết nó, máy chủ sẽ được thông báo rằng kết nối bị chấm dứt (vì đó là hạt nhân thực hiện công việc ).
Nếu thông báo được gửi đến máy chủ, nghĩa là như vậy. Tôi đoán đây là vấn đề. Mạng trên thiết bị của bạn bằng cách nào đó giảm xuống trước khi ssh
bị chấm dứt và không có cách nào để thông báo cho máy chủ kết nối SSH cụ thể này không còn nữa.
Có lẽ bạn có thể thiết kế lại thiết lập phía máy khách của mình để đảm bảo ssh
chấm dứt trước khi mọi thứ được thực hiện với kết nối mạng. Tôi không biết chi tiết về hệ thống phía khách hàng của bạn, vì vậy tôi sẽ không cho bạn biết chính xác những gì bạn có thể làm và làm thế nào. Ví dụ lỏng lẻo: systemd
các đơn vị và phụ thuộc của chúng, nếu có; một bọc trên reboot
và / hoặc shutdown
.
Câu chuyện phía máy chủ
Tôi giả sử máy chủ SSH là tiêu chuẩn sshd
. Cấu hình mặc định của nó ( sshd_config
) chỉ định
TCPKeepAlive yes
Từ man 5 sshd_config
:
TCPKeepAlive
Chỉ định xem hệ thống có nên gửi tin nhắn cố định TCP sang phía bên kia không. Nếu chúng được gửi đi, cái chết của kết nối hoặc sự cố của một trong các máy sẽ được chú ý chính xác. Tuy nhiên, điều này có nghĩa là các kết nối sẽ chết nếu tuyến đường tạm thời ngừng hoạt động và một số người cảm thấy phiền phức. Mặt khác, nếu các thủ tục TCP không được gửi, các phiên có thể bị treo vô thời hạn trên máy chủ, khiến người dùng '' ma '' và tiêu tốn tài nguyên máy chủ.
Mặc định là yes
(để gửi tin nhắn lưu giữ TCP) và máy chủ sẽ thông báo nếu mạng bị hỏng hoặc máy chủ của máy khách gặp sự cố. Điều này tránh các phiên treo vô hạn.
Cơ chế này không dành riêng cho SSH. Giải trình:
Trong Linux, các tham số giữ TCP là:
tcp_keepalive_intvl
tcp_keepalive_probes
tcp_keepalive_time
Giá trị mặc định của chúng là:
tcp_keepalive_time = 7200
(giây)
tcp_keepalive_intvl = 75
(giây)
tcp_keepalive_probes = 9
(số lượng đầu dò)
Điều này có nghĩa là quá trình giữ lại chờ trong hai giờ (7200 giây) cho hoạt động của ổ cắm trước khi gửi đầu dò giữ đầu tiên, sau đó gửi lại sau mỗi 75 giây. Nếu không có ACK
phản hồi nào được nhận trong chín lần liên tiếp, kết nối được đánh dấu là bị hỏng.
( Nguồn ).
Điều này giải thích tại sao bạn "phải đợi khoảng 1-2 giờ để cổng được miễn phí trở lại".
Ví dụ về cách bạn có thể thay đổi các giá trị này (nếu bạn có quyền):
tạm thời
echo 300 > /proc/sys/net/ipv4/tcp_keepalive_time
hoặc là
sysctl -w net.ipv4.tcp_keepalive_time=300
vĩnh viễn bằng cách chỉnh sửa /etc/sysctl.conf
tệp, thêm:
net.ipv4.tcp_keepalive_time=300
sau đó gọi sudo sysctl -p
để áp dụng thay đổi.
Nhưng đây là những thiết lập toàn hệ thống. Nói chung, bất kỳ thay đổi sẽ ảnh hưởng nhiều hơn sshd
. Đó là lý do tại sao nên sử dụng giải pháp dành riêng cho SSH. Một lần nữa từ man 5 sshd_config
:
ClientAliveCountMax
Đặt số lượng tin nhắn còn sống của khách hàng (xem bên dưới) có thể được gửi mà không sshd(8)
nhận được bất kỳ tin nhắn nào từ máy khách. Nếu đạt đến ngưỡng này trong khi tin nhắn còn sống của khách đang được gửi, sshd
sẽ ngắt kết nối máy khách, chấm dứt phiên. Điều quan trọng cần lưu ý là việc sử dụng tin nhắn sống của khách hàng rất khác với TCPKeepAlive
. [V]] Cơ chế sống của máy khách có giá trị khi máy khách hoặc máy chủ phụ thuộc vào việc biết khi nào kết nối không hoạt động.
Giá trị mặc định là 3
. Nếu ClientAliveInterval
(xem bên dưới) được đặt thành 15
và ClientAliveCountMax
được để mặc định, các máy khách SSH không phản hồi sẽ bị ngắt kết nối sau khoảng 45 giây. Tùy chọn này chỉ áp dụng cho giao thức phiên bản 2.
ClientAliveInterval
Đặt khoảng thời gian chờ tính bằng giây sau đó nếu không có dữ liệu nào được nhận từ máy khách, sshd(8)
sẽ gửi tin nhắn qua kênh được mã hóa để yêu cầu phản hồi từ máy khách. Mặc định là 0
, chỉ ra rằng những tin nhắn này sẽ không được gửi đến máy khách. Tùy chọn này chỉ áp dụng cho giao thức phiên bản 2.
Nếu chỉ bạn có thể cấu hình lại sshd
trên máy chủ, thì theo tôi đây là cách thanh lịch nhất. Hãy để các sshd_config
dòng chứa như:
ClientAliveCountMax 3
ClientAliveInterval 15
Ghi chú:
-o ServerAliveInterval=60
bạn sử dụng là một tùy chọn tương tự cho ssh
; nó cho phép khách hàng phát hiện kết nối bị hỏng. Nó không ảnh hưởng đến máy chủ mặc dù.
- bạn có thể xem xét
autossh
về khách hàng.
Trở lại khách hàng
Giả sử bạn không thể cấu hình lại máy chủ cũng như máy khách. Tôi biết bạn nói
thay vì giết tiến trình trên máy chủ
nhưng trong trường hợp này, giết nó có thể là lựa chọn tốt nhất. Vì sshd
các nhánh để phục vụ các kết nối cụ thể, nó đủ để giết đúng quy trình mà không ảnh hưởng đến phần còn lại. Để bắt đầu giết từ máy khách, bạn nên sửa đổi cách bạn gọi ssh
. Bạn đã nói:
cứ sau vài phút
ssh -f …
Thay vì điều này, bạn có thể chạy một tập lệnh tương tự như tập lệnh sau, chỉ một lần (ví dụ: thông qua @reboot
trong crontab). Trước tiên, nó cố gắng tiêu diệt PID đã lưu (của sshd
, trên máy chủ), sau đó thiết lập một đường hầm và lưu sshd
PID của nó . Nếu cuối cùng đường hầm không thể được thiết lập hoặc bị chấm dứt, tập lệnh sẽ ngủ một lúc và lặp lại.
#!/bin/sh
port=12345
address=user@server
while :; do
ssh $address '[ -f ~/.tunnel.pid ] && kill `cat ~/.tunnel.pid` && rm ~/.tunnel.pid'
ssh -o ExitOnForwardFailure=yes -R ${port}:localhost:22 $address 'echo $PPID > ~/.tunnel.pid; exec sleep infinity'
sleep 60
done
Ghi chú:
- Đây là kịch bản nhanh và bẩn, một bằng chứng về khái niệm; tính di động không phải là ưu tiên của tôi.
- Để rõ ràng, tôi đã bỏ qua một số tùy chọn ban đầu bạn sử dụng.
- Nếu bạn chạy tập lệnh hai lần, hai trường hợp sẽ lặp đi lặp lại các đường hầm của nhau.