SSH(Secure Shell)是当今系统管理员、开发者和运维工程师最为依赖的核心工具之一。它不仅仅是一个远程登录协议,更是一个构建安全网络基础设施的基石。在 Ubuntu Server 这样占据主导地位的 Linux 发行版上,深入理解 SSH 的方方面面,从基本的安装配置到高级的隧道与端口转发,从密钥管理到安全加固,对于保障系统安全、提升运维效率至关重要。
本文将带领您进行一次 SSH 的深度探索之旅。我们将超越简单的 ssh user@host
命令,深入 OpenSSH 服务器的架构、认证机制、配置细节、网络魔法以及安全最佳实践。无论您是刚刚接触 Ubuntu Server 的新手,还是希望深化理解的经验丰富的专家,本文都将为您提供全面而深入的见解。
SSH 协议由 Tatu Ylönen 于 1995 年创建,旨在替代不安全的远程登录程序(如 telnet、rsh)。其发展经历了 SSH-1 和 SSH-2 两个主要版本。SSH-2 是一个完全重写的协议,解决了 SSH-1 中的安全性和架构缺陷,现在是绝对的标准。
SSH 协议层:
SSH-2 协议被设计成分层的架构:
OpenSSH 是 SSH 协议套件最流行、最广泛部署的自由开源实现。它由 OpenBSD 项目开发,并移植到包括 Ubuntu 在内的几乎所有类 Unix 系统。其核心组件包括:
ssh
:SSH 客户端sshd
:SSH 服务器守护进程ssh-keygen
:生成、管理和转换密钥对ssh-copy-id
:将公钥便捷地安装到服务器scp
:基于 SSH 的安全文件拷贝sftp
:安全的文件传输协议客户端ssh-agent
和 ssh-add
:用于管理私钥的认证代理Ubuntu Server 默认使用 OpenSSH 来提供远程访问能力。
在最新的 Ubuntu Server 版本中,OpenSSH 服务器通常在系统安装过程中就会提示是否安装。如果当时未安装,或者需要重新安装,可以使用 apt 包管理器:
# 更新软件包列表
sudo apt update
# 安装 OpenSSH 服务器端
sudo apt install openssh-server
# 安装 OpenSSH 客户端(通常默认已安装)
sudo apt install openssh-client
安装后,SSH 服务 sshd
会自动启动并启用开机自启。可以通过以下命令验证:
# 检查 sshd 服务状态
sudo systemctl status ssh
# 如果未运行,则启动它
sudo systemctl start ssh
# 启用开机自启
sudo systemctl enable ssh
关键文件和目录:
/etc/ssh/sshd_config
:SSH 服务器主配置文件。/etc/ssh/ssh_host_*
:服务器的主机密钥对(SSH-2 RSA, ECDSA, Ed25519)。/var/log/auth.log
:SSH 认证日志(包括登录成功和失败记录)。~/.ssh/
:用户 SSH 配置和密钥目录。~/.ssh/known_hosts
:存储已验证过公钥的远程主机列表。~/.ssh/config
:用户自定义的 SSH 客户端配置。/etc/ssh/ssh_config
:系统级的 SSH 客户端配置。sshd_config
)/etc/ssh/sshd_config
是控制 sshd
行为的核心。修改此文件后,必须重启服务以使更改生效:sudo systemctl restart ssh
。
# 监听端口,默认为 22。改为非标准端口可减少自动化攻击(安全通过隐匿不是真安全,需结合其他措施)
Port 22
# 也可以监听多个端口
# Port 22
# Port 2222
# 监听地址,默认为 0.0.0.0(所有接口)。可以绑定到特定 IP 以提高安全性。
ListenAddress 192.168.1.100
# ListenAddress ::
# 用于 SSH-2 协议的协议版本。建议仅使用更安全的 SSH-2。
Protocol 2
# 主机密钥文件路径。服务器使用这些私钥来证明自己的身份。
HostKey /etc/ssh/ssh_host_rsa_key
HostKey /etc/ssh/ssh_host_ecdsa_key
HostKey /etc/ssh/ssh_host_ed25519_key
这是 sshd_config
中最关键的部分。
# 登录宽限时间。如果用户在此时间内未能成功登录,服务器将断开连接。
LoginGraceTime 60
# 是否允许 root 用户直接登录。强烈建议设置为 'no',先通过普通用户登录再 su/sudo。
PermitRootLogin no
# 或者更严格的,仅允许使用公钥认证登录 root
# PermitRootLogin prohibit-password
# 是否允许使用密码认证。建议设置为 'no' 并强制使用公钥认证,极大增强安全性。
PasswordAuthentication no
# 是否允许空密码。必须为 'no'。
PermitEmptyPasswords no
# 最大认证尝试次数。达到后断开连接。有助于抵御暴力破解。
MaxAuthTries 3
# 最大同时未认证连接数。有助于减缓暴力破解速度。
MaxStartups 10:30:60
# 是否允许公钥认证。这是密钥登录的关键。
PubkeyAuthentication yes
# 允许的公钥文件名称,默认为 .ssh/authorized_keys
AuthorizedKeysFile .ssh/authorized_keys
# 是否在用户家目录中使用 ~/.ssh/authorized_keys 文件。
# 如果设置为 no,则必须通过 AuthorizedKeysFile 指定绝对路径。
StrictModes yes # 确保 ~/.ssh 和 authorized_keys 的权限正确
# 是否允许基于主机的认证(一般不安全,建议禁用)。
HostbasedAuthentication no
# 是否允许使用旧的 .rhosts 认证(极不安全,必须禁用)。
IgnoreRhosts yes
RhostsRSAAuthentication no
# 强制要求某些用户或组使用特定的认证方式
# Match User john
# AuthenticationMethods publickey,password
# 每个网络连接允许打开的最大通道数(即端口转发、会话等)。
MaxSessions 10
# 每个认证用户允许的最大并发登录数。
MaxStartups 10
# 客户端活动检查。如果客户端在此时间内没有发送任何数据,服务器将发送一条消息请求响应。
ClientAliveInterval 300 # 300秒(5分钟)
ClientAliveCountMax 3 # 发送最多3次此类消息。如果都无响应,则断开连接。
# 总超时时间 = ClientAliveInterval * ClientAliveCountMax = 15分钟
# 是否允许 TCP 端口转发和 X11 转发。按需启用。
AllowTcpForwarding yes
X11Forwarding yes
# 限制 X11 转发的有效时间
X11UseLocalhost yes
# 是否允许 SFTP 子系统。如果需要文件传输,则启用。
Subsystem sftp /usr/lib/openssh/sftp-server
# 较新版本也可能使用 internal-sftp,性能更好
# Subsystem sftp internal-sftp
# 限制可以使用的用户、组、主机
AllowUsers user1 user2@192.168.1.*
AllowGroups ssh-users
DenyUsers baduser
DenyGroups badgroup
可以为特定用户或组设置一个受限的监狱环境(chroot),尤其适用于仅需 SFTP 访问的用户。
# 匹配所有用户
Match Group sftp-users
ChrootDirectory /var/sftp/%u # %u 代表用户名
ForceCommand internal-sftp # 强制使用 SFTP,禁止 shell 访问
PasswordAuthentication no # 强制使用公钥
PermitTunnel no
AllowAgentForwarding no
AllowTcpForwarding no
X11Forwarding no
注意:Chroot 目录 (/var/sftp/user1
) 必须由 root
用户拥有,且权限为 755
或 755
。用户可以在其中创建自己的目录(由自己拥有)来进行文件上传下载。
密码认证易受暴力破解和中间人攻击。公钥认证提供了更强、更便捷的安全性。
使用 ssh-keygen
生成密钥对。
# 1. 生成默认的 RSA 密钥(当前默认通常是 3072 位)
ssh-keygen -t rsa -b 4096 -C "comment (e.g., email or hostname)" -f ~/.ssh/my_rsa_key
# 2. 生成更现代、更快速、更短的 Ed25519 密钥(首选)
ssh-keygen -t ed25519 -C "john@laptop" -f ~/.ssh/id_ed25519
# 3. 生成 ECDSA 密钥
ssh-keygen -t ecdsa -b 521 -C "backup key" -f ~/.ssh/id_ecdsa
# 选项说明:
# -t type: 密钥类型 (rsa, dsa, ecdsa, ed25519).
# -b bits: 密钥长度(RSA 建议至少 3072,ECDSA 256/384/521, Ed25519 固定长度)。
# -C comment: 附加在公钥末尾的注释,便于识别。
# -f filename: 指定密钥文件路径和名称。如果不指定,会提示输入。
# 生成过程中会提示输入密码(passphrase)。强烈建议设置一个强密码!
# 它用于加密私钥文件,即使私钥被盗,也无法直接使用。
算法建议:
不要使用已过时且不安全的 DSA 算法。
ssh-copy-id
生成密钥后,需要将公钥(.pub
文件)部署到目标服务器的 ~/.ssh/authorized_keys
文件中。
手动方法:
# 在客户端,将公钥内容复制
cat ~/.ssh/id_ed25519.pub
# 然后登录服务器,编辑 ~/.ssh/authorized_keys,将上述输出粘贴进去。
# 确保服务器上 ~/.ssh 目录权限为 700,~/.ssh/authorized_keys 权限为 600。
自动方法(推荐):
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@hostname
ssh-copy-id
会自动处理上述所有步骤,包括设置正确的权限。
ssh-agent
):免密码输入通行短语每次使用加密的私钥连接时都输入通行短语很麻烦。ssh-agent
可以帮我们管理解密的私钥,在内存中暂存它们一段时间。
# 启动 ssh-agent 并设置环境变量(通常已在桌面环境中自动启动)
eval "$(ssh-agent -s)"
# 将私钥添加到代理中
ssh-add ~/.ssh/id_ed25519
# 此时会提示输入一次通行短语,之后在该终端会话中再使用此密钥就不再需要输入。
# 列出当前代理中已加载的密钥
ssh-add -l
# 删除特定密钥
ssh-add -d ~/.ssh/some_key
# 清空代理中的所有密钥
ssh-add -D
# 设置密钥在代理中的缓存时间(默认永久,直到代理结束)
ssh-add -t 1h ~/.ssh/id_ed25519 # 缓存1小时
为了让图形化环境或新终端自动识别 ssh-agent
,通常需要将环境变量设置添加到 ~/.bashrc
或类似文件中:
# 在 ~/.bashrc 中
if [ -z "$SSH_AUTH_SOCK" ]; then
# 启动 ssh-agent 并导出环境变量
eval "$(ssh-agent -s)"
# 可在此处自动添加常用密钥,但注意安全风险
# ssh-add ~/.ssh/id_ed25519 2>/dev/null
fi
更现代的替代方案:systemd
用户实例提供的 ssh-agent
集成(如果使用 systemd):
systemctl --user enable ssh-agent
systemctl --user start ssh-agent
# 然后配置 SSH_AUTH_SOCK 环境变量指向其 socket。
~/.ssh/config
文件可以让你为不同的主机定义别名、选项和默认值,极大简化命令行操作。
# ~/.ssh/config 示例
# 全局选项,适用于所有主机
Host *
ForwardAgent no # 谨慎使用!允许将本地代理转发到远程主机。
ServerAliveInterval 60 # 每60秒发送一次保活包,防止连接超时
ServerAliveCountMax 3 # 最多3次无响应则断开
TCPKeepAlive yes
Compression yes # 对慢速连接启用压缩
IdentityFile ~/.ssh/id_ed25519 # 默认使用的私钥
IdentitiesOnly yes # 只使用config文件中指定的密钥,不尝试所有默认密钥
LogLevel VERBOSE # 或 INFO, ERROR
# 特定主机或模式的定义
Host myserver
HostName server.example.com # 真实主机名或IP
User johndoe # 登录用户名
Port 2222 # 非标准端口
IdentityFile ~/.ssh/myserver_key # 为此主机指定特定密钥
Host github.com
User git
IdentityFile ~/.ssh/github_key
# 对GitHub禁用主机密钥检查(不推荐,见下文更安全的方法)
# StrictHostKeyChecking no
Host 192.168.1.*
User admin
ProxyJump bastion-host # 通过跳板机连接
# 通过跳板机(Bastion/Jump Host)连接内部网络主机
Host internal-server
HostName 10.0.1.5
User appuser
ProxyJump user@bastion.example.com:22
# 等效于之前的 -J 选项: ssh -J bastion.example.com internal-server
# 使用 SSH 连接共享(连接复用)
Host *
ControlMaster auto # 允许共享连接
ControlPath ~/.ssh/sockets/%r@%h:%p # 指定socket文件路径
ControlPersist 1h # 主连接在最后一个客户端断开后保持1小时
使用 ControlMaster
可以极大地加速连续连接到同一主机的速度,因为无需重复进行密钥交换和认证。
known_hosts
首次连接服务器时,会看到以下提示:
The authenticity of host 'hostname (IP)' can't be established.
ECDSA key fingerprint is SHA256:xxxxxxxxxxxx.
Are you sure you want to continue connecting (yes/no/[fingerprint])?
输入 yes
后,服务器的公钥会被添加到 ~/.ssh/known_hosts
文件中。后续连接会对比此密钥,如果不匹配,会发出严重警告,提示可能存在中间人攻击。
管理 known_hosts
:
ssh-keygen -R hostname
ssh-keyscan -H hostname
可以获取远程主机指纹,可与管理员提供的官方指纹对比。SSH 的强大之处在于它可以在不安全的网络上创建安全的加密隧道。
-L
)将远程服务器上的某个端口(或套接字)映射到本地机器的某个端口。
语法:ssh -L [local_bind_ip:]local_port:remote_host:remote_port user@ssh_server
应用场景:
-R
)将本地机器上的某个端口映射到远程服务器的某个端口。常用于从外部访问 NAT 或防火墙后的内部服务。
语法:ssh -R [remote_bind_ip:]remote_port:local_host:local_port user@ssh_server
应用场景:
my-vps
).ssh -R 0.0.0.0:8080:localhost:3000 user@my-vps
# 现在,任何人访问 http://my-vps:8080,流量都会通过隧道转发到你本地机器的 localhost:3000。注意:默认 sshd
配置 GatewayPorts
为 no
,远程转发只绑定到远程服务器的 127.0.0.1
。要绑定到所有接口(0.0.0.0
)让外人访问,需在服务器 sshd_config
中设置 GatewayPorts yes
或 clientspecified
。-D
)创建一个本地的 SOCKS 代理服务器。所有发送到本地 SOCKS 端口的流量都会通过 SSH 连接转发出去,由远程服务器代为请求。
语法:ssh -D [local_bind_ip:]local_port user@ssh_server
应用场景:
localhost:1080
。现在你的所有网络请求都经由 my-vps
发出。-J
或 ProxyJump
)用于通过一个或多个“跳板机”(Bastion Hosts)连接最终的目标主机。这是访问隔离网络(如私有子网)的安全最佳实践。
语法:ssh -J user@jump_host1,user@jump_host2 user@target_host
应用场景:
~/.ssh/config
中使用 ProxyJump
指令配置,更加清晰。基于 SSH 的简单文件复制命令。
# 从本地复制到远程
scp -P 2222 /path/to/local/file.txt user@remotehost:/path/to/remote/directory/
# 从远程复制到本地
scp -i ~/.ssh/key.pem user@remotehost:/path/to/remote/file.txt /path/to/local/directory/
# 递归复制整个目录
scp -r /local/dir user@remotehost:/remote/dir
# 在两点之间复制文件(通过本地中转)
scp -3 user1@host1:/file1 user2@host2:/file2
# 保留文件属性(修改时间、权限等)
scp -p /local/file user@remotehost:/remote/
注意:SCP 协议较老,在某些情况下性能不如 rsync
或 sftp
。对于大量小文件或需要增量同步时,rsync
是更好的选择。
提供一个交互式的类 FTP 界面,功能比 SCP 更丰富。
# 启动 SFTP 会话
sftp user@remotehost
# 常用 SFTP 命令
sftp> ls # 列出远程目录文件
sftp> lls # 列出本地目录文件
sftp> cd remote_dir # 更改远程工作目录
sftp> lcd local_dir # 更改本地工作目录
sftp> put local_file # 上传文件
sftp> get remote_file # 下载文件
sftp> mkdir new_dir # 在远程创建目录
sftp> rm remote_file # 删除远程文件
sftp> chmod 644 remote_file # 更改远程文件权限
sftp> exit # 退出
# 也可以非交互式使用
sftp -b batchfile.txt user@remotehost
# batchfile.txt 内容:
# put localfile
# get remotefile
# quit
SFTP 子系统:SFTP 实际上是作为 SSH 的一个子系统运行的(见 sshd_config
中的 Subsystem sftp ...
行)。这意味着你无法单独运行 sftp-server
,它必须通过 SSH 守护进程调用。
rsync
over SSHrsync
是一个强大的文件同步工具,它通常利用 SSH 作为传输层,提供高效的增量文件传输。
# 基本用法,同步本地目录到远程
rsync -avz -e "ssh -i ~/.ssh/key.pem" /local/source/ user@remotehost:/remote/dest/
# -a: 归档模式(保留权限、符号链接等)
# -v: 详细输出
# -z: 压缩传输
# -e: 指定远程 shell 命令,这里我们指定使用 SSH 和特定密钥
# 从远程同步到本地
rsync -avz user@remotehost:/remote/source/ /local/dest/
# 删除目标中源端不存在的文件(小心使用!)
rsync -avz --delete /source/ user@remotehost:/dest/
# 部分同步,排除某些文件或目录
rsync -avz --exclude 'tmp/' --exclude '*.log' /source/ user@remotehost:/dest/
rsync
是进行备份、部署代码或同步大型目录结构的首选工具。
# 使用 systemctl 管理 ssh 服务
sudo systemctl start ssh # 启动
sudo systemctl stop ssh # 停止
sudo systemctl restart ssh # 重启(中断现有连接)
sudo systemctl reload ssh # 重新加载配置(不中断现有连接!推荐)
sudo systemctl status ssh # 查看状态
sudo systemctl enable ssh # 启用开机自启
sudo systemctl disable ssh # 禁用开机自启
sudo systemctl is-enabled ssh # 检查是否启用
# 检查 sshd 是否正在监听
sudo ss -tlnp | grep sshd
# 或
sudo netstat -tlnp | grep sshd
SSH 的日志记录在 /var/log/auth.log
。使用 grep
, tail
, journalctl
等工具进行分析。
# 查看最近的 SSH 登录成功记录
sudo grep "Accepted" /var/log/auth.log
# 输出示例:Jun 10 14:23:45 server sshd[12345]: Accepted publickey for user from 192.168.1.50 port 54322 ssh2: ED25519 SHA256:xxxxxx
# 查看最近的 SSH 登录失败记录(暴力破解尝试)
sudo grep "Failed password" /var/log/auth.log
sudo grep "Invalid user" /var/log/auth.log
# 实时监控认证日志
sudo tail -f /var/log/auth.log | grep --line-buffered sshd
# 使用 systemd 日志工具查看
sudo journalctl -u ssh -f # -f 表示跟随(实时输出)
sudo journalctl -u ssh --since="2023-06-10" --until="2023-06-11"
sudo journalctl -u ssh -n 100 --no-pager # 查看最后100行
# 分析失败尝试的来源 IP
sudo grep "Failed password" /var/log/auth.log | awk '{print $11}' | sort | uniq -c | sort -nr
# 分析无效用户名尝试
sudo grep "Invalid user" /var/log/auth.log | awk '{print $8}' | sort | uniq -c | sort -nr
这些日志分析对于检测和阻止暴力破解攻击至关重要。
除了禁用密码认证和使用强密钥外,还有更多措施:
lastb
:显示所有失败的登录尝试。last
:显示所有成功的登录记录。iftop
, nethogs
等工具监控 SSH 连接产生的流量。ps auxf | grep ssh
或 top
查看 sshd
进程及其子进程(用户会话)。/etc/ssh/sshd_config
和 /etc/ssh/ssh_config
中,可以使用 Ciphers
, MACs
, KexAlgorithms
指令来优先选择更高效、更安全的算法。现代客户端和服务端应优先选择 chacha20-poly1305@openssh.com
, aes256-gcm@openssh.com
, ed25519
等。GSSAPIAuthentication
, UseDNS
(如果反向 DNS 解析慢,可设为 no
以加速连接)等。ControlMaster
可以极大减少后续连接的延迟。当 SSH 连接出现问题时,使用 -v
(verbose)选项是首要的调试手段。
ssh -v user@hostname # 显示详细信息
ssh -vv user@hostname # 更详细
ssh -vvv user@hostname # 最详细的调试输出
常见问题与解决:
Connection refused
:远程主机 sshd
服务未运行或防火墙阻止。Permission denied (publickey)
:~/.ssh/authorized_keys
文件权限或内容错误。sshd_config
中 PubkeyAuthentication
设置为 no
。Host key verification failed
:服务器密钥已更改(例如系统重装),需使用 ssh-keygen -R hostname
清除旧密钥。Enter passphrase for key
:私钥已加密,需要输入通行短语或使用 ssh-agent
。sshd_config
中的 AllowTcpForwarding
设置,以及本地防火墙规则。检查服务器日志 /var/log/auth.log
总能提供最直接的错误原因。
SSH 是 Ubuntu Server 乃至整个现代计算领域不可或缺的工具。它远不止于一个安全的远程 shell。通过深入理解其协议基础、灵活配置服务器和客户端、熟练运用密钥认证、掌握端口转发和隧道技术、并遵循严格的安全实践,你可以将 SSH 打造成一个强大、高效且坚如磐石的远程访问和管理基石。
从简单的系统维护到复杂的网络架构,SSH 都能提供安全可靠的支撑。持续学习并应用这些高级特性,将使你作为一名系统管理员或开发者的能力提升到一个新的水平。记住,强大的能力意味着巨大的责任,始终将安全原则放在首位。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。