
简介面向 Ubuntu 20.04 桌面版无网络环境的 SSH 服务部署需求这份离线安装包专为内网隔离或离线运维场景设计解决了系统未预装 SSH 服务、无法通过在线软件源安装的常见问题适合系统管理员、运维工程师以及有远程管理需求的开发者使用。资源整体仅1.05MB共包含四个必要文件三个 deb 格式的官方编译包分别对应服务端、客户端和 SFTP 文件传输模块另附一个 sh 格式的一键安装脚本该脚本预置了依赖校验与安装引导逻辑让不熟悉命令行的用户也能顺畅完成部署。目前已有1243人学习下载借助这个离线包使用者可以在断网条件下快速完成 SSH 服务的安装、启动与开机自启同时根据包内说明进行修改默认端口、启用公钥认证等安全加固从而安全可靠地开展远程管理与自动化运维工作。无论是离线生产环境还是教学实验场景这都是一份即取即用的实用工具包。 我一直觉得Linux的离线和联网完全是两个世界。联网的时候一条apt install openssh-server三秒钟搞定可一旦到了内网环境装个sshd能折腾你一下午。前段时间去客户现场一台Ubuntu 20.04的服务器console口能进但SSH服务根本没装系统也没外网就给了我一个U盘。这篇就是记录我在这种场景下怎么把sshd完整装起来、以及我踩过的所有坑。适合谁看内网运维、设备集成、任何需要在隔离网络里部署服务的场景。如果你手里正好有一台没网但需要开SSH的Ubuntu 20.04机器这篇可以直接照着抄。1. 为什么sshd离线安装不是一个包的事很多刚接触Linux的人第一反应是找个openssh-server的deb包拷过去装上不就行了这个想法不能说错但在实际环境中几乎肯定会翻车。因为openssh-server的依赖关系比大多数人想象中复杂得多。我在一台干净的Ubuntu 20.04.6上用apt-cache depends openssh-server查过直接依赖至少包括libc6、libgssapi-krb5-2、libkrb5-3、libpam0g、libselinux1、libssl1.1、zlib1g等。这些依赖里有些是经典的几乎所有程序都要用的底层库比如libc6、zlib1g它们在多数系统里本来就有但libssl1.1、libpam0g、libselinux1这些就不一定了尤其如果你面对的是一个最小化安装的系统缺什么都有可能。更麻烦的是版本。Ubuntu 20.04本身跨了好几个维护版本周期20.04.1和20.04.6的系统库版本可能完全不同。你在联网机器上下载的deb包如果依赖的库版本低于目标机器上的版本那倒是没事但如果依赖的最低版本比目标机器上的还高dpkg会直接拒绝安装告诉你依赖关系不满足需要libssl1.1 1.1.1这种话。还有个隐藏问题架构。x86_64的机器要amd64的包ARM的机器要arm64的包这个大家应该都知道但真到现场容易忽略。我曾经遇到过一台ARM版的鲲鹏服务器U盘里拷的全是amd64的包装一个报一个错白跑一趟。所以在动手之前先把思维模式切换过来这不是找一个安装包而是准备一整套相互匹配的依赖集合然后再决定用哪种方式把它们装上去。2. 在联网机器上一次性收集全套deb包离线安装的第一步是准备一台和目标机器系统版本尽量一致的联网Ubuntu 20.04。我一般用和客户同构的虚拟机来做这件事版本完全一致最好小版本差距通常问题不大但别拿22.04给20.04准备包架构不同更是大忌。最粗笨但可靠的方法是用apt-get download逐个下载apt-get download openssh-server openssh-client \ libssl1.1 libpam0g libselinux1 zlib1g \ libkrb5-3 libgssapi-krb5-2 libkeyutils1 \ libk5crypto3 libcom-err2 libkrb5support0 \ libgcrypt20 libgpg-error0 liblzma5注意20.04默认的openssh-server版本是1:8.2p1-4ubuntu0.11左右随更新渠道不同会有差异如果你要用dpkg -i安装最好让它指定包时能自动匹配版本。apt-get download会下载仓库中当前版本和apt-get install安装到系统里的版本是一致的这一点不用担心。但手动列举依赖容易漏我更喜欢用反向依赖分析的方式。先装个辅助工具apt-get update apt-get install -y apt-rdepends然后递归查看openssh-server的完整依赖树apt-rdepends openssh-server | grep -v ^ | sort -u这个命令输出的第一列是包名第二列以空格缩进的是版本要求。有了这份列表你就能按图索骥把所有需要的deb包都收集齐for pkg in $(apt-rdepends openssh-server | grep -v ^ | sort -u); do apt-get download $pkg 2/dev/null done这样会把几十个包一股脑下载下来。有些包和目标系统版本不一致也没关系多下载是允许的安装时只有安装方会检查依赖。下载完检查一下目录里的deb文件数量再用dpkg-deb -I openssh-server_*.deb | head -20看看包的架构是不是amd64别等到现场才发现拷错了。还有一点如果你的联网机器已经装过openssh-serverapt-get download下出来的可能是当前已安装版本而非仓库最新版这个没有影响。关键是下载目标机器上缺失的运行时依赖而不是开发头文件或文档包。另外强烈建议把结果打包时保留deb包的原始文件名不要重命名因为dpkg记录包名时依赖文件名重命名虽然一般能装但记录不干净。3. 拷到目标机器后的三种安装方式对比把deb包压缩打包拷贝到内网机器后安装方式有三种我按推荐程度排序本地仓库法、dpkg顺序安装法、dpkg强制依赖法。3.1 本地仓库法最推荐把准备好的deb包放进一个目录比如/opt/sshd-debs然后生成Packages索引cd /opt/sshd-debs dpkg-scanpackages . /dev/null | gzip Packages.gz如果系统提示没有dpkg-scanpackages装一下dpkg-devapt-get update apt-get install -y dpkg-dev但这又需要网络所以在准备阶段就在联网机器上把dpkg-dev的deb包一起下载好带过去。接着把本地目录挂成软件源echo deb [trustedyes] file:/opt/sshd-debs ./ /etc/apt/sources.list.d/local.list apt-get update apt-get install -y openssh-server这种方式的优势在于依赖解析完全交给apt它会在/opt/sshd-debs里找到所有需要的依赖并依次安装不会出现手动dpkg顺序错了导致装不上、或者装上了但依赖没打齐的问题。唯一的前提是目录里的包必须齐全否则apt照样报无法定位软件包或无法满足依赖关系。3.2 dpkg顺序安装法如果你不想在机器上留下软件源配置有些安全要求严格的现场不让改sources.list那就手动dpkgcd /opt/sshd-debs dpkg -i *.deb这个命令会把目录下所有deb包一起丢给dpkg处理dpkg会自动对包排序大部分情况下可行。但如果包的顺序中有循环依赖openssh-server依赖openssh-clientopenssh-client的部分组件也可能被openssh-server反向依赖可能会报错。遇到这种情况加一把-a参数dpkg -i -a *.deb或者在报错后执行apt-get -f install-f是修复损坏依赖的标准手段它会扫描系统中未完成的依赖尝试从本地能访问到的包来修复。但注意在离线环境里apt-get -f install如果没有可访问的源修复可能失败所以它只适合在包都已经拷进本地源但还没完全安装好的场景下用。3.3 强制依赖法应急手段不推荐dpkg -i --force-depends openssh-server_*.deb这个命令会跳过依赖检查强制安装。我强烈不建议轻易使用因为它会让系统处于一个dpkg认为依赖已满足的假象中一旦真正运行时缺少某个库sshd会启动失败而且排查起来比正常安装复杂得多。它只适合那种我已经确认所有底层库都在只是dpkg因为版本号小差异卡住的极端场景。三种方式我建议首推本地仓库法省心而且排错干净。绑定在某个目录作为本地源的做法安装完成后把sources.list.d里的local.list删掉即可对系统没有残留影响。4. 启动前的配置与首次启动依赖装上包也装好了接下来是配置阶段。这部分容易被人跳过但恰恰是离线安装后半程的关键。4.1 生成Host Key新安装的openssh-server不会自动生成主机密钥host key。这是SSH用来识别服务器身份的关键文件位于/etc/ssh/ssh_host_*。如果没有host key客户端连接时会提示服务器拒绝了密钥或无匹配的host key算法sshd服务本身也可能直接起不来。生成命令ssh-keygen -A-A会为dsa、ecdsa、ed25519、rsa各生成一套默认长度的密钥。然后检查一下/etc/ssh目录权限和文件权限chmod 755 /etc/ssh chmod 600 /etc/ssh/ssh_host_*_key chmod 644 /etc/ssh/ssh_host_*_key.pub chown root:root /etc/ssh/ssh_host_*_key*这段权限设置非常容易踩坑。SSH对权限要求很苛刻密钥文件权限过宽比如644会导致连接时报Permissions 0644 for /etc/ssh/ssh_host_rsa_key are too open服务端会拒绝加载该密钥。4.2 sshd_config关键项默认的/etc/ssh/sshd_config已经能工作但有几个参数值得检查Port 22 PermitRootLogin prohibit-password PasswordAuthentication yes PubkeyAuthentication yes AllowUsers youruserPermitRootLogin prohibit-password表示禁止root用密码登录但允许密钥登录。很多运维习惯直接改成yes方便管理但这在公网环境风险很高内网环境看你们的管理规范我的习惯是保持默认root账号用密钥日常操作用普通用户。PasswordAuthentication如果为no那客户端没有配置密钥就永远也连不进来离线环境里如果对新机器的密钥还没部署好很容易造成自己把自己锁在外面。我建议第一版先保留yes等密钥全部部署完再关掉。4.3 启动并验证systemctl enable --now ssh systemctl status ssh如果状态不是active (running)立刻看日志journalctl -u ssh -xe --no-pager | tail -50 tail -50 /var/log/auth.log常见的一种情况是sshd: no hostkeys available那基本就是第4.1步没做。还有一种情况是提示Missing privilege separation directory: /run/sshd这是sshd启动时需要创建运行时目录通常会在postinst脚本里自动处理如果手动安装时没触发就手动建mkdir -p /run/sshd防火墙层面也不要忽略。Ubuntu 20.04默认的ufw如果处于enable状态需要放行22端口ufw allow 22/tcp不过UFW默认是关闭的如果你不确定用ufw status看一眼不显示Status: active就不用管。5. 离线环境下的实测踩坑记录这个环节我分享几个自己真实遇到过的问题每一个都让人抓狂甚至一度让我怀疑是不是包里少了东西。5.1 libssl1.1的版本陷阱Ubuntu 20.04的openssh-server依赖libssl1.1而系统里可能存在openssl 3.0比如某些LTS的backports仓库把openssl升到了3.x。这时你下载的openssh-server依赖树里如果按照apt-rdepends自动下载了libssl1.1版本可能和你目标机器上的旧版本冲突反过来如果你图省事在联网机器上只下载了openssh-server的deb包没有带libssl1.1到了现场dpkg就会报依赖缺失。我第二次做离线安装时就踩了这个坑自信满满只带了一个openssh-server_8.2p1的包结果现场告诉我缺libssl1.1而目标机器的libssl1.1还是1.1.1f版本下载的包要求1.1.1f勉强满足却在PAM模块加载时出问题。后来我把apt-rdepends整个依赖树都带过去一次就过了。5.2 PAM认证模块缺失导致sshd起来但连不上这个坑比较隐蔽。sshd服务状态显示active日志里也没有明显报错但客户端一连接就提示Authentication failed而且多次尝试后没有记录任何认证失败的日志在auth.log里。排查下来发现是目标系统安装时用了极小化选项/lib/security/下根本没有pam_unix.so这些PAM模块而openssh-server的PAM配置里又依赖了它们。如果libpam-modules这个包不在目标系统上就会出现这种进程在跑但认证全部失败的假象。解决方式是把libpam-modules、libpam-runtime一起放进离线包集合里或者安装后检查一下ls /lib/security/ | grep pam_unix没有就补装。这属于最让人头疼的一类问题因为它不报错只是功能不工作。5.3 拷贝时文件权限被篡改U盘拷贝在Windows和Linux之间来回倒腾时有时会碰到deb包权限被改成744、644等情况。dpkg安装时对权限不太敏感一般没影响但扩展名为.deb的包作为内核模块或某些特殊包安装时可能要求600这时候会报subprocess installed post-installation script returned error exit status。更常见的是把整个/opt/sshd-debs目录用scp拷到目标机器时目录里所有文件的时间戳和权限会跟着变化。安装本身通常不受影响但如果脚本里用了umask或者postinst脚本依赖特定文件权限就可能出幺蛾子。我的习惯是拷贝前先tar czf打包到了现场tar xzf解包这样权限和所有权都能保留。5.4 host key权限过宽导致启动拒绝加载前面提过但值得单独强调很多人先zip打包拷过去再解包安装解包后host key的权限被解包工具重置为644。结果sshd进程起来前先加载密钥发现Permissions 0644 ... are too open直接放弃加载然后所有客户端都连不进来日志里写着sshd: no hostkeys available。遇到这种就该秒速反应chmod 600 /etc/ssh/ssh_host_*_key重启sshd就正常了。这个小问题我后来遇到很多次已经成了每次部署后的例行检查项。6. 我自己的例行检查和一个小技巧最后分享一个我自己每次做完离线部署都会跑一遍的检查脚本思路不算复杂但能省不少返工时间# 检查host key ls -l /etc/ssh/ssh_host_*_key # 检查sshd配置语法 sshd -t # 检查服务状态 systemctl is-active ssh # 检查端口监听 ss -tlnp | grep :22 # 本机回环测试 ssh -o StrictHostKeyCheckingno -o BatchModeyes localhost true echo SSH OK这五条命令从上到下分别管密钥文件、配置合法性、服务存活、端口监听、真实连接能力任何一条挂了都能精准定位问题在哪一层。还有个我习惯用的小技巧在准备阶段我会把联网机器上/usr/lib/openssh/sftp-server这个文件也一起拷贝走。虽然openssh-server包安装时会自己带但如果你遇到的是那种包源被裁过、或者系统版本极端的情况sftp-server缺失会导致SFTP连接建立成功后立刻断开而排这一路问题的时间远比拷贝一个文件多得多。离线安装sshd这件事本质上是把包管理器的依赖解析逻辑在脑子里再走一遍顺便替它把所有可能缺的底层组件都准备好。只要依赖树完整、权限一致、配置合理U盘里的东西和一个完整软件源其实没有区别。这些坑我踩过一遍希望后来的人能少踩两遍。本文还有配套的精品资源点击获取