SSH服务原理和使用技巧


SSH 是用于计算机之间的加密登录的一种网络协议

  • 在早期互联网使用当中,都是使用明文进行通信的,一旦消息被截获,内容就暴露无疑。1995 年,芬兰学者 Tatu Ylonen 设计了 SSH 协议,将登录信息全部加密,成为互联网安全的一个基本解决方案,迅速在全世界获得推广,目前已经成为 Linux 系统的标准配置。

  • SSH 之所以能够保证安全,原因在于它采用了公钥加密。需要指出的是,SSH 只是一种协议,存在多种实现,既有商业实现,也有开源实现。我们这里主要针对的是 OpenSSH 工具,它是自由软件且应用非常广泛。

SSH服务原理和使用技巧


1. 基本概念和知识点

了解一些关于 SSH 协议的基本知识点 => 参考链接地址

  • [1] 中间人攻击

这个过程本身是安全的,但是实施的时候存在一个风险:如果有人截获了登录请求,然后冒充远程主机,将伪造的公钥发送给用户,那么用户很难辨别真伪,安全机制也就荡然无存了。因为不像 https 协议那样,SSH 协议的公钥是没有证书中心(CA)的,也就是说都是自己签发的。

第一步: 远程主机收到用户的登录请求并把自己的公钥发送给用户(A <- B)
**第二步:** 用户使用这个公钥,将登录密码加密之后发送到远程主机(A .-> B)
第三步: 远程主机用自己的私钥解密登录密码,如果密码正确则同意用户登录(A -> B🤔)

$ tree ~/.ssh
├── authorized_keys  # 存储客户端公钥
├── id_rsa           # 私钥
├── id_rsa.pub       # 公钥
└──known_hosts       # 存储服务端公钥(防止中间人攻击|变更则有区别)
  • [2] 口令登录 - 用户保存服务端公钥(客户端 known_hosts 存储)

我们在第一次登录远程主机的时候,系统出现如下提示信息。大致意思就是告诉我们:无法确认主机的真实性,只知道它的公钥指纹,问你还想继续连接吗?因为公钥指纹的长度较长很难进行对比,所以对其进行了 MD5 加密,才能够真正的验证真伪。

这里有一个问题就是,用户怎么知道远程主机的公钥指纹应该是多少?其实也没有什么好办法,除非将远程主机公钥指纹存放每个用户都能够访问的地方,之后让用户自行核对。

$ ssh [email protected]
The authenticity of host 'host(192.168.31.191)' can't be established.
RSA key fingerprint is 98:2e:d7:xx:xx:xx:xx:xx:28:c2:42:2d:ab:cd:58:4d.
Are you sure you want to continue connecting (yes/no)?

假定经过风险衡量以后,用户决定接受这个远程主机的公钥。系统会出现一句提示,表示主机已经得到认可,然后会要求输入密码。该密码将会被远程主机的公钥加密,之后传送给远程主机,验证密码的合法性。

# known_hosts
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'host,192.168.31.191' (RSA) to the list of known hosts.
Password:
  • [3] 公钥登录 - 服务端保存客户公钥(服务端 authorized_keys 存储)

使用密码登录,则意味着每次登录的话都必须输入密码,非常麻烦。好在 SSH 还提供了公钥登录,可以省去输入密码的步骤。

所谓“公钥登录”,原理很简单,就是用户将自己的公钥储存在远程主机上。登录的时候,远程主机会向用户发送一段随机字符串,用户用自己的私钥加密后,再发回来。远程主机用事先储存的公钥进行解密,如果成功,就证明用户是可信的,直接允许登录 shell,不再要求密码。这种方法要求用户必须提供自己的公钥。

# 生成公私钥对
# 如果担心私钥的安全问题,可以设置一个私钥口令
# 在$HOME/.ssh/目录下,会新生成两个文件,id_rsa.pub和id_rsa
$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa):
Created directory '/root/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /root/.ssh/id_rsa
Your public key has been saved in /root/.ssh/id_rsa.pub
The key fingerprint is:
SHA256:i3DAdDsxxxxxlZLlxaaaMJjFtniYw root@2xxxbb1c05b
The key's randomart image is:
+---[RSA 3072]----+
| =B%O.. O+o o    |
|  O=o-.Xx* *     |
|...++.xx+.*      |
|..xx.* o.+       |
|   .= . S .      |
|   .             |
|                 |
|                 |
|                 |
+----[SHA256]-----+
# 将公钥传送到远程主机host上面
$ ssh-copy-id user@host

如果配置完成之后还是不能够无密码登录的话,就需要查看并配置远程主机的 sshd 服务了,检查下面几行前面 "#" 注释是否取掉。

# 配置远程主机的ssh服务
$ vim /etc/ssh/sshd_config
RSAAuthentication yes
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys

# 重启远程主机的ssh服务
$ systemctl restart sshd.service

远程主机将用户的公钥,保存在登录后的用户主目录的 $HOME/.ssh/authorized_keys 文件中。公钥就是一段字符串,只要把它追加在 authorized_keys 文件的末尾就行了。

# 不使用上面的ssh-copy-id命令,进行无密码登录配置
$ ssh user@host 'mkdir -p .ssh'
$ ssh user@host 'cat >> .ssh/authorized_keys' < ~/.ssh/id_rsa.pub

2. 本地端口绑定和转发 (-L)

你需要熟悉的骚操作终于来了,好好使用哈! => 参考链接地址

SSH服务原理和使用技巧 - 本地端口绑定和转发

# 格式说明
# ssh -vv -NT -L [本地IP]:本地端口:目标IP:目标端口 中转主机用户@中转主机IP -p 中转主机端口

# 实验目的: host1想要访问host2的ftp服务(在host1主机上操作)
# host1: 192.168.100.100
# host2: 192.168.200.200
# host3: 192.168.300.300
$ ssh -vv -N -L 2121:192.168.200.200:21 [email protected] -p 22

# 实验目的:host1想要访问host3的ftp服务(在host1主机上操作)
$ ssh -vv -N -L 2121:localhost:21 [email protected] -p 22

作用:将远程服务的端口转发到本地,然后通过访问本地端口访问远程服务!

SSH服务原理和使用技巧 - 本地端口绑定和转发

SSH服务原理和使用技巧 - 本地端口绑定和转发


  • [1] 绑定本地端口 (-D)

既然 SSH 可以传送数据,那么我们可以让那些不加密的网络连接,全部改走 SSH 连接,从而提高安全性。如下命令执行后,SSH 会建立一个 socket 链接,去监听本地的 8080 端口。一旦有数据传向那个端口,就自动把它转移到 SSH 连接上面,发往远程主机。可以想象,如果 8080 端口原来是一个不加密端口,现在将变成一个加密端口。

# 安装带有SSH的SOCKS服务器
# 让8080端口的数据都通过SSH传向远程主机
$ ssh -vv -D 8080 user@host
  • [2] 本地端口转发 (-L)

有时,绑定本地端口还不够,还必须指定数据传送的目标主机,从而形成点对点的“端口转发”。为了区别后文的“远程端口转发”,我们把这种情况称为“本地端口转发”

假定 host1 是本地主机,host2 是远程主机。由于种种原因,这两台主机之间无法连通。但是,另外还有一台 host3 主机,可以同时连通前面两台主机。因此,很自然的想法就是,通过 host3host1 连上 host2 主机。

下面命令中的 -L 参数一共接受三个值,分别是“本地端口:目标主机:目标主机端口”,它们之间用冒号分隔。这条命令的意思,就是指定 SSH 绑定到本地端口 2121 上,然后指定 host3 主机将所有的数据,转发到目标主机 host221 端口上。假定 host2 主机上运行了 FTP 服务,默认端口为 21。这样一来,我们只要连接 host1 主机上的 2121 端口,就等于连上了 host2 主机上的 21 端口,访问 FTP 服务了。

“本地端口转发”使得 host1host3 之间仿佛形成一个数据传输的秘密隧道,因此又被称为“SSH 隧道”。如果你在 ~/.ssh/config 文件配置对应主机名称,使用起来就更新加方便,直接使用配置的 host 名称即可完成端口转发的操作。

# 我们在host1执行下面的命令
$ ssh -vv -N -L 2121:host2:21 host3

# 本地就可以连接远程FTP服务
$ ftp localhost:2121
  • [3] 实用示例

例子一: 它表示将本机的 5900 端口绑定 host3 主机的 5900 端口,这里的 localhost 指的是 host3 主机,因为目标主机是相对 host3 而言的。

# 转发设置
$ ssh -vv -N -L 5900:localhost:5900 host3

例子二: 通过 host3 主机的端口转发 ssh 登录 host2 主机,这时只要 ssh 登录本机的 9001 端口,就相当于登录 host2 主机了。

# 转发设置
$ ssh -vv -N -L 9001:host2:22 host3

# 本地登录
$ ssh -p 9001 localhost

3. 远程端口转发功能 (-R)

你需要熟悉的骚操作终于来了,好好使用哈! => 参考链接地址

SSH服务原理和使用技巧 - 远程端口转发功能

# 格式说明
# ssh -vv -NT -R [远程IP]:远程端口:目标IP:目标端口 中转主机用户:中转主机IP -p 中转主机端口

# 注意事项
# 这里需要注意的是,这个远程IP地址默认不配置的话,使用本地的localhost作为默认值
# 但是如果中转主机使用localhost的话,我们的Host1就无法访问Host2了,所以需要绑定到主机IP上
# 如果host2使用的是云主机的话,需要在安全组里面将端口暴露出来,这样host1才可以在公网上访问

# 实验目的: 使host1的用户可以通过公网地址访问host3的服务(在host3主机上操作)
# host1: 192.168.100.100
# host2: 192.168.200.200
# host3: 192.168.300.300
$ ssh -vv -NT -R 0.0.0.0:2121:192.168.200.200:21 [email protected] -p 22

# 这时如果host1还是无法访问的话,则可能是因为ssh配置的问题(在host3主机上操作)
# 凭什么可以让远程服务器随便就暴露出一个端口呢?(因为安全问题) 所以ssh默认关闭了该选项
# 可以让sshd将远程端口转发绑定到非loopback地址,这样就可以允许远程主机连接了
$ vim /etc/ssh/sshd_config
GatewayPorts yes

# 重启ssh服务
$ systemctl restart sshd
# 没开启
$ netstat -tanlp | grep 2121
tcp        0        0        127.0.0.1:2121      0.0.0.0:*        LISTEN        -

# 开启了
$ netstat -tanlp | grep 2121
tcp        0        0        0.0.0.0:2121        0.0.0.0:*        LISTEN        -

作用:将远程服务器端口映射到本机的某个端口上,然后访问远程服务就可以访问本地服务了!

SSH服务原理和使用技巧 - 远程端口转发功能

SSH服务原理和使用技巧 - 远程端口转发功能


既然“本地端口转发”是指绑定本地端口的转发,那么“远程端口转发”当然是指绑定远程端口的转发。

还是接着看上面那个例子,host1host2 之间无法连通,必须借助 host3 转发。但是,特殊情况出现了,host3 是一台内网机器,它可以连接外网的 host1 主机,但是反过来就不行,外网的 host1 连不上内网的 host3 主机。这时,“本地端口转发”就不能用了,怎么办?

解决办法是,既然 host3 可以连 host1 主机,那么就从 host3 上建立与 host1SSH 连接,然后在 host1 上使用这条连接就可以了。

# 我们在host3执行下面的命令
$ ssh -vv -N -R 2121:host2:21 host1

上面命令中的 -R 参数也是接受三个值,分别是“远程主机端口:目标主机:目标主机端口”,它们之间用冒号分隔。这条命令的意思,就是让 host1 监听它自己的 2121 端口,然后将所有数据经由 host3 主机,转发到 host221 端口。由于对于 host3 来说,host1 是远程主机,所以这种情况就被称为“远程端口绑定”

绑定之后,我们在 host1 就可以连接 host2 了。这里必须指出,“远程端口转发”的前提条件是,host1host3 两台主机都有 sshd 服务和 ssh 客户端。

# 连接FTP服务
$ ftp localhost:2121

4. 动态端口转发功能 (-D)

你需要熟悉的骚操作终于来了,好好使用哈! => 参考链接地址

# 格式说明
$ ssh -vv -N -D local_host:local_port remote_user@remote_host -p remote_port

# 示例演示
$ ssh -vv -D 8080 [email protected] -p 22
$ ssh -vv -N -D 0.0.0.0:8080 [email protected] -p 22

# 实验目的
# 在本地开一个8080端口,此时本机与100机器之间建立隧道
# 这个端口支持sock5协议,此时我们使用如下命令就可以走100作为代理机器了(流量转发)
$ curl --socks5 127.0.0.1:8080 http://www.google.com
  • [1] 功能介绍

在上面的文章中,我们看到了处理端口转发的所有可能情况,不过那只是静态端口转发。也就是说,我只介绍了通过 SSH 连接来访问另一个主机的端口的情况。那什么是 SSH 动态端口转发呢?

你知道 Web 代理 是用来做什么的吗?答案可能是肯定的,因为很多公司和个人用户都在使用它。它是一个直接连接到互联网的系统,允许没有互联网访问的内部网客户端让其浏览器通过代理来浏览网页。Web 代理 除了允许输出到 Internet 之外,还可以缓存页面、图像等。已经由某客户端下载的资源,另一个客户端不必再下载它们。此外,它还可以过滤内容并监视用户的活动。当然了,它的基本功能是转发 HTTPHTTPS 流量。

SOCKS 服务器 提供的服务类似于公司内部网络提供的代理服务器服务,但不限于转发 HTTPHTTPS 流量,它还允许转发任何 TCP/IP 流量,其中 SOCKS5 也支持 UDP 转发。

  • [2] 使用方式

假设,我们希望在一个没有直接连接到互联网的内部网上通过 Thunderbird 使用邮件服务。如果,我们只有一个 web 代理可以用,我们可以使用的唯一的简单方式是使用某个 webmail。但是,最为简单的方式是在网络中设置一个 SOCKS 服务器,它可以让我们使用 POP3ICMPSMTP 邮件服务,而不会造成任何的不便。

虽然有很多软件可以配置非常专业的 SOCKS 服务器,但用 OpenSSH 工具设置只需要简简单单的一条命令,即可工作。选项 -D 类似于选项为 -L-R 的静态端口转发一样,我们可以让客户端只监听本地请求或从其他节点到达的请求,具体取决于我们将请求关联到哪个地址。使用 SOCKS 服务器的典型端口是 1080

# 简写
$ ssh -D 1080 user@host

# 全拼
$ ssh -fN -D 0.0.0.0:1080 user@host

在静态端口转发中可以看到,我们使用选项 -R 进行反向端口转发,而动态转发是不能这样使用的。我们只能在 SSH 客户端创建 SOCKS 服务器,而不能在 SSH 服务器端创建。即我们只能在 host1主机上设置,而不能在 host3 主机上设置。


5. 实用的 SSH 的特殊参数

使用端口转发的八种场景

  • [1] 使用 -p 参数
# 使用-p参数: 表示指定远程SSH服务的端口号
$ ssh -p 5554 [email protected]
  • [2] 使用 -N/-T 参数
# 两个参数一起用代表这个SSH连接只用来传数据不执行远程操作
# 使用-T参数: 表示不为这个连接分配TTY终端
# 使用-N参数: 表示只连接远程主机,不打开远程shell命令
$ ssh -NT -D 8080 host
  • [3] 使用 -f/-D 参数
# 使用-D参数: 表示进行动态端口转发
# 使用-f参数: 表示SSH连接成功后转入后台运行
# 这样一来就可以在不中断SSH连接的情况下在本地shell中执行其他操作
# 要关闭这个后台连接就只有用kill命令去杀掉进程
$ ssh -f -D 8080 host
  • [4] 使用 -o 参数
# 使用-o参数: 指定配置选项
$ ssh -o ServerAliveInterval=30 \
    -o ProxyCommand='ssh -W %h:%p [email protected]' \
    [email protected]

6. 常见问题处理

记录日常使用 ssh 命令常见遇到的问题

SSHLinux 中的基础服务,作为 IT 从业者,基本上每天都要 SSH 连接到服务器中,从事各种各样的工作。作为这样基础的不能再基础的服务,一旦出现问题,影响也是巨大的,连不上就无法对服务器进行配置和更改,所有的事情也就无从谈起。

SSH服务原理和使用技巧 - 常见问题处理


6.1 秘钥权限不正确

SSH Key: “Permissions 0644 for 'id_rsa.pub' are too open.” on mac

# 报错信息 - 提示信息
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0644 for '/Users/escape/.ssh/vm/vm_id_rsa.pub' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.
bad permissions: ignore key: /Users/escape/.ssh/vm/vm_id_rsa.pub
Permission denied (publickey,password).
# 解决方法 - 权限不对
# https://stackoverflow.com/questions/29933918/ssh-key-permissions-0644-for-id-rsa-pub-are-too-open-on-mac
$ chmod 400 ~/.ssh/id_rsa

# SSH登录机器配置
Host ubuntu
    Hostname 10.100.100.100
    Port 1022
    User root
    IdentityFile /Users/escape/.ssh/id_rsa.pub

6.2 没有安装服务端

在 Linux 桌面端操作系统中,默认是没有安装 SSH 的服务端!

# 报错信息 - 提示信息ssh -v -p 22 [email protected]
kex_exchange_identification: read: Connection reset by peer
Connection reset by 127.0.0.1 port 7890
# 问题排除 - 对应域名地址可以ping通ping 222.22.222.22
0 ▁ 10 ▂ 20 ▃ 30 ▄ 40 ▅ 50 ▆ 60 ▇ 70 █ 80 ▁ 90 ▂ 100 ▃ 110 ▄ 120 ▅
PING 222.22.222.22 (222.22.222.22): 56 data bytes
▂▂▁^C
 0/  3 ( 0%) lost;    5/  10/  13ms; last:    5ms
 0/  3 ( 0%) lost;    5/  10/  13/   4ms (last 3)
--- 222.22.222.22 ping statistics ---

# 问题排除 - 使用-v参数并没有发现问题ssh -v -p 22 [email protected]

# 问题排除 - 登录远程服务器进行本机登录ssh -v -p 22 192.168.33.100

# 问题排除 - 确保已经安装OpenSSH
# 发现只安装了openssh-client工具并没有服务端
$ sudo apt list --installed | grep openssh-server
# 解决方法 - 安装服务端
$ sudo apt install -y openssh-server

# 解决方法 - 重启服务
$ sudo systemctl restart sshd.service

# 解决方法 - 成功登录ssh -v -p 22 [email protected]
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])?

6.3 强制使用 IPv4 地址

SSH 端口转发的时候,有时会用到 IPv6 的地址上面!

# 报错信息 - 提示信息
$ autossh -T -N -L 0.0.0.0:1234:12.12.12.12:1234 [email protected]
bind [::1]:56633: Cannot assign requested address
# 解决方法 - 权限不对
# 可能是ssh在试图使用ipv6的地址,可以在命令中加个参数-4强制使用ipv4
$ autossh -T -N -L 0.0.0.0:1234:12.12.12.12:1234 [email protected] -4

6.4 删除之前域名解析

如果连接的服务器域名会动态变化的话,会导致连接异常!

我们首次建立远程连接的时候,双方主机会相互记录对方的公钥信息,在之后的连接过程中会查找这个信息的。然后,当 ssh 服务主机重装系统/访问地址修改等之后,公钥发生了改变,任以旧版本公钥的主机自然是无法与新系统连接的,所以这就会导致我们远程的时候会出问题。

如果我们都没有修改的话,遇到上面问题,首先需要想到的是 主机被人黑了,其在消除入侵记录时对 known_hosts 文件做了改动,导致了上述问题的出现。解决的方法很简单,就是删除我们本地 known_hosts 中保留的对应主机的公钥信息,然后再次建立新的连接,即可获得新的公钥。

  • [1] 提示有冲突,但是并没有给出解决方法
# 报错信息 - 提示信息ssh -v -p 1234 [email protected]
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the ED25519 key sent by the remote host is
SHA256:1234A1234B1234CahVgF/N233.
Please contact your system administrator.
Add correct host key in /Users/escape/.ssh/known_hosts to get rid of this message.
Offending ED25519 key in /Users/escape/.ssh/known_hosts:1236
Host key for [escape-xxx-life]:1234 has changed and you have requested strict checking.
Host key verification failed.
# 解决方法 - 之前有记录
# 删除本地 .ssh 下的 known_hosts 文件中之前的域名记录历史
➜ vim .ssh/known_hosts
  • [2] 提示有冲突,但是给出了解决的方法
# 报错信息 - 提示信息
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the ECDSA key sent by the remote host is
SHA256:Z/1234A1234B1234CahVgF.
Please contact your system administrator.
Add correct host key in /home/escape/.ssh/known_hosts to get rid of this message.
Offending ECDSA key in /home/escape/.ssh/known_hosts:285
  remove with:
  ssh-keygen -f "/home/escape/.ssh/known_hosts" -R "10.100.0.100"
ECDSA host key for 10.100.0.100 has changed and you have requested strict checking.
Host key verification failed.
# 解决方法 - 之前有记录
# 执行如下命令即可删除其对应的本地 known_hosts 中的那一条记录
➜ ssh-keygen -f "/home/escape/.ssh/known_hosts" -R "10.100.0.100"

# 解决方法 - 忽略对应提示
# 服务端修改配置并重启服务器重试连接(不安全|不推荐使用)
➜ vim ~/.ssh/config
StrictHostKeyChecking no
UserKnownHostsFile /dev/null

6.5 默认不再支持 rsa 私钥

简言之就是 ssh client 更新了,不支持 rsa 的私钥,导致无法登陆。

使用 ssh 登陆服务器,出现类似下面的提示:

debug3: authmethod_is_enabled publickey
debug1: Next authentication method: publickey
debug1: Offering public key: /home/user/.ssh/id_rsa RSA ... agent
debug1: send_pubkey_test: no mutual signature algorithm <-- ssh-rsa is not enabled
debug1: No more authentication methods to try.
user@hostname: Permission denied (publickey).

本来以为是 key 弄错了,加上 -v 参数才发现问题。解决方案也很简单,就是在 ~/.ssh/config 中增加:

Host *
    ServerAliveInterval 30
    PubkeyAcceptedKeyTypes +ssh-rsa

如果是出现:no matching host key type found. Their offer: ssh-rsa,ssh-dss 的错误,还需要配置:

HostKeyAlgorithms=ssh-rsa,ssh-dss

7. 命令常用参数总结

OpenSource: 总结的快捷键使用文档

之前在浏览网页的时候,发现这个 OpenSource 发的这个图片,感觉挺好的,这里转载一下。

SSH服务原理和使用技巧 - 快捷键总结


文章作者: Escape
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Escape !
  目录