ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

SSH免密登录原理与实战:从密钥对生成到集群自动化配置

SSH免密登录原理与实战:从密钥对生成到集群自动化配置 1. 项目概述为什么我们需要SSH免密登录每次登录服务器都要敲密码烦不烦尤其是在管理多台服务器、需要频繁进行文件同步、批量执行命令或者构建自动化运维流程时手动输入密码不仅效率低下更是自动化脚本的“天敌”。SSH免密登录本质上就是利用非对称加密的密钥对让服务器A可以无需密码直接登录到服务器B。这就像是你给信任的朋友配了一把你家大门的钥匙他下次来就不用敲门等你开了。这个需求在服务器集群管理、持续集成/持续部署CI/CD、数据备份同步等场景下几乎是刚需。想象一下你有一个脚本需要每天凌晨3点从10台服务器上拉取日志进行分析如果每台都需要交互式输入密码这个脚本根本没法自动运行。免密登录就是解决这个痛点的标准方案。它基于公钥加密体系操作本身并不复杂但里面的细节和可能踩的坑却不少。今天我就结合自己这些年折腾几十台服务器的经验把从单对单到多对多的免密登录设置掰开揉碎了讲清楚。2. 核心原理与密钥体系解析在动手之前我们必须搞清楚SSH免密登录到底是怎么一回事。它核心依赖的是非对称加密技术而不是我们常说的“记住密码”。2.1 非对称加密公钥与私钥你可以把非对称加密理解为一套特制的锁和钥匙。但这套锁很特别它有两把钥匙一把叫“公钥”可以公开给任何人它的作用是“上锁”另一把叫“私钥”必须绝对私密它的作用是“开锁”。公钥加密的信息只有对应的私钥才能解密。在SSH场景下私钥存放在你打算发起登录的客户端机器上比如你的个人电脑或者服务器A。它就像你的身份证原件绝不能外泄。公钥存放在你打算登录的目标服务器上比如服务器B。它就像公开的锁芯模具谁都可以知道但只有对应的“钥匙”才能打开。整个免密登录的流程可以简化为客户端对服务器说“我是某某某请用我的公钥锁一个挑战信息给我。”服务器用存储的公钥加密一段随机信息发回。客户端用自己的私钥解密这段信息再发回去。服务器验证解密结果正确就认为客户端身份合法允许登录。全程无需传输密码。2.2 关键文件id_rsaid_rsa.pub与authorized_keys实际操作中我们会接触到几个关键文件~/.ssh/id_rsa 默认的私钥文件名。这是你的命根子文件权限通常设置为600仅所有者可读写。~/.ssh/id_rsa.pub 与上述私钥对应的公钥文件。它的内容是一长串以ssh-rsa或ssh-ed25519开头的文本这就是你要分发出去的“锁”。~/.ssh/authorized_keys 位于目标服务器的用户家目录下的.ssh文件夹内。这个文件存储了所有被该用户授权免密登录的公钥列表。每行一个公钥。当客户端尝试登录时服务器会检查客户端的公钥是否在这个“白名单”里。注意.ssh目录的权限也至关重要。通常~/.ssh目录权限应为700authorized_keys文件权限应为600。权限设置过松如755SSH出于安全考虑会直接拒绝使用密钥登录这是新手最常见的坑之一。2.3 算法选择RSA vs. Ed25519在生成密钥时我们会面临算法选择。目前主流的有两种RSA 老牌、兼容性极佳。在2022年之前默认长度是2048位但现在更推荐使用4096位以增强安全性。命令如ssh-keygen -t rsa -b 4096。Ed25519 新秀基于椭圆曲线加密。在相同安全强度下密钥更短、生成更快、签名速度也更快。且被认为更能抵抗某些类型的密码学攻击。命令如ssh-keygen -t ed25519。如何选择如果你的环境涉及非常老旧的系统比如十年前未升级的OpenSSH为了最大兼容性选RSA 4096。对于绝大多数现代Linux发行版CentOS 7/Ubuntu 16.04和macOSEd25519是最佳选择它更安全、更高效。我个人现在在新环境统一使用Ed25519。3. 单台到单台标准免密登录设置全流程我们现在从最简单的场景开始从服务器A免密登录到服务器B。假设A的IP是192.168.1.100B的IP是192.168.1.200我们想用A上的root用户登录B的root用户。3.1 在源服务器A上生成密钥对首先登录服务器A。# 1. 生成密钥对这里以Ed25519为例 ssh-keygen -t ed25519执行命令后会有一系列交互提示Enter file in which to save the key (/root/.ssh/id_ed25519):直接回车使用默认路径和文件名。Enter passphrase (empty for no passphrase):这里需要重点说明。它询问你是否为私钥设置一个“密码短语”。如果设置了每次使用该私钥时都需要输入这个短语相当于为私钥本身再加一把锁安全性更高。但对于全自动化的脚本设置密码短语会导致自动化中断因为脚本无法交互式输入。请根据你的安全需求权衡追求极致自动化如CI/CD 直接回车不设密码。兼顾安全与便利 设置一个强密码短语并配合ssh-agent工具在会话期内管理只需输入一次。个人管理安全第一 强烈建议设置密码短语。Enter same passphrase again:再次确认密码短语。生成成功后在~/.ssh/目录下你会看到两个新文件id_ed25519私钥和id_ed25519.pub公钥。3.2 将公钥分发到目标服务器B接下来需要把A的公钥安装到B的authorized_keys文件中。有几种方法方法一使用ssh-copy-id命令最推荐这是最安全、最便捷的方式它会自动处理目录创建、权限设置等问题。# 在服务器A上执行 ssh-copy-id -i ~/.ssh/id_ed25519.pub root192.168.1.200系统会提示你输入服务器B上root用户的密码。输入正确后命令会自动将公钥内容追加到B服务器的~/.ssh/authorized_keys文件末尾并设置好权限。方法二手动复制当ssh-copy-id不可用时如果目标服务器没有ssh-copy-id命令可以手动操作。在A上查看公钥内容cat ~/.ssh/id_ed25519.pub复制全部输出。登录服务器B。确保~/.ssh目录存在权限正确mkdir -p ~/.ssh chmod 700 ~/.ssh将复制的公钥内容追加到authorized_keys文件echo ‘你复制的公钥内容‘ ~/.ssh/authorized_keys设置authorized_keys文件权限chmod 600 ~/.ssh/authorized_keys3.3 测试免密登录在服务器A上尝试登录服务器Bssh root192.168.1.200如果一切配置正确你应该能直接登录而不会被要求输入密码。如果失败了别急我们后面有详细的排错指南。4. 一对多与多对多集群环境下的高效配置管理三五台服务器上述方法足矣。但当你面对几十上百台机器的集群时手动逐台分发公钥无异于一场灾难。这时就需要更高效的策略。4.1 使用配置管理工具Ansible/Puppet这是生产环境的最佳实践。以Ansible为例你可以编写一个Playbook批量在所有目标服务器上部署你的公钥。准备一个包含所有目标服务器IP或主机名的清单文件inventory.ini[web_servers] 192.168.1.201 192.168.1.202 192.168.1.203 [db_servers] 192.168.1.210编写Playbookdeploy_ssh_key.yml--- - name: Deploy SSH public key to all servers hosts: all # 针对所有主机 become: yes # 使用sudo/root权限 tasks: - name: Ensure .ssh directory exists ansible.builtin.file: path: “/root/.ssh“ state: directory mode: ‘0700‘ - name: Deploy authorized key ansible.builtin.authorized_key: user: root state: present key: “{{ lookup(‘file‘, ‘/path/to/your/id_ed25519.pub‘) }}“ # 指定你的公钥文件路径执行Playbookansible-playbook -i inventory.ini deploy_ssh_key.yml -k-k参数表示让Ansible询问SSH密码初次执行时需要。执行成功后你的公钥就被批量添加到所有服务器的authorized_keys文件中了。4.2 使用循环脚本进行批量分发如果没有配置管理工具可以写一个简单的Shell脚本配合ssh-copy-id。前提是所有服务器的密码相同这在初始化环境中很常见但生产环境务必在分发后立即修改密码。创建一个服务器IP列表文件server_list.txt192.168.1.201 192.168.1.202 192.168.1.203编写分发脚本batch_ssh_copy.sh#!/bin/bash # 定义你的密码注意安全生产环境建议用ssh-agent或expect交互此处仅为示例 PASSWORD“your_common_password“ # 读取IP列表 while read SERVER_IP do echo “Deploying to $SERVER_IP...“ # 使用sshpass工具自动输入密码需先安装yum install sshpass / apt install sshpass sshpass -p “$PASSWORD“ ssh-copy-id -o StrictHostKeyCheckingno -i ~/.ssh/id_ed25519.pub root$SERVER_IP if [ $? -eq 0 ]; then echo “Success: $SERVER_IP“ else echo “Failed: $SERVER_IP“ fi done server_list.txt重要警告在脚本中明文存储密码是极不安全的上述方法仅适用于一次性、封闭的测试环境。更安全的方式是使用ssh-agent或在第一次手动登录每台服务器接受主机密钥后使用无密码的脚本。4.3 多对多互信配置在某些集群场景如Hadoop、Spark集群可能需要所有节点之间两两免密登录。手动配置复杂度是O(n²)。高效的做法是选定一台管理节点在这台节点上生成密钥对。使用上述批量分发方法将管理节点的公钥分发到集群所有其他节点。将所有其他节点的公钥都收集到管理节点并合并成一个“全局”的authorized_keys文件。再将这个包含了所有节点公钥的“全局”authorized_keys文件分发回集群中的每一个节点。这样每个节点都拥有集群内所有其他节点的公钥从而实现全网状互信。这个过程同样用Ansible等工具自动化完成是最优雅的。5. 高级配置与安全加固免密登录带来了便利也引入了风险私钥泄露等于全线溃败。因此安全加固必不可少。5.1 SSH客户端配置优化~/.ssh/config频繁输入ssh userhostname也很麻烦。通过配置~/.ssh/config文件可以极大简化操作并固化一些安全参数。# 编辑客户端配置 vim ~/.ssh/config添加如下内容Host server-alias # 自定义一个简短别名 HostName 192.168.1.200 # 真实IP或域名 User root # 登录用户名 Port 22 # 端口如果修改过请对应 IdentityFile ~/.ssh/id_ed25519 # 指定使用的私钥文件 # 以下是安全与连接优化参数 ServerAliveInterval 60 # 每60秒发送一次保活包防止连接被中断 ServerAliveCountMax 3 # 最多发送3次保活包无响应则断开 TCPKeepAlive yes # 启用TCP保活 Compression yes # 启用压缩加速传输 # 严格的主机密钥检查防中间人攻击 StrictHostKeyChecking yes UserKnownHostsFile ~/.ssh/known_hosts配置后你只需要执行ssh server-alias即可登录系统会自动使用指定的用户、端口和私钥。5.2 服务器端安全加固/etc/ssh/sshd_config光配置客户端不够服务器端sshd的配置才是安全的大门。修改前请备份原文件。sudo vim /etc/ssh/sshd_config建议进行以下关键修改# 1. 禁止root用户直接密码登录强制使用密钥 PermitRootLogin prohibit-password # 或 without-password 新版本用 prohibit-password # 2. 禁用密码认证强制使用密钥确保密钥可用后再改 PasswordAuthentication no # 3. 使用更安全的密钥算法 HostKey /etc/ssh/ssh_host_ed25519_key HostKey /etc/ssh/ssh_host_rsa_key # 4. 禁用不安全的协议和算法 KexAlgorithms curve25519-sha256libssh.org,ecdh-sha2-nistp521 Ciphers chacha20-poly1305openssh.com,aes256-gcmopenssh.com MACs hmac-sha2-512-etmopenssh.com,hmac-sha2-256-etmopenssh.com # 5. 限制登录用户白名单 AllowUsers root your_username # 6. 修改默认端口可选可减少自动化攻击扫描 Port 2222每次修改sshd_config后需要重启服务生效sudo systemctl restart sshd。务必在另一个活跃的SSH会话中测试新配置无误后再关闭当前会话防止配置错误导致自己也无法登录。5.3 私钥管理最佳实践使用密码短语 如前所述为私钥设置强密码短语。使用ssh-agent 它可以管理你的私钥和密码短语。在一个终端会话中只需输入一次密码短语ssh-agent就会在后台帮你记住解密的私钥后续所有SSH连接都无需再输入。# 启动ssh-agent并添加私钥 eval “$(ssh-agent -s)“ ssh-add ~/.ssh/id_ed25519 # 此时会提示输入一次密码短语定期轮换密钥 像换密码一样定期如每半年或一年生成新的密钥对并更新所有服务器的authorized_keys文件。最小权限原则 不要用root密钥去登录普通用户。为不同的用途如个人登录、CI/CD流水线、备份脚本创建不同的密钥对并在服务器端通过authorized_keys文件的选项来限制其权限例如限制允许执行的命令。6. 实战问题排查与调试指南配置过程很少一帆风顺。下面是我遇到过的典型问题及解决方法。6.1 权限问题最常见SSH对文件和目录权限极其敏感。请严格按照以下顺序检查在客户端A检查私钥权限ls -l ~/.ssh/id_*。私钥文件权限必须是600-rw-------。如果不是用chmod 600 ~/.ssh/id_ed25519修正。在服务端B检查.ssh目录权限必须是700drwx------。chmod 700 ~/.ssh。authorized_keys文件权限必须是600。chmod 600 ~/.ssh/authorized_keys。用户家目录权限不能对组或其他人有写权限755可以775或777不行。建议设置为755chmod 755 ~。6.2 调试模式-v参数当连接失败时使用-vverbose参数查看详细过程一个-v不够可以用-vvv获取最详细信息。ssh -vvv root192.168.1.200仔细阅读输出关键信息通常在最后。你会看到客户端尝试了哪些认证方式publickey, password, keyboard-interactive。它是否找到了你的私钥Offering public key: /home/you/.ssh/id_ed25519。服务器是否接受了你的公钥Authentication succeeded (publickey)。如果失败原因是什么例如Permission denied (publickey)。6.3 服务器端日志查看在目标服务器B上查看SSH守护进程的日志通常位于/var/log/auth.logDebian/Ubuntu或/var/log/secureRHEL/CentOS。sudo tail -f /var/log/secure然后从客户端A尝试连接观察服务器日志的输出。常见的错误信息会直接指明问题比如“Authentication refused: bad ownership or modes for directory /home/xxx”。6.4 常见问题速查表问题现象可能原因解决方案Permission denied (publickey).1. 公钥未正确添加到authorized_keys。2. 文件/目录权限错误。3. 服务器sshd_config中PubkeyAuthentication被设置为no。1. 检查authorized_keys内容。2. 严格检查客户端私钥和服务端.ssh目录、authorized_keys文件权限。3. 检查/etc/ssh/sshd_config确保PubkeyAuthentication yes。仍然提示输入密码1. 客户端使用的私钥不是分发公钥对应的那个。2.authorized_keys文件格式错误如有多余空格、换行。1. 使用ssh -i /path/to/key指定私钥或检查ssh-agent是否加载了正确的密钥。2. 用cat -A查看authorized_keys文件确保每行是一个完整的公钥行末无多余字符。Agent admitted failure to sign using the key.ssh-agent没有加载私钥或加载的私钥需要密码短语而未解锁。运行ssh-add ~/.ssh/id_ed25519添加并解锁私钥。Connection closed by remote host.服务器sshd配置严格拒绝了连接如只允许特定用户、IP。检查服务器sshd_config中的AllowUsers,DenyUsers,AllowGroups,DenyGroups以及防火墙设置。连接缓慢DNS反向解析问题。在服务器sshd_config中设置UseDNS no并重启sshd。或在客户端~/.ssh/config中为特定主机设置CheckHostIP no。7. 在特定工具与场景中的应用7.1 在VS Code Remote-SSH中配置VS Code的Remote-SSH扩展极大方便了远程开发。配置免密登录后体验更佳。确保本地已生成密钥对并将公钥部署到远程服务器。在VS Code中按下F1输入“Remote-SSH: Open SSH Configuration File”选择你的本地config文件通常是~/.ssh/config。按照前面第5.1节的格式添加远程服务器的配置。保存后在VS Code侧边栏的“远程资源管理器”中就能看到配置好的主机点击即可连接无需输入密码。7.2 在CI/CD流水线如GitLab CI GitHub Actions中使用在自动化流水线中通常需要从构建服务器RunnerSSH到测试或生产服务器执行部署命令。生成部署专用密钥对 在本地生成一对新的密钥切勿使用个人私钥。将公钥添加到目标服务器 将生成的公钥添加到目标部署服务器的authorized_keys文件中。将私钥作为变量Variable存储在CI平台在GitLab CI中进入项目设置 - CI/CD - Variables添加一个变量如SSH_PRIVATE_KEY将私钥文件的内容包括-----BEGIN OPENSSH PRIVATE KEY-----和-----END OPENSSH PRIVATE KEY-----完整粘贴进去。勾选“Mask variable”和“Protect variable”。在GitHub Actions中进入仓库Settings - Secrets and variables - Actions添加仓库机密Repository secret。在.gitlab-ci.yml或GitHub Actions工作流文件中使用# GitLab CI 示例 deploy: stage: deploy before_script: - mkdir -p ~/.ssh - echo “$SSH_PRIVATE_KEY“ ~/.ssh/id_ed25519 - chmod 600 ~/.ssh/id_ed25519 - ‘[[ -f /.dockerenv ]] echo -e “Host *\n\tStrictHostKeyChecking no\n\n“ ~/.ssh/config‘ # 非生产环境可禁用主机检查 script: - ssh userproduction-server “cd /app git pull docker-compose up -d“安全警告 为降低风险应在目标服务器的authorized_keys文件中通过command选项严格限制该密钥只能执行特定的部署命令。7.3 为Git配置SSH密钥虽然主题是服务器间免密但原理完全一样。Git使用SSH协议与仓库如GitHub GitLab通信时配置免密认证能让你无需每次输入密码。生成密钥对ssh-keygen -t ed25519 -C “your_emailexample.com“将公钥~/.ssh/id_ed25519.pub的内容添加到你的Git托管平台GitHubSettings - SSH and GPG keys GitLabPreferences - SSH Keys。测试连接ssh -T gitgithub.com看到欢迎信息即表示成功。整个SSH免密登录的设置从原理到实践从单机到集群从基础配置到安全加固核心就在于理解“公钥锁私钥开”这个模型并严谨地处理好密钥文件和目录的权限。一旦配置妥当它就像给服务器之间的信任关系铺上了高速公路让自动化运维和日常管理变得行云流水。我个人的习惯是对于任何需要长期管理的服务器配置SSH免密登录和优化~/.ssh/config文件永远是登录后的第一件事。
返回列表