SSH vô hiệu hóa cổng chuyển tiếp từ xa thay vì giết quá trình trên máy chủ


1

Tôi đang thực hiện một yêu cầu chuyển tiếp cổng SSH từ xa tự động từ thiết bị của mình cứ sau vài phút:

ssh -y -f -N -R port:localhost:22 user@server \
-o ExitOnForwardFailure=yes -o ServerAliveInterval=60 \
-o StrictHostKeyChecking=no

Nếu chuyển tiếp cổng thành công và tôi khởi động lại thiết bị của mình và thiết lập lại kết nối internet, tôi không thể kết nối với nó hoặc thực hiện chuyển tiếp cổng mới (điều này là bình thường và ổn vì IP của tôi bị thay đổi và cổng dường như bị chiếm dụng). Tôi phải đợi khoảng 1-2 giờ để cổng được miễn phí trở lại hoặc tôi cần phải giết quá trình trên máy chủ của mình.

Tôi muốn gửi một tập lệnh từ thiết bị của mình trước khi tắt nó để giải phóng cổng, để sau khi khởi động lại, yêu cầu chuyển tiếp cổng mới thành công.

Để tóm tắt: 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:


2

tl; dr

Sử dụng các sshdtùy chọn này trên máy chủ (trong sshd_configtệ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 sshtrê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 sshbị 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 sshchấ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: systemdcác đơn vị và phụ thuộc của chúng, nếu có; một bọc trên rebootvà / 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ó ACKphả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.conftệ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, sshdsẽ 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 15ClientAliveCountMaxđượ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 sshdtrên máy chủ, thì theo tôi đây là cách thanh lịch nhất. Hãy để các sshd_configdòng chứa như:

ClientAliveCountMax 3
ClientAliveInterval 15

Ghi chú:

  • -o ServerAliveInterval=60bạ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 autosshvề 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ì sshdcá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 @reboottrong 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 sshdPID 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.

Cảm ơn bạn rất nhiều vì câu trả lời của bạn. Tôi sẽ chấm dứt shh ở phía máy khách trước khi khởi động lại. Tôi không biết rằng máy chủ sẽ được chú ý về điều đó. Ngoài ra, tôi sẽ sửa đổi cấu hình sshd trên máy chủ.
Jacek

0

Chuyển tiếp có thể bị hủy bằng cách sử dụng sshký tự thoát ( ~ở đầu dòng, theo mặc định) hoặc ~?để được trợ giúp

Supported escape sequences:
 ~.   - terminate connection (and any multiplexed sessions)
 ~B   - send a BREAK to the remote system
 ~C   - open a command line
 ~R   - request rekey
 ~V/v - decrease/increase verbosity (LogLevel)
 ~^Z  - suspend ssh
 ~#   - list forwarded connections
 ~&   - background ssh (when waiting for connections to terminate)
 ~?   - this message
 ~~   - send the escape character by typing it twice
(Note that escapes are only recognized immediately after newline.)

~#sẽ liệt kê các kết nối nhưng không thực sự theo cách phù hợp để giết chúng thông qua lệnh

The following connections are open:
  #3 client-session (t4 r0 i0/0 o0/0 fd 8/9 cc -1)

có thể được tìm thấy thông qua ~Cvà sau đóhelp

ssh> help
Commands:
      -L[bind_address:]port:host:hostport    Request local forward
      -R[bind_address:]port:host:hostport    Request remote forward
      -D[bind_address:]port                  Request dynamic forward
      -KL[bind_address:]port                 Cancel local forward
      -KR[bind_address:]port                 Cancel remote forward
      -KD[bind_address:]port                 Cancel dynamic forward

vì vậy về lý thuyết, bạn có thể bị hủy thông qua ~C-KL22để mở dòng lệnh và hủy LocalForwardsố cổng.

Liệu điều này sẽ giải quyết vấn đề của bạn không rõ ràng; các cổng thường mất ít hơn 1-2 giờ để xóa TIME_WAIThoặc ...

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.