SSH远程访问及控制
SSH概述
SSH(Secure Shell)是一种安全通道协议,主要用来实现字符界面的远程登录、远程复制等功能。SSH 协议对通信双方的数据传输进行了加密处理,其中包括用户登录时输入的用 户口令,与TELNET(远程登录)等应用相比,SSH
默认监听端口:TCP 22
加密方式
- 对称加密:是指加密和解密用的是同一套密钥(效率高)
- 非对称加密:加密用公钥,解密用私有(加密和解密用的不是同一套密钥)(非对称加密的安全性高)
网络通信流程
mermaid
sequenceDiagram
participant Client
participant Server
%% ========== TCP三次握手 ==========
Note over Client,Server: 【TCP连接建立】三次握手
Client->>Server: SYN (seq=x)
Server->>Client: SYN-ACK (ack=x+1, seq=y)
Client->>Server: ACK (ack=y+1)
%% ========== 非对称加密部分 ==========
rect rgb(255, 240, 240)
Note over Server: 【非对称加密阶段】密钥交换
Server->>Client: 发送服务器公钥
Note over Client: 客户端保存服务器公钥
Client->>Server: 用服务器公钥加密的对称密钥
Note over Server: 服务器用私钥解密获取对称密钥
end
%% ========== 对称加密部分 ==========
rect rgb(240, 240, 255)
Note over Client,Server: 【对称加密阶段】安全通信
loop 加密数据传输
Client->>Server: 加密数据[对称密钥]
Server->>Client: 加密响应[对称密钥]
end
end一、TCP三次握手建立连接
- 客户端发起连接(SYN)
- 客户端发送
SYN包(同步序列号)到服务器 - 包含初始序列号
seq=x,表示客户端数据流的起始点
- 客户端发送
- 服务器响应(SYN-ACK)
- 服务器回复
SYN-ACK包:ack=x+1确认收到客户端的 SYN- 同时发送自己的初始序列号
seq=y
- 服务器回复
- 客户端确认(ACK)
- 客户端发送
ACK包:ack=y+1确认收到服务器的 SYN
- 连接建立完成,双方进入可通信状态
- 客户端发送
二、非对称加密阶段(紫色文字部分)
- 服务器发送公钥
- 服务器将自己的 公钥 发送给客户端
- 客户端收到后保存该公钥(标注:"客户端收到公钥后保存")
- 客户端加密对称密钥
- 客户端生成一个 对称加密的临时密钥(会话密钥)
- 使用服务器的公钥加密该密钥,并发送给服务器(标注:"客户端用服务端公钥将自己的对称加密密钥传给服务端")
- 服务器解密密钥
- 服务器用 私钥 解密获取对称密钥(标注:"服务端用自己的私钥解密")
- 此时双方共享同一对称密钥,后续通信将切换为对称加密
三、对称加密数据传输(红色箭头部分)
- 加密通信开始
- 所有数据传输均使用 对称密钥 加密(标注:"数据传输用的是对称加密的密钥")
- 包括:
- 客户端→服务器的请求(如登录凭证、命令)
- 服务器→客户端的响应(如执行结果、文件内容)
- 流程特点
- 高效性:对称加密比非对称加密计算量小,适合持续数据传输
- 安全性:密钥通过非对称加密安全交换,避免中间人攻击
关键设计解析
- 非对称加密的作用
- 仅用于 安全交换对称密钥(解决密钥分发问题)
- 公钥公开,私钥保密(标注:"私钥在自己手里")
- 对称加密的作用
- 加密实际通信内容(标注:"对称加密数据传输")
- 算法如 AES,速度快且资源占用低
- 完整安全性
- 双向认证(服务器通过公钥证明身份,客户端通过密码/密钥证明身份)
- 前向保密(临时对称密钥会话结束后销毁)
OpenSSH
OpenSSH 是 SSH (Secure Shell) 协议的开源实现,用于在不安全的网络中提供安全的加密通信。它主要用于远程登录、文件传输等操作。
重点路径
- 服务名称:sshd
- 服务端主程序:/usr/sbin/sshd
- 服务端配置文件:/etc/ssh/sshd_config
- 客户端配置文件:/etc/ssh/ssh_config
配置文件解析
服务端sshd_config解析
ini
# 网络监听
Port 22 # 监听端口(建议改为1024以上端口防扫描)
ListenAddress 0.0.0.0 # 监听所有IP(生产环境建议绑定具体IP)
AddressFamily inet # 仅IPv4(inet6为IPv6)
# 密钥文件配置(对应非对称加密)
HostKey /etc/ssh/ssh_host_ed25519_key # ED25519算法(首选)
HostKey /etc/ssh/ssh_host_rsa_key # RSA备份(兼容旧客户端)
# 密钥重新生成周期(默认2小时)
KeyRegenerationInterval 3600
ServerKeyBits 4096 # RSA密钥长度(至少2048)
# 非对称密钥认证(对应图中公钥认证流程)
PubkeyAuthentication yes # 启用公钥认证
AuthorizedKeysFile .ssh/authorized_keys # 用户公钥存储路径
# 密码认证(高风险,建议关闭)
PasswordAuthentication no
PermitEmptyPasswords no
# 其他认证限制
PermitRootLogin no # 禁止root直接登录。prohibit-password允许root密钥登录
MaxAuthTries 3 # 每连接最大认证尝试次数
# 密钥交换算法(对应图中密钥协商阶段)
KexAlgorithms curve25519-sha256,ecdh-sha2-nistp521
# 对称加密算法(对应图中加密数据传输阶段)
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com
# MAC完整性校验算法
MACs hmac-sha2-512-etm@openssh.com
# 协议版本控制
Protocol 2 # 禁用旧的SSHv1
# 超时设置(防僵死连接)
ClientAliveInterval 300 # 300秒检测一次客户端活动
ClientAliveCountMax 3 # 3次无响应后断开
# 连接限制
MaxSessions 10 # 每IP最大会话数
MaxStartups 10:30:60 # 并发未认证连接限制
# 日志级别(调试时设为VERBOSE)
LogLevel INFO
# 权限严格检查
StrictModes yes # 检查用户目录权限
UsePrivilegeSeparation sandbox # 权限隔离生产环境推荐配置
ini
# 安全基线配置
Port 2222
Protocol 2
# 设置客户端完成认证的最大时间窗口(从TCP连接建立到成功认证)
LoginGraceTime 1m
# 允许root密钥登录
PermitRootLogin prohibit-password
# 每连接最大认证尝试次数
MaxAuthTries 2
# 每IP最大会话数
MaxSessions 2
# 300秒检测一次客户端活动
ClientAliveInterval 300
# 完全禁用保活(特殊场景使用)
ClientAliveCountMax 0 # 需配合TCP层KeepAlive客户端ssh_conf解析
ini
# 非对称密钥认证(匹配图中"客户端用服务端公钥加密"流程)
IdentityFile ~/.ssh/id_ed25519 # 指定客户端私钥文件(用于认证)
IdentitiesOnly yes # 只使用配置的密钥文件
# 服务器公钥验证(对应图中"客户端收到公钥后保存")
StrictHostKeyChecking ask # 首次连接时询问是否保存服务器公钥(保存到~/.ssh/known_hosts)
UserKnownHostsFile ~/.ssh/known_hosts # 服务器公钥存储路径生成密钥
ssh-keygen
| 加密算法 | 说明 |
|---|---|
| rsa | 兼容性最好,但性能较低,密钥长度:默认 3072 位(推荐 ≥2048,安全临界值为 4096)-b 4096指定长度 |
| ed25519 | 安全性高,性能最优,密钥长度短,密钥长度:固定 256 位 |
| ecdsa | 比 RSA 更高效,密钥尺寸更小,密钥长度:256/384/521 位。-b 384指定长度 |
| dsa(已淘汰) | OpenSSH 7.0+ 已默认禁用,存在安全性问题,密钥长度:固定 1024 位 |
案例
bash
# 生成ED25519密钥对(推荐)
ssh-keygen -t ed25519 -C "admin@server" -f ~/.ssh/server_key
# 生成4096位RSA密钥
ssh-keygen -t rsa -b 4096 -C "backup@example.com"
# 为现有私钥生成公钥
ssh-keygen -y -f ~/.ssh/id_rsa > ~/.ssh/id_rsa.pub
# 批量生成无密码密钥对(自动化场景)
ssh-keygen -t rsa -b 2048 -N "" -f /tmp/auto_key-C:添加注释(通常用邮箱标识)-f:指定密钥文件路径
SSH密钥文件解析
私钥文件(保密!)
默认路径:
~/.ssh/id_[algorithm](如id_ed25519)权限要求:600(仅用户可读写)
内容特征:
text-----BEGIN OPENSSH PRIVATE KEY----- BASE64编码的密钥数据... -----END OPENSSH PRIVATE KEY-----
公钥文件(可分发)
默认路径:
~/.ssh/id_[algorithm].pub内容格式:
textssh-ed25519 AAAAC3Nz... comment # 算法类型 Base64密钥 备注信息
已知主机记录(自动生成)
~/.ssh/known_hosts:存储已验证过的主机指纹
客户端密钥登录
流程
mermaid
sequenceDiagram
participant Client as 客户端
participant Server as 服务器
Note over Client: 准备阶段
Client->>Client: 生成密钥对(ssh-keygen)
Client->>Server: 将公钥写入~/.ssh/authorized_keys
Note over Client: 认证阶段
Client->>Server: SSH连接请求(TCP 22)
Server-->>Client: 发送随机质询(challenge)
alt 密钥验证成功
Client->>Client: 用私钥签名质询
Client->>Server: 发送签名结果
Server->>Server: 用存储的公钥验证签名
Server-->>Client: 认证通过
Client->>Server: 建立加密通道
else 密钥验证失败
Server-->>Client: 退回密码认证或拒绝
endxshell连接
1.将私钥id_rsa下载到客户端
密钥生成后需要将id_rsa.pub内容写入到~/.ssh/authorized_keys认证文件中,否则客户端无法登录
bash
cat ~/.ssh/id_rsa.pub > ~/.ssh/authorized_keys
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys2.设置用户身份验证
勾选Public Key并选中后右边选设置,将id_rsa秘钥上传
Linux连接(免密登录)
Linux 服务器之间的连接需要将当前服务器的公钥id_rsa.pub上传到要登录的服务器上
bash
# 生成密钥
ssh-copy-id -i ~/.ssh/id_rsa.pub IP
# 产生认证文件
cat ~/.ssh/id_rsa.pub > ~/.ssh/authorized_keys
# 验证(无需密码)
ssh root@IP免密免指纹
客户端第一次连接时会有指纹确认,保存后下次会跳过指纹确认
bash
# 生成密钥
ssh-keygen -t rsa -P "" -C "免密登录"
# 产生认证文件
cat ~/.ssh/id_rsa.pub > ~/.ssh/authorized_keys
# 远程发送
ssh-copy-id -i ~/.ssh/id_rsa.pub 目标IP
# 免指纹确认
sshpass -p "" ssh -o StrictHostKeyChecking=no root@IPssh密钥免交户批量分发脚本
shell
#!/bin/bash
# description:SSH密钥批量分发
IP_FILE="IP.txt"
# 定义加密技术
ENCCRYT="rsa"
# 定义ssh私钥文件
PRIVATE_KEY_FILE="$HOME/.ssh/id_$ENCCRYT"
# 定义ssh公钥文件
PUBLIC_KEY_FILE="$HOME/.ssh/id_$ENCCRYT.pub"
# 清空旧密钥
rm -rf "$HOME"/.ssh/
# 生成新秘钥
ssh-keygen -t "$ENCCRYT" -f "$PRIVATE_KEY_FILE" -P "" -C "批量主机免密登录" &>/dev/null
# 读取IP文件中的IP地址
while read -r line; do
# 跳过空行
[ -z "$line" ] && continue
# 跳过以#开头的注释行
[[ "$line" =~ ^#.*$ ]] && continue
IP=$(echo "$line" | awk '{print $1}')
USER=$(echo "$line" | awk '{print $2}')
PWD=$(echo "$line" | awk '{print $3}')
if ! ping "$IP" -c 1 &>/dev/null; then
echo "$1 无法ping通请检查网络"
continue
fi
sshpass -p "$PWD" ssh-copy-id -i "$PUBLIC_KEY_FILE" -o StrictHostKeyChecking=no "$USER"@"$IP" &>/dev/null
if [[ $? -eq 0 ]]; then
echo "$1 设置成功"
else
echo "$1 设置失败" "ERROR"
fi
done <"$IP_FILE"