@snownstone

SSH 笔记

Published on

此前 已经遇到过 SSH 相关的问题,今天又碰到了别的,症状是与服务器失联,SSH 登陆认证失败,于是再把一些要点梳理记录一下。


问题:非 root 用户不能使用 root 的 SSH 公钥导致登陆验证失败

服务器有 root 权限和普通用户权限,出于安全考虑,通常会禁止用户以 root 权限 SSH 远程登陆服务器。

客户端本机也有 root 权限和普通用户权限,同样出于安全考虑,一般都是以普通用户的身份使用自己的机器。

因为我既是普通用户,同时又是自己系统的管理员(无论是本机还是服务器都有 root 权限),于是不小心出现了一些身份和权限的混淆。

我在本机用普通用户的身份生成了 key pair,然后把公钥添加到了服务器,因为在服务器上此时仍是 root 身份,所以这个公钥对应的使用者是 root。但是,接下来我在服务器上新建了一个普通用户,然后修改了 SSH 用户登陆的设置,具体地,禁止以 root 身份 SSH 登陆,同时禁止通过密码验证登陆。于是当我用服务器上的这个普通用户身份试图 SSH 登陆时,根本没有可用的公钥了,前面存放的那个是 root 的。SSH 的密钥和具体的用户身份是严格绑定的,root 和普通用户虽然实际都是我,但概念上是两个完全不同的用户,对应公钥具体的存放路径也不同。结果就是自断后路,无法完成登陆验证而把自己锁在门外了。

解决

思路 1: 把服务器上 root 的公钥转让给普通用户

  1. root 权限登陆系统

  2. 创建普通用户 new_user 对应的公钥存放路径

mkdir /home/new_user/.ssh
  1. 将 root 路径下的公钥转移给普通用户 new_user
mv /root/.ssh/authorized_keys /home/new_user/.ssh
  1. .ssh 路径(包括其下所有文件)的所属权由 root 变更为 new_user
chown -R new_user:new_user /home/new_user/.ssh

注意! 由此一来,root 就不能再以此公钥完成 SSH 登陆验证,密钥已完全归属 new_user 这个普通用户。

思路 2: 再给普通用户新生成一对密钥

  1. root 权限登陆系统

  2. 为用户 new_user 生成新密钥对

# 前后操作都是 root 身份,这条通过 sudo -u new_user
# 实现以 new_user 的身份运行后面 ssh-keygen 命令
sudo -u new_user ssh-keygen
  1. 把上一步生成的私钥 /home/new_user/.ssh/id_rsa复制到客户端本机 ~/.ssh 路径下,然后删除服务器上的数据
rm /home/new_user/.ssh/id_rsa
  1. 将服务器上刚生成的公钥重命名为 authorized_keys
mv /home/new_user/.ssh/id_rsa.pub /home/new_user/.ssh/authorized_keys

后面就可以用 new_user 的身份验证登陆: ssh -i ~/.ssh/id_rsa new_user@ip


知识点备忘

公钥认证(Public Key authentication)

  • 非对称加密算法

  • 公钥私钥对(key pair),公钥用于服务器,即 authorized keys,用来验证试图登陆的用户的身份,要小心管理存放( 添加者之外的用户不该有写入权限);私钥仅客户端本机所有,是用户身份的证明,即 identity keys,也要在本机小心保管存放(可以设置读取 密码添加者之外的用户不该有读取权限

  • 公钥私钥对可用 ssh-keygen 由普通用户在客户端本机生成,通常保存在 ~/.ssh 路径下

授权密钥 (Authorized Keys)

授权密钥规定了哪些用户(谁)被允许通过使用 SSH 公钥认证的方式登陆服务器;OpenSSH 中,每一个用户(root 或非 root)需要创建添加各自的公钥,对应配置文件是 authorized_keys

配置文件 authorized_keys

确认服务器端已将公钥添加入 authorized_keys 配置文件;可以在文件中设置允许使用的命令 permitted command

mkdir ~/.ssh #通常默认存放在该用户的此路径下
chmod 700 ~/.ssh #设置权限,仅创建用户自己可以读写和执行,group 或 other 都不可以
touch ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys #用户自己可以读和写,不能执行
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys #把公钥添加进去

主机密钥 (Host Keys)

这个密钥是区别于前面 key pair (公钥、私钥)的另外第三种钥,是和每台计算机(即主机 host)唯一绑定的,通常是第一次安装 OpenSSH 或计算机第一次启动时自动就会生成的。它的功能有些类似标记客户端身份的私钥(即前面说的 identity keys),它被用来标记服务器端(server)的身份。

当 SSH 客户端 第一次 发起和某台服务器的连接时,通常会向用户显示该服务器的 host key,并询问用户是否要继续与这台主机建立连接,当选择是后,这台服务器的 host key 就会被客户端本机存储在 ~/.ssh/known_hosts 文档中(即本机已知的、打过交道的服务器)。

主机密钥虽然是唯一的,但是是可以更改的。当客户端再次和已知的主机(known host)发起连接时,如果发现对方的 host key 变化了,与之前自己这边记录在册的信息不同时,就会暂停连接向用户发出警告信息,因为这种不明原因的变化可能是有风险的。如果对方主机密钥的改变是管理员有意为之的,那就只是用户客户端这边信息更新不及时没有同步;但如果是有其他第三方恶意冒充对方主机(即 man-in-the-middle attack),那么继续与之建立连接就是非常危险的。所以 host key 主要起这样一种服务器身份标记的作用,保证客户端与之连接的安全性。

这个问题我之前也遇到过了,当时是因为 github 网站的域名解析出错,ip 指向了我本机,那我本机的 host key 自然和 github 服务器的 host key 不同,所以当我本机 SSH 客户端试图与 github 服务器建立连接时,客户端就向我提出了 host key 不匹配的警告。

从安全的角度考虑,host key 以及前面的 key pair 最好动态更新,不要一直固定不变。另外,和某个服务器第一次建立连接时,做好 host key 的查验工作。

排错

  • 本地密钥的权限设置

  • 服务器公钥的所属权,root 权限外,普通用户的权限问题

  • 服务器的防火墙设置 (iptablesufw

  • 确认 SSH 服务在运行:systemctl status sshd

  • 查看服务器 SSH 设置 /etc/ssh/sshd_config,如 PubkeyAuthentication 是否开启

  • 查看客户端本机 SSH 设置 /etc/ssh/config 是否开启 PubkeyAuthentication

  • 使用本机相同的端口,尝试连接其他主机,确认是否是本机的端口被阻拦了

  • 命令中加 -v 获取更多反馈信息纠错

    • TCP 连接是否成功建立

    • 用户名

    • 加密方式是否协商成功

    • 认证方法是否是服务器具备的

    • 若认证成功,则可能是用户端这边 shell 等出问题

命令

nano /etc/ssh/sshd_config #查看 ssh 配置文件确认 ssh 使用的端口

ss -plnt #查看正在运行的进程,确认 ssh 服务实际使用的端口

参考