SSH 服务器完全指南 / 第3章 密钥对与基本连接
第3章 密钥对与基本连接
3.1 SSH 密钥对基础
SSH 密钥对由两部分组成:
| 组成部分 | 存储位置 | 能否共享 |
|---|---|---|
| 私钥 (Private Key) | 客户端本地 | ❌ 绝对不能 |
| 公钥 (Public Key) | 部署到服务器 | ✅ 可以分发给任何人 |
工作原理:
客户端 服务器
│ 用私钥签名挑战 ──────────────────>│
│ │ 用公钥验证签名
│ ──────────────────────────────── │
│ 签名有效,身份确认 ─────────────>│
│ │
│<══ 加密会话建立,允许登录 ═══════>│
核心原则: 公钥可以公开,私钥必须保密。拥有私钥 = 拥有身份。
3.2 生成密钥对
ssh-keygen 基本用法
# 生成 Ed25519 密钥(推荐)
ssh-keygen -t ed25519 -C "[email protected]"
交互过程:
Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/user/.ssh/id_ed25519): [回车使用默认]
Enter passphrase (empty for no passphrase): [输入密码短语,强烈建议设置]
Enter same passphrase again: [再次输入密码短语]
Your identification has been saved in /home/user/.ssh/id_ed25519
Your public key has been saved in /home/user/.ssh/id_ed25519.pub
The key fingerprint is:
SHA256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx user@hostname
The key's randomart image is:
+--[ED25519 256]--+
| . . |
| o o. . |
| . + o. |
| . o o |
| o = oS |
| . = +.. |
| = E .. |
| o =.oo |
| .+.oB* |
+----[SHA256]-----+
不同算法的密钥生成
# Ed25519(推荐 - 256 bit,速度快,安全性高)
ssh-keygen -t ed25519 -C "comment" -f ~/.ssh/id_ed25519
# RSA 4096(兼容性最好,适合老系统)
ssh-keygen -t rsa -b 4096 -C "comment" -f ~/.ssh/id_rsa
# ECDSA(折中选择)
ssh-keygen -t ecdsa -b 256 -C "comment" -f ~/.ssh/id_ecdsa
密钥生成参数对照
| 参数 | 说明 | 示例 |
|---|---|---|
-t | 密钥类型 | ed25519, rsa, ecdsa |
-b | 密钥位数(RSA/ECDSA) | 4096, 256 |
-C | 注释(通常用邮箱) | "user@host" |
-f | 输出文件路径 | ~/.ssh/id_ed25519 |
-N | 密码短语(空字符串表示无密码) | "mypassphrase" 或 "" |
-q | 静默模式 | |
-a | KDF 轮数(越大暴力破解越难) | -a 100 |
批量生成(脚本场景)
# 无交互式生成密钥(适合自动化脚本)
ssh-keygen -t ed25519 \
-f /home/deploy/.ssh/id_ed25519 \
-N "strong-passphrase-here" \
-C "deploy@ci-server" \
-q
3.3 密钥文件详解
文件结构
# 查看公钥内容
cat ~/.ssh/id_ed25519.pub
# 示例输出:
# ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx user@host
# 公钥结构:
# [算法类型] [Base64编码的密钥数据] [注释]
# 私钥文件内容(仅供参考,切勿分享)
cat ~/.ssh/id_ed25519
# 结构:
# -----BEGIN OPENSSH PRIVATE KEY-----
# [Base64 编码的私钥数据]
# -----END OPENSSH PRIVATE KEY-----
文件权限要求
SSH 对密钥文件的权限非常严格:
# .ssh 目录:仅所有者可访问(700)
chmod 700 ~/.ssh
# 私钥文件:仅所有者可读写(600)
chmod 600 ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_rsa
# 公钥文件:所有人可读(644)
chmod 644 ~/.ssh/id_ed25519.pub
chmod 644 ~/.ssh/id_rsa.pub
# authorized_keys 文件
chmod 600 ~/.ssh/authorized_keys
# known_hosts 文件
chmod 644 ~/.ssh/known_hosts
# config 文件
chmod 600 ~/.ssh/config
# 查看当前权限
ls -la ~/.ssh/
期望的输出:
drwx------ 2 user user 4096 Jan 1 10:00 .
-rw------- 1 user user 411 Jan 1 10:00 config
-rw------- 1 user user 749 Jan 1 10:00 id_ed25519
-rw-r--r-- 1 user user 106 Jan 1 10:00 id_ed25519.pub
-rw------- 1 user user 444 Jan 1 10:00 authorized_keys
-rw-r--r-- 1 user user 888 Jan 1 10:00 known_hosts
⚠️ 警告: 如果 SSH 检测到私钥权限过宽(如 644),它会拒绝使用该密钥并发出警告。
3.4 使用 SSH 客户端连接
基本连接
# 最简单的连接方式
ssh user@hostname
# 指定端口
ssh -p 2222 user@hostname
# 使用 IP 地址
ssh [email protected]
# 使用 IPv6
ssh user@2001:db8::1
连接过程详解
# 使用 -v 查看详细连接过程
ssh -v user@hostname
输出关键阶段:
# 1. 解析主机名
debug1: Connecting to hostname [192.168.1.100] port 22.
# 2. 协议版本协商
debug1: Remote protocol version 2.0, remote software version OpenSSH_9.0
# 3. 密钥交换
debug1: SSH2_MSG_KEXINIT sent
debug1: kex: algorithm: curve25519-sha256
# 4. 主机密钥验证
debug1: Host 'hostname' is known and matches the ED25519 host key.
# 或
The authenticity of host 'hostname' can't be established.
ED25519 key fingerprint is SHA256:xxxxx.
# 5. 用户认证
debug1: Offering public key: /home/user/.ssh/id_ed25519 ED25519 SHA256:xxxxx
debug1: Server accepts key: /home/user/.ssh/id_ed25519 ED25519 SHA256:xxxxx
# 6. 会话建立
debug1: Authentication succeeded (publickey).
常用 SSH 客户端参数
| 参数 | 说明 | 示例 |
|---|---|---|
-p | 指定端口 | -p 2222 |
-i | 指定私钥 | -i ~/.ssh/id_work |
-v | 调试模式(可叠加 -vvv) | -vvv |
-o | 设置选项 | -o StrictHostKeyChecking=no |
-J | 跳板机 | -J jump@bastion |
-L | 本地端口转发 | -L 8080:localhost:80 |
-R | 远程端口转发 | -R 9090:localhost:3000 |
-D | 动态端口转发 | -D 1080 |
-N | 不打开 Shell | -N |
-f | 后台运行 | -f |
-T | 禁用伪终端 | -T |
-t | 强制分配伪终端 | -t |
-A | 启用 Agent 转发 | -A |
-X | 启用 X11 转发 | -X |
-C | 启用压缩 | -C |
常用运行模式
# 执行单条命令(不进入交互 Shell)
ssh user@host "uname -a && uptime"
# 执行多条命令
ssh user@host << 'EOF'
cd /var/log
tail -n 20 syslog
df -h
EOF
# 后台运行长时间命令
ssh user@host "nohup /opt/scripts/backup.sh > /tmp/backup.log 2>&1 &"
# 不打开 Shell(仅做端口转发)
ssh -N -L 8080:localhost:80 user@host
# 保持连接(配合 -f 后台运行)
ssh -fN -L 8080:localhost:80 user@host
3.5 主机密钥验证
首次连接信任
首次连接某台服务器时,SSH 会提示确认主机指纹:
The authenticity of host '192.168.1.100' can't be established.
ED25519 key fingerprint is SHA256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])?
安全做法:通过带外渠道(如服务器控制台、云平台控制面板)验证指纹:
# 在服务器上查看主机密钥指纹
ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub
# 输出:256 SHA256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx root@server (ED25519)
ssh-keygen -lf /etc/ssh/ssh_host_rsa_key.pub
# 输出:4096 SHA256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx root@server (RSA)
known_hosts 文件
已接受的主机密钥存储在 ~/.ssh/known_hosts 中:
# 查看已知主机
cat ~/.ssh/known_hosts
# 删除特定主机的密钥(主机密钥变更时需要)
ssh-keygen -R 192.168.1.100
# 删除所有已知主机(谨慎使用)
rm ~/.ssh/known_hosts
主机密钥变更处理
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ 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.
这个警告可能意味着:
| 情况 | 处理方式 |
|---|---|
| 服务器重装了系统 | 确认后删除旧密钥 |
| 服务器更换了主机密钥 | 确认后删除旧密钥 |
| IP 地址被重新分配 | 确认后删除旧密钥 |
| 中间人攻击 | 不要继续连接! |
# 安全地处理主机密钥变更
# 1. 通过带外渠道确认新密钥
# 2. 删除旧密钥
ssh-keygen -R 192.168.1.100
# 3. 重新连接并接受新密钥
ssh [email protected]
3.6 SSH 客户端配置文件
~/.ssh/config 文件可以大大简化 SSH 使用:
基本配置
# ~/.ssh/config
# 全局默认设置
Host *
ServerAliveInterval 60
ServerAliveCountMax 3
AddKeysToAgent yes
IdentityFile ~/.ssh/id_ed25519
# 生产服务器
Host production
HostName 203.0.113.10
User admin
Port 2222
IdentityFile ~/.ssh/id_prod
# 开发服务器
Host dev
HostName 192.168.1.50
User developer
ForwardAgent yes
# 跳板机 + 内网服务器
Host bastion
HostName bastion.example.com
User jumpuser
IdentityFile ~/.ssh/id_bastion
Host internal-*
User internaluser
ProxyJump bastion
IdentityFile ~/.ssh/id_internal
Host internal-web
HostName 10.0.0.10
Host internal-db
HostName 10.0.0.20
Port 5432
使用配置
# 使用别名连接(等同于完整的 ssh 命令)
ssh production
# 等同于:ssh -p 2222 -i ~/.ssh/id_prod [email protected]
ssh dev
ssh internal-web
ssh internal-db
配置参数详解
| 参数 | 说明 | 示例 |
|---|---|---|
Host | 匹配模式(支持通配符) | Host *, Host web-* |
HostName | 实际主机名或 IP | HostName 192.168.1.100 |
User | 登录用户名 | User admin |
Port | SSH 端口 | Port 2222 |
IdentityFile | 私钥文件路径 | IdentityFile ~/.ssh/id_work |
ProxyJump | 跳板机 | ProxyJump jump@bastion |
ProxyCommand | 自定义代理命令 | ProxyCommand nc %h %p |
ForwardAgent | Agent 转发 | ForwardAgent yes |
LocalForward | 本地端口转发 | LocalForward 8080 localhost:80 |
RemoteForward | 远程端口转发 | RemoteForward 9090 localhost:3000 |
DynamicForward | SOCKS 代理 | DynamicForward 1080 |
ServerAliveInterval | 心跳间隔(秒) | ServerAliveInterval 60 |
ServerAliveCountMax | 心跳失败次数 | ServerAliveCountMax 3 |
StrictHostKeyChecking | 主机密钥检查 | StrictHostKeyChecking ask |
UserKnownHostsFile | known_hosts 路径 | UserKnownHostsFile ~/.ssh/known_hosts |
AddKeysToAgent | 自动添加到 Agent | AddKeysToAgent yes |
Compression | 启用压缩 | Compression yes |
TCPKeepAlive | TCP 保活 | TCPKeepAlive yes |
ConnectTimeout | 连接超时(秒) | ConnectTimeout 10 |
LogLevel | 日志级别 | LogLevel VERBOSE |
RequestTTY | 伪终端分配 | RequestTTY force |
SendEnv | 发送环境变量 | SendEnv LANG LC_* |
SetEnv | 设置环境变量 | SetEnv MY_VAR=value |
Host 匹配模式
# 精确匹配
Host myserver
# 通配符匹配
Host *.example.com # 匹配所有 example.com 子域名
Host 192.168.1.* # 匹配 192.168.1.0/24 网段
Host web-* # 匹配 web- 开头的名称
# 多个模式
Host web-* db-* cache-*
# 全局匹配(必须放在最后)
Host *
3.7 ssh-agent 密钥管理
ssh-agent 是一个后台进程,用于缓存私钥的密码短语,避免每次都输入。
基本使用
# 启动 ssh-agent
eval "$(ssh-agent -s)"
# 输出:Agent pid 12345
# 添加密钥到 agent
ssh-add ~/.ssh/id_ed25519
# 输入密码短语
# 查看已加载的密钥
ssh-add -l
# 删除所有已加载的密钥
ssh-add -D
# 删除特定密钥
ssh-add -d ~/.ssh/id_ed25519.pub
自动启动 ssh-agent
在 ~/.bashrc 或 ~/.zshrc 中添加:
# 自动启动 ssh-agent(避免重复启动)
if [ -z "$SSH_AUTH_SOCK" ]; then
eval "$(ssh-agent -s)" > /dev/null 2>&1
fi
GNOME Keyring / KDE Wallet
桌面环境通常自动管理 ssh-agent:
# 检查是否在使用桌面环境的 agent
echo $SSH_AUTH_SOCK
# GNOME: /run/user/1000/keyring/ssh
# KDE: /tmp/ssh-xxxxx/agent.pid
# 如果需要使用独立的 agent,设置环境变量
export SSH_AUTH_SOCK=~/.ssh/ssh_agent.sock
3.8 SSH 实用技巧
使用 SSH 执行远程命令
# 单条命令
ssh user@host "ps aux | grep nginx"
# 多条命令
ssh user@host "cd /var/log && ls -la && tail -5 syslog"
# 交互式脚本
ssh -t user@host "sudo bash -c 'apt update && apt upgrade -y'"
# 将本地脚本传到远程执行
ssh user@host 'bash -s' < local_script.sh
# 执行需要 sudo 的命令(使用 -t 分配终端)
ssh -t user@host "sudo systemctl restart nginx"
文件传输快捷方式
# scp 基本用法
scp localfile.txt user@host:/remote/path/
scp user@host:/remote/file.txt ./
# 目录传输
scp -r localdir/ user@host:/remote/path/
# rsync over SSH(推荐,支持增量传输)
rsync -avz -e ssh localdir/ user@host:/remote/path/
# 限速传输(避免占满带宽)
rsync -avz --bwlimit=1000 -e ssh largefile user@host:/tmp/
SSH 连接复用(ControlMaster)
避免重复建立连接,加速后续连接:
# ~/.ssh/config
Host *
ControlMaster auto
ControlPath ~/.ssh/sockets/%r@%h-%p
ControlPersist 600
# 创建 socket 目录
mkdir -p ~/.ssh/sockets
效果:
# 第一次连接:正常建立(约 1-2 秒)
ssh user@host
# 连接建立完成
# 第二次连接:复用已有连接(几乎瞬时)
ssh user@host
# 立即进入
# 查看已有连接
ssh -O check user@host
# 关闭已有连接
ssh -O exit user@host
3.9 SSH 加速连接
连接慢的常见原因
| 原因 | 解决方案 |
|---|---|
| DNS 反向解析 | 在服务端设置 UseDNS no |
| GSSAPI 认证 | 设置 GSSAPIAuthentication no |
| 主机密钥检查 | 设置 StrictHostKeyChecking accept-new |
| 网络延迟 | 使用 ControlMaster 复用连接 |
客户端优化配置
# ~/.ssh/config
Host *
# 启用连接复用
ControlMaster auto
ControlPath ~/.ssh/sockets/%r@%h-%p
ControlPersist 600
# 跳过 DNS 反向解析
AddressFamily inet
# 禁用 GSSAPI
GSSAPIAuthentication no
# 保持连接
ServerAliveInterval 60
ServerAliveCountMax 3
# 使用压缩(带宽有限时)
Compression yes
服务端优化配置
# /etc/ssh/sshd_config
# 禁用 DNS 反向解析(显著加速登录)
UseDNS no
# 禁用 GSSAPI(除非你使用 Kerberos)
GSSAPIAuthentication no
3.10 SSH 文件权限清单
| 路径 | 权限 | 所有者 | 说明 |
|---|---|---|---|
~/.ssh/ | 700 | 用户 | SSH 配置目录 |
~/.ssh/config | 600 | 用户 | 客户端配置文件 |
~/.ssh/id_* | 600 | 用户 | 私钥文件 |
~/.ssh/id_*.pub | 644 | 用户 | 公钥文件 |
~/.ssh/authorized_keys | 600 | 用户 | 授权公钥列表 |
~/.ssh/known_hosts | 644 | 用户 | 已知主机列表 |
/etc/ssh/sshd_config | 600 | root | 服务端配置文件 |
/etc/ssh/ssh_host_*_key | 600 | root | 主机私钥 |
/etc/ssh/ssh_host_*_key.pub | 644 | root | 主机公钥 |
# 一键修复所有 SSH 权限
chmod 700 ~/.ssh
chmod 600 ~/.ssh/config ~/.ssh/id_* ~/.ssh/authorized_keys
chmod 644 ~/.ssh/id_*.pub ~/.ssh/known_hosts
扩展阅读
下一章: 第4章 密钥认证与证书 → 深入学习 authorized_keys 管理、SSH 证书认证体系。