Ubuntu SSH远程登录完整指南:从安装配置到安全加固 最近帮朋友调设备又碰到一连串和 SSH 相关的头疼问题连不上、连上就断、密钥不对、VSCode 远程又说扩展禁用搞得人一度怀疑人生。其实这些东西拆开看背后就是一套“客户端—服务端”的认证和会话管理逻辑只要把服务端配置和客户端连接这两头的门道摸清楚问题基本都能自己解决。这篇我就结合自己的实际经验完整讲讲在 Ubuntu 上使用 SSH 服务远程登录另一台设备这件事从装服务、连第一次、配密钥到搞不定时怎么排查一次说透。我默认你的场景是手头有一台 Ubuntu 机器也可能是 Windows 装了 OpenSSH 客户端想要远程登录另一台设备这台设备大概率也是 Ubuntu 或 Debian 系的 Linux可能是局域网里的服务器、树莓派也可能是云主机。下面所有操作我都在 Ubuntu 22.04/24.04 上实测过命令也都兼容旧版本你直接照着敲就行。1. SSH 远程登录的整体设计为什么它是最可靠的方案1.1 一次远程登录背后的 Client/Server 思路远程登录这件事说白了就是“我在本地打个命令让远程机器执行”。但早期像 telnet 那类工具用户和密码在网络里走明文抓包就能看到风险太高。SSHSecure Shell从诞生起就把重点放在“加密”和“认证”上所有通信数据都经过加密通道传输登录时还支持密码和密钥两种方式这让它成了 Linux/Unix 系统里远程管理的事实标准。工作模式是典型的 Client/Server 结构被登录的机器要跑一个 sshd 服务端进程默认监听 22 端口发起登录的机器上有 ssh 客户端程序主动向服务端的 IP 和端口发起连接。连接建立后双方先做版本协商和密钥交换再进入用户认证阶段密码也好、密钥也好都走加密通道。认证通过后你就能拿到一个远程 shell看起来就像在本地终端里敲命令一样。理解这个结构很重要因为后面所有排查思路都是沿着这条链路展开客户端有没有装、网络通不通、服务端有没有监听、防火墙有没有拦、认证方式对不对。你只要脑中有这条链路看到“Connection refused”不会慌看到“Permission denied”也知道该往哪个方向查。1.2 适用场景与工具选型的取舍SSH 覆盖的场景远比你想的广。我最常碰到的有三类第一类是局域网内管理无显示器设备比如树莓派、NAS、工控机没有屏幕也能干活第二类是远程服务器运维不管是自己买的云主机还是公司内网服务器SSH 永远是第一入口第三类是日常文件传输和远程开发用 scp、rsync 传代码用 VSCode Remote-SSH 在远程目录里写代码体验和本地几乎一样。工具选型上我强烈建议优先用 OpenSSH。因为它是 Linux 自带的实现Ubuntu 安装源里就是它命令行下零学习成本搭配 ssh-keygen、ssh-copy-id、scp、sftp 这些配套工具能覆盖绝大部分需求。Windows 的话Win10/11 自带 OpenSSH 客户端PowerShell 里直接敲 ssh 就能用如果更喜欢图形化MobaXterm、FinalShell 也可以但底层还是同一套 SSH 协议。Windows 端如果需要在“另一台 Windows 机器”上开 SSH 服务可以通过“可选功能”安装 OpenSSH 服务端也可以用 Bitvise SSH Server 这类第三方工具但本文还是以 Ubuntu 端的 OpenSSH 为主因为不管客户端还是服务端这套方案最通用、最稳定。2. 从零配置目标机SSH 服务端准备2.1 安装并启动 OpenSSH 服务首先登录到那台要被远程连接的设备上。如果你机器上从来没装过 SSH 服务直接装 openssh-serversudo apt update sudo apt install -y openssh-server装完之后服务一般会自动启动。确认一下运行状态sudo systemctl status ssh如果看到绿色的 active (running)说明服务端已经起来了。没起来的话手动启动并设置开机自启sudo systemctl start ssh sudo systemctl enable ssh这里有个小细节Ubuntu 上服务的名称是 ssh不是 sshd和 CentOS 系不同。你在网上搜教程经常看到 systemctl start sshd那是 CentOS/RHEL 的写法在 Ubuntu 上会报错。另外如果有人习惯用 service ssh status那也兼容的只是它最终调的也是 systemd。确认服务监听端口也很关键用这条命令看 22 端口是否在监听sudo ss -tlnp | grep :22正常会看到类似这样的输出进程名通常是 sshdLISTEN 0 4096 0.0.0.0:22 0.0.0.0:* users:((sshd,pid1234,fd3))看到 0.0.0.0:22 表示对所有网卡都监听局域网和公网地址都能收到连接如果看到的是 127.0.0.1:22那说明只监听本机回环地址外部设备根本连不进来这种情况需要检查 sshd_config 里的 ListenAddress 配置。2.2 确认监听状态与防火墙放行服务端装好不代表一定能连上Ubuntu 自带的 ufw 防火墙默认可能是放行 OpenSSH 的但如果你手动开过防火墙很容易忽略这一步。先看防火墙状态sudo ufw status如果是 active并且输出里没有 OpenSSH 或 22/tcp 相关的规则那就需要放行sudo ufw allow OpenSSH或者如果你改了端口比如后面要改到 22022就写成sudo ufw allow 22022/tcp这里我踩过一次坑在阿里云、腾讯云这类云主机上光开系统防火墙还不够安全组策略也需要放行对应端口。很多时候你本地 ufw 都关了系统日志里也看不到连接记录连不上就是云控制台的安全组没放行。检查的时候一定要记得“系统防火墙 云安全组”两头都看。还有一个很常见的坑目标机器如果开了多个网卡或者有虚拟网卡比如 Docker、VirtualBox、VMware 的桥接网卡ss 输出可能会看到多个监听地址。只要 0.0.0.0:22 或具体内网 IP 上有监听就没问题别被一堆 veth 网卡干扰。2.3 目标机 IP 地址与账户信息准备要连接一台机器必须知道三样东西IP 地址、用户名、密码或密钥。用户名就是你登录那台机器时使用的账号比如此刻你是 cts 用户那连接命令就是 ssh cts目标IP。如果目标机上没这个用户或者你想用别的账号连接那就在目标机上先创建好用户sudo adduser yourname查看 IP 地址最统一的方式ip addr show或者只想看 IPv4 地址用这个hostname -I优先记下 192.168.x.x 或 10.x.x.x 这种内网地址。如果是云主机直接用公网 IP。如果不知道密码sudo passwd yourname 可以给指定用户设置新的登录密码。这里注意如果你登录的是 root需要确保 sshd_config 里 PermitRootLogin 没有被设为 no这个我们后面安全加固部分再展开。在你离开目标机之前我建议先验证一下网络互通。在本地另一台设备上 ping 一下目标 IPping -c 3 192.168.1.100能通再继续下一步不通就先查网线、Wi-Fi、IP 网段不然后面所有 SSH 操作都是白费功夫。3. 发起第一次连接客户端实操与命令拆解3.1 第一条 SSH 命令怎么敲服务端准备好了回到你本地的 Ubuntu 终端或者 Windows 的 PowerShellWin10/11 自带了 ssh 客户端输入ssh cts192.168.1.100第一次连接时系统会提示确认远程主机指纹fingerprint就像你第一次加微信好友要确认对方身份一样。输入 yes 回车然后输入密码即可。这里有个基本逻辑我要强调指纹确认是用来防中间人攻击的。远程主机的公钥指纹会被记录到本地 ~/.ssh/known_hosts 文件里下次再连同一台机器就不会再问。如果你之后重装了目标机系统或者换了 sshd 的 host key本地记录的指纹就和实际对不上会报 WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED这时候需要手动删掉旧记录ssh-keygen -R 192.168.1.100然后再重新连接。如果你不清理就直接连客户端会拒绝继续这也是很多人“昨天还能连、今天突然连不上”的经典原因之一。我自己就吃过这个亏重装了一台 Ubuntu 服务器后忘了清理 known_hosts来回折腾了十分钟才想起来。如果你修改了 SSH 端口比如服务端 Port 改成了 22022那连接的时候要加 -p 参数ssh -p 22022 cts192.168.1.100登录成功后你的终端提示符会变成远程机器的用户名和主机名此时你敲的命令都跑在远程机器上。想退出的话输入 exit 或者按 CtrlD 都能断开。3.2 知道这几件事连接成功率翻倍第一客户端也要装东西。Ubuntu 桌面版默认可能没有 openssh-client但绝大多数情况下是自带的。万一敲 ssh 提示 command not found装一下sudo apt install -y openssh-client第二连接后的“当前目录”是你登录用户的家目录。这也意味着如果你在本地 ~/codes 下敲 ssh 过去并不会跑到远程的 ~/codes而是落在远程用户的 home 下。想直接进入指定目录可以这样做ssh -t cts192.168.1.100 cd /var/www bash-t 参数强制分配伪终端不加的话可能报错说没有 TTY。这条命令适合远程执行一次 cd 再开 shell 的场景工作里挺常用的。第三要想临时在远程执行一条命令而不进入交互式 shell直接用ssh cts192.168.1.100 uptime这条会返回远程机器 uptime 结果然后退出。很多自动化脚本就是靠这种方式批量收集服务器状态的。如果你有多台机器结合一个 for 循环就能批量查for ip in 192.168.1.10 192.168.1.11 192.168.1.12; do echo $ip ssh cts$ip hostname uptime done3.3 用配置文件把常用连接固化下来每次敲 ssh cts192.168.1.100 -p 22022 很麻烦多台机器更痛苦。建议在本地 ~/.ssh/config 里写好主机别名。这个文件默认可能不存在创建一下mkdir -p ~/.ssh chmod 700 ~/.ssh touch ~/.ssh/config chmod 600 ~/.ssh/config然后编辑它写入类似这样的内容Host lab HostName 192.168.1.100 User cts Port 22022 IdentityFile ~/.ssh/id_ed25519 Host cloud HostName 203.0.113.10 User deploy Port 22配好之后本地直接敲 ssh lab、ssh cloud 就行。scp、sftp、rsync 也会自动读取这个配置非常好用。这里尤其提醒一下~/.ssh 目录权限必须是 700config 和私钥文件权限最好是 600否则 ssh 客户端会认为权限过于开放拒绝使用私钥直接报 Permissions 0664 for id_ed25519 are too open。这种问题非常常见大部分“密钥不生效”都是权限惹的祸。4. 免密登录与远程开发密钥的完整玩法4.1 密钥认证原理与生成步骤密码登录虽然简单但每次都输密码不说密码本身还有被暴力破解的风险。我更推荐使用密钥登录。原理不复杂本地生成一对公私钥公钥放到远程机器的 ~/.ssh/authorized_keys 里私钥留在本地。连接时客户端用私钥签名一段数据服务端用公钥验证签名验证通过就放行。因为私钥不会在网络里传输安全性比密码高一个量级。生成密钥对ssh-keygen -t ed25519 -C my-ubuntu-client这里选了 ed25519 算法比传统 RSA 更短更快也更安全现代 OpenSSH 版本都支持。如果你要连接的旧系统不支持 ed25519那就退一步用ssh-keygen -t rsa -b 4096 -C my-ubuntu-client生成过程中会问你要不要设置 passphrase也就是私钥的“解锁密码”。我个人建议设一个哪怕简单点也行这样即使私钥文件泄露别人没 passphrase 也用不了。代价是每次连接要多输一次密码但可以通过 ssh-agent 记住解锁状态体验影响不大。生成完会在 ~/.ssh 下产生两个文件id_ed25519私钥和 id_ed25519.pub公钥。私钥千万不能发给别人。4.2 公钥分发与权限检查把公钥放到远程机器上最省事的命令是ssh-copy-id cts192.168.1.100它会提示你输入一次远程密码然后自动把公钥追加到远程用户的 authorized_keys 文件里。执行成功后再 ssh 登录就不用输密码了。验证公钥是否真的写入可以在远程机器上看cat ~/.ssh/authorized_keys如果没有 ssh-copy-id某些精简系统确实没有可以手动追加cat ~/.ssh/id_ed25519.pub | ssh cts192.168.1.100 mkdir -p ~/.ssh chmod 700 ~/.ssh cat ~/.ssh/authorized_keys chmod 600 ~/.ssh/authorized_keys权限问题我再啰嗦一遍远程的 ~/.ssh 目录是 700authorized_keys 文件是 600这是 OpenSSH 的安全要求。如果远程的 home 目录本身权限是 777StrictModes 也会拒绝公钥登录。所以改完公钥不生效时第一时间检查这三处权限。还有一种情况远程 home 目录是加密的比如某些企业环境sshd 在验证阶段可能读不到 authorized_keys这时日志里会提示 Authentication refused。解决办法复杂一些普通用户基本遇不到真遇到就查 /var/log/auth.log 看具体报错。4.3 VSCode Remote-SSH 远程开发配置密钥配好了日常写代码强烈推荐 VSCode 的 Remote-SSH 扩展。装好扩展后按 F1 输入 Remote-SSH: Connect to Host会读取你本地 ~/.ssh/config 的配置选择之前定义的 lab 或 cloud 就能连上。连接成功后左下角会显示 SSH: 192.168.1.100打开远程目录就和本地一样。我遇到过一个热搜词提到的问题“此扩展在此工作区中被禁用因为其被定义为在远程扩展主机中运行”。这通常是你的 VSCode 里某个扩展不支持远程场景或者本地设置覆盖了远程设置。解决办法就是在远程连接状态下打开扩展面板把那些标着“已禁用”的扩展逐个查看原因最常见的不是扩展本身的问题而是 VSCode 的 Remote 服务端没有安装成功或者版本和本地不匹配。可以试着在 VSCode 命令面板里执行 Remote-SSH: Kill VS Code Server on Host然后重新连接让服务端组件重装一遍八成能解决。另外VSCode 连不上还有一个高频原因远程机器用户对 home 目录没有写权限VSCode Server 无法安装。我之前在一台共享服务器上就遇到过home 目录属于 root普通用户只读VSCode 一直卡在 Setting up SSH Host。解决办法是让管理员给用户 home 目录加上写权限或者换一个可写的目录作为 VSCode 工作区。5. 远程文件管理与日常运维操作5.1 scp/sftp/rsync 对比与实操远程登录不只是敲命令文件传输同样高频。scp 是最直接的scp ./local_file.txt cts192.168.1.100:/home/cts/files/从远程拉文件到本地调换一下源和目标scp cts192.168.1.100:/home/cts/logs/app.log ./传输整个目录加 -rscp -r ./code cts192.168.1.100:/home/cts/codescp 适合一次性小文件。如果你经常同步目录rsync 更合适它只传输差异部分断点也能续传。基本用法rsync -avz --progress ./code cts192.168.1.100:/home/cts/code其中 -a 是归档模式保留权限和时间戳-v 是输出详情-z 是传输时压缩。你还可以加 --delete 让远端删除本地没有的文件实现真正的“目录同步”。但用 --delete 前一定确认路径写对了不然会把远端目录清空别问我是怎么知道的。sftp 则适合交互式浏览管理远程文件sftp cts192.168.1.100进入后就是一组类 FTP 命令ls、cd、get、put、rm、mkdir还有个好处是支持 Tab 补全远程路径。其实 OpenSSH 自带 sftp 足够用不需要额外装图形化客户端。5.2 远程执行命令与脚本自动化SSH 除了交互式登录更强大的地方在于“远程执行”。你想在 5 台机器上同时查磁盘占用不用一台台登录一条命令搞定ssh lab df -h多条命令用引号包起来ssh lab uptime free -h df -h | grep /dev/sda注意交互命令别这么干。比如 ssh lab sudo apt update如果 sudo 需要密码远程端因为没有终端输入环境会直接失败。解决办法是用 -t 强制分配伪终端ssh -t lab sudo apt update更复杂的场景可以写个本地脚本然后通过 stdin 传给远程 bashcat deploy.sh | ssh lab bash -s这样部署脚本不用先传到远程再从远程执行一条管道就完成了。日常更新几台服务器的代码、重启服务我基本都用这种方式比一台台登录省时间。6. 常见问题排查实录与性能优化6.1 连不上目标机的三类典型原因“连不上”是 SSH 里遇到最多的问题但排查路径其实很固定。我把常见现象按可能性从高到低列出来。第一类Connection refused。这个报错说明你访问的目标 IP 和端口上没有进程在监听。先确认服务端 sshd 有没有起来systemctl status ssh 看一眼再确认端口ss -tlnp | grep :22最后确认你连的端口对不对如果服务端改了端口而客户端用默认 22就是这个错。第二类Connection timed out。这是网络层问题请求发出去了没人回应。优先检查目标 IP 是否可达ping 一下如果 ping 不通查网线、Wi-Fi、网段、网关如果 ping 得通但 SSH 超时那多半是防火墙丢包了检查 ufw、云安全组、路由器访问控制。尤其是云主机安全组没有放行 22 端口是最常见原因。第三类Host key verification failed 或 REMOTE HOST IDENTIFICATION HAS CHANGED。这个上面提过目标机重装系统或重装了 sshd 会导致 host key 变化本地 known_hosts 还留着旧指纹。用 ssh-keygen -R IP 清掉旧记录重新连就行。6.2 登录失败与认证异常处理账号密码都对却提示 Permission denied (publickey,password)。这种情况要分几层看。先确认你使用的用户有没有远程登录权限。比如目标机的 sshd_config 里设置了 AllowUsers 白名单没在名单里的用户会被直接拒绝。再确认 PermitRootLogin如果你连 root默认 Ubuntu 的 sshd_config 是 prohibit-password意思是允许密钥登录但禁止密码登录这时你光用 root 加密码登录会被拒解决办法要么改用普通用户要么显式设置 PermitRootLogin yes生产环境不建议要么给 root 配密钥。如果密码登录本身就报错检查目标机 /etc/ssh/sshd_config 里 PasswordAuthentication 是不是 yesgrep ^PasswordAuthentication /etc/ssh/sshd_config如果显示 no那服务端不接受密码认证只能密钥登录。改完配置记得重启服务sudo systemctl restart ssh密钥登录失败最常见的是权限问题。远程检查 ~/.ssh 权限是否为 700authorized_keys 是否为 600~ 目录是否为 755 或更严格。还有一个容易忽略的点home 目录如果属于 root普通用户无法写入sshd 也会拒绝。最后再看服务端日志sudo tail -f /var/log/auth.log日志里会有具体拒绝原因比如 Authentication refused: bad ownership or modes。这个日志是 SSH 排错最重要的帮手任何登录问题先看它比自己瞎猜高效太多。6.3 SSH 连接卡顿的优化参数连上了但敲命令卡顿、输入延迟明显排除网络本身差的原因后很大概率是 sshd 在做反向 DNS 解析和 GSSAPI 认证。客户端连接时在本地 ssh 命令加这几个参数试试ssh -o StrictHostKeyCheckingno -o UserKnownHostsFile/dev/null -o ConnectTimeout10 -o ServerAliveInterval60 lab其中 ServerAliveInterval60 表示每 60 秒发送一次心跳包防止长时间空闲被断开。不过治本还是要改服务端配置。好一点的方案是在服务端 /etc/ssh/sshd_config 里加上UseDNS no GSSAPIAuthentication no第一行让 sshd 不反查客户端域名省去可能长达十几秒的 DNS 超时第二行关闭 GSSAPI 认证避免内网环境里 Kerberos 认证卡住。改完重启 ssh。很多“连上后卡几秒才出密码提示”“命令响应慢半拍”的问题这两个参数关掉就顺畅了。另外客户端这边也可以在 ~/.ssh/config 里统一加上这些参数避免每次敲一长串Host * ServerAliveInterval 60 ServerAliveCountMax 3 ConnectTimeout 106.4 常见问题速查表下面这个表是我实际运维中总结的基本覆盖 90% 的日常问题遇到情况可以直接对号入座。问题现象可能原因排查步骤解决方法Connection refusedsshd 未运行或端口不对systemctl status sshss -tlnp | grep :22启动 sshd确认端口后用 -p 指定Connection timed out网络不通或防火墙拦截ping 目标 IP检查网段、ufw、云安全组Permission denied (password)密码错或服务端禁用密码登录看 auth.log确认密码改 PasswordAuthentication yesPermission denied (publickey)公钥没配好或权限不对远程看 authorized_keys 和权限重新 ssh-copy-idchmod 700/600Host key verification failed目标机 host key 变更无ssh-keygen -R IP 清除旧指纹连接后卡顿DNS 反查或 GSSAPI 拖慢观察登录耗时UseDNS noGSSAPIAuthentication no空闲后断开无保活心跳客户端观察断线时机ServerAliveInterval 60VSCode 一直 Setting up远程无法写 VSCode Server看远程用户目录权限修复 home 目录写权限7. 安全加固与长期使用建议7.1 服务端安全基线配置能连上、能传文件之后下一步必须考虑安全。SSH 服务直接暴露在网络上每天都在被扫描和试探如果不做加固迟早会遇到暴力破解。首先要改默认端口。虽然改端口不能根治攻击但能挡掉 99% 的自动扫描器。在 /etc/ssh/sshd_config 里修改Port 22022然后重启 sshd。注意改完端口后防火墙、云安全组都需要同步放行新端口客户端连接也要用 ssh -p 22022 指定端口。其次如果用密钥登录已经稳定可以把密码登录关掉PasswordAuthentication no这步做完只有持有私钥的客户端才能登录密码暴力破解直接失效。改这个之前务必确认你的密钥登录已经完全可用否则把自己锁在外面就尴尬了。我自己在服务器上做这个操作前一定会另外开一个 ssh 会话保持连接改完测试过新会话没问题才断开旧的这个习惯救了我好几次。还需要限制 root 直接登录。Ubuntu 下默认禁止 root 密码登录但建议更严格一点PermitRootLogin no日常操作先登录普通用户再用 sudo 提权日志审计也更清晰。最后如果你管理多台机器强烈建议用 Fail2ban 这类工具。它是一个日志监控工具能自动检测到连续认证失败的 IP并对该 IP 做临时封禁sudo apt install -y fail2ban配置文件在 /etc/fail2ban/jail.local通常要新建。基础配置[sshd] enabled true port ssh maxretry 5 bantime 3600bantime 单位是秒3600 表示封 1 小时。这个工具威慑效果很好部署后 auth.log 里的暴力尝试数量会断崖式下降。7.2 日常维护与故障预防环境配置不是一次性的我把日常维护建议总结为四个“习惯”。第一定期检查 auth.log。不用每天看但每两周翻一次重点看有没有大量认证失败记录sudo grep Failed password /var/log/auth.log | tail -n 20有异常就查来源 IP再决定要不要升级防火墙规则或 Fail2ban 封禁。第二定期更新系统包。SSH 相关的安全补丁会随 Ubuntu 源发布养成习惯sudo apt update sudo apt upgrade -y云主机的话建议先备份或拍快照再升级避免依赖冲突搞挂服务。第三备份好你的 sshd_config 和 authorized_keys。我是把重要配置都放进 Git 仓库管理的机器重装后拉下来就能恢复。特别是 authorized_keys多台客户端公钥都在里面丢了就全连不上。第四不要在客户端随便清空 ~/.ssh 目录。known_hosts、私钥、config每一样都有用。真需要清理就删已知的某条记录比如 ssh-keygen -R IP。私钥请单独打包备份到加密存储里丢失就意味着所有配置了这把公钥的服务器你会全部失联。最后分享一个动作改任何 sshd_config 之前先备份原文件sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak然后用 sshd -t 检查配置语法确认无报错再重启服务sudo sshd -t sudo systemctl restart ssh这个习惯能帮你避免“改错一个字母导致 SSH 起不来只能跑去机房接显示器”的悲剧。我早期就犯过这种错现在不管改什么服务配置都先备份、先校验、再重启给自己留条后路。