Difference between revisions of "Secure Shell (简体中文)"

From ArchWiki
Jump to navigation Jump to search
m (添加相关链接)
(update interlanguage links)
Tag: wiki-scripts
 
(52 intermediate revisions by 12 users not shown)
Line 1: Line 1:
 +
[[Category:简体中文]]
 
[[Category:Secure Shell (简体中文)]]
 
[[Category:Secure Shell (简体中文)]]
{{translateme (简体中文)}}
 
[[ar:Ssh]]
 
[[de:SSH]]
 
 
[[en:Secure Shell]]
 
[[en:Secure Shell]]
[[es:Secure Shell]]
+
[[es:OpenSSH]]
[[fr:ssh]]
 
[[it:Secure Shell]]
 
[[ja:Secure Shell]]
 
[[ko:Secure Shell]]
 
[[pl:Secure Shell]]
 
 
[[pt:Secure Shell]]
 
[[pt:Secure Shell]]
[[ru:Secure Shell]]
+
[[ru:OpenSSH]]
[[sr:Secure Shell]]
 
 
{{Related articles start (简体中文)}}
 
{{Related articles start (简体中文)}}
{{Related|SSH keys}}
+
{{Related|SSH keys (简体中文)}}
 
{{Related|Pam abl}}
 
{{Related|Pam abl}}
 
{{Related|fail2ban}}
 
{{Related|fail2ban}}
 
{{Related|sshguard}}
 
{{Related|sshguard}}
{{Related|Sshfs}}
+
{{Related|SSHFS (简体中文)}}
 
{{Related|Syslog-ng}}
 
{{Related|Syslog-ng}}
 
{{Related|SFTP chroot}}
 
{{Related|SFTP chroot}}
 +
{{Related|SCP and SFTP}}
 
{{Related articles end}}
 
{{Related articles end}}
 +
 +
{{TranslationStatus (简体中文)|Secure Shell|2018-05-04|518925}}
 +
 
'''Secure Shell''' ('''SSH''') 是一个允许两台电脑之间通过安全的连接进行数据交换的网络协议。加密保证了数据的保密性和完整性。SSH采用公钥加密技术来验证远程主机,以及(必要时)允许远程主机验证用户。
 
'''Secure Shell''' ('''SSH''') 是一个允许两台电脑之间通过安全的连接进行数据交换的网络协议。加密保证了数据的保密性和完整性。SSH采用公钥加密技术来验证远程主机,以及(必要时)允许远程主机验证用户。
  
 
SSH 通常用于远程访问和执行命令,但是它也支持隧道,转发任意 TCP 端口以及 X11 连接;它还能够用 SFTP 或 SCP 协议来传输文件。
 
SSH 通常用于远程访问和执行命令,但是它也支持隧道,转发任意 TCP 端口以及 X11 连接;它还能够用 SFTP 或 SCP 协议来传输文件。
  
一个 SSH 服务器,默认地,在 TCP 端口 22 进行监听。一个 SSH 客户端程序通常被用来建立一个远程连接到 '''sshd''' 守护进程。这两者都被广泛地存在于现代操作系统中,包括 Mac OS X,GNU/Linux,Solaris 和 OpenVMS 等。以专利的,自由软件的以及开源版本的形式和不同的复杂性和完整性存在。
+
一个 SSH 服务器默认情况下,在 TCP 端口 22 进行监听。一个 SSH 客户端程序通常被用来建立一个远程连接到 '''sshd''' 守护进程。这两者都被广泛地存在于现代操作系统中,包括 Mac OS X,GNU/Linux,Solaris 和 OpenVMS 等。以专有软件、自由软件以及开源版本的形式和不同的复杂性和完整性存在。
  
(Source: [[Wikipedia:Secure Shell]])
+
(来源:[[Wikipedia:Secure Shell|维基百科 Secure Shell]])
  
 
== OpenSSH ==
 
== OpenSSH ==
  
OpenSSH (OpenBSD Secure Shell) 是一套使用ssh协议,通过计算机网络,提供加密通讯会话的计算机程序。它被创建为 SSH Communications Security 公司拥有专利的 Secure Shell 软件套装的一个开源替代。OpenSSH是由Theo de Raadt领导的OpenBSD项目的一部分。
+
OpenSSH (OpenBSD Secure Shell) 是一套使用 ssh 协议,通过计算机网络,提供加密通讯会话的计算机程序。它被创建为 SSH Communications Security 公司拥有专利的 Secure Shell 软件套装的一个开源替代。OpenSSH 是由 Theo de Raadt 领导的 OpenBSD 项目的一部分。
  
 
人们常把 OpenSSH 与相似名字的 OpenSSL 搞混,但是,这两个项目是由不同的团队出于不同的目的开发出来的。相似的名字只是由于相似的目标。
 
人们常把 OpenSSH 与相似名字的 OpenSSL 搞混,但是,这两个项目是由不同的团队出于不同的目的开发出来的。相似的名字只是由于相似的目标。
  
 
=== 安装OpenSSH ===
 
=== 安装OpenSSH ===
从[[官方源]]中[[安装]] {{ic|openssh}}
+
从[[官方源]]中[[安装]] {{pkg|openssh}}.
  # pacman -S openssh
+
 
 +
 
 +
===SSH 客户端===
 +
 
 +
连接SSH服务器,运行命令
 +
 
 +
$ ssh -p ''port'' ''user''@''server-address''
 +
 
 +
如果服务器仅允许使用密钥登录,请参考 [[SSH keys (简体中文)|SSH Keys]] 。
 +
 
 +
====配置====
 +
 
 +
客户端可以在配置文件中存储常用选项和常用主机,下列选项都可以应用至全局或应用至特定主机。 例如:
 +
 
 +
{{hc|~/.ssh/config|# global options
 +
User ''user''
 +
 
 +
# host-specific options
 +
Host myserver
 +
    HostName ''server-address''
 +
    Port    ''port''}}
 +
 
 +
进行了如上的配置后,以下命令是等效的
 +
$ ssh -p ''port'' ''user''@''server-address''
 +
$ ssh myserver
 +
 
 +
查看 {{man|5|ssh_config}} 获取更多信息。
 +
 
 +
某些选项没有命令行参数,但是可以使用 {{ic|-o}} 在命令行中配置指定选项的参数。
 +
例如 {{ic|1=-oKexAlgorithms=+diffie-hellman-group1-sha1}}.
 +
 
 +
===SSH 服务端===
 +
 
 +
====配置====
 +
 
 +
SSH 守护进程的配置文件是{{ic|/etc/ssh/ssh'''d'''_config}}。
 +
 
 +
只允许某些用户访问的话,加入这一行:
 +
AllowUsers    ''user1 user2''
 +
 
 +
只允许一些组访问:
 +
AllowGroups  ''group1 group2''
 +
 
 +
你也可以运行以下命令关联文件(如{{ic|/etc/issue}}文件)到登录欢迎信息:
 +
  Banner /etc/issue
 +
 
 +
公钥和私钥在 ''sshd'' [[#管理 sshd 守护进程|service 文件]] 安装的时候就自动生成在 {{ic|/etc/ssh}} 里面了,四个秘钥对分别由四种算法生成: [[SSH_keys#Choosing_the_authentication_key_type|dsa、rsa、ecdsa 和 ed25519]]。要让 sshd 使用一组特定的密钥,请指定以下选项:
 +
 
 +
  HostKey /etc/ssh/ssh_host_rsa_key
 +
 
 +
如果此服务器在公网中,建议运行以下命令以更改sshd服务监听端口:
 +
  Port 39901
 +
 
 +
{{Tip|
 +
* 参考 [[Wikipedia:List of TCP and UDP port numbers|TCP 和 UDP 端口号列表]] 和本地的 {{ic|/etc/services}} 文件来选择一个未被常用服务占用的端口。把端口从默认的 22 改成别的可以减少由于端口扫描器尝试自动登录造成的登录日志条目,更多信息请参考 [[Port knocking]]。
 +
* 完全取消密码登录方式可以极大的增强安全性,请查看[[#强制公钥验证]]。查看[[#安全防护]]了解更多增强安全性的手段。
 +
* OpenSSH 可以监听多个端口,只需在配置文件中加入多行{{ic|Port ''port_number''}}即可。}}
 +
 
 +
==== 管理 sshd 守护进程 ====
 +
 
 +
{{Pkg|openssh}} 包括了两种 [[systemd]] 服务:
 +
#{{ic|sshd.service}},使 SSH 守护进程始终运行,并为每个入站连接创建子进程。[https://projects.archlinux.org/svntogit/packages.git/tree/trunk/sshd.service?h=packages/openssh#n16] 适用于有大量 SSH 流量的系统。[https://projects.archlinux.org/svntogit/packages.git/tree/trunk/sshd.service?h=packages/openssh&id=4cadf5dff444e4b7265f8918652f4e6dff733812#n15]
 +
#{{ic|sshd.socket}} + {{ic|sshd@.service}}, 为每个连接生成 SSH 守护进程的实例。它意味着让 ''systemd'' 监听 SSH socket,并且只有在有连接传入时启动守护进程。几乎所有情况下都推荐使用{{ic|sshd}}。 [https://projects.archlinux.org/svntogit/packages.git/tree/trunk/sshd.service?h=packages/openssh&id=4cadf5dff444e4b7265f8918652f4e6dff733812#n18][http://lists.freedesktop.org/archives/systemd-devel/2011-January/001107.html][http://0pointer.de/blog/projects/inetd.html]
 +
 
 +
[[start]] 并 [[enable]] {{ic|sshd.service}} '''或''' {{ic|sshd.socket}} 中的任何一个都可以启动守护进程。
 +
 
 +
如果选择了 sshd.socket,并且不在默认的 22 端口监听,你需要[[edit|编辑]] ststemd 单元文件:
 +
 
 +
{{hc|# systemctl edit sshd.socket|<nowiki>
 +
[Socket]
 +
ListenStream=
 +
ListenStream=12345
 +
</nowiki>}}
 +
 
 +
{{警告|使用 {{ic|sshd.socket}} 会使 {{ic|ListenAddress}} 设置无效,这将允许来自任何地址的连接。为了达到与 {{ic|ListenAddress}} 一样设置 IP 的效果, 你必须在 {{ic|ListenStream}} 中指定端口''和'' IP (例如:{{ic|1=ListenStream=192.168.1.100:22}})。你还需要在 {{ic|[Socket]}} 下面增加 {{ic|1=FreeBind=true}},否则设置 IP 与设置 {{ic|ListenAddress}} 有着相同的缺陷:如果网络未及时启动,socket 将无法启动。}}
 +
 
 +
{{提示|打开 {{ic|sshd.service}} 时将为每个连接启动一个 {{ic|sshd@.service}} 的临时实例(实例名称不同)。因此,{{ic|sshd.socket}} 和常规 {{ic|sshd.service}} 都不允许监视日志中的连接尝试。使用 {{ic|journalctl -u "sshd@*"}} 或 {{ic|journalctl /usr/bin/sshd}} 可以看到 socket 激活的 SSH 实例的日志。}}
 +
 
 +
==== 安全防护 ====
 +
 
 +
允许通过SSH进行远程登录对管理服务器很有用,但也会对服务器构成安全威胁。SSH 通常是暴力攻击的目标,因此 SSH 访问需要适当限制,以防止第三方访问您的服务器。
 +
 
 +
下列是有关该主题的优秀指南:
 +
 
 +
*[https://wiki.mozilla.org/Security/Guidelines/OpenSSH Article by Mozilla Infosec Team]
 +
*[https://stribika.github.io/2015/01/04/secure-secure-shell.html Secure sshd]
 +
 
 +
===== 强制公钥验证 =====
 +
 
 +
如果客户端无法通过公钥进行身份验证,则默认情况下,SSH服务器将使用密码来验证,从而允许恶意用户通过[[#防止暴力破解|暴力破解]]密码获取访问权限。一种防止此类攻击的有效方法是完全禁用密码登录,并强制使用[[SSH keys]]。可以在 {{ic|sshd_config}} 中禁用以下选项:
 +
 
 +
PasswordAuthentication no
 +
 
 +
{{警告|在将上述选项添加到你的配置之前,请确保所有需要 SSH 访问的帐户都在相应的 {{ic|authorized_keys}} 文件中设置了公钥验证。请参阅 [[SSH keys#Copying the public key to the remote server]] 以获取更多信息。}}
 +
 
 +
===== 双因素验证与公钥 =====
 +
 
 +
SSH 可以设置为采用多种方式进行身份验证,你可以使用 {{ic|AuthenticationMethods}} 选项来指明在登录时需要哪些身份验证方式。这使你可以用公钥与双因素验证结合来登录。
  
=== 配置SSH ===
+
参阅 [[Google Authenticator (简体中文)]] 来设置 Google Authenticator。
====客户端====
 
SSH客户端的配置文件是{{ic|/etc/ssh/ssh_config}}。
 
  
以下是一个范例::
+
为了使 [[PAM (简体中文)]] 与 OpenSSH 协同工作, 编辑下列文件:
  
{{hc|/etc/ssh/ssh_config|
+
{{hc|/etc/ssh/sshd_config|
# $OpenBSD: ssh_config,v 1.26 2010/01/11 01:39:46 dtucker Exp $
+
ChallengeResponseAuthentication yes
 +
AuthenticationMethods publickey keyboard-interactive:pam
 +
}}
  
# This is the ssh client system-wide configuration file.  See
+
然后,你可以使用公钥'''或''' PAM 中设置的用户验证信息两者之一登录。
# ssh_config(5) for more information.  This file provides defaults for
 
# users, and the values can be changed in per-user configuration files
 
# or on the command line.
 
  
# Configuration data is parsed as follows:
+
另外,如果你想登录时同时验证公钥'''和''' PAM,请使用逗号而不是空格来分隔 AuthenticationMethods:
#  1. command line options
 
#  2. user-specific file
 
#  3. system-wide file
 
# Any configuration value is only changed the first time it is set.
 
# Thus, host-specific definitions should be at the beginning of the
 
# configuration file, and defaults at the end.
 
  
# Site-wide defaults for some commonly used options.  For a comprehensive
+
{{hc|/etc/ssh/sshd_config|
# list of available options, their meanings and defaults, please see the
+
ChallengeResponseAuthentication yes
# ssh_config(5) man page.
+
AuthenticationMethods publickey''','''keyboard-interactive:pam
 +
}}
  
# Host *
+
通过要求提供公钥'''和''' PAM 认证,你可能希望禁用密码登录:
#  ForwardAgent no
+
{{hc|/etc/pam.d/sshd|
#  ForwardX11 no
+
auth      required  pam_securetty.so    #disable remote root
#  RhostsRSAAuthentication no
+
#Require google authenticator
#  RSAAuthentication yes
+
auth      required  pam_google_authenticator.so
#  PasswordAuthentication yes
+
#But not password
#  HostbasedAuthentication no
+
#auth      include   system-remote-login
#  GSSAPIAuthentication no
+
account   include   system-remote-login
#  GSSAPIDelegateCredentials no
+
password  include   system-remote-login
#  BatchMode no
+
session   include   system-remote-login
#  CheckHostIP yes
 
#  AddressFamily any
 
#  ConnectTimeout 0
 
#  StrictHostKeyChecking ask
 
#  IdentityFile ~/.ssh/identity
 
#   IdentityFile ~/.ssh/id_rsa
 
#   IdentityFile ~/.ssh/id_dsa
 
#   Port 22
 
Protocol 2,1
 
#   Cipher 3des
 
#   Ciphers aes128-ctr,aes192-ctr,aes256-ctr,arcfour256,arcfour128,aes128-cbc,3des-cbc
 
#   MACs hmac-md5,hmac-sha1,umac-64@openssh.com,hmac-ripemd160
 
#  EscapeChar ~
 
#  Tunnel no
 
#  TunnelDevice any:any
 
#  PermitLocalCommand no
 
#   VisualHostKey no
 
#   ProxyCommand ssh -q -W %h:%p gateway.example.com
 
 
}}
 
}}
  
推荐将“Protocol”行改为:
+
===== 防止暴力破解 =====
Protocol 2
+
暴力破解的概念很简单,即某人不断尝试用大量随机产生的用户名和密码对来登录网页或服务器的某个服务(比如 SSH)。
  
这表示只有协议2将被用到,因为协议1被认为一定程度上不太安全。
+
====== 使用 ufw ======
  
====守护进程====
+
请参阅 [[ufw#Rate limiting with ufw]].
SSH 守护进程的配置文件是{{ic|/etc/ssh/ssh'''d'''_config}}
+
 
 +
====== 使用 iptables ======
 +
 
 +
{{Merge|Simple_stateful_firewall#Bruteforce_attacks|Out of scope, same technique as already described in the SSF.}}
  
以下是一个范例::
+
如果你已经在用 iptables,可以配置以下规则来保护 SSH 免受暴破。
  
{{hc|/etc/ssh/sshd_config|2=
+
{{注意|此示例中 SSH 所用的 TCP 端口已经改为了 42660。}}
# $OpenBSD: sshd_config,v 1.82 2010/09/06 17:10:19 naddy Exp $
 
  
# This is the sshd server system-wide configuration file.  See
+
在应用后面的规则之前,我们先新建一条规则链来记录并拒绝过多的连接请求:
# sshd_config(5) for more information.
 
  
# This sshd was compiled with PATH=/usr/bin:/bin:/usr/sbin:/sbin
+
# iptables -N LOG_AND_DROP
  
# The strategy used for options in the default sshd_config shipped with
+
第一条规则将应用于预示 TCP 42660 端口有新连接的数据包:
# OpenSSH is to specify options with their default value where
 
# possible, but leave them commented.  Uncommented options change a
 
# default value.
 
  
#Port 22
+
# iptables -A INPUT -p tcp -m tcp --dport 42660 -m state --state NEW -m recent --set --name DEFAULT --rsource
#AddressFamily any
 
#ListenAddress 0.0.0.0
 
#ListenAddress ::
 
  
# The default requires explicit activation of protocol 1
+
下一条规则告诉 iptables 查找匹配前一条规则的数据包,这些数据包也来自已添加到监视列表中的主机。
#Protocol 2
 
  
# HostKey for protocol version 1
+
# iptables -A INPUT -p tcp -m tcp --dport 42660 -m state --state NEW -m recent --update --seconds 90 --hitcount 4 --name DEFAULT --rsource -j LOG_AND_DROP
#HostKey /etc/ssh/ssh_host_key
 
# HostKeys for protocol version 2
 
#HostKey /etc/ssh/ssh_host_rsa_key
 
#HostKey /etc/ssh/ssh_host_dsa_key
 
#HostKey /etc/ssh/ssh_host_ecdsa_key
 
  
# Lifetime and size of ephemeral version 1 server key
+
现在,让 iptables 决定如何处理 TCP 42660 端口的通信中不符合上述规则的数据包。
#KeyRegenerationInterval 1h
 
#ServerKeyBits 1024
 
  
# Logging
+
# iptables -A INPUT -p tcp -m tcp --dport 42660 -j ACCEPT
# obsoletes QuietMode and FascistLogging
 
#SyslogFacility AUTH
 
#LogLevel INFO
 
  
# Authentication:
+
我们向 LOG_AND_DROP 表增加如下规则,并使用 -j (jump) 参数将数据包的信息传递给日志记录工具。
  
#LoginGraceTime 2m
+
# iptables -A LOG_AND_DROP -j LOG --log-prefix "iptables deny: " --log-level 7
#PermitRootLogin yes
 
#StrictModes yes
 
#MaxAuthTries 6
 
#MaxSessions 10
 
  
#RSAAuthentication yes
+
在按照第一条规则进行记录后,所有数据包将被丢弃。
#PubkeyAuthentication yes
 
#AuthorizedKeysFile .ssh/authorized_keys
 
  
# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts
+
# iptables -A LOG_AND_DROP -j DROP
#RhostsRSAAuthentication no
 
# similar for protocol version 2
 
#HostbasedAuthentication no
 
# Change to yes if you don't trust ~/.ssh/known_hosts for
 
# RhostsRSAAuthentication and HostbasedAuthentication
 
#IgnoreUserKnownHosts no
 
# Don't read the user's ~/.rhosts and ~/.shosts files
 
#IgnoreRhosts yes
 
  
# To disable tunneled clear text passwords, change to no here!
+
====== 防止暴力破解的工具 ======
#PasswordAuthentication yes
 
#PermitEmptyPasswords no
 
  
# Change to no to disable s/key passwords
+
你可以用类似 [[fail2ban]] 或 [[sshguard]] 的自动防暴破的脚本来阻挡攻击者。
ChallengeResponseAuthentication no
 
  
# Kerberos options
+
* 仅允许来自受信任位置的 SSH 入站连接。
#KerberosAuthentication no
+
* 使用 [[fail2ban]] 或 [[sshguard]] 自动阻止多次密码验证失败的 IP 地址。
#KerberosOrLocalPasswd yes
+
* 使用 [https://github.com/jtniehof/pam_shield pam_shield] 来阻止在一定时间内执行过多登录尝试的 IP 地址。与 [[fail2ban]] 或 [[sshguard]]不同,该程序不考虑登录成功或失败。
#KerberosTicketCleanup yes
 
#KerberosGetAFSToken no
 
  
# GSSAPI options
+
===== 禁用或限制 root 账户登录 =====
#GSSAPIAuthentication no
+
{{Out of date|最新版本默认已禁用 root 账户登录。暂不清楚本节的哪些部分是多余的。}}
#GSSAPICleanupCredentials yes
 
  
# Set this to 'yes' to enable PAM authentication, account processing,
+
允许 root 账户随意通过 SSH 登录通常是不安全的,有两种方法可以限制 root 账户通过 SSH 登录,从而提高安全性。
# and session processing. If this is enabled, PAM authentication will
 
# be allowed through the ChallengeResponseAuthentication and
 
# PasswordAuthentication.  Depending on your PAM configuration,
 
# PAM authentication via ChallengeResponseAuthentication may bypass
 
# the setting of "PermitRootLogin without-password".
 
# If you just want the PAM account and session checks to run without
 
# PAM authentication, then enable this but set PasswordAuthentication
 
# and ChallengeResponseAuthentication to 'no'.
 
UsePAM yes
 
  
#AllowAgentForwarding yes
+
====== 禁用 root 登录 ======
#AllowTcpForwarding yes
 
#GatewayPorts no
 
#X11Forwarding no
 
#X11DisplayOffset 10
 
#X11UseLocalhost yes
 
#PrintMotd yes
 
#PrintLastLog yes
 
#TCPKeepAlive yes
 
#UseLogin no
 
#UsePrivilegeSeparation yes
 
#PermitUserEnvironment no
 
#Compression delayed
 
#ClientAliveInterval 0
 
#ClientAliveCountMax 3
 
#UseDNS yes
 
#PidFile /var/run/sshd.pid
 
#MaxStartups 10
 
#PermitTunnel no
 
#ChrootDirectory none
 
  
# no default banner path
+
Sudo 可以有选择地为需要 root 权限的操作提供相应的权限,且不需要登录 root 账户。这样即可关闭 root 登录,并且可以看做一种防范暴力攻击的安全措施,因为现在攻击者除了要猜测密码外还要猜测帐户名称。
#Banner none
 
  
# override default of no subsystems
+
通过编辑 {{ic|/etc/ssh/sshd_config}} 中的 "Authentication" 一节可以使 SSH 屏蔽 root 用户登录,只要将 {{ic|#PermitRootLogin prohibit-password}} 改成 {{ic|no}} 并取消该行注释即可:
Subsystem sftp /usr/lib/ssh/sftp-server
 
  
# Example of overriding settings on a per-user basis
+
{{hc|/etc/ssh/sshd_config|
#Match User anoncvs
+
PermitRootLogin no
# X11Forwarding no
+
...
# AllowTcpForwarding no
 
# ForceCommand cvs server
 
 
}}
 
}}
  
只允许某些用户访问的话,加入这一行:
+
然后 [[restart|重启]] SSH 守护进程。
  AllowUsers    user1 user2
+
 
 +
现在你将无法通过 root 账户登录,但仍可以用普通账户登录并使用 [[su]] 或者 [[sudo]] 来完成系统维护工作。
 +
 
 +
====== 限制 root 登录 ======
 +
 
 +
一些自动化的维护任务(比如远程备份整个系统)需要完整的 root 权限。要以安全的方式允许 root 登录而不是禁用它,可以只允许远程登录的 root 用户执行指定的命令,在 {{ic|~root/.ssh/authorized_keys}} 头部加上指定的密钥即可,例如:
 +
 
 +
command="/usr/lib/rsync/rrsync -ro /" ssh-rsa …
 +
 
 +
这样,任何用户持有该秘钥即可执行引号之间的命令。
 +
 
 +
为了弥补因 root 用户名称暴露而导致受攻击的可能性增加,可以将以下命令加入 {{ic|sshd_config}}:
 +
 
 +
PermitRootLogin forced-commands-only
 +
 
 +
该设置不仅会限制 root 用户通过 SSH 执行的命令,还会禁用密码登录方式,强制 root 帐户使用公钥登录。
 +
 
 +
如果不想限制 root 用户可执行的命令,可以仅关闭密码验证来强制使用公钥验证:
 +
 
 +
PermitRootLogin prohibit-password
 +
 
 +
===== 保护 authorized_keys 文件 =====
 +
 
 +
你可以阻止其他用户向该文件加入新公钥且通过新的公钥连接。
 +
 
 +
把 {{ic|authorized_keys}} 文件的权限全部去掉,只保留读权限:
 +
 
 +
$ chmod 400 ~/.ssh/authorized_keys
 +
 
 +
为防止用户把权限改回来,可以对 {{ic|authorized_keys}} 文件采取 [[File permissions and attributes#chattr and lsattr|set the immutable bit(设为不可变)]] 操作。尽管如此,用户仍然可以重命名 {{ic|~/.ssh}} 并新建一个 {{ic|~/.ssh}} 目录和 {{ic|authorized_keys}} 文件。所以 {{ic|~/.ssh}} 目录也要设置 immutable bit。
  
要禁止通过SSH进行root用户登录,加入以下行:
+
{{注意|如果你自己需要新增一个公钥,你需要先移除 {{ic|authorized_keys}} 文件的 immutable bit,并增加写权限,最后按上述步骤重新加密。}}
PermitRootLogin no
 
  
你也可以取消BANNER选项的注释,然后编辑{{ic|/etc/issue}}加入友好的欢迎信息内容。
+
== 其他 SSH 客户端与服务端 ==
 +
除了 OpenSSH,还有很多可用的 SSH [[Wikipedia:Comparison of SSH clients|客户端]] 和 [[Wikipedia:Comparison of SSH servers|服务端]]。
  
{{小贴士|你可能想要把默认的端口从22改成其它更高的端口(参考 [http://en.wikipedia.org/wiki/Security_through_obscurity security through obscurity]).}}
+
=== Dropbear ===
 +
[[Wikipedia:Dropbear (software)|Dropbear]] 是一个 SSH-2 客户端与服务端。 {{Pkg|dropbear}} 可以从 [[official repositories|官方仓库]] 下载。
  
尽管ssh的运行端口可以被像nmap这样的端口扫描器侦测到,但改变它可以减少由于自动验证的尝试造成的登录日志条目。
+
它的命令行版客户端叫 dbclient。
  
{{小贴士|完全取消密码登录方式可以极大的增强安全性,(参考 [[SSH Keys (简体中文)|SSH Keys]]).}}
+
=== Mosh ===
 +
来自 Mosh [http://mosh.mit.edu/ 网站]
  
=== 管理sshd守护进程 ===
+
:Remote terminal application that allows roaming, supports intermittent connectivity, and provides intelligent local echo and line editing of user keystrokes. Mosh is a replacement for SSH. It is more robust and responsive, especially over slow connections such as Wi-Fi, cellular, and long-distance.
你可以使用下面的命令启动sshd:
+
:翻译:允许“漫游”的远程终端,支持间歇性的连接,并提供对于用户按键的智能本地回馈和行编辑回馈。Mosh 是 SSH 的替代品。它更加强大而快速,特别针对诸如 Wi-Fi,移动网络和超远距离等慢速连接环境。
# systemctl start sshd
 
  
你可以使用下面的命令开机启动sshd:
+
[[Install|安装]] {{Pkg|mosh}} 这个包, 或安装最新版:{{AUR|mosh-git}}。
  # systemctl enable sshd.service
 
  
{{警告|Systemd 是一个异步启动的进程。如果你绑定 SSH 守护进程到某个特定的 IP 地址 {{ic|ListenAddress 192.168.1.100}},它可能会在引导时启动失败,因为默认的 sshd.service 单元文件没有对网络接口启动的依赖。当绑定到一个 IP 地址时,你需要添加 {{ic|After&#61;network.target}} 到自定义的 sshd.service 单元文件中。参见 [[Systemd#Replacing provided unit files]].}}
+
Mosh 有一个未写入文档的命令行选项:{{ic|1=--predict=experimental}},它可以产生更有力的本地按键响应。对降低键盘输入视觉上的延迟确认感兴趣的用户可能更喜欢这个预测模式。
  
或者你可以启用SSH Daemon socket,这样当第一次传入连接时启动守护进程:
+
{{提示|Mosh 从设计上就不允许你访问会话的历史记录,请考虑安装终端复用工具,如 [[tmux]] 或 [[GNU Screen]] 。}}
# systemctl enable sshd.socket
 
如果你使用非默认端口22,你必须在文件(/lib/systemd/system/sshd.socket)中设置"ListenStream"为相应的端口。
 
  
===连接到服务器===
+
== 提示与技巧 ==
  
运行下面的命令:
+
{{Accuracy|根据目前本文的布局,这部分看起来应该是通用的,但实际上大部分提供的技巧只能在 ''openssh'' 中使用。比如 ''dropbear'' (在 [[#Other SSH clients and servers]] 列表中) 不支持 SOCKS 协议。[https://en.wikipedia.org/wiki/Comparison_of_SSH_clients#Technical]}}
$ ssh -p port user@server-address
 
  
== 小细节和小戏法 ==
+
=== 加密 Socks 通道 ===
  
=== 加密Socks通道 ===
+
对于连接到各种不安全的无线网络上的笔记本电脑用户来说,这个是特别有用的!唯一所需要的就是一个一定程度上处于安全的地点的 SSH 服务器,比如在家里或办公室。用动态的 DNS 服务 [http://www.dyndns.org/ DynDNS] 也可能是很有用的,这样你就不必记住你的 IP 了。
对于连接到各种不安全的无线网络上的笔记本电脑用户来说,这个是特别有用的!唯一所需要的就是一个一定程度上处于安全的地点的SSH服务器,比如在家里或办公室。用动态的DNS服务[http://www.dyndns.org/ DynDNS]也可能是很有用的,这样你就不必记住你的IP了。
 
  
 
==== 第一步:开始连接 ====
 
==== 第一步:开始连接 ====
你只要在你喜欢的终端中执行这一个命令就能开始你的连接:
 
$ ssh -ND 4711 user@host
 
这里的“user”是你在“host”这台SSH服务器上运行的用户名。它会让你输入密码,然后你就能连上了。“N”表示不采用交互提示,而“D”表示指定监听的本地端口(你可以使用任何你喜欢的数字)
 
  
一个办法可以让这个过程更简单,那就是在~/.bashrc中加入这样一行:
+
你只要执行这一个命令就能开始你的连接:
  alias sshtunnel="ssh -ND 4711 -v user@host"
+
 
加入冗长的“-v”标志更好,因为这样你可以验证是真的是从那个端口连接的。现在你只要执行命令“sshtunnel”就可以。 :)
+
  $ ssh -TND 4711 ''user''@''host''
 +
 
 +
这里的 {{Ic|''user''}} 是你在 {{Ic|''host''}} 这台 SSH 服务器上的用户名。它会让你输入密码,然后你就能连上了。 {{Ic|N}} 表示不采用交互提示,而 {{Ic|D}} 表示指定监听的本地端口(你可以使用任何你喜欢的数字),{{Ic|T}} 表示禁用伪 tty 分配。
 +
 
 +
加了 {{Ic|-v}} (verbose) 标志以后的输出可以让你能够验证到底连了哪个端口。
  
 
==== 第二步:配置你的浏览器(或其它程序) ====
 
==== 第二步:配置你的浏览器(或其它程序) ====
  
如果你不配置你的web浏览器以便使用新创建的socks通道的话,上面的一步完全没用!
+
如果你没有配置你的浏览器(或其他程序)使用这个新创建的 socks 隧道,上述步骤是无效的。由于当前版本的 SSH 支持 SOCKS4 和 SOCKS5,因此您可以使用其中任何一种。
  
* 对于Firefox: ''Edit -> Preferences -> Advanced -> Network -> Connection -> Setting'':
+
* 对于 Firefox: ''Edit > Preferences > Advanced > Network > Connection > Setting'': <br> 选中 ''Manual proxy configuration'' 单选框, 然后在 ''SOCKS host'' 里输入 {{ic|localhost}}, 然后在后面那个框中输入你的端口号(本例中为 {{ic|4711}})。
: 检查"Manual proxy configuration" radio button?, 并且在"SOCKS host" 文本段输入"localhost" , 然后在接下来的一个文本框中输入你的端口数(上面我们用的是4711)。
 
  
: 确定你选择使用SOCKS4。这个程序对不会对SOCKS5起作用.
+
Firefox 不会自动通过 socks 隧道发送 DNS 请求,这一潜在的隐私问题可以通过以下步骤来解决:
  
:享受你的安全通道吧!
+
# 在 Firefox 地址栏中输入:about:config 。
 +
# 搜索:network.proxy.socks_remote_dns
 +
# 将该值设为 true。
 +
# 重启浏览器。
  
=== X11 Forwarding ===
+
* 对于 Chromium: 你可以将 SOCKS 设置设置为环境变量或命令行选项。我建议将下列函数之一加入到你的 {{ic|.bashrc}}:
 +
function secure_chromium {
 +
    port=4711
 +
    export SOCKS_SERVER=localhost:$port
 +
    export SOCKS_VERSION=5
 +
    chromium &
 +
    exit
 +
}
 +
或者
 +
function secure_chromium {
 +
    port=4711
 +
    chromium --proxy-server="socks://localhost:$port" &
 +
    exit
 +
}
  
为了通过SSH运行图形程序你必须使用X11 Forwarding。一个选项就是需要设置服务器和客户端的配置文件(这里所说的“客户端”指运行你的X11服务器的电脑,而你的X应用程序运行在“服务器”上)。
+
现在打开终端然后输入:
  
在服务器上安装xorg-xauth:
+
  $ secure_chromium
  # pacman -S xorg-xauth
 
  
* 在'''服务器'''上,编辑{{ic|ssh'''d'''_config}}启用'''AllowTcpForwarding'''选项。
+
享受你的安全隧道吧!
* 在'''服务器'''上,编辑{{ic|ssh'''d'''_config}}启用'''X11Forwarding'''选项。
 
* 在'''服务器'''上,编辑{{ic|ssh'''d'''_config}}将'''X11DisplayOffset'''选项设为10。
 
* 在'''服务器'''上,编辑{{ic|ssh'''d'''_config}}启用'''X11UseLocalhost'''选项。
 
同时:
 
* 在'''客户端'''上,编辑{{ic|ssh_config}}启用'''ForwardX11'''选项。
 
  
当GUI大量绘制时,请启用'''ForwardX11Trusted'''。
+
=== X11 转发 ===
  
为了使用X11 forwarding,首先通过SSH登陆到你的服务器:
+
为了通过 SSH 运行图形程序你必须使用 X11 转发 (forwarding)。这不要求对端安装了完整的 X11,但是至少要装好 ''xauth''。''xauth'' 是一个用来管理 {{ic|Xauthority}} 配置的工具,该配置用于服务器与客户端之间的 X11 会话认证([http://xmodulo.com/2012/11/how-to-enable-x11-forwarding-using-ssh.html source])。
  $ ssh -X -p port user@server-address
+
 
如果你在运行图形应用程序时收到了一些错误,请尝试可信的X11 forwarding:
+
{{警告|X11 转发有着重要的安全问题需要考虑,至少应先阅读 {{man|1|ssh}}、{{man|5|sshd_config}} 和 {{man|5|ssh_config}} 手册页。也可以参考 [https://security.stackexchange.com/questions/14815/security-concerns-with-x11-forwarding 这个 StackExchange 帖]。}}
  $ ssh -Y -p port user@server-address
+
 
你现在可以在远程服务器上启用任何的X程序,输出将会被转发到你的本地会话中:
+
==== 配置 ====
 +
 
 +
在远程主机上:
 +
 
 +
*[[install|安装]] {{Pkg|xorg-xauth}} 和 {{Pkg|xorg-xhost}} 这两个包
 +
*在 {{ic|/etc/ssh/ssh'''d'''_config}} 上:
 +
**确保 {{ic|AllowTcpForwarding}} 和 {{ic|X11UseLocalhost}} 已经设置为 ''yes'',并且 {{ic|X11DisplayOffset}} 设置为 ''10'' (这些是默认设置,参考 {{man|5|sshd_config}})
 +
**将 {{ic|X11Forwarding}} 设置为 ''yes''
 +
* 最后 [[restart|重启]]  [[#Daemon management|''sshd'' 守护进程]]{{Broken section link}}.
 +
 
 +
在客户端上,通过在命令行设置 {{ic|-X}} 参数启用 {{ic|ForwardX11}},或者在[[#配置|客户端配置文件]]中将 {{ic|ForwardX11}} 设置为 ''yes''。
 +
 
 +
{{提示|如果 GUI 绘制不正常或者有错误提示,你可以启用 {{ic|ForwardX11Trusted}} 选项(或在命令行中加上 {{ic|-Y}} 参数),这将使 X11 转发脱离 [http://www.x.org/wiki/Development/Documentation/Security/ X11 SECURITY extension] 的控制,如果你这样做,请确保已经读过本节开头的[[#X11 转发|警告]]。}}
 +
 
 +
==== 使用方法 ====
 +
 
 +
{{Accuracy|{{ic|xhost}} [http://unix.stackexchange.com/questions/12755/how-to-forward-x-over-ssh-from-ubuntu-machine#comment-17148 通常不是必须的]}}
 +
 
 +
正常登录远程主机,如果客户端的配置文件中没有启用 ''ForwardX11'' 那就加上 {{ic|-X}} 参数:
 +
  $ ssh -X ''user@host''
 +
 
 +
如果在运行图形程序的时候碰到错误,尝试用 ''ForwardX11Trusted'' 代替 ''ForwardX11'' :
 +
  $ ssh -Y ''user@host''
 +
 
 +
现在你应该可以运行服务器上的任何 X 图形程序,任何输出都会重定向至你当前的会话:
 
  $ xclock
 
  $ xclock
  
如果你遇到了“Cannot open display”错误,请尝试使用非root账户执行下述命令:
+
如果碰到 "Cannot open display" 的错误,请尝试用非管理员账户运行下列命令:
 
  $ xhost +
 
  $ xhost +
  
上述命令将会允许任何用户转发X11应用程序。为了限制只能转发到特定主机:
+
上述命令将允许任何人转发 X11 应用程序,这个命令可以限制特定的主机类型:
 
  $ xhost +hostname
 
  $ xhost +hostname
  
上述命令中的hostname是你想要转发到的主机名。使用“man xhost”获取更多信息。
+
其中 hostname 是要转发到的特定主机的名称。更多信息可以查看 {{man|1|xhost}}。
 +
 
 +
请注意某些应用程序,它们会检查本地计算机上正在运行的实例。[[Firefox]] 就是其中之一:你可以关掉本机上的 Firefox 或者使用以下启动参数来启动远程实例:
 +
$ firefox --no-remote
 +
 
 +
当你连接时收到 "X11 forwarding request failed on channel 0" 错误(或者服务器上的 {{ic|/var/log/errors.log}} 文件显示 "Failed to allocate internet-domain X11 display socket" 错误),请确保已经安装 {{Pkg|xorg-xauth}},如果装完了仍然不起作用,尝试以下方法之一:
 +
 
 +
* 在''服务器''的 {{ic|ssh'''d'''_config}} 中启用 {{ic|AddressFamily any}} 选项,或者
 +
* 将''服务器''的 {{ic|ssh'''d'''_config}} 中的 {{ic|AddressFamily}} 选项设为 inet。
 +
将其设置为 inet 可能会修复 IPv4 上的 Ubuntu 客户端的问题。
 +
 
 +
要以其他用户身份运行 SSH 服务器上的 X 应用程序,你需要先用已知用户登录,取出 {{Ic|xauth list}} 中的身份认证行,然后 {{Ic|xauth add}} 它。
 +
 
 +
{{提示|[http://unix.stackexchange.com/a/12772/29867 这里] 是 [http://unix.stackexchange.com/a/46748/29867 一些] 用来诊断 {{ic|X11 Forwarding}} 问题有用的 [http://superuser.com/a/805060/185665 链接]。}}
  
值得注意的是,一些应用程序会检查本地正在运行的实例。Firefox就是一个例子。你可以关闭正在执行的Firefox,或者使用下述启动参数来在本地启动一个远程实例:
+
=== 转发其他端口 ===
$ firefox -no-remote
 
  
=== Forwarding Other Ports ===
+
除了 SSH 内建的对 X11 的支持之外,它也能通过本地转发和远程转发,来为任何的TCP连接建立隧道。
In addition to SSH's built-in support for X11, it can also be used to securely tunnel any TCP connection, by use of local forwarding or remote forwarding.
 
  
Local forwarding opens a port on the local machine, connections to which will be forwarded to the remote host and from there on to a given destination. Very often, the forwarding destination will be the same as the remote host, thus providing a secure shell and, e.g. a secure VNC connection, to the same machine. Local forwarding is accomplished by means of the {{Ic|-L}} switch and it's accompanying forwarding specification in the form of {{Ic|<tunnel port>:<destination address>:<destination port>}}.
+
本地转发时,会在本机打开一个端口,连接将被转发到一个远程主机,并给定一个目的地。很多时候,转发目的地和远程主机会相同,因此也提供了一条SSH命令来建立一个安全的VNC连接。本地转发可以通过 {{Ic|-L}} 来设置,后面可以指定一个地址及端口 {{Ic|<tunnel port>:<destination address>:<destination port>}}
  
Thus:
+
如下:
  
 
  $ ssh -L 1000:mail.google.com:25 192.168.0.100
 
  $ ssh -L 1000:mail.google.com:25 192.168.0.100
  
will use SSH to login to and open a shell on 192.168.0.100, and will also create a tunnel from the local machine's TCP port 1000 to mail.google.com on port 25. Once established, connections to localhost:1000 will connect to the Gmail SMTP port. To Google, it will appear that any such connection (though not necessarily the data conveyed over the connection) originated from 192.168.0.100, and such data will be secure as between the local machine and 192.168.0.100, but not between 192.168.0.100, unless other measures are taken.
+
以上指令将会通过 SSH 得到一个在 {{ic|192.168.0.100}} 的 shell,同时也会创建一个从本机 TCP 1000 端口到 mail.google.com 上的 25 端口的隧道。建立之后,通过 {{ic|localhost:1000}} 的连接可以直接连接到 Gmail SMTP 端口。对 Google 而言,任何这样的连接都是来自 {{ic|192.168.0.100}} 的(即使这些连接中没有数据传输),并且,在本机和 192.168.0.100 之间的数据传递是安全的,但 {{ic|192.168.0.100}} 和 Google 之间是不安全的,除非还采取了别的手段保障数据安全。
  
Similarly:
+
同样:
  
 
  $ ssh -L 2000:192.168.0.100:6001 192.168.0.100
 
  $ ssh -L 2000:192.168.0.100:6001 192.168.0.100
  
will allow connections to localhost:2000 which will be transparently sent to the remote host on port 6001. The preceding example is useful for VNC connections using the vncserver utility--part of the tightvnc package--which, though very useful, is explicit about its lack of security.
+
以上指令会将到 {{ic|localhost:2000}} 的连接直接转发到远程主机 192.168.0.100 的 6001 端口。对于使用 VNC 服务器(tightvns包的一部分)建立的 VNC 连接来说,以上的例子尽管很有效,但是安全性有待商榷。
  
Remote forwarding allows the remote host to connect to an arbitrary host via the SSH tunnel and the local machine, providing a functional reversal of local forwarding, and is useful for situations where, e.g., the remote host has limited connectivity due to firewalling. It is enabled with the {{Ic|-R}} switch and a forwarding specification in the form of {{Ic|<tunnel port>:<destination address>:<destination port>}}.
+
远程转发允许任何远程主机通过 SSH 隧道连接到本机,提供了和本地转发相反的功能,突破了防火墙的限制。通过 {{Ic|-R}} 参数,以及 {{Ic|<tunnel port>:<destination address>:<destination port>}} 能够实现远程转发。
  
Thus:
+
如下:
  
 
  $ ssh -R 3000:irc.freenode.net:6667 192.168.0.200
 
  $ ssh -R 3000:irc.freenode.net:6667 192.168.0.200
  
will bring up a shell on 192.168.0.200, and connections from 192.168.0.200 to itself on port 3000 (remotely speaking, localhost:3000) will be sent over the tunnel to the local machine and then on to irc.freenode.net on port 6667, thus, in this example, allowing the use of IRC programs on the remote host to be used, even if port 6667 would normally be blocked to it.
+
将会在 {{ic|192.168.0.200}} 上得到一个 shell,同时,来自 {{ic|192.168.0.200}} 的 3000 端口(远程主机的 {{ic|localhost:3000}})的数据将会通过隧道转发至本机,然后转发至 irc.freenode.net 上的 6667 端口。因此,在这个例子中,在远程主机上能够使用 IRC 程序,即使端口 6667 被阻止。
 +
 
 +
本地转发和远程转发都可以提供一个安全的“网关”,允许其他计算机无需运行 SSH 或者 SSH daemon 来使用 SSH 隧道,即在隧道起点提供绑定的地址,作为转发规则。例如 {{Ic|<tunnel address>:<tunnel port>:<destination address>:<destination port>}}。{{Ic|<tunnel address>}} 可以是作为隧道起点的机器上的任何地址,地址 {{Ic|localhost}} 允许来自本地回环的连接,空地址 {{Ic|*}} 允许来自任意网卡的连接。默认情况下,转发仅限于连接至位于隧道“起点”的主机,即 {{Ic|<tunnel address>}} 被设置为 {{Ic|localhost}}。本地转发不需要额外的设置,而远程转发受限于对端的 SSH daemon 设置。更多关于远程转发和本地转发的信息可分别参阅 {{man|5|sshd_config}} 中的 {{Ic|GatewayPorts}} 选项和 {{man|1|ssh}} 中的 {{ic|-L address}} 选项。
 +
 
 +
=== 跳板机 ===
 +
 
 +
在某些情况下,你与目标主机之间可能无法直接连接,此时就要用到跳板机。因此,我们尝试将两个或更多 SSH 隧道连接在一起,并假设您的本地密钥已针对链中的每个服务器授权。这可以通过使用SSH代理转发 ({{ic|-A}}) 和伪终端分配 ({{ic|-t}}) 来实现,它使用以下语法转发本地密钥:
 +
 
 +
$ ssh -A -t -l user1 bastion1 \
 +
  ssh -A -t -l user2 intermediate2 \
 +
  ssh -A -t -l user3 target
 +
 
 +
一个更简单的方法是使用 {{ic|-J}} 选项:
 +
 
 +
$ ssh -J user1@bastion1,user2@intermediate2 user3@target
 +
 
 +
 
 +
{{ic|-J}} 指令中的多个主机可以用逗号隔开,它们将按照列出的顺序连接。{{ic|user...@}} 部分不是必需的,但可以使用。定义 {{ic|-J}} 选项里的不同的主机规格可以使用 ssh 配置文件,因此如果需要,可以在那里设置特定的每个主机选项。
 +
 
 +
=== 通过中继反向 SSH 连接 ===
 +
 
 +
{{Style|SSH 隧道的设想是很经典的,所以添加一些参考资料的详细解释会更好。比如 [https://unix.stackexchange.com/questions/46235/how-does-reverse-ssh-tunneling-work/118650#118650] 涵盖了一些其他情况。}}
 +
 
 +
这个想法是客户端通过一个中继连接到服务器,而服务器使用反向 SSH 隧道连接到同一个中继。例如,当服务器位于 NAT 后面时,这是很有用的,而此处的中继是一个可公开访问的 SSH 服务器,用作用户有权访问的代理服务器。前提是客户端的密钥同时对中继和服务器都已经授权,服务器需要授权中继用于反向 SSH 连接。
 +
 
 +
以下配置假设 user1 是客户端使用的账户,user2 是中继的,user3 是服务器的。首先服务器要先建立反向隧道:
 +
 
 +
ssh -R 2222:localhost:22 -N user2@relay
 +
 
 +
这可以利用启动脚本、systemd service 或者 {{Pkg|autossh}} 来自动完成。
 +
 
 +
{{Expansion|需要解释为何光有 {{ic|ssh user3@relay -p 2222}} 是不够的。}}
 +
 
 +
在客户端使用以下命令建立连接:
 +
 
 +
ssh user2@relay ssh user3@localhost -p 2222
 +
 
 +
可以在中继的 {{ic|~/.ssh/authorized_keys}} 中定义 {{ic|command}} 字段来建立反向隧道:
 +
 
 +
command="ssh user3@localhost -p 2222" ssh-rsa KEY2 user1@client
 +
 
 +
在这种情况下用下列命令建立连接:
 +
 
 +
ssh user2@relay
 +
 
 +
注意,客户端内 scp 的自动完成功能失效,甚至在某些配置下 scp 本身也无法工作。
 +
 
 +
=== 端口复用 ===
 +
 
 +
SSH 守护进程通常监听 22 端口,但是许多公共热点会屏蔽非常规 HTTP/S 端口(分别是 80 和 443 端口)的流量,这样就屏蔽了 SSH 连接。最快的解决方法是让 {{ic|sshd}} 额外监听白名单上的端口:
  
Both local and remote forwarding can be used to provide a secure "gateway," allowing other computers to take advantage of an SSH tunnel, without actually running SSH or the SSH daemon by providing a bind-address for the start of the tunnel as part of the forwarding specification, e.g. {{Ic|<tunnel address>:<tunnel port>:<destination address>:<destination port>}}. The {{Ic|<tunnel address>}} can be any address on the machine at the start of the tunnel, {{Ic|localhost}}, {{Ic|*}} (or blank), which, respectively, allow connections via the given address, via the loopback interface, or via any interface. By default, forwarding is limited to connections from the machine at the "beginning" of the tunnel, i.e. the {{Ic|<tunnel address>}} is set to {{Ic|localhost}}. Local forwarding requires no additional configuration, however remote forwarding is limited by the remote server's SSH daemon configuration. See the {{Ic|GatewayPorts}} option in {{Ic|sshd_config(5)}} for more information.
+
{{hc|/etc/ssh/sshd_config|
 +
Port 22
 +
Port 443
 +
}}
  
=== Speed up SSH ===
+
但是443端口很有可能已经被 HTTPS 服务占用,在这种情况下可以使用端口复用工具,比如 {{Pkg|sslh}},它可以监听在一个被复用的端口上并转发相应的数据包给对应的服务。
You can make all sessions to the same host use a single connection, which will greatly speed up subsequent logins, by adding these lines under the proper host in {{ic|/etc/ssh/ssh_config}}:
 
ControlMaster auto
 
ControlPath ~/.ssh/socket-%r@%h:%p
 
  
Changing the ciphers used by SSH to less cpu-demanding ones can improve speed. In this aspect, the best choices are arcfour and blowfish-cbc. '''Please do not do this unless you know what you are doing; arcfour has a number of known weaknesses'''. To use them, run SSH with the {{Ic|"c"}} flag, like this:
+
=== 加速 SSH ===
$ ssh -c arcfour,blowfish-cbc user@server-address
 
To use them permanently, add this line under the proper host in {{ic|/etc/ssh/ssh_config}}:
 
Ciphers arcfour,blowfish-cbc
 
Another option to improve speed is to enable compression with the {{Ic|"C"}} flag. A permanent solution is to add this line under the proper host in {{ic|/etc/ssh/ssh_config}}:
 
Compression yes
 
Login time can be shorten by using the {{Ic|"4"}} flag, which bypasses IPv6 lookup. This can be made permanent by adding this line under the proper host in {{ic|/etc/ssh/ssh_config}}:
 
AddressFamily inet
 
Another way of making these changes permanent is to create an alias in {{ic|~/.bashrc}}:
 
alias ssh='ssh -C4c arcfour,blowfish-cbc'
 
  
==== Troubleshooting ====
+
此处列出一些可以加速全部连接或针对某台主机加速的 [[#配置|客户端配置]] 选项。要了解这些选项的完整概述,请参阅 {{man|5|ssh_config}}。
Make sure your DISPLAY string is resolveable on the remote end:
 
  
$ ssh -X user@server-address
+
* 使用以下参数来使到某一台主机的所有回话 (sessions) 共享同一个连接: {{bc|<nowiki>
server $ echo $DISPLAY
+
ControlMaster auto
localhost:10.0
+
ControlPersist yes
server $ telnet localhost 6010
+
ControlPath ~/.ssh/sockets/socket-%r@%h:%p
localhost/6010: lookup failure: Temporary failure in name resolution 
+
</nowiki>}}
 +
: 其中 {{ic|~/.ssh/sockets}} 可以是一个其他用户不可写入的任意目录。
  
can be fixed by adding localhost to {{ic|/etc/hosts}}.
+
* {{ic|ControlPersist}} 指定在初始客户端连接关闭后,主服务器在后台等待新客户端的时间。可能的值是:
 +
** {{ic|no}} 指定在最后一个客户端断开后立即关闭连接,
 +
** 一个用秒数表示的时间,
 +
** {{ic|yes}} 连接不会自动关闭,而是始终处于等待。
  
=== 用SSHFS挂载远程文件系统 ===
+
* 另一种加速的方法是通过 {{ic|Compression yes}} 选项或者 {{ic|-C}} 参数来启用压缩。
 +
: {{注意|{{man|1|ssh}} 指出:“在调制解调器线路或其他慢速线路上启用压缩是可取的,但在网速快的情况下只会降低速度。”这条提示可能会适得其反,具体取决于你的网络配置。}}
  
安装sshfs
+
* 通过使用 {{ic|AddressFamily inet}} 选项或者 {{ic|-4}} 参数来跳过 IPv6 查找,可以缩短登录时间。
# pacman -S sshfs
 
  
将你想要允许挂载SSH文件夹的用户添加到fuse组里
+
* 最后,如果你想用 SFTP 或 SCP,[https://www.psc.edu/index.php/hpn-ssh High Performance SSH/SCP] 可以通过动态提高 SSH 缓冲区大小来显著提高吞吐量。安装 {{AUR|openssh-hpn-git}} 这个包来使用打过这一增强补丁的 OpenSSH 版本。
  
# gpasswd -a USER fuse
+
=== 用 SSHFS 挂载远程文件系统 ===
  
加载fuse模块(比如说在/etc/rc.conf中)
+
请参阅 [[SSHFS]] 来将一个 SSH 可访问的远程文件系统挂载至一个本地目录,然后你就能在挂载好的文件上执行常规操作(复制,重命名,用 vim 编辑等等)。''sshfs'' 比 ''shfs'' 更好,因为后者自 2004 年起就没再更新。
 +
{{提示|软件包 {{AUR|autosshfs-git}}{{Broken package link|package not found}} 可以用于在登录时自动运行 autosshfs。}}
  
And then然后,在登录后,你就可以试着挂用sshfs载远程文件夹了:
+
=== 保持在线 ===
# mkdir ~/remote_folder
 
# sshfs USER@remote_server:/tmp ~/remote_folder
 
  
上面的命令将把远程服务器上的/tmp文件夹挂载到本地的~/remote_folder目录下。复制任何文件到这个目录将使文件通过SCP通过网络传输。
+
默认情况下,如果你的会话空闲了某个时间之后,它会自动登出。为了保持会话,在长时间没有数据传输时客户端可以向服务器发送一个激活信号。与之对应,服务器也可以在一段时间没有收到消息时定期发送一个信号。
Same concerns direct file editing, creating or removing.
 
  
当我们完成在远程文件夹下的工作,我们可以这样来卸载它:
+
* 在 '''服务器''',{{ic|ClientAliveInterval}} 是没有从客户端收到消息后的超时时间,超时后 ''sshd'' 将会发送一个请求来等待回应。默认是 0,指不会发出请求。比如要求每隔 60 秒向客户端发送响应请求,在你的 [[#配置_2|服务器配置]] 里设置 {{ic|ClientAliveInterval 60}} 即可。{{ic|ClientAliveCountMax}} 和 {{ic|TCPKeepAlive}} 选项也可以参考一下。
# fusermount -u ~/remote_folder
+
* 在 '''客户端''',{{ic|ServerAliveInterval}} 控制着从客户端发往服务器的响应请求的时间间隔。比如要求服务器每隔 120 秒响应一次,在你的 [[#配置|客户端配置]] 里加入 {{ic|ServerAliveInterval 120}} 即可。{{ic|ServerAliveCountMax}} 和 {{ic|TCPKeepAlive}} 选项也可以参考一下。
  
如果我们需要经常在这个文件夹下,让它通过/etc/fstab挂载是一个明智的选择。这个办法可以让它在启动的时候挂载或者通过手动挂载(如果是noauto选项的话),而不需要每次都去挂载它。下面是一个简单的样本:
+
  {{注意| 为确保会话保持活动状态,客户端或服务器中只有一个需要发送保持活动请求。如果用户同时控制服务器和客户端,那么合理的选择是使用 {{ic|ServerAliveInterval}} 选项配置需要保持会话的客户端,并保留其他客户端和服务器的默认配置。}}
  sshfs#USER@remote_server:/tmp /full/path/to/directory fuse    defaults,auto    0 0
 
=== Keep Alive ===
 
Your ssh session will automatically log out if it is idle. To keep the connection active (alive) add this to {{ic|~/.ssh/config}} or to {{ic|/etc/ssh/ssh_config}} on the client.
 
  
ServerAliveInterval 120
+
=== 利用 systemd 自动重启 SSH 隧道 ===
  
This will send a "keep alive" signal to the server every 120 seconds.
+
[[systemd]] 可以在开机/登录时自动启动 SSH,''还可以'' 在 SSH 连接断开时自动重连。这使它成为管理 SSH 隧道的有力工具。
  
Conversely, to keep incoming connections alive, you can set
+
下面的 service 可以使用 [[#配置|ssh 配置]] 里面的配置在你登录系统的时候自动开启一个 SSH 隧道。如果连接因为某种原因断开,它将会每隔10秒重启一下:
 
ClientAliveInterval 120
 
  
(or some other number greater than 0) in {{ic|/etc/ssh/sshd_config}} on the server.
+
{{hc|~/.config/systemd/user/tunnel.service|<nowiki>
 +
[Unit]
 +
Description=SSH tunnel to myserver
  
=== Save connection data in ssh config ===
+
[Service]
Whenever you want to connect to a ssh server, you usually have to type at least its address and the username. To save that typing work for servers you regularly connect to, you can use the personal {{ic|$HOME/.ssh/config}} or the global {{ic|/etc/ssh/ssh_config}} files as shown in the following example:
+
Type=simple
 +
Restart=always
 +
RestartSec=10
 +
ExecStart=/usr/bin/ssh -F %h/.ssh/config -N myserver
 +
</nowiki>}}
  
{{hc|$HOME/.ssh/config|
+
然后 [[enable]] 并且 [[start]] 这个 user service。欲知如何防止连接超时,请参阅 [[#Keep alive]]{{Broken section link}}。如果你想在系统引导后就打开这个连接,你需要将这个 unit 重写为 system service。
Host myserver
+
 
    HostName 123.123.123.123
+
=== Autossh - 自动重启 SSH 会话和隧道连接 ===
    Port 12345
+
 
    User bob
+
当一个 SSH 会话或隧道无法保持连接(比如网络环境差导致客户端断线),可以使用 {{Pkg|autossh}} 来自动重启它们。
Host other_server
+
 
    HostName test.something.org
+
使用范例:
    User alice
+
$ autossh -M 0 -o "ServerAliveInterval 45" -o "ServerAliveCountMax 2" username@example.com
    CheckHostIP no
+
 
    Cipher blowfish
+
结合 [[SSHFS]]:
 +
$ sshfs -o reconnect,compression=yes,transform_symlinks,ServerAliveInterval=45,ServerAliveCountMax=2,ssh_command='autossh -M 0' username@example.com: /mnt/example
 +
 
 +
通过一个由 [[Proxy settings]] 设置好的 SOCKS 代理来连接:
 +
$ autossh -M 0 -o "ServerAliveInterval 45" -o "ServerAliveCountMax 2" -NCD 8080 username@example.com
 +
 
 +
使用 {{ic|-f}} 选项以后可以使 autossh 作为后台进程运行,然而以这种方式运行意味着不能交互输入密码。
 +
 
 +
当你在会话中打出 {{ic|exit}} 即可结束会话,或者 autossh 收到了 SIGTERM, SIGINT of SIGKILL 信号。
 +
 
 +
==== 利用 systemd 在引导后自动运行 autossh ====
 +
 
 +
如果你想自动启动 autossh,创建一个 systemd unit 文件:
 +
 
 +
{{hc|/etc/systemd/system/autossh.service|2=
 +
[Unit]
 +
Description=AutoSSH service for port 2222
 +
After=network.target
 +
 
 +
[Service]
 +
Environment="AUTOSSH_GATETIME=0"
 +
ExecStart=/usr/bin/autossh -M 0 -NL 2222:localhost:2222 -o TCPKeepAlive=yes foo@bar.com
 +
 
 +
[Install]
 +
WantedBy=multi-user.target
 
}}
 
}}
  
Now you can simply connect to the server by using the name you specified:
+
其中 {{ic|1=AUTOSSH_GATETIME=0}} 是一个环境变量,它表示 ssh 需要连上多久,autossh 才判定这个连接是成功的。将它设为 0 后 autossh 也会忽略 ssh 的第一次运行失败。这在开机启动 autossh 时可能是有用的。其他环境变量可以在手册页找到。当然,如果需要的话,你可以使这个单元更加复杂(详情请参阅 systemd 文档),显然你可以使用自己的 autossh 选项,但请注意 {{ic|-f}} 选项意味着 {{ic|1=AUTOSSH_GATETIME=0}} 无法在 systemd 中起效。
 +
 
 +
别忘了 [[start]] 且/或 [[enable]] 这个 service。
 +
 
 +
你可能还会需要关闭 ControlMaster,像这样:
 +
 
 +
ExecStart=/usr/bin/autossh -M 0 -o ControlMaster=no -NL 2222:localhost:2222 -o TCPKeepAlive=yes foo@bar.com
 +
 
 +
{{提示|同时管理多个 autossh 进程也是很简单的,要保持多个隧道连接,只需要用不同的文件名创建多个 service 文件。}}
  
$ ssh myserver
+
=== 当 SSH 守护进程出错时的其他选择 ===
  
To see a complete list of the possible options, check out ssh_config's manpage on your system or the [http://www.openbsd.org/cgi-bin/man.cgi?query=ssh_config ssh_config documentation] on the official website.
+
对于仅依赖 SSH 的远程或无头服务器,启动 SSH 守护程序失败(例如系统升级后)可能会阻止管理员访问。[[systemd]] 通过 {{ic|OnFailure}} 选项提供了简便的解决方案。
  
=== Change bash prompt when logged over ssh ===
+
假设服务器运行 {{ic|sshd}} 并且 [[telnet]] 是所选的故障安全替代方案。按如下所示创建一个文件。'''不要''' [[enable]] telnet.socket!
It can sometimes be useful to be able to make the difference between your local and your remote prompt, in particular when they are both configured in the same way. To do that, just insert this in your bashrc:
 
  
{{hc|$HOME/.bashrc|2=
+
{{hc|/etc/systemd/system/sshd.service.d/override.conf|2=
if [ -n "$SSH_CLIENT" ]; then
+
[Unit]
        PS1='\[\e[0;33m\]\u@\h:\wSSH$\[\e[m\] '
+
OnFailure=telnet.socket
else
 
        PS1='\[\e[0;32m\]\u\[\e[m\] \[\e[1;34m\]\w\[\e[m\] \[\e[1;32m\]\$\[\    e[m\] '
 
fi
 
 
}}
 
}}
  
See [[Color Bash Prompt]] for more information about the PS1 variable customization.
+
这样就行了。当 {{ic|sshd}} 正在运行时,Telnet 是不可用的。如果 {{ic|sshd}} 无法启动,可以打开一个 telnet 会话进行恢复。
 +
 
 +
== 疑难解答 ==
 +
 
 +
=== 自检清单 ===
  
=== Automatically logout all ssh users when the sshd server is shutdown ===
+
在进一步阅读前,请先仔细检查下面这些常见故障。
To automatically log out all remote ssh users when the sshd server system shuts down, for reboot or halt, add this line to /etc/rc.local.shutdown on the sshd server:
 
  
who | cut -d " " -f1 | uniq | xargs pkill -KILL -u
+
# 配置文件存放目录 {{ic|~/.ssh}} 及目录下的文件应该只有你的账户才有访问权限(在客户端和服务器上都检查这一条): {{bc|<nowiki>
 +
$ chmod 700 ~/.ssh
 +
$ chmod 600 ~/.ssh/*
 +
$ chown -R $USER ~/.ssh
 +
</nowiki>}}
 +
# 检查客户端的公钥(比如 {{ic|id_rsa.pub}})在服务器的 {{ic|~/.ssh/authorized_keys}} 文件里面。
 +
# 检查有没有在 [[#配置_2|服务器配置]] 里面设置 {{ic|AllowUsers}} 或 {{ic|AllowGroups}} 来限制 SSH 访问。
 +
# 检查用户是否设置了密码。有时还没有登录过服务器的新用户没有密码。
 +
# 把 {{ic|LogLevel DEBUG}} 加到 {{ic|/etc/ssh/sshd_config}} 文件尾部。
 +
# 使用 {{ic|journalctl -xe}} 查看可能的错误信息。
 +
# 在客户端和服务器上 [[Restart|重启]] {{ic|sshd}} 然后注销/重新登录。
  
This prevents ssh client terminals from hanging during a lengthy timeout, which eventually ends with:
+
=== 拒绝连接或者超时问题 ===
  
Write failed: Broken pipe
+
==== 端口转发 ====
  
== Troubleshooting ==
+
如果您位于 NAT 模式/路由器之后(除非您位于 VPS 或可公开寻址的主机上),请确保您的路由器可以将传入的 ssh 连接转发到您的计算机。使用 {{ic|$ ip addr}} 查找服务器的内网 IP 地址,并将您的路由器设置为将 SSH 端口上的 TCP 数据包转发到该 IP。[http://portforward.com portforward.com] 可以提供帮助。
=== Connection Refused or Timeout Problem ===
+
 
==== Is SSH running and listening? ====
+
==== SSH服务是否开启并且正在监听? ====
 
  $ ss -tnlp
 
  $ ss -tnlp
  
If the above command do not show SSH port is open, SSH is NOT running. Check {{ic|/var/log/messages}} for errors etc.
+
如果以上的命令没有显示 SSH 端口是打开的,那么说明 SSH 服务没有启动。查看 {{ic|/var/log/messages}} 来寻找错误信息。
 +
 
 +
==== 是否是防火墙阻止了连接? ====
 +
 
 +
[[Iptables]] 可能会阻止 {{ic|22}} 端口的连接。使用 {{bc|# iptables -nvL}} 来检查可能会在 {{ic|INPUT}} 链上导致丢包的规则。必要情况下可以用以下命令来解锁端口:
 +
{{bc|
 +
# iptables -I INPUT 1 -p tcp --dport 22 -j ACCEPT
 +
}}
 +
更多配置防火墙的信息,请参阅 [[firewalls]].
 +
 
 +
==== 你的电脑和目的主机之间是否连接? ====
 +
测试你的电脑和目的主机的连接情况:
 +
 
 +
# tcpdump -lnn -i any port ssh and tcp-syn
 +
 
 +
它会显示一些基本信息,然后等待数据交换。现在尝试你的连接。如果没有输出,就可能是你的电脑网络阻塞了。(也许是防火墙问题,也许是 NAT 路由的问题)
 +
 
 +
==== 你的 ISP 或第三方屏蔽了默认端口? ====
 +
{{注意|只有在你'''确保'''你没有运行任何防火墙,你已经在路由器上配置了 DMZ 主机或已经将端口映射到你的计算机,而这些都没有用的情况下才尝试以下步骤。可以在此找到诊断步骤或可能的解决方案。}}
 +
 
 +
某些情况下,你的运营商会屏蔽默认端口(22 端口),无论怎么尝试(尝试开启端口、强化堆栈、防范洪水攻击)都无济于事。要确认确实存在屏蔽,只要创建一个接受任何来源(0.0.0.0)的服务器并远程连接它。
 +
 
 +
如果你收到与此类似的错误消息:
 +
ssh: connect to host www.inet.hr port 22: Connection refused
 +
 
 +
就表示你的 ISP '''没有'''屏蔽端口,但是服务器没有在该端口上运行 SSH 服务(请参阅 [[wikipedia:Security_through_obscurity|security through obscurity]])。
 +
 
 +
但是,如果你收到与这条类似的错误消息:
 +
ssh: connect to host 111.222.333.444 port 22: Operation timed out
 +
 
 +
这就表示有人阻止了 22 端口的 TCP 连接,基本上是通过防火墙或第三方干预(如 ISP 阻止和/或拒绝端口 22 上的传入通信),使得端口不可用。如果你的计算机上没有运行任何防火墙,并且在你的路由器和交换机中没有这方面的流量,那么你的 ISP 屏蔽了通讯。
 +
 
 +
为了再次检查确认,可以在服务器上运行 Wireshark 并让它监听在 22 端口。由于 Wireshark 是一个二层数据包嗅探工具,而 TCP/UDP 工作在第三层及以上(参阅 [[wikipedia:Internet protocol suite|IP Network stack]]),如果在连接时未收到任何内容,则第三方很可能阻止了该端口上到服务器的流量。
 +
 
 +
===== 诊断 =====
 +
 
 +
[[Install|安装]] {{Pkg|tcpdump}} 或 Wireshark ({{Pkg|wireshark-cli}})。
 +
 
 +
用于 tcpdump:
 +
 
 +
# tcpdump -ni ''interface'' "port 22"
 +
 
 +
用于 Wireshark:
 +
 
 +
$ tshark -f "tcp port 22" -i ''interface''
  
==== Are there firewall rules blocking the connection? ====
+
其中 {{ic|''interface''}} 是用于连接 WAN 的网络适配器(用 {{ic|ip a}} 来查找)。如果在尝试远程连接时没有收到任何数据包,则可以确信你的 ISP 屏蔽了 22 端口上的传入连接。
Flush your iptables rules to make sure they are not interfering:
 
  
# rc.d stop iptables
+
===== 可能的解决方案 =====
 +
此方案是换一个 ISP 没有屏蔽的端口。编辑 {{ic|/etc/ssh/sshd_config}} 文件来使用不同的端口。例如,新增这几行:
  
or:
+
Port 22
 +
Port 1234
  
# iptables -P INPUT ACCEPT
+
还要确保文件中的其他“Port”配置行被注释掉。只是注释“Port 22”并加上“Port 1234”不会解决问题,因为那样 sshd 将只监听在 1234 端口上。写入这两行可以在两个端口上运行 SSH 服务器。
# iptables -P OUTPUT ACCEPT
 
# iptables -F INPUT
 
# iptables -F OUTPUT
 
  
==== Is the traffic even getting to your computer? ====
+
[[Restart|重启]] 服务器上的 {{ic|sshd.service}} 就基本完成了。你还需要配置客户端来使用与默认端口不同的端口,这个问题有很多种解决方案,在这里我们只介绍两种。
Start a traffic dump on the computer you're having problems with:
 
  
  # tcpdump -lnn -i any port ssh and tcp-syn
+
==== "Read from socket failed: connection reset by peer" 错误 ====
 +
 
 +
使用最近版本的 openssh 连接到较旧的 ssh 服务器时有时会失败,并显示上述错误消息。这可以通过为该主机设置各种 [[#配置|客户端选项]] 来解决。有关下列选项的更多信息,请参阅 {{man|5|ssh_config}}。
 +
 
 +
问题可能出在 {{ic|ecdsa-sha2-nistp*-cert-v01@openssh}} 椭圆曲线算法上。这些算法可以通过在 {{ic|HostKeyAlgorithms}} 里设置可用算法来排除那些算法。
 +
 
 +
如果这不起作用,可能是秘钥列表太长了。设置 {{ic|Ciphers}} 来减少列表长度(少于 80 个字符应该可以)。同样的,也可以尝试缩短 {{ic|MACs}} 列表。
 +
 
 +
参阅 openssh bug forum 上的 [http://www.gossamer-threads.com/lists/openssh/dev/51339 讨论]。
 +
 
 +
=== "[your shell]: No such file or directory" / SSH 认证问题 ===
 +
对于这个问题,一个可能的原因是需要 SSH 客户端在 {{Ic|$SHELL}} 中提供绝对路径(例如可以通过 {{Ic|whereis -b [your shell]}} 得到),即使你的 shell 在 {{Ic|$PATH}} 里的某个路径中。
 +
 
 +
==="Terminal unknown" 或 "Error opening terminal" 错误 ===
 +
如果你在登录时收到上述错误,这意味着服务器无法识别你的终端。使用 Ncurses 的应用程序(如 nano)可能会失败,并显示“Error opening terminal”。
 +
 
 +
正确的解决方案是在服务器上安装客户端终端的 terminfo 文件。这会告诉服务器上的控制台程序如何正确地与终端进行交互。你可以使用 {{ic|$ infocmp}} 获得关于当前 terminfo 的信息,然后找出 [[Pacman#Querying_package_databases|哪个包包括了它们]]。
 +
 
 +
如果你不能正常[[install|安装]]它,可以把 terminfo 复制到服务器上你的主目录里面:
 +
 
 +
$ ssh myserver mkdir -p  ~/.terminfo/${TERM:0:1}
 +
$ scp /usr/share/terminfo/${TERM:0:1}/$TERM myserver:~/.terminfo/${TERM:0:1}/
 +
 
 +
重新登录、登出服务器后这个问题应该已经解决。
 +
 
 +
==== TERM hack ====
 +
 
 +
{{警告|这只能作为最后的手段。}}
 +
 
 +
你可以在服务器上的环境(例如 {{ic|.bash_profile}})中简单地设置 {{ic|1=TERM=xterm}} 。这将消除错误并允许 ncurses 应用程序再次运行,但除非你的终端的控制序列与 xterm 完全匹配,否则可能会遇到奇怪的行为和图形界面问题。
 +
 
 +
=== "Connection closed by x.x.x.x [preauth]" 错误 ===
 +
如果你在 sshd 的 log 里看到这条错误,请确保你已经设置了可用的 HostKey
 +
HostKey /etc/ssh/ssh_host_rsa_key
 +
 
 +
=== id_dsa 被 OpenSSH 7.0 拒绝 ===
 +
 
 +
出于安全原因,OpenSSH 7.0 弃用了 DSA 公钥。如果你必须启用它们,请[[#配置|设置]] {{ic|PubkeyAcceptedKeyTypes +ssh-dss}} 选项(http://www.openssh.com/legacy.html 没有提到这一点)。
  
This should show some basic information, then wait for any matching traffic to happen before displaying it. Try your connection now. If you do not see any output when you attempt to connect, then something outside of your computer is blocking the traffic (e. g., hardware firewall, NAT router etc.).
+
=== OpenSSH 7.0 的 "No matching key exchange method found" 错误 ===
  
==== Read from socket failed: Connection reset by peer ====
+
OpenSSH 7.0 弃用了 diffie-hellman-group1-sha1 密钥算法,因为它很弱并且在所谓 Logjam 攻击的理论范围内(参阅http://www.openssh.com/legacy.html)。如果特定主机需要这个密钥算法,ssh 会产生如下错误消息:
Recent versions of openssh sometimes fail with the above error message, due to a bug involving elliptic curve cryptography. In that case, edit the file
 
  
  ~/.ssh/config
+
  Unable to negotiate with 127.0.0.1: no matching key exchange method found.
 +
Their offer: diffie-hellman-group1-sha1
  
or create it, if it doesn't already exist.  Add the line
+
这个问题的最佳解决方案是将服务器升级/配置为不使用不推荐的算法。如果做不到这一点,可以配置[[#配置|客户端选项]] {{ic|KexAlgorithms +diffie-hellman-group1-sha1}} 强制客户端使用这个算法。
  
HostKeyAlgorithms ssh-rsa-cert-v01@openssh.com,ssh-dss-cert-v01@openssh.com,ssh-rsa-cert-v00@openssh.com,ssh-dss-cert-v00@openssh.com,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-rsa,ssh-dss
+
=== 断开 SSH 连接时 tmux/screen 会话被关闭 ===
  
With openssh 5.9, the above fix doesn't work. Instead, put the following lines in ~/.ssh/config
+
如果进程在会话结束时被终止,那么你可能是用 ssh.socket 激活的,{{Pkg|systemd}} 注意到 SSH 会话进程退出,然后杀掉了 tmux/screen 进程。这种情况有两种解决方案。一种是通过使用 {{ic|ssh.service}} 来代替 {{ic|ssh.socket}} 来避免使用 socket 激活。另一个是在 {{ic|ssh@.service}} 的 Service 部分设置 {{ic|1=KillMode=process}}。
  
Ciphers aes128-ctr,aes192-ctr,aes256-ctr,aes128-cbc,3des-cbc
+
在常规的 {{ic|ssh.service}} 里面 {{ic|1=KillMode=process}} 这个选项也是有用的,它可以在 service 停止或重启时防止 SSH 会话进程或 {{Pkg|screen}} 或 {{Pkg|tmux}} 进程被 kill 掉。
MACs hmac-md5,hmac-sha1,hmac-ripemd160
 
  
See also the [http://www.gossamer-threads.com/lists/openssh/dev/51339 discussion] on the openssh bug forum.
+
=== SSH 会话无响应 ===
  
=== "[your shell]: No such file or directory" / ssh_exchange_identification Problem ===
+
SSH 响应 [[Wikipedia:Software_flow_control|流控制命令]] 中的 {{ic|XON}} 和 {{ic|XOFF}} 命令。 当你按 {{ic|Ctrl+s}} 时,它会冻结/挂起/停止响应。按 {{ic|Ctrl+q}} 恢复会话。
  
One possible cause for this is the need of certain SSH clients to find an absolute path (one returned by {{Ic|whereis -b [your shell]}}, for instance) in {{Ic|$SHELL}}, even if the shell's binary is located in one of the {{Ic|$PATH}} entries. Another reason can be that the user is no member of the ''network'' group.
+
== 参阅 ==
  
== See Also ==
+
* [[Wikipedia:Secure Shell]]
*[[SSH keys]]
+
* [http://www.la-samhna.de/library/brutessh.html Defending against brute force ssh attacks]
*[[Pam abl]]
+
* [http://www.ibm.com/developerworks/library/l-keyc/index.html OpenSSH key management, Part 1] and [http://www.ibm.com/developerworks/library/l-keyc2 Part 2] on IBM developerWorks
*[[fail2ban]]
+
* [https://stribika.github.io/2015/01/04/secure-secure-shell.html Secure Secure Shell]
*[[sshguard]]
 
*[[Sshfs]]
 
*[http://www.soloport.com/iptables.html A Cure for the Common SSH Login Attack]
 
*[[使用SSH密钥]]
 
*[http://www.la-samhna.de/library/brutessh.html Defending against brute force ssh attacks]
 
*[http://www.ibm.com/developerworks/library/l-keyc/index.html OpenSSH key management, Part 1] and [http://www.ibm.com/developerworks/library/l-keyc2 Part 2] on IBM developerWorks
 

Latest revision as of 05:14, 30 May 2019

翻译状态: 本文是英文页面 Secure Shell翻译,最后翻译时间:2018-05-04,点击这里可以查看翻译后英文页面的改动。

Secure Shell (SSH) 是一个允许两台电脑之间通过安全的连接进行数据交换的网络协议。加密保证了数据的保密性和完整性。SSH采用公钥加密技术来验证远程主机,以及(必要时)允许远程主机验证用户。

SSH 通常用于远程访问和执行命令,但是它也支持隧道,转发任意 TCP 端口以及 X11 连接;它还能够用 SFTP 或 SCP 协议来传输文件。

一个 SSH 服务器默认情况下,在 TCP 端口 22 进行监听。一个 SSH 客户端程序通常被用来建立一个远程连接到 sshd 守护进程。这两者都被广泛地存在于现代操作系统中,包括 Mac OS X,GNU/Linux,Solaris 和 OpenVMS 等。以专有软件、自由软件以及开源版本的形式和不同的复杂性和完整性存在。

(来源:维基百科 Secure Shell)

Contents

OpenSSH

OpenSSH (OpenBSD Secure Shell) 是一套使用 ssh 协议,通过计算机网络,提供加密通讯会话的计算机程序。它被创建为 SSH Communications Security 公司拥有专利的 Secure Shell 软件套装的一个开源替代。OpenSSH 是由 Theo de Raadt 领导的 OpenBSD 项目的一部分。

人们常把 OpenSSH 与相似名字的 OpenSSL 搞混,但是,这两个项目是由不同的团队出于不同的目的开发出来的。相似的名字只是由于相似的目标。

安装OpenSSH

官方源安装 openssh.


SSH 客户端

连接SSH服务器,运行命令

$ ssh -p port user@server-address

如果服务器仅允许使用密钥登录,请参考 SSH Keys

配置

客户端可以在配置文件中存储常用选项和常用主机,下列选项都可以应用至全局或应用至特定主机。 例如:

~/.ssh/config
# global options
User user

# host-specific options
Host myserver
    HostName server-address
    Port     port

进行了如上的配置后,以下命令是等效的

$ ssh -p port user@server-address
$ ssh myserver

查看 ssh_config(5) 获取更多信息。

某些选项没有命令行参数,但是可以使用 -o 在命令行中配置指定选项的参数。 例如 -oKexAlgorithms=+diffie-hellman-group1-sha1.

SSH 服务端

配置

SSH 守护进程的配置文件是/etc/ssh/sshd_config

只允许某些用户访问的话,加入这一行:

AllowUsers    user1 user2

只允许一些组访问:

AllowGroups   group1 group2

你也可以运行以下命令关联文件(如/etc/issue文件)到登录欢迎信息:

 Banner /etc/issue

公钥和私钥在 sshd service 文件 安装的时候就自动生成在 /etc/ssh 里面了,四个秘钥对分别由四种算法生成: dsa、rsa、ecdsa 和 ed25519。要让 sshd 使用一组特定的密钥,请指定以下选项:

 HostKey /etc/ssh/ssh_host_rsa_key

如果此服务器在公网中,建议运行以下命令以更改sshd服务监听端口:

 Port 39901
Tip:
  • 参考 TCP 和 UDP 端口号列表 和本地的 /etc/services 文件来选择一个未被常用服务占用的端口。把端口从默认的 22 改成别的可以减少由于端口扫描器尝试自动登录造成的登录日志条目,更多信息请参考 Port knocking
  • 完全取消密码登录方式可以极大的增强安全性,请查看#强制公钥验证。查看#安全防护了解更多增强安全性的手段。
  • OpenSSH 可以监听多个端口,只需在配置文件中加入多行Port port_number即可。

管理 sshd 守护进程

openssh 包括了两种 systemd 服务:

  1. sshd.service,使 SSH 守护进程始终运行,并为每个入站连接创建子进程。[1] 适用于有大量 SSH 流量的系统。[2]
  2. sshd.socket + sshd@.service, 为每个连接生成 SSH 守护进程的实例。它意味着让 systemd 监听 SSH socket,并且只有在有连接传入时启动守护进程。几乎所有情况下都推荐使用sshd[3][4][5]

startenable sshd.service sshd.socket 中的任何一个都可以启动守护进程。

如果选择了 sshd.socket,并且不在默认的 22 端口监听,你需要编辑 ststemd 单元文件:

# systemctl edit sshd.socket
[Socket]
ListenStream=
ListenStream=12345
警告: 使用 sshd.socket 会使 ListenAddress 设置无效,这将允许来自任何地址的连接。为了达到与 ListenAddress 一样设置 IP 的效果, 你必须在 ListenStream 中指定端口 IP (例如:ListenStream=192.168.1.100:22)。你还需要在 [Socket] 下面增加 FreeBind=true,否则设置 IP 与设置 ListenAddress 有着相同的缺陷:如果网络未及时启动,socket 将无法启动。
提示: 打开 sshd.service 时将为每个连接启动一个 sshd@.service 的临时实例(实例名称不同)。因此,sshd.socket 和常规 sshd.service 都不允许监视日志中的连接尝试。使用 journalctl -u "sshd@*"journalctl /usr/bin/sshd 可以看到 socket 激活的 SSH 实例的日志。

安全防护

允许通过SSH进行远程登录对管理服务器很有用,但也会对服务器构成安全威胁。SSH 通常是暴力攻击的目标,因此 SSH 访问需要适当限制,以防止第三方访问您的服务器。

下列是有关该主题的优秀指南:

强制公钥验证

如果客户端无法通过公钥进行身份验证,则默认情况下,SSH服务器将使用密码来验证,从而允许恶意用户通过暴力破解密码获取访问权限。一种防止此类攻击的有效方法是完全禁用密码登录,并强制使用SSH keys。可以在 sshd_config 中禁用以下选项:

PasswordAuthentication no
警告: 在将上述选项添加到你的配置之前,请确保所有需要 SSH 访问的帐户都在相应的 authorized_keys 文件中设置了公钥验证。请参阅 SSH keys#Copying the public key to the remote server 以获取更多信息。
双因素验证与公钥

SSH 可以设置为采用多种方式进行身份验证,你可以使用 AuthenticationMethods 选项来指明在登录时需要哪些身份验证方式。这使你可以用公钥与双因素验证结合来登录。

参阅 Google Authenticator (简体中文) 来设置 Google Authenticator。

为了使 PAM (简体中文) 与 OpenSSH 协同工作, 编辑下列文件:

/etc/ssh/sshd_config
ChallengeResponseAuthentication yes
AuthenticationMethods publickey keyboard-interactive:pam

然后,你可以使用公钥 PAM 中设置的用户验证信息两者之一登录。

另外,如果你想登录时同时验证公钥 PAM,请使用逗号而不是空格来分隔 AuthenticationMethods:

/etc/ssh/sshd_config
ChallengeResponseAuthentication yes
AuthenticationMethods publickey,keyboard-interactive:pam

通过要求提供公钥 PAM 认证,你可能希望禁用密码登录:

/etc/pam.d/sshd
auth      required  pam_securetty.so     #disable remote root
#Require google authenticator
auth      required  pam_google_authenticator.so
#But not password
#auth      include   system-remote-login
account   include   system-remote-login
password  include   system-remote-login
session   include   system-remote-login
防止暴力破解

暴力破解的概念很简单,即某人不断尝试用大量随机产生的用户名和密码对来登录网页或服务器的某个服务(比如 SSH)。

使用 ufw

请参阅 ufw#Rate limiting with ufw.

使用 iptables

Merge-arrows-2.pngThis article or section is a candidate for merging with Simple_stateful_firewall#Bruteforce_attacks.Merge-arrows-2.png

Notes: Out of scope, same technique as already described in the SSF. (Discuss in Talk:Secure Shell (简体中文)#)

如果你已经在用 iptables,可以配置以下规则来保护 SSH 免受暴破。

注意: 此示例中 SSH 所用的 TCP 端口已经改为了 42660。

在应用后面的规则之前,我们先新建一条规则链来记录并拒绝过多的连接请求:

# iptables -N LOG_AND_DROP

第一条规则将应用于预示 TCP 42660 端口有新连接的数据包:

# iptables -A INPUT -p tcp -m tcp --dport 42660 -m state --state NEW -m recent --set --name DEFAULT --rsource

下一条规则告诉 iptables 查找匹配前一条规则的数据包,这些数据包也来自已添加到监视列表中的主机。

# iptables -A INPUT -p tcp -m tcp --dport 42660 -m state --state NEW -m recent --update --seconds 90 --hitcount 4 --name DEFAULT --rsource -j LOG_AND_DROP

现在,让 iptables 决定如何处理 TCP 42660 端口的通信中不符合上述规则的数据包。

# iptables -A INPUT -p tcp -m tcp --dport 42660 -j ACCEPT

我们向 LOG_AND_DROP 表增加如下规则,并使用 -j (jump) 参数将数据包的信息传递给日志记录工具。

# iptables -A LOG_AND_DROP -j LOG --log-prefix "iptables deny: " --log-level 7

在按照第一条规则进行记录后,所有数据包将被丢弃。

# iptables -A LOG_AND_DROP -j DROP
防止暴力破解的工具

你可以用类似 fail2bansshguard 的自动防暴破的脚本来阻挡攻击者。

  • 仅允许来自受信任位置的 SSH 入站连接。
  • 使用 fail2bansshguard 自动阻止多次密码验证失败的 IP 地址。
  • 使用 pam_shield 来阻止在一定时间内执行过多登录尝试的 IP 地址。与 fail2bansshguard不同,该程序不考虑登录成功或失败。
禁用或限制 root 账户登录

Tango-view-refresh-red.pngThis article or section is out of date.Tango-view-refresh-red.png

Reason: 最新版本默认已禁用 root 账户登录。暂不清楚本节的哪些部分是多余的。 (Discuss in Talk:Secure Shell (简体中文)#)

允许 root 账户随意通过 SSH 登录通常是不安全的,有两种方法可以限制 root 账户通过 SSH 登录,从而提高安全性。

禁用 root 登录

Sudo 可以有选择地为需要 root 权限的操作提供相应的权限,且不需要登录 root 账户。这样即可关闭 root 登录,并且可以看做一种防范暴力攻击的安全措施,因为现在攻击者除了要猜测密码外还要猜测帐户名称。

通过编辑 /etc/ssh/sshd_config 中的 "Authentication" 一节可以使 SSH 屏蔽 root 用户登录,只要将 #PermitRootLogin prohibit-password 改成 no 并取消该行注释即可:

/etc/ssh/sshd_config
PermitRootLogin no
...

然后 重启 SSH 守护进程。

现在你将无法通过 root 账户登录,但仍可以用普通账户登录并使用 su 或者 sudo 来完成系统维护工作。

限制 root 登录

一些自动化的维护任务(比如远程备份整个系统)需要完整的 root 权限。要以安全的方式允许 root 登录而不是禁用它,可以只允许远程登录的 root 用户执行指定的命令,在 ~root/.ssh/authorized_keys 头部加上指定的密钥即可,例如:

command="/usr/lib/rsync/rrsync -ro /" ssh-rsa …

这样,任何用户持有该秘钥即可执行引号之间的命令。

为了弥补因 root 用户名称暴露而导致受攻击的可能性增加,可以将以下命令加入 sshd_config

PermitRootLogin forced-commands-only

该设置不仅会限制 root 用户通过 SSH 执行的命令,还会禁用密码登录方式,强制 root 帐户使用公钥登录。

如果不想限制 root 用户可执行的命令,可以仅关闭密码验证来强制使用公钥验证:

PermitRootLogin prohibit-password
保护 authorized_keys 文件

你可以阻止其他用户向该文件加入新公钥且通过新的公钥连接。

authorized_keys 文件的权限全部去掉,只保留读权限:

$ chmod 400 ~/.ssh/authorized_keys

为防止用户把权限改回来,可以对 authorized_keys 文件采取 set the immutable bit(设为不可变) 操作。尽管如此,用户仍然可以重命名 ~/.ssh 并新建一个 ~/.ssh 目录和 authorized_keys 文件。所以 ~/.ssh 目录也要设置 immutable bit。

注意: 如果你自己需要新增一个公钥,你需要先移除 authorized_keys 文件的 immutable bit,并增加写权限,最后按上述步骤重新加密。

其他 SSH 客户端与服务端

除了 OpenSSH,还有很多可用的 SSH 客户端服务端

Dropbear

Dropbear 是一个 SSH-2 客户端与服务端。 dropbear 可以从 官方仓库 下载。

它的命令行版客户端叫 dbclient。

Mosh

来自 Mosh 网站

Remote terminal application that allows roaming, supports intermittent connectivity, and provides intelligent local echo and line editing of user keystrokes. Mosh is a replacement for SSH. It is more robust and responsive, especially over slow connections such as Wi-Fi, cellular, and long-distance.
翻译:允许“漫游”的远程终端,支持间歇性的连接,并提供对于用户按键的智能本地回馈和行编辑回馈。Mosh 是 SSH 的替代品。它更加强大而快速,特别针对诸如 Wi-Fi,移动网络和超远距离等慢速连接环境。

安装 mosh 这个包, 或安装最新版:mosh-gitAUR

Mosh 有一个未写入文档的命令行选项:--predict=experimental,它可以产生更有力的本地按键响应。对降低键盘输入视觉上的延迟确认感兴趣的用户可能更喜欢这个预测模式。

提示: Mosh 从设计上就不允许你访问会话的历史记录,请考虑安装终端复用工具,如 tmuxGNU Screen

提示与技巧

Tango-inaccurate.pngThe factual accuracy of this article or section is disputed.Tango-inaccurate.png

Reason: 根据目前本文的布局,这部分看起来应该是通用的,但实际上大部分提供的技巧只能在 openssh 中使用。比如 dropbear (在 #Other SSH clients and servers 列表中) 不支持 SOCKS 协议。[6] (Discuss in Talk:Secure Shell (简体中文)#)

加密 Socks 通道

对于连接到各种不安全的无线网络上的笔记本电脑用户来说,这个是特别有用的!唯一所需要的就是一个一定程度上处于安全的地点的 SSH 服务器,比如在家里或办公室。用动态的 DNS 服务 DynDNS 也可能是很有用的,这样你就不必记住你的 IP 了。

第一步:开始连接

你只要执行这一个命令就能开始你的连接:

$ ssh -TND 4711 user@host

这里的 user 是你在 host 这台 SSH 服务器上的用户名。它会让你输入密码,然后你就能连上了。 N 表示不采用交互提示,而 D 表示指定监听的本地端口(你可以使用任何你喜欢的数字),T 表示禁用伪 tty 分配。

加了 -v (verbose) 标志以后的输出可以让你能够验证到底连了哪个端口。

第二步:配置你的浏览器(或其它程序)

如果你没有配置你的浏览器(或其他程序)使用这个新创建的 socks 隧道,上述步骤是无效的。由于当前版本的 SSH 支持 SOCKS4 和 SOCKS5,因此您可以使用其中任何一种。

  • 对于 Firefox: Edit > Preferences > Advanced > Network > Connection > Setting:
    选中 Manual proxy configuration 单选框, 然后在 SOCKS host 里输入 localhost, 然后在后面那个框中输入你的端口号(本例中为 4711)。

Firefox 不会自动通过 socks 隧道发送 DNS 请求,这一潜在的隐私问题可以通过以下步骤来解决:

  1. 在 Firefox 地址栏中输入:about:config 。
  2. 搜索:network.proxy.socks_remote_dns
  3. 将该值设为 true。
  4. 重启浏览器。
  • 对于 Chromium: 你可以将 SOCKS 设置设置为环境变量或命令行选项。我建议将下列函数之一加入到你的 .bashrc
function secure_chromium {
    port=4711
    export SOCKS_SERVER=localhost:$port
    export SOCKS_VERSION=5
    chromium &
    exit
}

或者

function secure_chromium {
    port=4711
    chromium --proxy-server="socks://localhost:$port" &
    exit
}

现在打开终端然后输入:

$ secure_chromium

享受你的安全隧道吧!

X11 转发

为了通过 SSH 运行图形程序你必须使用 X11 转发 (forwarding)。这不要求对端安装了完整的 X11,但是至少要装好 xauthxauth 是一个用来管理 Xauthority 配置的工具,该配置用于服务器与客户端之间的 X11 会话认证(source)。

警告: X11 转发有着重要的安全问题需要考虑,至少应先阅读 ssh(1)sshd_config(5)ssh_config(5) 手册页。也可以参考 这个 StackExchange 帖

配置

在远程主机上:

在客户端上,通过在命令行设置 -X 参数启用 ForwardX11,或者在客户端配置文件中将 ForwardX11 设置为 yes

提示: 如果 GUI 绘制不正常或者有错误提示,你可以启用 ForwardX11Trusted 选项(或在命令行中加上 -Y 参数),这将使 X11 转发脱离 X11 SECURITY extension 的控制,如果你这样做,请确保已经读过本节开头的警告

使用方法

Tango-inaccurate.pngThe factual accuracy of this article or section is disputed.Tango-inaccurate.png

正常登录远程主机,如果客户端的配置文件中没有启用 ForwardX11 那就加上 -X 参数:

$ ssh -X user@host

如果在运行图形程序的时候碰到错误,尝试用 ForwardX11Trusted 代替 ForwardX11

$ ssh -Y user@host

现在你应该可以运行服务器上的任何 X 图形程序,任何输出都会重定向至你当前的会话:

$ xclock

如果碰到 "Cannot open display" 的错误,请尝试用非管理员账户运行下列命令:

$ xhost +

上述命令将允许任何人转发 X11 应用程序,这个命令可以限制特定的主机类型:

$ xhost +hostname

其中 hostname 是要转发到的特定主机的名称。更多信息可以查看 xhost(1)

请注意某些应用程序,它们会检查本地计算机上正在运行的实例。Firefox 就是其中之一:你可以关掉本机上的 Firefox 或者使用以下启动参数来启动远程实例:

$ firefox --no-remote

当你连接时收到 "X11 forwarding request failed on channel 0" 错误(或者服务器上的 /var/log/errors.log 文件显示 "Failed to allocate internet-domain X11 display socket" 错误),请确保已经安装 xorg-xauth,如果装完了仍然不起作用,尝试以下方法之一:

  • 服务器sshd_config 中启用 AddressFamily any 选项,或者
  • 服务器sshd_config 中的 AddressFamily 选项设为 inet。

将其设置为 inet 可能会修复 IPv4 上的 Ubuntu 客户端的问题。

要以其他用户身份运行 SSH 服务器上的 X 应用程序,你需要先用已知用户登录,取出 xauth list 中的身份认证行,然后 xauth add 它。

提示: 这里一些 用来诊断 X11 Forwarding 问题有用的 链接

转发其他端口

除了 SSH 内建的对 X11 的支持之外,它也能通过本地转发和远程转发,来为任何的TCP连接建立隧道。

本地转发时,会在本机打开一个端口,连接将被转发到一个远程主机,并给定一个目的地。很多时候,转发目的地和远程主机会相同,因此也提供了一条SSH命令来建立一个安全的VNC连接。本地转发可以通过 -L 来设置,后面可以指定一个地址及端口 <tunnel port>:<destination address>:<destination port>

如下:

$ ssh -L 1000:mail.google.com:25 192.168.0.100

以上指令将会通过 SSH 得到一个在 192.168.0.100 的 shell,同时也会创建一个从本机 TCP 1000 端口到 mail.google.com 上的 25 端口的隧道。建立之后,通过 localhost:1000 的连接可以直接连接到 Gmail 的 SMTP 端口。对 Google 而言,任何这样的连接都是来自 192.168.0.100 的(即使这些连接中没有数据传输),并且,在本机和 192.168.0.100 之间的数据传递是安全的,但 192.168.0.100 和 Google 之间是不安全的,除非还采取了别的手段保障数据安全。

同样:

$ ssh -L 2000:192.168.0.100:6001 192.168.0.100

以上指令会将到 localhost:2000 的连接直接转发到远程主机 192.168.0.100 的 6001 端口。对于使用 VNC 服务器(tightvns包的一部分)建立的 VNC 连接来说,以上的例子尽管很有效,但是安全性有待商榷。

远程转发允许任何远程主机通过 SSH 隧道连接到本机,提供了和本地转发相反的功能,突破了防火墙的限制。通过 -R 参数,以及 <tunnel port>:<destination address>:<destination port> 能够实现远程转发。

如下:

$ ssh -R 3000:irc.freenode.net:6667 192.168.0.200

将会在 192.168.0.200 上得到一个 shell,同时,来自 192.168.0.200 的 3000 端口(远程主机的 localhost:3000)的数据将会通过隧道转发至本机,然后转发至 irc.freenode.net 上的 6667 端口。因此,在这个例子中,在远程主机上能够使用 IRC 程序,即使端口 6667 被阻止。

本地转发和远程转发都可以提供一个安全的“网关”,允许其他计算机无需运行 SSH 或者 SSH daemon 来使用 SSH 隧道,即在隧道起点提供绑定的地址,作为转发规则。例如 <tunnel address>:<tunnel port>:<destination address>:<destination port><tunnel address> 可以是作为隧道起点的机器上的任何地址,地址 localhost 允许来自本地回环的连接,空地址 * 允许来自任意网卡的连接。默认情况下,转发仅限于连接至位于隧道“起点”的主机,即 <tunnel address> 被设置为 localhost。本地转发不需要额外的设置,而远程转发受限于对端的 SSH daemon 设置。更多关于远程转发和本地转发的信息可分别参阅 sshd_config(5) 中的 GatewayPorts 选项和 ssh(1) 中的 -L address 选项。

跳板机

在某些情况下,你与目标主机之间可能无法直接连接,此时就要用到跳板机。因此,我们尝试将两个或更多 SSH 隧道连接在一起,并假设您的本地密钥已针对链中的每个服务器授权。这可以通过使用SSH代理转发 (-A) 和伪终端分配 (-t) 来实现,它使用以下语法转发本地密钥:

$ ssh -A -t -l user1 bastion1 \
  ssh -A -t -l user2 intermediate2 \
  ssh -A -t -l user3 target

一个更简单的方法是使用 -J 选项:

$ ssh -J user1@bastion1,user2@intermediate2 user3@target


-J 指令中的多个主机可以用逗号隔开,它们将按照列出的顺序连接。user...@ 部分不是必需的,但可以使用。定义 -J 选项里的不同的主机规格可以使用 ssh 配置文件,因此如果需要,可以在那里设置特定的每个主机选项。

通过中继反向 SSH 连接

Tango-edit-clear.pngThis article or section needs language, wiki syntax or style improvements. See Help:Style for reference.Tango-edit-clear.png

Reason: SSH 隧道的设想是很经典的,所以添加一些参考资料的详细解释会更好。比如 [7] 涵盖了一些其他情况。 (Discuss in Talk:Secure Shell (简体中文)#)

这个想法是客户端通过一个中继连接到服务器,而服务器使用反向 SSH 隧道连接到同一个中继。例如,当服务器位于 NAT 后面时,这是很有用的,而此处的中继是一个可公开访问的 SSH 服务器,用作用户有权访问的代理服务器。前提是客户端的密钥同时对中继和服务器都已经授权,服务器需要授权中继用于反向 SSH 连接。

以下配置假设 user1 是客户端使用的账户,user2 是中继的,user3 是服务器的。首先服务器要先建立反向隧道:

ssh -R 2222:localhost:22 -N user2@relay

这可以利用启动脚本、systemd service 或者 autossh 来自动完成。

Tango-view-fullscreen.pngThis article or section needs expansion.Tango-view-fullscreen.png

Reason: 需要解释为何光有 ssh user3@relay -p 2222 是不够的。 (Discuss in Talk:Secure Shell (简体中文)#)

在客户端使用以下命令建立连接:

ssh user2@relay ssh user3@localhost -p 2222

可以在中继的 ~/.ssh/authorized_keys 中定义 command 字段来建立反向隧道:

command="ssh user3@localhost -p 2222" ssh-rsa KEY2 user1@client

在这种情况下用下列命令建立连接:

ssh user2@relay

注意,客户端内 scp 的自动完成功能失效,甚至在某些配置下 scp 本身也无法工作。

端口复用

SSH 守护进程通常监听 22 端口,但是许多公共热点会屏蔽非常规 HTTP/S 端口(分别是 80 和 443 端口)的流量,这样就屏蔽了 SSH 连接。最快的解决方法是让 sshd 额外监听白名单上的端口:

/etc/ssh/sshd_config
Port 22
Port 443

但是443端口很有可能已经被 HTTPS 服务占用,在这种情况下可以使用端口复用工具,比如 sslh,它可以监听在一个被复用的端口上并转发相应的数据包给对应的服务。

加速 SSH

此处列出一些可以加速全部连接或针对某台主机加速的 客户端配置 选项。要了解这些选项的完整概述,请参阅 ssh_config(5)

  • 使用以下参数来使到某一台主机的所有回话 (sessions) 共享同一个连接:
    ControlMaster auto
    ControlPersist yes
    ControlPath ~/.ssh/sockets/socket-%r@%h:%p
    
其中 ~/.ssh/sockets 可以是一个其他用户不可写入的任意目录。
  • ControlPersist 指定在初始客户端连接关闭后,主服务器在后台等待新客户端的时间。可能的值是:
    • no 指定在最后一个客户端断开后立即关闭连接,
    • 一个用秒数表示的时间,
    • yes 连接不会自动关闭,而是始终处于等待。
  • 另一种加速的方法是通过 Compression yes 选项或者 -C 参数来启用压缩。
注意: ssh(1) 指出:“在调制解调器线路或其他慢速线路上启用压缩是可取的,但在网速快的情况下只会降低速度。”这条提示可能会适得其反,具体取决于你的网络配置。
  • 通过使用 AddressFamily inet 选项或者 -4 参数来跳过 IPv6 查找,可以缩短登录时间。
  • 最后,如果你想用 SFTP 或 SCP,High Performance SSH/SCP 可以通过动态提高 SSH 缓冲区大小来显著提高吞吐量。安装 openssh-hpn-gitAUR 这个包来使用打过这一增强补丁的 OpenSSH 版本。

用 SSHFS 挂载远程文件系统

请参阅 SSHFS 来将一个 SSH 可访问的远程文件系统挂载至一个本地目录,然后你就能在挂载好的文件上执行常规操作(复制,重命名,用 vim 编辑等等)。sshfsshfs 更好,因为后者自 2004 年起就没再更新。

提示: 软件包 autosshfs-gitAUR[broken link: package not found] 可以用于在登录时自动运行 autosshfs。

保持在线

默认情况下,如果你的会话空闲了某个时间之后,它会自动登出。为了保持会话,在长时间没有数据传输时客户端可以向服务器发送一个激活信号。与之对应,服务器也可以在一段时间没有收到消息时定期发送一个信号。

  • 服务器ClientAliveInterval 是没有从客户端收到消息后的超时时间,超时后 sshd 将会发送一个请求来等待回应。默认是 0,指不会发出请求。比如要求每隔 60 秒向客户端发送响应请求,在你的 服务器配置 里设置 ClientAliveInterval 60 即可。ClientAliveCountMaxTCPKeepAlive 选项也可以参考一下。
  • 客户端ServerAliveInterval 控制着从客户端发往服务器的响应请求的时间间隔。比如要求服务器每隔 120 秒响应一次,在你的 客户端配置 里加入 ServerAliveInterval 120 即可。ServerAliveCountMaxTCPKeepAlive 选项也可以参考一下。
注意: 为确保会话保持活动状态,客户端或服务器中只有一个需要发送保持活动请求。如果用户同时控制服务器和客户端,那么合理的选择是使用 ServerAliveInterval 选项配置需要保持会话的客户端,并保留其他客户端和服务器的默认配置。

利用 systemd 自动重启 SSH 隧道

systemd 可以在开机/登录时自动启动 SSH,还可以 在 SSH 连接断开时自动重连。这使它成为管理 SSH 隧道的有力工具。

下面的 service 可以使用 ssh 配置 里面的配置在你登录系统的时候自动开启一个 SSH 隧道。如果连接因为某种原因断开,它将会每隔10秒重启一下:

~/.config/systemd/user/tunnel.service
[Unit]
Description=SSH tunnel to myserver

[Service]
Type=simple
Restart=always
RestartSec=10
ExecStart=/usr/bin/ssh -F %h/.ssh/config -N myserver

然后 enable 并且 start 这个 user service。欲知如何防止连接超时,请参阅 #Keep alive[broken link: invalid section]。如果你想在系统引导后就打开这个连接,你需要将这个 unit 重写为 system service。

Autossh - 自动重启 SSH 会话和隧道连接

当一个 SSH 会话或隧道无法保持连接(比如网络环境差导致客户端断线),可以使用 autossh 来自动重启它们。

使用范例:

$ autossh -M 0 -o "ServerAliveInterval 45" -o "ServerAliveCountMax 2" username@example.com

结合 SSHFS

$ sshfs -o reconnect,compression=yes,transform_symlinks,ServerAliveInterval=45,ServerAliveCountMax=2,ssh_command='autossh -M 0' username@example.com: /mnt/example 

通过一个由 Proxy settings 设置好的 SOCKS 代理来连接:

$ autossh -M 0 -o "ServerAliveInterval 45" -o "ServerAliveCountMax 2" -NCD 8080 username@example.com 

使用 -f 选项以后可以使 autossh 作为后台进程运行,然而以这种方式运行意味着不能交互输入密码。

当你在会话中打出 exit 即可结束会话,或者 autossh 收到了 SIGTERM, SIGINT of SIGKILL 信号。

利用 systemd 在引导后自动运行 autossh

如果你想自动启动 autossh,创建一个 systemd unit 文件:

/etc/systemd/system/autossh.service
[Unit]
Description=AutoSSH service for port 2222
After=network.target

[Service]
Environment="AUTOSSH_GATETIME=0"
ExecStart=/usr/bin/autossh -M 0 -NL 2222:localhost:2222 -o TCPKeepAlive=yes foo@bar.com

[Install]
WantedBy=multi-user.target

其中 AUTOSSH_GATETIME=0 是一个环境变量,它表示 ssh 需要连上多久,autossh 才判定这个连接是成功的。将它设为 0 后 autossh 也会忽略 ssh 的第一次运行失败。这在开机启动 autossh 时可能是有用的。其他环境变量可以在手册页找到。当然,如果需要的话,你可以使这个单元更加复杂(详情请参阅 systemd 文档),显然你可以使用自己的 autossh 选项,但请注意 -f 选项意味着 AUTOSSH_GATETIME=0 无法在 systemd 中起效。

别忘了 start 且/或 enable 这个 service。

你可能还会需要关闭 ControlMaster,像这样:

ExecStart=/usr/bin/autossh -M 0 -o ControlMaster=no -NL 2222:localhost:2222 -o TCPKeepAlive=yes foo@bar.com
提示: 同时管理多个 autossh 进程也是很简单的,要保持多个隧道连接,只需要用不同的文件名创建多个 service 文件。

当 SSH 守护进程出错时的其他选择

对于仅依赖 SSH 的远程或无头服务器,启动 SSH 守护程序失败(例如系统升级后)可能会阻止管理员访问。systemd 通过 OnFailure 选项提供了简便的解决方案。

假设服务器运行 sshd 并且 telnet 是所选的故障安全替代方案。按如下所示创建一个文件。不要 enable telnet.socket!

/etc/systemd/system/sshd.service.d/override.conf
[Unit]
OnFailure=telnet.socket

这样就行了。当 sshd 正在运行时,Telnet 是不可用的。如果 sshd 无法启动,可以打开一个 telnet 会话进行恢复。

疑难解答

自检清单

在进一步阅读前,请先仔细检查下面这些常见故障。

  1. 配置文件存放目录 ~/.ssh 及目录下的文件应该只有你的账户才有访问权限(在客户端和服务器上都检查这一条):
    $ chmod 700 ~/.ssh
    $ chmod 600 ~/.ssh/*
    $ chown -R $USER ~/.ssh
    
  2. 检查客户端的公钥(比如 id_rsa.pub)在服务器的 ~/.ssh/authorized_keys 文件里面。
  3. 检查有没有在 服务器配置 里面设置 AllowUsersAllowGroups 来限制 SSH 访问。
  4. 检查用户是否设置了密码。有时还没有登录过服务器的新用户没有密码。
  5. LogLevel DEBUG 加到 /etc/ssh/sshd_config 文件尾部。
  6. 使用 journalctl -xe 查看可能的错误信息。
  7. 在客户端和服务器上 重启 sshd 然后注销/重新登录。

拒绝连接或者超时问题

端口转发

如果您位于 NAT 模式/路由器之后(除非您位于 VPS 或可公开寻址的主机上),请确保您的路由器可以将传入的 ssh 连接转发到您的计算机。使用 $ ip addr 查找服务器的内网 IP 地址,并将您的路由器设置为将 SSH 端口上的 TCP 数据包转发到该 IP。portforward.com 可以提供帮助。

SSH服务是否开启并且正在监听?

$ ss -tnlp

如果以上的命令没有显示 SSH 端口是打开的,那么说明 SSH 服务没有启动。查看 /var/log/messages 来寻找错误信息。

是否是防火墙阻止了连接?

Iptables 可能会阻止 22 端口的连接。使用

# iptables -nvL

来检查可能会在 INPUT 链上导致丢包的规则。必要情况下可以用以下命令来解锁端口:

# iptables -I INPUT 1 -p tcp --dport 22 -j ACCEPT

更多配置防火墙的信息,请参阅 firewalls.

你的电脑和目的主机之间是否连接?

测试你的电脑和目的主机的连接情况:

# tcpdump -lnn -i any port ssh and tcp-syn

它会显示一些基本信息,然后等待数据交换。现在尝试你的连接。如果没有输出,就可能是你的电脑网络阻塞了。(也许是防火墙问题,也许是 NAT 路由的问题)

你的 ISP 或第三方屏蔽了默认端口?

注意: 只有在你确保你没有运行任何防火墙,你已经在路由器上配置了 DMZ 主机或已经将端口映射到你的计算机,而这些都没有用的情况下才尝试以下步骤。可以在此找到诊断步骤或可能的解决方案。

某些情况下,你的运营商会屏蔽默认端口(22 端口),无论怎么尝试(尝试开启端口、强化堆栈、防范洪水攻击)都无济于事。要确认确实存在屏蔽,只要创建一个接受任何来源(0.0.0.0)的服务器并远程连接它。

如果你收到与此类似的错误消息:

ssh: connect to host www.inet.hr port 22: Connection refused

就表示你的 ISP 没有屏蔽端口,但是服务器没有在该端口上运行 SSH 服务(请参阅 security through obscurity)。

但是,如果你收到与这条类似的错误消息:

ssh: connect to host 111.222.333.444 port 22: Operation timed out 

这就表示有人阻止了 22 端口的 TCP 连接,基本上是通过防火墙或第三方干预(如 ISP 阻止和/或拒绝端口 22 上的传入通信),使得端口不可用。如果你的计算机上没有运行任何防火墙,并且在你的路由器和交换机中没有这方面的流量,那么你的 ISP 屏蔽了通讯。

为了再次检查确认,可以在服务器上运行 Wireshark 并让它监听在 22 端口。由于 Wireshark 是一个二层数据包嗅探工具,而 TCP/UDP 工作在第三层及以上(参阅 IP Network stack),如果在连接时未收到任何内容,则第三方很可能阻止了该端口上到服务器的流量。

诊断

安装 tcpdump 或 Wireshark (wireshark-cli)。

用于 tcpdump:

# tcpdump -ni interface "port 22"

用于 Wireshark:

$ tshark -f "tcp port 22" -i interface

其中 interface 是用于连接 WAN 的网络适配器(用 ip a 来查找)。如果在尝试远程连接时没有收到任何数据包,则可以确信你的 ISP 屏蔽了 22 端口上的传入连接。

可能的解决方案

此方案是换一个 ISP 没有屏蔽的端口。编辑 /etc/ssh/sshd_config 文件来使用不同的端口。例如,新增这几行:

Port 22
Port 1234

还要确保文件中的其他“Port”配置行被注释掉。只是注释“Port 22”并加上“Port 1234”不会解决问题,因为那样 sshd 将只监听在 1234 端口上。写入这两行可以在两个端口上运行 SSH 服务器。

重启 服务器上的 sshd.service 就基本完成了。你还需要配置客户端来使用与默认端口不同的端口,这个问题有很多种解决方案,在这里我们只介绍两种。

"Read from socket failed: connection reset by peer" 错误

使用最近版本的 openssh 连接到较旧的 ssh 服务器时有时会失败,并显示上述错误消息。这可以通过为该主机设置各种 客户端选项 来解决。有关下列选项的更多信息,请参阅 ssh_config(5)

问题可能出在 ecdsa-sha2-nistp*-cert-v01@openssh 椭圆曲线算法上。这些算法可以通过在 HostKeyAlgorithms 里设置可用算法来排除那些算法。

如果这不起作用,可能是秘钥列表太长了。设置 Ciphers 来减少列表长度(少于 80 个字符应该可以)。同样的,也可以尝试缩短 MACs 列表。

参阅 openssh bug forum 上的 讨论

"[your shell]: No such file or directory" / SSH 认证问题

对于这个问题,一个可能的原因是需要 SSH 客户端在 $SHELL 中提供绝对路径(例如可以通过 whereis -b [your shell] 得到),即使你的 shell 在 $PATH 里的某个路径中。

"Terminal unknown" 或 "Error opening terminal" 错误

如果你在登录时收到上述错误,这意味着服务器无法识别你的终端。使用 Ncurses 的应用程序(如 nano)可能会失败,并显示“Error opening terminal”。

正确的解决方案是在服务器上安装客户端终端的 terminfo 文件。这会告诉服务器上的控制台程序如何正确地与终端进行交互。你可以使用 $ infocmp 获得关于当前 terminfo 的信息,然后找出 哪个包包括了它们

如果你不能正常安装它,可以把 terminfo 复制到服务器上你的主目录里面:

$ ssh myserver mkdir -p  ~/.terminfo/${TERM:0:1}
$ scp /usr/share/terminfo/${TERM:0:1}/$TERM myserver:~/.terminfo/${TERM:0:1}/

重新登录、登出服务器后这个问题应该已经解决。

TERM hack

警告: 这只能作为最后的手段。

你可以在服务器上的环境(例如 .bash_profile)中简单地设置 TERM=xterm 。这将消除错误并允许 ncurses 应用程序再次运行,但除非你的终端的控制序列与 xterm 完全匹配,否则可能会遇到奇怪的行为和图形界面问题。

"Connection closed by x.x.x.x [preauth]" 错误

如果你在 sshd 的 log 里看到这条错误,请确保你已经设置了可用的 HostKey

HostKey /etc/ssh/ssh_host_rsa_key

id_dsa 被 OpenSSH 7.0 拒绝

出于安全原因,OpenSSH 7.0 弃用了 DSA 公钥。如果你必须启用它们,请设置 PubkeyAcceptedKeyTypes +ssh-dss 选项(http://www.openssh.com/legacy.html 没有提到这一点)。

OpenSSH 7.0 的 "No matching key exchange method found" 错误

OpenSSH 7.0 弃用了 diffie-hellman-group1-sha1 密钥算法,因为它很弱并且在所谓 Logjam 攻击的理论范围内(参阅http://www.openssh.com/legacy.html)。如果特定主机需要这个密钥算法,ssh 会产生如下错误消息:

Unable to negotiate with 127.0.0.1: no matching key exchange method found.
Their offer: diffie-hellman-group1-sha1

这个问题的最佳解决方案是将服务器升级/配置为不使用不推荐的算法。如果做不到这一点,可以配置客户端选项 KexAlgorithms +diffie-hellman-group1-sha1 强制客户端使用这个算法。

断开 SSH 连接时 tmux/screen 会话被关闭

如果进程在会话结束时被终止,那么你可能是用 ssh.socket 激活的,systemd 注意到 SSH 会话进程退出,然后杀掉了 tmux/screen 进程。这种情况有两种解决方案。一种是通过使用 ssh.service 来代替 ssh.socket 来避免使用 socket 激活。另一个是在 ssh@.service 的 Service 部分设置 KillMode=process

在常规的 ssh.service 里面 KillMode=process 这个选项也是有用的,它可以在 service 停止或重启时防止 SSH 会话进程或 screentmux 进程被 kill 掉。

SSH 会话无响应

SSH 响应 流控制命令 中的 XONXOFF 命令。 当你按 Ctrl+s 时,它会冻结/挂起/停止响应。按 Ctrl+q 恢复会话。

参阅