SSH是什么?

  • SSH(Secure Shell)是一种用于安全远程登录和其他安全网络服务的协议。它提供了在不安全的网络上安全访问远程计算机的方式,常用于远程服务器管理和安全文件传输。
  • 如果一个用户从本地计算机,使用SSH协议登录另一台远程计算机,我们就可以认为,这种登录是安全的,即使被中途截获,密码也不会泄露。
  • SSH本质上只是一种协议,存在多种实现,既有商业实现,也有开源实现。本文针对的SSH是开源实现OpenSSH
  • 此外SSH是一种C/S架构,分为服务端和客户端。本文只针对服务端是Linux的情况,这也是目前最普遍的一种情况。

安装SSH

客户端

现代的大部分操作系统,包括Windows和Linux,都已经内置了SSH客户端。具体可以通过以下命令检测是否已经安装了SSH客户端,以及查看当前SSH客户端版本。

ssh -V

服务端

  1. 有些Linux系统发行版可能会内置服务端,有些则没有,我们则首先需要执行以下命令,查看返回的结果中是否有 sshd字样。如果有,则说明已经开启了SSH服务端,如果没有,则说明需要进行安装。
    ps -e | grep ssh
    
  2. 安装SSH服务端非常简单,基本都可以通过内置的包管理器进行安装,这里只列举两个我常用的Linux系统。
    1. Ubuntu
      apt install openssh-server
      
    2. ArchLinux
      pacman -S openssh
      systemctl start sshd
      systemctl enable sshd
      

远程登录

SSH最常见的用法就是在本地主机(客户端)上通过网络登录到远程主机(服务端)的Shell上。

  1. 打开本地主机Shell,假定你要以用户名user,登录远程主机host(可以是IP地址或者域名),那么只需要执行以下命令即可进行登录。
    ssh user@host
    
  2. 如果是第一次登录远程主机,那么就会出现以下提示:
  3. 要完全了解这个提示,我们必须要对SSH的登录过程有个大概的了解,大致分为三步:
    1. 远程主机收到SSH登录请求,把自己的公钥发给本地主机
    2. 本地主机使用这个公钥,将登录密码加密后,发送回来
    3. 远程主机用自己的私钥,解密登录密码,如果密码正确,就同意登录
  4. 如果对公私钥这部分不理解,建议先去看我博客的另一篇文章计算机网络八股文(HTTP)中的HTTPS公私钥加密部分
  5. 由于SSH不存在CA,因此可能会出现有人冒充远程主机,将伪造的公钥发送给本地主机。为了避免这种情况,SSH会对远程主机的公钥进行哈希运算,其结果被称为“指纹”,然后让用户对比该指纹是否与真正远程主机上的公钥指纹一致,来避免公钥伪装。
  6. 如果你觉得该指纹值得信任,则输入 yes并回车。此时系统会出现一句警告,表示该远程主机已经得到了认可,之后就可以正常输入密码登录远程主机的Shell了。注意:不能直接回车,否则默认不信任该远程主机,需要再次进行连接
  7. 当远程主机的公钥被接受以后,它就会被保存在本地主机的当前用户家目录下的 ~/.ssh/known_hosts文件中。下次再连接这台主机时,系统就会认出它的公钥已经保存在本地了,从而跳过警告部分,直接提示输入密码。

免密登录

SSH协议提供了一种免密登录的方式,不再需要每次登录都输入密码了,这种方式也利用了公私钥的加解密过程。

  1. 首先,在本地主机执行以下命令,用以生成一对公私钥。

    ssh-keygen -t rsa -b 4096 -f <文件路径> -C <备注>
    
    1. -t:这个参数指定了密钥的类型。在这里,rsa表示使用RSA算法生成密钥。RSA是一种广泛使用的公钥加密算法,具有较高的安全性。
    2. -b:这个参数指定了密钥的位数。在这里,4096表示生成一个4096位的密钥。位数越高,安全性越强,推荐至少使用4096位的密钥。
    3. -f:指定密钥文件名和保存路径,如果不指定,则默认将文件保存在 ~/.ssh下,并且命名为 id_rsa.pubid_rsa注意,如果本地主机已经存在了默认的公私钥,那么使用该命令会直接覆盖原文件
    4. -C:在公钥文件内容末尾添加一个注释,方便管理。
  2. 执行命令后,会询问你是否需要添加密码短语(passphrase)?这是一种安全性保障,使得你每次使用该私钥时,都需要使用密码短语来解锁。如果不需要的话,则直接回车即可。我个人一般是不使用的,太麻烦了。

  3. 命令运行结束后,如果没有修改公私钥的文件名和保存路径,则默认在 ~/.ssh目录下生成公钥文件 id_rsa.pub和私钥文件 id_rsa

  4. 将公钥文件中的内容拷贝添加到远程主机上的用户主目录下的 ~/.ssh/authorized_keys文件中,如果文件不存在则创建。之后尝试在本地主机使用正常的SSH登录命令,会发现已经不用在输入密码即可直接登录了。

  5. 这种免密登录的原理很简单,因为本地主机的公钥已经保存在远程主机上,那么每次登录时,远程主机都会发送一段随机字符串过来,本地主机使用私钥加密后再发送过去。远程主机此时再使用公钥解密,如果成功,则证明本地主机可信,不需要输入密码即可直接登录。

  6. 由于需要使用私钥,如果之前创建公私钥时设置了密码短语(passphrase),那么每次SSH连接时,还是需要输入密码短语来解锁私钥。

  7. 默认使用本地主机下的 ~/.ssh/id_rsa私钥进行加密,如果之前创建公私钥时修改了文件名称和保存路径,则我们需要进行额外的配置,否则会导致SSH验证失败,还是需要输入密码登录,这个我们后文会谈到。

  8. 远程主机的 ~/.ssh/authorized_keys文件可以包含多个不同主机的公钥,每个主机都可以以此方式免密登录远程主机。每个公钥都需要占据一行,公钥之间的空行不影响使用,因此建议使用空行隔开不同公钥,并且公钥在创建时最好就设置下备注,方便远程主机的公钥管理。

配置文件

  1. SSH本地主机(客户端)的配置文件为 ~/.ssh/config,通过它可以对多个远程主机(服务端)的连接信息进行配置,并且实现SSH连接的进一步简化。
  2. 在配置文件中,每个远程主机的连接信息所配置的格式如下:
    Host myserver
       HostName server_ip
       User username
       IdentityFile /path/to/your/private/key
       Port port_number
    
  3. 以下是对每个配置项的详细解释:
    1. Host:你为远程主机指定的别名。可以是任何你喜欢的名称,方便记忆。
    2. HostName:远程主机的实际IP地址或域名。
    3. User:你想要以什么用户名登录到远程主机上。
    4. IdentityFile:可选项,指定在SSH连接该远程主机时,要使用哪个私钥文件进行免密登录。为空或不存在则默认为 ~/.ssh/id_rsa
    5. Port:可选项,指定要使用远程主机的哪个端口来建立SSH连接。为空或不存在则默认为22。
  4. 通过这些配置,可以针对单个远程主机的连接信息实现细粒度的修改。并且很方便的是,我们可以直接使用别名来连接远程主机了。比如我这里设置远程主机别名为myserver,由于远程主机的IP地址和用户名等信息我已经预设好了,在连接时直接输入以下命令即可:
    ssh myserver
    

其他用途

SSH除了进行远程登录外,还有很多其他用途,下面分别简单介绍一下。注意,以下用途都基于本地主机(客户端)可以正常通过SSH登录到远程主机(服务端)上。

文件传输

我们可以使用 scp命令来让文件通过网络实现跨主机拷贝,它的用法与 cp基本一致,只不过需要加上远程主机的信息。

  1. 从本地主机拷贝文件到远程主机
    scp <本地文件路径> <用户名>@<host>:<远程文件路径>
    
  2. 从远程主机拷贝文件到本地主机
    scp <用户名>@<host>:<远程文件路径> <本地文件路径>
    
  3. 如果你在本地主机上,通过配置文件给远程主机起了别名,那么也可以直接输入 <别名>:<远程文件路径>来指定远程主机上的文件。

端口转发

端口转发是一个非常实用的功能,其本质是把一台机器的端口映射到另一台机器的端口上,这样别人访问A机器的某个端口时,实际上是在访问B机器上的某个服务。使用SSH可以很方便地实现端口转发。

本地端口转发

  1. 访问本地端口时会自动映射到远程端口上,命令格式如下:
    ssh -L <本地端口>:<目标地址>:<目标端口> <用户名>@<host>
    
  2. 比如我执行 ssh -L 8080:localhost:80 user@host,那么本地主机就会开始监听8080端口,任何访问本地主机8080端口的请求,都会通过SSH转发到远程主机上,再由远程主机访问 localhost:80,即远程主机的80端口。
  3. 如果你在本地主机上,通过配置文件给远程主机起了别名,那么也可以直接输入远程主机别名来指定。
  4. 如果你想关闭端口转发,直接关闭该SSH连接即可

远程端口转发

  1. 访问远程端口时会自动映射到本地端口上,命令格式如下:
    ssh -R <远程端口>:<目标地址>:<目标端口> <用户名>@<host>
    
  2. 比如我执行 ssh -R 9000:localhost:22 user@host,那么远程主机就会开始监听9000端口,任何访问远程主机9000端口的请求,都会通过SSH转发到本地主机上,再由本地主机访问 localhost:22,即本地主机的22端口。
  3. 如果你在本地主机上,通过配置文件给远程主机起了别名,那么也可以直接输入远程主机别名来指定。
  4. 如果你想关闭端口转发,直接关闭该SSH连接即可

常见问题

安装完SSH服务端后,无法通过SSH客户端进行登录

  1. 我们需要对服务端配置文件 /etc/ssh/sshd_config进行检查,以下是其配置项以及相关的说明,你可以按下面的配置进行修改。
    LognGraceTme 2m  # 设置SSH连接的登录超时时间为2分钟
    PermitRoctLogin yes  # 允许root用户可以通过SSH登录
    StnaModes yes  # 提高~/.ssh目录及相关文件的权限,确保不会被其他用户读取
    PasswordAuthentication yes # 允许使用密码进行SSH登录
    
  2. 需要注意的是,有些配置项或许是不安全的,你需要按需进行修改。并且,原配置可能前面有个 #号将配置项给注释掉了,需要将 #号去除才能使其生效。
  3. 如果你对配置文件进行了修改,那就必须执行以下命令重启SSH服务端,然后再检测是否可以通过客户端连接。
    systemctl restart sshd
    

本地主机连接远程主机后一段时间不操作,会很容易掉线

  1. 打开本地主机的SSH配置文件,即 ~/ssh/config
  2. 在文件最上面添加以下代码,它会让本地主机每60秒向服务器发送一次“存活”信号,防止SSH连接因长时间无操作而断开。
    ServerAliveInterval 60
    

参考资料

SSH原理与运用(一):远程登录

SSH原理与运用(二):远程操作与端口转发