深入了解SSH

学习:深入了解SSH

ssh

有商业和开源版本,其中openssh是开源中最流行的。

ssh历史

1995 年 7 月, 芬兰学者Tatu Ylonen 以免费软件的形式将一套保护信息传输的程序(也就是 SSH )发布出去。程序很快流行,到年底已经有两万用户,遍布五十国家。所以在年底时,他创立了 SSH 通信安全公司来继续开发和销售 SSH,所以它变成了专有软件。在 1999 年,瑞典程序员基于 SSH 最后一个开源的版本 1.2.12 开发了 OSSH,之后 OpenBSD 开发者在 OSSH 的基础上进行大量修改,形成了 OpenSSH,它是目前唯一一种最流行的 SSH 实现,成为了所有操作系统的默认组件。

用法

生成密钥

ssh-keygen

用生成、管理和转换身份验证密钥。

参数说明:

  • -b bits 指定要创建的秘钥中的位数,默认 2048 位,值越大,密码越复杂
  • -C comment 注释,在 id_rsa.pub 中末尾
  • -t rsa/dsa等 指定要创建的秘钥类型,默认为 RSA
  • -f filename 指定公私钥的名称,会在 $HOME/.ssh 目录下生产私钥 filename 和公钥 filename.pub
  • -N password 指定使用秘钥的密码,使得多人使用同一台机器时更安全

常用命令:

# 生成公私钥,默认文件为 ~/.ssh/id_rsa
ssh-keygen -t rsa -b 4096 -C "[email protected]"

id_rsa:私钥
id_rsa.pub:公钥

image-20231115133703251

管理私钥

ssh-agent和ssh-add

用于管理私钥,一般情况下会使用ssh-keygen生成的(没有密码的)私钥作为默认私钥,此时没有必要使用ssh-agent,当出现以下两种情况时则需要使用:

  • 使用不同的私钥连接不同的主机时,需要手动指定对应的私钥。(ssh-agent能帮助选择对应的私钥进行认证)
  • 当私钥设置了密码时,又需要频繁的使用私钥进行认证。(ssh-agent能帮助免去重复输入密码)
# 启动代理
eval `ssh-agent`
# 关闭代理
ssh-agent -k
# 在 ~/.bashrc 中加入以下来实现登陆自动启动 ssh-agent,退出自动 kill 掉程序
eval $(ssh-agent -s) > /dev/null
trap 'test -n "$SSH_AGENT_PID" && eval `/usr/bin/ssh-agent -k` > /dev/null' 0

# 查看代理中的私钥
ssh-add -l
# 查看代理中私钥对应的公钥
ssh-add -L
# 移除指定的私钥
ssh-add -d /path/of/key/key_name
# 移除所有的私钥
ssh-add -D

发送公钥

ssh-copt-id

在登陆时,将公钥放在(绑定)要登陆的服务器上。(云服务器大都默认关闭密钥登陆 PasswordAuthentication no

# 发送公钥的两种方式(等价)
ssh-copy-id -i ~/.ssh/id_rsa.pub user@host
ssh user@host 'mkdir -p .ssh && cat >> .ssh/authorized_keys' < ~/.ssh/id_rsa.pub

登陆

登陆配置

SSH 登陆服务器需要知道服务器的主机地址(主机名或主机 IP 地址),用户名和密码,有时还要指定端口号(默认 22 ),一般使用的登陆命令如下:

# 登陆目标服务器( 172.17.132.120 )
ssh -p 58422 [email protected]
# 通过跳板机登陆目标服务器( 172.17.132.120 )
ssh -p 58422 [email protected] ssh [email protected]
# 端口映射
ssh -p 58422 [email protected] -fNL 5433:172.17.132.120:5432 -N

通过设置$$HOME/.ssh/config$ ,可以使用别名直接登陆。

# 登陆目标服务器( 172.17.132.120 )
ssh target
# 通过跳板机登陆目标服务器( 172.17.132.120 )
ssh jump_target
# 端口映射
## 登陆时通过 LocalForward 配置
ssh jump_target
## 使用-L来实现本地端口映射
ssh -C -N -g -L 5433:127.0.0.1:5432 jump_target


# 通用配置,所有配置都使用
Host *
    AddKeysToAgent yes      # 将私钥添加到ssh-agent中
    UseKeychain yes         # 保存密码到agent中
    ServerAliveInterval 10  # 连接心跳间隔10s
    ServerAliveCountMax 3   # 重连次数为3
# target配置
Host target
    HostName 172.17.132.120
    User user
    Port 58422
    IdentityFile ~/.ssh/id_rsa
# 跳板机配置
Host jumper
    HostName jumper.example.com
    User user
    Port 58422
    IdentityFile ~/.ssh/id_rsa
Host jump_target
    HostName 172.17.132.120
    User user
    Port 22
    IdentityFile ~/.ssh/id_rsa
    ProxyCommand ssh user@jumper -W %h:%p 2>/dev/null
    LocalForward 5433 localhost:5432    # 本地5433映射到jump_target的5432

一般在做远程开发时,VS Code 的 Remote 插件会读取本地的配置文件 $HOME/.ssh/config,所以可以在本地匹配好,直接登陆。

首次登陆

一般在 $HOME/.ssh 目录下除了公私钥文件,config 配置文件,authorized_keys 认证文件外,还有一个 known_hosts 文件, 这个文件记录了远程主机 ip 和远程主机对应的公钥指纹

image-20231115135713253

image-20231115140119642

我们在第一次登陆(密码或秘钥登陆)服务器时,会有如下的提示界面:

### SSH 首次登陆的提示
The authenticity of host '127.0.0.1 (127.0.0.1)' can't be established.
ECDSA key fingerprint is SHA256:HosOqhcUmbB7QG81yCuDPkvxTgot+vpple+czXPrEug.
ECDSA key fingerprint is MD5:fd:d7:e1:2c:42:4e:b4:2d:a3:21:4d:d1:c4:74:64:2d.
Are you sure you want to continue connecting (yes/no)?

此时 known_hosts 并没有 127.0.0.1 这台机器的指纹信息,所以显示这个提示来让我们确认这个指纹是否是目标机器的 ECDSA 算法的指纹。 当我们输入 yes 确认后,在下次登录的时候,远程主机发送过来的公钥指纹,直接和 known_hosts 文件中对应 ip 的公钥指纹比较即可。

# 本机查看服务器 172.17.132.120 的所有公钥(要与服务器上 /etc/ssh 下面的公钥 *.pub 一致)
ssh-keyscan -p 22 172.17.132.120

# 查看服务器公钥 ecdsa 的指纹 -E md5/sha256 指纹 hash 算法
ssh-keygen -E md5 -lf /etc/ssh/ssh_host_ecdsa_key.pub
## 256 MD5:84:3d:9c:6e:75:f2:6b:b2:0b:40:aa:d6:29:2f:b4:40 no comment (ECDSA)
## 256 SHA256:ZoGnph63gnKLC9wQYrHYVU8ROTf6+K9LKAjn+jrXB2o no comment (ECDSA)

# 从客户端查看服务器公钥 ecdsa 的指纹(初次登陆时要验证的指纹)
ssh-keyscan -t ecdsa -p 22 172.17.132.120 |ssh-keygen -lf -

# 公钥转换成特定指纹 hash 算法的指纹
awk '{print $2}' /etc/ssh/ssh_host_ecdsa_key.pub | base64 -d|openssl sha256 -binary |base64

known_hosts 这个文件是客户端验证服务端身份的重要依据。每次客户端向服务端发起连接请求时,不仅服务端要验证客户端的合法性,客户端也需要验证服务端的身份。

客户端就是通过 known_hosts 中的公钥指纹来验证服务器是否发生了变化,它在一定程度上能避免中间人攻击,除了第一次登陆,因为那时 known_hosts 中还没有服务器的身份信息,所以对于首次提示的登陆指纹信息还是需要和服务器比对,最安全保险的做法是第一次登陆就使用秘钥登陆。

登录流程

  1. 版本号协商阶段
  2. 密钥和算法协商阶段
  3. 认证阶段
  4. 会话请求阶段
  5. 会话交互阶段

登陆方式分为密码登陆和密钥登录。

  • 密码登陆
  1. 客户端使用密钥和算法协商阶段生成的会话密钥加密账号和密码(口令)、认证方式,将结果发送给服务器。
  2. 服务端使用获得的会话密钥解密报文,得到账号和口令。
  3. 服务端对这个账号和口令进行判断,如果失败,向客户端发送认证失败报文,其中包含了可以再次认证的方法列表。
  4. 客户端从认证方法列表中选择一种方法进行再次认证。
  5. 这个过程反复进行,直到认证成功或者认证次数达到上限,服务端关闭本次TCP连接。

image-20231115142332073

  • 密钥登录
  1. 客户端使用密钥和算法协商阶段生成的会话密钥加密账号、认证方法、id_rsa.pub,将结果发送给服务端。
  2. 服务端使用会话密钥解密报文,得到账号、id_rsa.pub。服务端在 $HOME/.ssh/authorized_keys 中找对应的公钥,如果没有找到,发送失败消息给客户端;如果找到,比较客户发送过来的这个公钥和找到的公钥,如果内容相同,服务端生成一个随机的字符串,简称“质询”,然后使用找到的公钥加密这个质询,然后使用会话密钥再次加密
  3. 服务端把这个双重加密的数据发送给客户端。
  4. 客户端使用会话密钥解密报文,然后使用 id_rsa 再次解密数据,得到质询。
  5. 客户端使用会话密钥加密质询,发送给服务端。
  6. 服务端使用会话密钥解密报文,得到质询,判断是不是自己生成的那个质询,如果不相同,发送失败消息给客户端,如果相同,认证通过。

免密安全传输

scp/rsync/sftp 都可以基于 SSH 来进行免密安全传输。

# 从本地同步 src.tar.gz 文件到远程服务器 jump_target 的目录 /path/to/des/
scp src.tar.gz jump_target:/path/to/des/
rsync -avz src.tar.gz jump_target:/path/to/des/
sftp登录 & lcd src.tar.gz /path/to/des

# 从远程服务器 jump_target 的文件 /path/to/src.tar.gz 到本地
scp jump_target:/path/to/src.tar.gz .
rsync -avz jump_target:/path/to/src.tar.gz .
sftp登录 & put /path/to/des/src.tar.gz
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章