
简介面向无网络连接或网络受限环境中的Ubuntu 20.04桌面版用户这份离线资源包解决了系统默认未预装SSH服务组件的问题为需要远程登录管理、自动化运维或学习SSH协议原理的读者提供了一套开箱即用的本地部署方案应用场景覆盖企业内网隔离机房、个人虚拟机调试环境以及各类应急响应与故障修复场合使用者无需配置在线软件源即可完成全部安装过程。资源包整体包含四个文件压缩后大小约为1.05MB其中有三个标准安装包和一个自动化安装脚本三个安装包分别覆盖SSH客户端、服务端守护进程及SFTP文件传输功能脚本则封装了依赖检测、安装顺序、服务启动等关键步骤用户只需执行一条命令即可完成部署免去逐条处理安装包的繁琐操作。截至目前该资源已有一千二百四十三人学习使用尤其适合负责系统初始化配置的运维工程师与正在练习远程服务搭建的入门学习者。借助这份资源使用者能够一次性获得完整组件、安装脚本与清晰部署逻辑无需临时查找零散资料即可快速启用SSH服务建立安全可靠的远程登录与文件传输通道同时也可将其作为剖析SSH软件包构成与离线安装机制的学习样例在此基础上进一步实施更换监听端口、启用公钥认证等安全加固策略。安装完成后系统即可接受来自客户端的连接请求满足日常远程维护、脚本执行与自动化任务调度的实际需要。 先说一下这次分享的来由。最近在客户现场遇到一台 Ubuntu 20.04 服务器内网环境物理隔离没法直接apt install openssh-server但远程运维又必须先把 SSH 服务拉起来。我当时的做法是找一台同样架构、同样系统版本、能联网的机器把 sshd 相关的 deb 包全部拉下来用 U 盘拷进内网然后dpkg离线装好。整个过程不难但里面有几个坑很容易把人卡住尤其是依赖包缺失、libssl 版本冲突这类问题。这篇文章就围绕“Ubuntu 20.04 sshd 离线安装包”这件事把我踩过的坑和验证过的完整流程整理出来给同样受困于内网环境的朋友做个参考。1. 什么场景下必须离线装 sshd1.1 网络受限环境的经典痛点先说场景。大多数时候我们拿到一台 Ubuntu 服务器第一件事就是apt update apt install openssh-server一条命令搞定。可一旦系统处于下面几种环境这条命令就废了纯内网机房物理隔离没有互联网出口代理网络需要认证APT 源根本访问不到安全加固后的服务器主动禁用了外网 DNS 和 HTTP 访问现场设备预装了 Ubuntu 20.04但部署时光盘/U 盘里只有系统镜像没有软件包仓库。这时候你就面临一个很尴尬的局面SSH 没装人在机器前只能插显示器键盘操作可后续所有远程操作都依赖 SSH。所以离线准备好 sshd 安装包本质上解决的是“在没有互联网的机器上把远程运维通道打通”这个核心问题。1.2 离线方案的核心思路离线安装的核心思路其实就一句话在另一台相同系统版本、相同 CPU 架构的联网机器上把目标软件及其依赖全部拉下来然后搬运到目标机器上安装。为什么强调“相同系统版本”和“相同架构”因为 Ubuntu 的 deb 包和系统库版本强相关。比如 Ubuntu 20.04 对应 focal 仓库如果你拿 Ubuntu 22.04jammy的 openssh-server 包去装依赖的 libssl1.1 版本、libc6 版本很可能对不上装到一半就报依赖错误。架构就更不用说了x86_64 的包无法在 arm64 系统上安装。所以离线准备的第一步就是确认目标机器是 Ubuntu 20.04 x86_64或者 aarch64、armhf然后找一台完全一致的机器来下载。提示如果你手头只有一台联网机器但系统版本不是 20.04也可以用 Docker 临时跑一个 ubuntu:20.04 容器用来下载依赖包效果一样后面会讲。2. 离线安装包准备在联网机器上把料备齐2.1 apt download 基本用法最笨也最稳妥的方法就是用apt download逐个下载 deb 包。在联网的 Ubuntu 20.04 机器上执行mkdir -p ~/sshd_offline cd ~/sshd_offline apt download openssh-server执行完后当前目录会多出一个openssh-server_1:8.2p1-4ubuntu0.11_amd64.deb这样的文件。但注意这只下载了 openssh-server 本体它的依赖包一个都不会下载。直接把这个 deb 拷到内网机器上安装几乎必然报错因为缺依赖。2.2 一步拉全依赖的实用命令一条命令把 openssh-server 及其所有依赖全部拉下来是这段操作的关键。使用apt-cache depends递归解析依赖再用apt download批量下载cd ~/sshd_offline apt download $(apt-cache depends --recurse --no-recommends --no-suggests --no-conflicts --no-breaks --no-replaces --no-enhances openssh-server | grep ^\w | sort -u)拆开解释一下这个命令干了什么apt-cache depends列出 openssh-server 的依赖关系--recurse递归解析每一层依赖把“依赖的依赖”也找出来--no-recommends --no-suggests排除推荐和建议安装的包只保留硬依赖减少包数量和体积grep ^\w把输出中的依赖包名称行筛出来过滤掉带、|等描述符号的行sort -u去重$(...)把结果作为参数传给apt download批量下载。执行完后目录下会得到十几个 deb 文件。对于 openssh-server 来说通常包括这几个关键成员包名作用是否需要关注openssh-serverSSH 服务端主程序核心openssh-clientSSH 客户端工具服务端依赖它核心依赖openssh-sftp-serverSFTP 子系统默认需要libssl1.1OpenSSL 加密库重点检查版本libedit2命令行编辑库依赖项libpam0gPAM 认证模块依赖项下载完成后可以用ls -lh *.deb检查文件大小和数量。这块有两个常见问题第一命令执行后如果报 “Unable to locate package”说明当前机器没有更新软件源缓存先执行apt update。第二如果某些包提示“已经是最新版本”而不下载可能是因为这些包已经从缓存中安装了。这不会影响后续离线安装但为了保险起见建议在下载前先apt clean清一下缓存避免漏包。2.3 使用 Docker 快速准备下载环境如果你只有一台联网的 Windows 机器或 Mac也可以借助 Docker 快速创建一个 Ubuntu 20.04 下载环境docker run -it --rm -v ~/sshd_offline:/packages ubuntu:20.04 bash进入容器后先更新源apt update apt download $(apt-cache depends --recurse --no-recommends --no-suggests --no-conflicts --no-breaks --no-replaces --no-enhances openssh-server | grep ^\w | sort -u)因为容器内部的/packages目录已经映射到宿主机下载好的 deb 文件会直接出现在宿主机~/sshd_offline里。这个方式尤其适合你手头没有 Ubuntu 实体机、但需要快速准备离线包的情况。2.4 把包搬运到目标机deb 文件准备好之后传输方式根据现场条件来定最快最稳的是 U 盘拷贝用完记得sync再拔避免文件丢失如果内网已经有一台可访问的机器可以用scp传到那台机器再内部转发如果目标机器网卡可用只是没有互联网还可以临时搭一个局域网 HTTP 服务让它远程拉取。我个人在实际项目里用得最多的还是 U 盘。因为内网安全要求高插拔式拷贝最不容易被审计发现也最可靠。3. 目标机器离线安装与 sshd 部署3.1 安装前的系统检查在目标 Ubuntu 20.04 机器上执行离线安装之前一定要先做三轮检查避免白折腾第一确认系统版本cat /etc/os-release lsb_release -a确认是Ubuntu 20.04.x。如果系统是 20.04 没错但内核版本被做进了定制化内核也不影响 sshd 安装因为 sshd 依赖的是用户态库不绑定内核版本。第二确认架构dpkg --print-architecture uname -mdpkg --print-architecture如果输出amd64那下载的 deb 包也必须是amd64。如果你误拿了 arm64 的包dpkg -i会直接报 architecture 错误。第三确认目标机器上是否已经有部分依赖包dpkg -l | grep libssl dpkg -l | grep libpam0g如果某些系统自带依赖已经存在安装时 dpkg 会自动跳过。另外有一个重要的细节——Ubuntu 20.04 默认自带openssh-client所以下载依赖时可能没有重复下载它但目标机器上如果被精简掉了也要一并准备。3.2 dpkg 安装操作与依赖处理把所有 deb 文件拷贝到目标机器的同一个目录比如/tmp/sshd_offlinecd /tmp/sshd_offline sudo dpkg -i *.debdpkg -i会逐个安装当前目录下的所有 deb 包。正常情况下输出应该显示每个包都成功安装最后没有 error 信息。如果某一步报依赖缺失比如dpkg: dependency problems prevent configuration of openssh-server: openssh-server depends on libssl1.1 ( 1.1.1); however: Package libssl1.1 is not installed.这说明 libssl1.1 没有下载或者没有被正确安装。排查顺序如下# 查看当前目录所有 deb 包 ls -l *.deb # 查看指定依赖是否被打包进来 ls -l | grep libssl1.1如果确实没有这个包回到联网机器补下载apt download libssl1.1这里有一个容易踩坑的点Ubuntu 20.04 原生自带 libssl1.1但如果你的目标机器装过 Python 3.10 或者其他第三方软件可能系统里已经存在 libssl3OpenSSL 3.x而 libssl1.1 被替换或部分移除。这种情况下你在离线包目录里额外准备一个 libssl1.1 的 deb 是明智的。有些教程会让你直接apt --fix-broken install来修复依赖但那是联网环境下的做法离线环境根本没有源执行了也没用只能手动补齐依赖包。安装完成后验证核心包版本dpkg -l | grep openssh输出应该包含openssh-server、openssh-client、openssh-sftp-server状态都是iiinstalled ok installed。3.3 启动服务与开机自启把服务启动起来这一步在 Ubuntu 上有几个小细节需要注意。Ubuntu 的 SSH 服务单元名和 RHEL 系不一样是ssh.service而不是sshd.service。网上很多教程写systemctl start sshd那是 CentOS 的写法在 Ubuntu 上直接执行会报 Unit not found。正确的操作是sudo systemctl enable --now ssh这条命令把开机自启和立即启动一起完成了。然后确认状态sudo systemctl status ssh如果看到active (running)说明服务已经跑起来了。再检查端口监听情况ss -tlnp | grep 22输出里应该能看到sshd进程监听在 0.0.0.0:22。有一个容易被忽略的坑是系统里已经有一个 sshd 进程占用了 22 端口但那个进程不是来自 systemd 的 ssh.service而是之前用./sshd手动拉起的老残留。这种情况systemctl start ssh会报端口被占用。解决办法是先把老进程杀掉再启动 servicepkill sshd systemctl restart ssh4. 配置与常见问题排查4.1 改端口、root 登录等常用配置sshd 装好后默认配置基本可用但内网环境通常要做几项调整。配置文件在/etc/ssh/sshd_config修改后执行sudo systemctl restart ssh生效。第一项是允许 root 远程登录。Ubuntu 20.04 默认配置PermitRootLogin prohibit-password意思是禁止密码登录 root但允许密钥登录。如果现场环境确实需要直接 root 密码登录改PermitRootLogin yes但这里我建议你先想清楚安全问题。内网环境如果没人审计root 密码登录倒还好一旦这台机器会接入更大的网络建议保持prohibit-password只放公钥。第二项是修改监听端口。默认 22 端口容易被扫描改成比如 2222Port 2222改完之后注意测试时不要断开当前连接而是新开一个窗口测试确认能连上再关旧连接。不然端口写错你把自己锁在门外就尴尬了。第三项是检查防火墙。Ubuntu 20.04 默认可能没装 ufw但如果装了sudo ufw status sudo ufw allow 22/tcp还有 selinux 的问题Ubuntu 默认没有启用 selinux但如果你手动装过 apparmor 的额外配置也要留意。4.2 常见故障排查速查表离线安装 sshd 最容易遇到的问题我整理成一个速查表现象可能原因解决办法dpkg -i报依赖错误依赖包未下载完整回到联网机器补下载缺失依赖libssl1.1not installed系统里只剩 libssl3单独下载 libssl1.1 的 deb 并安装Ubuntu 上systemctl start sshd报 Unit not found服务名写错Ubuntu 用systemctl start sshBind to port 22 failed: Address already in use端口被旧进程或其他服务占用pkill sshd后重启或改配置换端口sshd: no hostkeys available主机密钥未生成执行sudo ssh-keygen -A生成所有主机密钥客户端连接提示Host key verification failed目标机器系统重装密钥变化在客户端执行ssh-keygen -R 目标IP密码正确但登录失败配置里PermitRootLogin设置不当按需改为yes或使用密钥登录这里重点说两个实战多发的坑。第一个是sshd: no hostkeys available。装好 openssh-server 后如果系统没有自动生成主机密钥sshd 启动时会直接退出。执行sudo ssh-keygen -A重新生成全部主机密钥再systemctl restart ssh即可。这个命令在第一次启动 sshd 前执行更稳妥。第二个坑是/var/empty/sshd权限问题。如果启动时看到/var/empty/sshd must be owned by root and not group or world-writable.这是因为/var/empty/sshd目录的权限被改过。解决方式sudo chown root:root /var/empty/sshd sudo chmod 755 /var/empty/sshd这个小问题在离线环境里碰到的人不少因为很多内网机器会批量部署脚本顺手就把权限改了。记一下这个命令能省去不少排查时间。4.3 验证客户端连接安装和配置都完成后从客户端验证一下ssh user目标IP -p 22如果连不上先看 sshd 的状态然后看防火墙再看端口监听。三步走下来绝大多数问题都能定位。另外如果你在一台全新的 Ubuntu 20.04 上离线安装了 sshd但发现ssh localhost都连不上优先检查/etc/hosts.allow和/etc/hosts.deny。有些安全加固过的系统会在这些文件里限制 sshd 的访问来源。5. 最后再分享几个实操体会离线装 sshd 这件事看着简单但真正在现场踩过坑之后我对它的理解是方案的核心不是那一条dpkg -i命令而是前面准备依赖包的那几步。我个人的习惯是这样的准备离线包时除了 openssh-server 本身还会顺手多下载几个与 ssh 强相关的工具包比如openssh-client、net-tools、rsync。前者是很多运维脚本的底层依赖后者在做内网日志同步时经常用到。一次多准备一些免得到现场发现缺包再折返。还有一个小技巧把下载好的 deb 包在联网机器上先解压验证一下完整性。用dpkg -c 包名.deb可以提前检查 deb 包结构是否完整而不是等到现场装到一半才发现文件损坏。对 U 盘拷贝这种离线传输方式这一步尤其值得做。如果你负责维护多台离线机器还可以把打包好的 deb 目录直接做成一个简易的本地仓库。网上有不少教程教你用dpkg-scanpackages生成 Packages 索引然后配合apt使用。这样一来内网其他机器要装什么软件只要把这个仓库地址配进 sources.list就能像联网一样apt install了。这个扩展方向很实用但前提是那台放软件包的机器要先有一台“离线软件仓库机”而仓库机的第一个软件往往就是通过今天这套 U 盘搬运装的 sshd。最后再说一次离线的核心不是“没有包”而是“没有找到正确的包”。把依赖准备好把架构看清把服务名记住Ubuntu 20.04 离线装 sshd 就是一次很顺畅的操作。希望这篇分享能帮你在现场少踩几个坑。本文还有配套的精品资源点击获取