Làm thế nào để giữ một đường hầm SSH đáng tin cậy?


234

Tôi sử dụng một đường hầm SSH từ công việc để đi xung quanh các tường lửa khác nhau (với sếp của tôi là được rồi :)). Vấn đề là, sau một thời gian, kết nối ssh thường bị treo và đường hầm bị hỏng.

Nếu tôi ít nhất có thể tự động giám sát đường hầm, tôi có thể khởi động lại đường hầm khi nó bị treo, nhưng tôi thậm chí không tìm ra cách nào để làm điều đó.

Điểm thưởng cho người có thể cho tôi biết làm thế nào để ngăn kết nối ssh của tôi bị treo, tất nhiên!


Đó là đường hầm của bạn chết vì không hoạt động? Tôi gặp vấn đề này khi đào các cổng từ điện thoại của mình vì vậy cuối cùng tôi đã kết thúc việc sinh ra các lệnh giả trên kết nối để làm cho nó "sống" bằng cách sử dụng watchlệnh như : watch -n1 60 echo "wiiiii". Đường hầm sẽ không chết trừ khi mạng bị hỏng hoặc bạn không sử dụng nó.
erm3nda

Câu trả lời:


280

Âm thanh như bạn cần autossh . Điều này sẽ giám sát một đường hầm ssh và khởi động lại nó khi cần thiết. Chúng tôi đã sử dụng nó trong một vài năm và nó dường như hoạt động tốt.

autossh -M 20000 -f -N your_public_server -R 1234:localhost:22 -C

Thêm chi tiết về tham số -M tại đây


2
+1 cho autossh, nó thực hiện những gì nó nói trên hộp thiếc. Tôi tin rằng một phần chức năng của nó cũng là gửi các gói kiểu giữ nguyên để ngăn chặn bất kỳ loại thời gian chờ nào.
akent

30
Bạn có thể đặt đường hầm ví dụ bằng cách sử dụng autosshtrong câu trả lời?
Ehtesh Choudhury

5
autossh -f -nNT -i ~/keypair.pem -R 2000:localhost:22 username@myoutsidebox.com Bạn có thể nhận thấy rằng tôi đã thiết lập điều này bằng cách sử dụng -nNT không tạo thiết bị đầu cuối từ xa để tôi có thể đặt tự động vào nền và tùy chọn -i để SSH sử dụng tệp .pem. Nếu bạn sẽ luôn giữ kết nối mở, tôi chắc chắn khuyên bạn nên thực hiện cài đặt bổ sung.
juckele

2
Đối với giá trị của nó, có vẻ như tốt hơn là bỏ qua -Mtham số: bug.debian.org/cgi-bin/ormsreport.cgi?orms=351162
rinogo

2
Tôi đã làm điều này để làm cho nó thử lại khi thay đổi mạng, nó hoạt động tốt với tôi: autossh -M 0 -o "ServerAliveInterval 10" -o "ServerAliveCountMax 2" -L 9999: localhost: 19999 server@example.com
Luke Stanley

39

Tất cả các tường lửa trạng thái quên một kết nối sau khi không thấy gói tin cho kết nối đó trong một thời gian (để ngăn các bảng trạng thái trở nên đầy các kết nối trong đó cả hai đầu đều chết mà không đóng kết nối). Hầu hết các triển khai TCP sẽ gửi một gói giữ lại sau một thời gian dài mà không nghe thấy từ phía bên kia (2 giờ là một giá trị chung). Tuy nhiên, nếu có một tường lửa trạng thái mà quên kết nối trước khi các gói giữ nguyên có thể được gửi, một kết nối dài nhưng nhàn rỗi sẽ chết.

Nếu đó là trường hợp, giải pháp là ngăn chặn kết nối không hoạt động. OpenSSH có một tùy chọn gọi là ServerAliveInterval , có thể được sử dụng để ngăn kết nối không hoạt động quá lâu (như một phần thưởng, nó sẽ phát hiện khi đồng nghiệp chết sớm hơn ngay cả khi kết nối không hoạt động).


Khoảng thời gian được chỉ định là tính bằng giây, vì vậy bạn có thể cung cấp một số điều chỉnh tốt. Nếu tường lửa trạng thái của bạn có thời gian chờ 5 phút, thì 60 hoặc 120 giây là đủ để giữ kết nối mở. Đó là một trong những cách tôi giữ các phiên ssh của mình thông qua bộ định tuyến gia đình.
Hội trường Darren

Cảm ơn, điều này đã giúp. Nhưng lưu ý (từ câu trả lời được xếp hạng thấp hơn ở đây, superuser.com/a/146641/115515 ) rằng nếu bạn chỉ định ServerAliveInterval và không phải ServerAliveCountMax, bạn có thể thấy ssh bị ngắt kết nối sớm hơn bạn muốn.
metamatt

4
@metamatt, câu trả lời xếp hạng thấp hơn mà bạn tham khảo được xếp hạng thấp hơn vì lý do chính đáng: NÓ LÀ SAU.
Lambart

24

Trên máy mac hoặc linux của riêng bạn, cấu hình ssh của bạn giữ cho máy chủ ssh tồn tại cứ sau 3 phút. Mở một thiết bị đầu cuối và đi .ssh vô hình của bạn trong nhà của bạn:

cd ~/.ssh/ 

sau đó tạo tệp cấu hình 1 dòng với:

echo "ServerAliveInterval 180" >> config

bạn cũng nên thêm:

ServerAliveCountMax xxxx (high number)

mặc định là 3 vì vậy ServerAliveInterval 180 sẽ ngừng gửi sau 9 phút (3 trong khoảng thời gian 3 phút được chỉ định bởi ServerAliveInterval).


2
Lưu ý rằng lệnh của bạn không được khuyến nghị nếu bạn đã có tệp cấu hình. Sử dụng >> để chuyển hướng sẽ tốt hơn rất nhiều!
Peltier

Tại sao ServerAliveInterval 180cho chúng tôi 6 phút? trực giác làm cho tôi thử điều này : 180/60 == 3. Vì vậy, làm ServerAliveIntervalviệc trong bội số của 30 giây?
nemeisfixx

@mcnemesis: ServerAliveInterval 180 có nghĩa là 3 phút. ServerAliveCountMax mặc định 3 có nghĩa là 3 trong số các khoảng đó, vì vậy 9 phút.
metamatt

2
Tôi đang bỏ phiếu cho câu trả lời này vì cảm ơn vì đã đề cập đến ServerAliveCountMax và điều gì xảy ra nếu bạn chỉ định ServerAliveInterval mà không có ServerAliveCountMax. Nhưng giống như các bình luận trước, tôi nhận thấy phép tính trên "sẽ ngừng gửi sau" là sai và tôi nghĩ câu trả lời này sẽ phục vụ tốt hơn nếu nó chỉ cung cấp thông tin về các tùy chọn này, không cho chúng tôi biết cách áp dụng chúng với các lệnh cd và echo .
metamatt

20
Giảm xuống vì không có ý nghĩa gì khi đặt ServerAliveCountMax thành "số cao". ServerAliveCountMax chỉ định số lần họ sẽ cố gắng gửi tin nhắn "keepalive" trước khi từ bỏ. Mặc định là 3, vì vậy với ServerAliveInterval 180, nó sẽ ngừng gửi CHỈ nếu máy chủ KHÔNG TRẢ LỜI sau 9 phút, trong trường hợp đó kết nối của bạn có thể đã chết và thực sự chết.
Lambart

22

Tôi đã sử dụng tập lệnh Bash sau để tiếp tục sinh ra các đường hầm ssh mới khi cái trước đó chết. Sử dụng tập lệnh rất tiện lợi khi bạn không muốn hoặc không thể cài đặt các gói bổ sung hoặc sử dụng trình biên dịch.

while true
do
  ssh <ssh_options> [user@]hostname
  sleep 15
done

Lưu ý rằng điều này đòi hỏi một keyfile để thiết lập kết nối tự động, nhưng đó cũng là trường hợp với autossh.


2
Bạn nên thêm bất kỳ lý do nào mà bạn sử dụng tập lệnh này trên autossh, hoặc nó chỉ đơn giản hơn theo cách này?
kyrias

4
Điều này sẽ không giúp nếu ssh tự đóng băng, phải không?
nafg

1
Nó giúp nếu bạn không thể cài đặt mọi thứ trong máy chủ. autossh không được cài đặt sẵn và bureucuity đôi khi nó rất khó hiểu.
quarkex

Có, tốt nhất là không phải cài đặt mọi thứ. Tôi đã làm theo cách này trong một năm vì cách duy nhất của tôi là giữ cho một máy từ xa có thể truy cập được (thậm chí đặt crontab để chạy nó khi khởi động lại). Nó không bao giờ thất bại, và quan trọng hơn, tôi biết tại sao nó sẽ không bao giờ thất bại.
sudo

16

Systemd là lý tưởng phù hợp cho việc này.

Tạo một tệp dịch vụ /etc/systemd/system/sshtunnel.servicecó chứa:

[Unit]
Description=SSH Tunnel
After=network.target

[Service]
Restart=always
RestartSec=20
User=sshtunnel
ExecStart=/bin/ssh -NT -o ServerAliveInterval=60 -L 5900:localhost:5900 user@otherserver

[Install]
WantedBy=multi-user.target

(Sửa đổi lệnh ssh cho phù hợp)

  • cái này sẽ chạy như người dùng, sshtunnelvì vậy hãy chắc chắn rằng người dùng đó tồn tại trước
  • vấn đề systemctl enable sshtunnelđể thiết lập nó để bắt đầu lúc khởi động
  • vấn đề systemctl start sshtunnelđể bắt đầu ngay lập tức

Cập nhật tháng 1 năm 2018 : một số bản phát hành (ví dụ Fedora 27) có thể sử dụng chính sách SELinux để ngăn chặn việc sử dụng SSH từ systemd init, trong trường hợp đó, chính sách tùy chỉnh sẽ cần được tạo để cung cấp các miễn trừ cần thiết.


2
Điều này trông rất giống với ý chính của tôi: gist.github.com/guettli/ Phản hồi được chào đón!
guettli

Tuyệt vời cho một systemdhệ thống. Nếu một người sử dụng Restart=on-failurethì việc giết thủ công máy khách SSH sẽ không dẫn đến việc khởi động lại theo hệ thống như máy khách SSH thoát ra thành công.
David Tonhofer

Nếu bạn muốn bắt đầu ssh từ một tập lệnh (bash) được đưa ra làm đối số để ExecStartlấy ví dụ để xây dựng sshdanh sách đối số, hãy kiểm tra cơ bản, v.v. sau đó gọi nó từ tập lệnh như vậy exec /bin/ssh -N .... Đây là mệnh lệnh của tôi: exec /bin/ssh -N -oExitOnForwardFailure=Yes -oTCPKeepAlive=no -oServerAliveInterval=5 -oServerAliveCountMax=6 -i "${LOCAL_PRIVATE_KEY}" -L "${TUNNEL_INLET}:${TUNNEL_OUTLET}" "${REMOTE_USER}@${REMOTE_MACHINE}"ở đâu TUNNEL_INLET="127.0.0.1:3307"TUNNEL_OUTLET="127.0.0.1:3306"
David Tonhofer

10

Tôi chắc chắn rằng tất cả các bạn đều hiểu sai về ServerAliveCountMax. Theo tôi hiểu các tài liệu, đó là số lượng tin nhắn còn sống của máy chủ có thể không được trả lời mà không bị ngắt kết nối. Vì vậy, trong những trường hợp như chúng ta đang thảo luận ở đây, việc đặt nó thành giá trị cao sẽ chỉ đảm bảo rằng kết nối treo sẽ không bị phát hiện và chấm dứt!

Chỉ cần đặt ServerAliveInterval là đủ để giải quyết vấn đề với tường lửa mà quên kết nối và để ServerAliveCountMax ở mức thấp sẽ cho phép kết thúc ban đầu nhận thấy lỗi và chấm dứt nếu kết nối không thành công.

Những gì bạn muốn là, 1) để kết nối được mở vĩnh viễn trong các trường hợp thông thường, 2) để phát hiện lỗi kết nối và phía khởi tạo để thoát khi lỗi và 3) cho lệnh ssh được cấp lại mỗi lần thoát (cách bạn làm điều đó phụ thuộc rất nhiều vào nền tảng, tập lệnh "while true" được đề xuất bởi Jawa là một cách, trên OS XI thực sự thiết lập một mục launchd).


9

Luôn sử dụng ServerAliveIntervaltùy chọn SSH trong trường hợp các sự cố đường hầm được tạo bởi các phiên NAT đã hết hạn.

Luôn sử dụng phương pháp hồi sinh trong trường hợp kết nối bị hỏng hoàn toàn, bạn có ít nhất ba tùy chọn ở đây:

  • chương trình tự động
  • bash script ( while true do ssh ...; sleep 5; done) không xóa lệnh ngủ, sshcó thể thất bại nhanh chóng và bạn sẽ hồi sinh quá nhiều quá trình
  • /etc/inittab, để có quyền truy cập vào một hộp được vận chuyển và cài đặt ở một quốc gia khác, phía sau NAT, mà không cần chuyển tiếp cổng tới hộp, bạn có thể định cấu hình nó để tạo đường hầm ssh cho bạn:

    tun1:2345:respawn:/usr/bin/ssh -i /path/to/rsaKey -f -N -o "ServerAliveInterval 180" -R 55002:localhost:22 user@publicip 'sleep 365d'
    
  • tập lệnh khởi động trên Ubuntu, nơi /etc/inittabkhông có sẵn:

    start on net-device-up IFACE=eth0
    stop on runlevel [01S6]
    respawn
    respawn limit 180 900
    exec ssh -i /path/to/rsaKey -N -o "ServerAliveInterval 180" -R 55002:localhost:22 user@publicip
    post-stop script
        sleep 5
    end script
    

hoặc luôn luôn sử dụng cả hai phương pháp.


1
+1 cho tùy chọn nội tuyến trong trường hợp bạn không muốn nó cho tất cả các kết nối SSH của mình
user1146334

Bạn viết "trong trường hợp kết nối bị mất hoàn toàn". Bây giờ tôi không hiểu, những vấn đề nào tự động sửa lỗi, và những gì nó không? Tôi nghĩ, tất nhiên, nó sẽ quan tâm đến bất kỳ kết nối bị hỏng nào, như rút cáp trong vài giờ, nhưng có lẽ không?
Mads Skjern

6

Tôi đã giải quyết vấn đề này bằng cách này:

Biên tập

~/.ssh/config

Và thêm

ServerAliveInterval 15
ServerAliveCountMax 4

Theo trang man cho ssh_config:

ServerAliveCountMax
         Sets the number of server alive messages (see below) which may be
         sent without ssh(1) receiving any messages back from the server.
         If this threshold is reached while server alive messages are
         being sent, ssh will disconnect from the server, terminating the
         session.  It is important to note that the use of server alive
         messages is very different from TCPKeepAlive (below).  The server
         alive messages are sent through the encrypted channel and there‐
         fore will not be spoofable.  The TCP keepalive option enabled by
         TCPKeepAlive is spoofable.  The server alive mechanism is valu‐
         able when the client or server depend on knowing when a connec‐
         tion has become inactive.

         The default value is 3.  If, for example, ServerAliveInterval
         (see below) is set to 15 and ServerAliveCountMax is left at the
         default, if the server becomes unresponsive, ssh will disconnect
         after approximately 45 seconds.  This option applies to protocol
         version 2 only.

 ServerAliveInterval
         Sets a timeout interval in seconds after which if no data has
         been received from the server, ssh(1) will send a message through
         the encrypted channel to request a response from the server.  The
         default is 0, indicating that these messages will not be sent to
         the server.  This option applies to protocol version 2 only.

Cứ sau 15 giây dường như khá thường xuyên để ping máy chủ.
Lambart

@Lambart nhưng nếu kết nối thực sự không ổn định và thường xuyên ngắt kết nối, ít nhất nó sẽ phát hiện ra kết nối chết và cho cơ hội thử lại sớm hơn.
b Liệu

4

ExitOnForwardFailure yeslà một sự bổ sung tốt cho các đề xuất khác. Nếu nó kết nối nhưng không thể thiết lập cổng chuyển tiếp thì nó cũng vô dụng với bạn như thể nó hoàn toàn không kết nối.


Đây là một ý tưởng rất tốt. Ngay cả autossh cũng vô dụng nếu kết nối trước đó được coi là hết thời gian sớm hơn ở phía xa so với máy chủ cục bộ, vì trong trường hợp này, máy chủ cục bộ sẽ cố gắng kết nối lại, nhưng chuyển tiếp không thể được thiết lập vì cổng vẫn mở.
Raúl Salinas-Monteagudo

1

Tôi đã có nhu cầu duy trì đường hầm SSH lâu dài. Giải pháp của tôi là chạy từ máy chủ Linux và đó chỉ là một chương trình C nhỏ đáp ứng ssh bằng xác thực dựa trên khóa.

Tôi không chắc chắn về việc treo cổ, nhưng tôi đã có đường hầm chết do hết thời gian.

Tôi rất thích cung cấp mã cho người phản hồi, nhưng dường như tôi không thể tìm thấy nó ngay bây giờ.


1

trong khi có các công cụ như autossh giúp khởi động lại phiên ssh ... điều tôi thấy thực sự hữu ích là chạy lệnh 'screen'. Nó cho phép bạn TIẾP TỤC các phiên ssh của bạn ngay cả sau khi bạn ngắt kết nối. Đặc biệt hữu ích nếu kết nối của bạn không đáng tin cậy như mong muốn.

... đừng quên đánh dấu đây là câu trả lời 'chính xác' nếu nó giúp bạn k! ;-)


7
... nhưng câu hỏi là về cách giữ cho các đường hầm SSH mở, không chỉ là một phiên cuối. Màn hình IS tuyệt vời mặc dù!
akent

Tôi đã sử dụng màn hình, nhưng nó không giải quyết được vấn đề của tôi: - / Mặc dù vậy, cảm ơn câu trả lời của bạn.
Peltier

1

Một chút hack, nhưng tôi thích sử dụng màn hình để giữ điều này. Tôi hiện có một điều khiển từ xa đã chạy trong nhiều tuần.

Ví dụ, bắt đầu tại địa phương:

screen
ssh -R ......

Khi điều khiển từ xa được áp dụng và bạn có một vỏ trên máy tính từ xa:

screen
Ctrl + a + d

Bây giờ bạn có một điều khiển từ xa không bị gián đoạn. Mẹo nhỏ là chạy màn hình ở cả hai đầu


1

Gần đây có vấn đề này, bởi vì các giải pháp này yêu cầu bạn nhập lại mật khẩu mỗi lần nếu bạn sử dụng mật khẩu đăng nhập, tôi đã sử dụng sshpass trong một vòng lặp cùng với lời nhắc văn bản để tránh có mật khẩu trong tệp bó.

Tôi nghĩ rằng tôi sẽ chia sẻ giải pháp của mình về vấn đề này trong trường hợp bất kỳ ai khác có cùng vấn đề:

#!/bin/bash
read -s -p "Password: " pass
while true
do
    sshpass -p "$pass" ssh user@address -p port
    sleep 1
done

0

Tôi đã có vấn đề tương tự với ISP trước đây của tôi. Đối với tôi nó cũng giống với bất kỳ kết nối tcp nào, truy cập trang web hoặc gửi thư.

Giải pháp là cấu hình kết nối VPN qua UDP (Tôi đang sử dụng OpenVPN). Kết nối này khoan dung hơn với bất cứ điều gì gây ra sự ngắt kết nối. Sau đó, bạn có thể chạy bất kỳ dịch vụ thông qua kết nối này.

Vẫn có thể có vấn đề với kết nối nhưng vì đường hầm sẽ khoan dung hơn nên bất kỳ phiên ssh nào cũng sẽ cảm thấy bị giữ lại ngắn hơn là bị ngắt kết nối.

Để làm điều này, bạn sẽ cần một dịch vụ VPN trực tuyến mà bạn có thể thiết lập trên máy chủ của riêng mình.


0

autosshkhông đáp ứng nhu cầu của chúng tôi (nó tồn tại do lỗi nếu không thể kết nối với máy chủ ngay lần thử đầu tiên), chúng tôi đã viết một ứng dụng bash thuần túy: https://github.com/aktos-io/link- với máy chủ

Nó tạo một đường hầm ngược cho cổng sshd của NODE (22) trên máy chủ theo mặc định. Nếu bạn cần thực hiện bất kỳ hành động nào khác (như chuyển tiếp các cổng bổ sung, gửi thư trên kết nối, v.v ...), bạn có thể đặt tập lệnh on-connecton-disconnectthư mục của mình.

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.