资讯动态

离线环境装sshd:从依赖闭包到本地源固化全攻略

发布时间:2026/10/13 10:35:07 来源:尧图企业网站定制
简介面向需要在无网络环境中部署远程管理能力的 Ubuntu 20.04 用户这份离线安装包针对桌面版默认不预装 sshd 的痛点提供完整的 OpenSSH 服务组件。适用于内网服务器、离线机房或安全隔离场景运维人员无需上网逐一拉取依赖即可完成 SSH 服务部署。包体共 4 个文件包含 openssh-client、openssh-server、openssh-sftp-server 三个 deb 安装包附带一个自动化安装脚本整体仅 1.05MB便于通过 U 盘等介质快速拷贝。脚本可减少手工 dpkg 操作可能出现的依赖报错提升安装效率。目前已有 1253 人学习资源实用性获广泛认可。使用后不仅能建立安全的远程登录通道还能为后续配置密钥认证、修改默认端口等安全加固措施打下基础是管理员维护离线 Ubuntu 节点时的高效备选资源。1. 离线装一个 sshd为什么反而要先把依赖链想清楚机房交付现场机器能通电、能跑业务就是连不上外网。Ubuntu 20.04 装完之后发现 sshd 被裁剪镜像去掉了唯一进系统的途径是找管理员借显卡和键盘。这种场景下要解决的不是装一个软件而是在不联网的情况下把 openssh-server 以及它背后一串依赖的 deb 包完整搬到机器上装完还能稳定跑。很多人第一反应是找同事要一个 openssh-server 的 .deb 拷进去 dpkg -i迎面就是 depends: libc6 ( 2.31) 一串报错。问题不在操作而在 deb 包只声明依赖、不携带依赖离线时没有软件源帮你自动补齐。下面按这个闭环走联网机器上拉全依赖链、内网传输并校验、三种安装方式选型、装完验证与加固最后给一个能重复用的本地源脚本。写给还在跟离线环境较劲的运维和交付工程师照着走能少踩一半坑。2. 在联网机器上把 sshd 依赖链拉全装包之前先解决来源2.1 为什么不能只下载 openssh-server 一个 deb 包Ubuntu 20.04 的 sshd 由 openssh-server 提供这个包本身并不孤单。它的直接依赖至少有 libc6、libssl1.1、zlib1g、libpam0g、libselinux1、openssh-client、openssh-sftp-server、debconf、init-system-helpers、ucf 这一层openssh-client 又会继续拉 libedit2、libgssapi-krb5-2、libkrb5-3、passwd 等。递归展开之后常见的拉包数量在 25 到 35 个之间具体取决于联网机器的软件源状态。deb 包的打包机制决定了它只在自己控制文件里写我需要谁不会把需要的包也塞进包体内。安装器必须在系统里找到满足版本要求的依赖找不到就报错退出。在线环境里 apt 负责这件事它会自动去源里把依赖一并解析出来离线环境没有源可用这个解析工作就必须由人为提前完成。所以先把结论放在前面离线装 sshd 的本质工作是收集依赖闭包而不是下载一个安装包。收集的地点放在一台能上网、且系统版本和架构与目标机一致的 Ubuntu 20.04 机器上。先看直接依赖心里有个数# 在联网的 Ubuntu 20.04 机器上查看 openssh-server 的直接依赖 apt depends openssh-server输出里每一行形如Depends: libc6 ( 2.15)包名是第二列括号里的版本约束是针对已安装版本的最低要求。先看这一层确认 openssh-sftp-server 和 openssh-client 这种容易被忽略的硬依赖确实在清单里后面拉包时就不容易漏。2.2 用 apt-cache 递归依赖加 apt download 拉全 deb 包我这边常用的做法是先用 apt-cache 把依赖递归展开过滤掉推荐、建议、冲突项只留硬依赖然后交给 apt download 逐个下载。整个步骤放在一个干净的目录里做包名和校验文件名都留档。# 创建目录并进入 mkdir -p ~/sshd-offline cd ~/sshd-offline # 递归展开依赖只保留 Depends 行取包名去重写入列表 apt-cache depends --recurse --no-recommends --no-suggests \ --no-conflicts --no-breaks --no-replaces --no-enhances \ openssh-server | grep Depends | awk {print $2} | sort -u dep.list # 逐个下载对应的 deb 到当前目录 xargs -d \n apt download dep.list # 统计包数量和总大小 ls -lh *.deb | wc -l du -sh .参数解释--no-recommends 和 --no-suggests 是把推荐依赖和建议依赖剔除掉只保留硬依赖这样拉包清单最小避免把一堆用不上的库带进内网--no-conflicts、--no-breaks、--no-replaces、--no-enhances 分别去掉冲突、破坏、替换、增强这几类声明它们和安装需要的包无关。grep Depends 过滤出依赖行awk {print $2} 取出包名sort -u 去重因为递归展开时同一个包会出现多次。apt download 的行为是把当前软件源里能解析到的该包版本下载到当前目录它不解析依赖、不检查目标机器所以很适合这里的使用方式。xargs -d \n 保证包名列表按行处理避免包名里出现换行时解析出错。这一步有两个必须检查的前提条件。第一是架构在联网机和目标机上分别执行dpkg --print-architecture两边必须一致常见大头是 amd64但 arm64 的板子也不少架构不一致的 deb 拷过去等于白拷。第二是版本基线# 确认当前源里 openssh-server 的可用版本 apt-cache policy openssh-server # 如果需要指定版本下载时带上版本号 apt download openssh-server1:8.2p1-4ubuntu0.5为什么要确认版本联网机器如果最近刚 apt update源里很可能是最新补丁版它对 libc6、libssl1.1 的最低要求可能高于目标机已装版本。这时候如果直接全部下载到目标机安装就会碰到依赖报错。稳妥做法是先看目标机的ldd --version再回来挑一个兼容的 openssh-server 版本连同它的依赖一起下载。提示apt download 解析的是当前机器 sources 里能拿到的版本和联网机的 apt update 时机强相关。拉包前先 update 一次并且记录下当时拉到的版本号方便出问题时回查。2.3 另一种来源思路apt-get install --download-only 的适用边界还有一条常见路径是在联网机上用 apt-get 的下载模式把 openssh-server 连同解析好的依赖先下载到本地缓存再把缓存里的 deb 拷贝走。命令本身很直接# 只下载不安装注意这个命令本身不会改变系统状态 apt-get install --download-only --no-install-recommends openssh-server # 下载到的 deb 全部在缓存目录里 ls -lh /var/cache/apt/archives/*.deb这条路径的坑在于apt 只下载当前系统还没有安装的包。如果联网机器上已经装过 openssh-client 或 libssl1.1缓存里就不会出现这些包你拿到的清单天然缺一块。我第一次用这个方案给内网机器做交付包时翻车就翻在这以为缓存目录里的东西就是全部依赖结果目标机装到一半报缺包现场没有外网可用只能再跑一趟机房。所以我的建议是download-only 模式只适合联网机是一台刚装好的最小系统这种干净场景用它做抽查、比对可以做正式交付包不靠谱。正式的离线包还是回到 2.2 的递归下载方案因为 dep.list 文件本身就是一份可审查的交付清单哪些包、什么版本、有没有缺一眼能看全。把这份清单和 deb 放在一起拷进内网后续排查也有依据。3. 把 deb 包搬到目标机并完成安装三种方式怎么选3.1 拷贝前先做 sha256 校验别让坏道毁掉半小时依赖闭包收集齐了剩下的就是把这些 deb 从联网机器搬到目标机。传输通道按现场条件来能用 scp 的用 scp大多数离线现场是拿 U 盘或移动硬盘拷也有用内网 FTP 的。不管哪种通道我都建议在拷之前生成一份校验文件拷完在目标机先校验再动手安装。# 联网机器上在 deb 目录里生成校验文件 cd ~/sshd-offline sha256sum *.deb sha256.txt # 目标机器上把整个目录拷过去后执行校验 cd /data/sshd-offline sha256sum -c sha256.txt校验文件里记录的是相对路径所以 sha256sum -c 必须在 deb 所在的同一个目录执行否则它会报无法打开而不是校验失败。这一步能拦下两类问题一是 U 盘老化产生的静默坏块拷出来的文件能打开但内容已经变了直接 dpkg -i 会得到莫名其妙的解包错误二是中途中断导致的文件不完整文件大小明显不对。校验通过再安装比装到一半回头查传输问题省时得多。3.2 dpkg -i 与 apt install ./*.deb 的差别以及本地仓库方案到了目标机上安装方式有几种差别主要在谁来解析依赖上。下面这张表可以帮你按场景选方式命令依赖解析适用场景dpkg 直接装dpkg -i ./*.deb不解析缺了就报错依赖闭包已确认完整apt 本地安装apt install ./*.deb解析本地文件缺包时尝试远程源目录里依赖齐全且源已处理干净本地仓库先建 Packages 索引再 apt install标准 apt 解析完全离线多台机器重复交付、后续还要加包dpkg -i 一次传入全部 deb 时它会先把所有包解包再按依赖顺序依次配置。只要依赖闭包完整这种方式能装成功报错集中在某个依赖的版本不满足这一种情况。它最大的好处是快、可预测不碰源列表。apt install ./*.deb 会把当前目录里的 deb 当作候选包如果能满足依赖就直接装。它的坑在于一旦目录里缺某个依赖apt 会按 /etc/apt/sources.list 里配置的源去网络上找离线机上这一步通常表现为长时间卡在 Reading package lists最后超时报错。这不一定比 dpkg 更优雅所以离线机上用之前最好先确认源里没有公网项——这个问题在第 4 章会展开讲。多台机器重复交付时我更推荐本地仓库方案。在联网机器上用 dpkg-dev 自带的 dpkg-scanpackages 生成索引再把索引和 deb 一起拷走# 联网机器上在 deb 目录里生成本地源索引 dpkg-scanpackages . /dev/null | gzip Packages.gz然后在每台目标机上把这个目录注册成一个 file 源apt update 后就能像在线环境一样装。具体脚本放到第 6 章这里先把原理记住file 源的本质是让 apt 把一个本地目录当作软件源目录树索引文件 Packages.gz 告诉它里面有谁、什么版本、依赖是什么。索引和实际 deb 必须同步更新否则会出现列表里有包、装的时候找不到文件的怪问题。3.3 装完立刻完成三件事启服务、看端口、放防火墙安装完成后不要急着关终端三步确认服务真正起来# 1) 设置开机自启并立即启动Ubuntu 20.04 的服务单元是 ssh.service sudo systemctl enable --now ssh # 2) 确认 22 端口在监听 sudo ss -tlnp | grep :22 # 3) 目标机开了 ufw 的话放行 SSH sudo ufw allow 22/tcp sudo ufw status第一条里用的是 ssh 而不是 sshdUbuntu 上 openssh-server 安装出来的 systemd 单元名是 ssh.service很多发行版习惯叫 sshd这里容易踩名字的坑。systemctl enable --now 一条命令完成开机自启和立即启动比分开两条少一次手误。第二条的 ss 输出里如果看到监听地址是 0.0.0.0:22 或 [::]:22说明 sshd 已经在等连接看不到任何输出先 systemctl status ssh 看单元状态。20.04 的某些裁剪镜像里常见 socket 激活模式sshd 进程可能不常驻22 端口由 systemd 的 ssh.socket 接管具体情况在 4.2 讲。第三条容易被忽略很多离线机器是业务内网安全策略统一要求开 ufw。默认策略通常是拒绝入站只放行了几个指定端口SSH 不在放行列表里的话服务起来了也连不进去。ufw status 看一眼状态就知道是不是这个原因。4. sshd 离线安装避坑清单五个高频翻车现场离线安装的核心步骤不多翻车基本集中在下面五类。每条按现象 → 原因 → 解决过一遍遇到可以直接对号入座。4.1 现象一dpkg 报依赖版本太低装到一半退出现象dpkg -i 执行到一半输出dpkg: dependency problems prevent configuration of openssh-server后面跟着depends: libc6 ( 2.31)之类的最低版本要求安装流程退出系统里留下半配置状态的包。原因联网机器上的源是新的apt download 拉下来的 openssh-server 是带最新补丁的版本它对 libc6、libssl1.1 的最低版本要求高于目标机上实际装的版本。目标机长期不打补丁glibc 停留在老版本这是离线交付里最常见的版本错位。解决先看目标机的实际基线ldd --version看 glibc 版本再回到联网机器上用apt-cache policy openssh-server列出历史版本挑一个和基线兼容的版本重新下载。必要时把匹配版本的 libssl1.1 也一起拉回来apt download libssl1.1版本号。不要试图在离线机上强装新版核心库那种玩法一旦 libc6 升级失败整台机器的命令都可能跑不起来。4.2 现象二systemd 显示 active端口却没监听登录拒连现象systemctl status ssh 显示服务是 active但 ssh 客户端连接被拒绝ss -tlnp 里看不到 22 端口。或者反过来端口在监听但进程列表里根本没有 sshd。原因20.04 的某些裁剪镜像默认启用了 ssh.socket 的 socket 激活机制systemd 在 22 端口上建立监听有连接进来时才拉起 sshd 进程还有一种是 sshd_config 里把 ListenAddress 写死成了某个内网 IP而网卡实际地址不是它。解决统一收敛到常驻服务方式执行sudo systemctl stop ssh.socket和sudo systemctl enable --now ssh再确认ss -tlnp | grep ssh里出现的是 sshd 进程。如果改过 sshd_config先用sudo sshd -t做语法检查再sudo systemctl reload ssh平滑生效。注意 socket 单元和 service 单元同时存在时会抢 22 端口停掉 socket 单元是第一步。4.3 现象三apt 安装卡在 debconf 交互界面进退两难现象执行 apt install ./*.deb 或 dpkg -i 时终端停在 Configuring openssh-server 的蓝屏或黑屏界面按回车没反应等多久都不走。原因openssh-server 的 postinst 脚本会调用 debconf 询问配置项离线机器上缺少完整 locale 或终端宽度不合适时whiptail 渲染异常交互直接挂住。这个问题在在线安装里极少遇到因为一般机器配置完整离线裁剪镜像里却不少见。解决安装命令前加上非交互环境变量让 debconf 使用默认回答# 用非交互前端一次性装完变量只对本次命令生效 sudo DEBIAN_FRONTENDnoninteractive apt install -y ./*.deb如果已经有挂死的 debconf 进程占着先ps aux | grep debconf把它清掉再重试。这个变量写一次命令就够了不需要写进系统环境变量联网环境下也不建议长期全局设置它会让后续所有 apt 操作都跳过配置询问。4.4 现象四离线机残留外部源apt 卡在解析列表直到超时现象在目标机上执行任何 apt 命令都长时间停在 Reading package lists最后报超时或找不到包dpkg 反而一切正常。原因目标机的 /etc/apt/sources.list 或 /etc/apt/sources.list.d/ 里还保留着公网源地址。apt 每次执行都要重建包索引离线环境下连不上源只能反复重试直到超时。这不是 sshd 安装包的问题是所有离线机上 apt 的第一道坎。解决安装前先把外部源清干净# 备份并移除公网源保留现场便于事后还原 sudo mv /etc/apt/sources.list /etc/apt/sources.list.bak sudo rm -f /etc/apt/sources.list.d/*.list # 用空源重建一次索引之后 apt 不再卡解析 sudo apt-get update公网源移除之后apt 不会再尝试联网本地 deb 安装就不会被这个超时拖住。后续如果内部有镜像源再按内网规范写回 sources 列表。4.5 现象五漏拷依赖或手工解包sshd 起动时报缺库现象包都装完了systemctl start ssh 起不来命令行直接报error while loading shared libraries: libssl.so.1.1或类似信息。原因拷贝阶段漏了某个运行库包libssl1.1、zlib1g 这类属于最常被漏的或者有人为了省事用dpkg-deb -x手工把包解到根目录只解出了文件没执行 postinst 脚本连用户、目录权限、符号链接都没建sshd 自然起不来。解决先用 ldd 定位缺的库再用 dpkg --audit 查未配置的包# 查看 sshd 缺哪些动态库 sudo ldd /usr/sbin/sshd | grep not found # 检查系统里有没有处于半配置状态的包 sudo dpkg -C缺哪个库就把对应 deb 补拷进目录dpkg -i 重装一遍。手工解包只适合用来查看包里的文件内容不该作为安装手段这是离线安装里最容易自己骗自己的动作也是很多黑匣子问题的源头。5. 安装之后的验证与最小加固让 sshd 能长期稳定用5.1 三方面验证systemd 状态、端口监听、真实登录装完不算完远程通道要演示给验收方看得有能一次过的验证清单。验证要分三个层面因为三个层面分别对应三类故障systemd 状态只说明服务单元加载成功不代表端口在听端口监听说明网络栈就绪不代表认证链路可用真实登录才是从握手到会话建立的全链路验证。只看其中任意一个都会漏掉问题。# 状态层面开机自启和服务运行状态 systemctl is-enabled ssh systemctl is-active ssh # 端口层面确认监听地址顺便看有没有 socket 激活残留 sudo ss -tlnp | grep ssh # 登录层面本机回环做一次真实登录5 秒连接超时 ssh -o StrictHostKeyCheckingno -o ConnectTimeout5 localhost uptime第三条最容易被人跳过它做的是一次真实的 SSH 握手和认证往返能验证密钥交换、认证方式、会话建立整个链路。StrictHostKeyCheckingno 是避免第一次连接时的指纹确认交互卡住测试只用于连通性检查日常使用不建议省掉指纹确认。配置文件有没有真正生效还有一条更直接的命令sshd -T 会把当前生效的运行配置全部打印出来比人眼看 sshd_config 里被注释、被覆盖的部分可靠得多# 打印生效配置grep 关键项 sudo sshd -T | grep -E ^(port|permitrootlogin|passwordauthentication)这里能看到真实生效的端口、是否允许 root 密码登录、是否允许密码认证。很多我改了配置但没生效的玄学问题用这条命令一眼就能定位是没 reload、还是改的那行被后面的配置覆盖了。5.2 最小加固参数改端口、关 root 密码登录、只留密钥离线机不代表安全豁免内网里被扫描撞库的事一点都不少。离线安装的收尾工作我一般至少做三件事改端口、root 只留密钥登录、关掉密码认证。顺序很重要先把公钥配好再关密码通道。# 第一步把管理员的公钥写入 root 的 authorized_keys sudo mkdir -p /root/.ssh sudo chmod 700 /root/.ssh echo ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... admin-key | sudo tee /root/.ssh/authorized_keys /dev/null sudo chmod 600 /root/.ssh/authorized_keys密钥文件和目录的权限是硬要求目录 700、文件 600authorized_keys 权限给宽了sshd 会直接拒绝使用这个文件还没法从日志里看出明显报错。这一步做完先用密钥登录验证一遍确认能进再动下面的配置。# 第二步备份原配置改完必须语法检查 sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak # 改监听端口按现场规划选不一定要 22 sudo sed -i s/^#\?Port 22/Port 2222/ /etc/ssh/sshd_config # root 只允许密钥登录杜绝密码爆破 sudo sed -i s/^#\?PermitRootLogin.*/PermitRootLogin prohibit-password/ /etc/ssh/sshd_config # 关闭密码认证只走密钥 sudo sed -i s/^#\?PasswordAuthentication.*/PasswordAuthentication no/ /etc/ssh/sshd_config # 语法检查通过后平滑加载 sudo sshd -t sudo systemctl reload ssh三个 sed 是在默认配置文件基础上做替换^#\?同时匹配被注释的行和没注释的行避免改了没作用的错觉。改端口前确认现场网络策略允许这个端口出网否则可能自己把门锁死。顺序是这条链路里最不能省的一环先配密钥、验证密钥登录成功最后才执行关闭密码认证那条配置。离线机的救援通道几乎没有一旦密钥没配好就把密码认证关掉等于把自己锁在机房外面。改完配置再用 5.1 的三条命令过一遍确认服务状态、端口、登录三个层面都正常这台机器才算真正交付。6. 进阶把离线安装包固化成内网源脚本一次交付多台机器6.1 从拷一堆 deb到一个本地源脚本把过程固化下来只交付一台机器手动装完也就几分钟。但内网环境往往是一批机器每台都重复一遍拉包、拷贝、dpkg 的流程低效且容易漏。更好的做法是把整个 deb 目录变成一个可复用的本地源后续所有依赖这个源来完成安装。下面这个脚本放在 deb 目录里目标机上执行一次就把当前目录注册成一个可信的 file 源并完成 openssh-server 的安装。索引文件在联网机器上生成好、随目录一起拷过来#!/usr/bin/env bash # 离线本地源安装脚本把当前 deb 目录注册为 file 源并安装 openssh-server set -euo pipefail REPO_DIR$(cd $(dirname $0) pwd) LIST_FILE/etc/apt/sources.list.d/sshd-offline.list # 索引必须存在由联网机器执行 dpkg-scanpackages 生成并一并拷贝 if [ ! -f $REPO_DIR/Packages.gz ]; then echo 缺少 Packages.gz请回联网机执行 echo dpkg-scanpackages . /dev/null | gzip Packages.gz exit 1 fi # 写入可信本地源file:// 源不需要 GPG 签名 echo deb [trustedyes] file://$REPO_DIR ./ | sudo tee $LIST_FILE /dev/null # 刷新索引并安装跳过推荐包保持最小安装 sudo apt-get update sudo apt-get install -y --no-install-recommends openssh-server脚本里三个关键点REPO_DIR 用脚本自身所在目录保证源路径解析正确trustedyes 是因为 file 源没有 GPG 签名不加这个 apt 会拒绝使用--no-install-recommends 避免把推荐的一串包带进来和拉包阶段的裁剪口径保持一致。这个源的另一个好处是可扩展。后面想补装 vim、net-tools、htop只要在联网机器上把对应 deb 及其依赖同样拉进这个目录重新生成 Packages.gz 拷进内网目标机一条 apt-get install 就能装上不再需要重新走一套离线拷贝流程。回头看我自己的交付习惯第一次给离线机装 sshd 时只拷了一个包现场折腾了近两个小时才把依赖补齐。后来把递归下载、sha256 校验、本地源脚本固化成一整套流程再交付一台机器基本在十分钟内完成修依赖版本不匹配这类问题也快多了。教训就一条离线安装的功夫全花在装之前确认依赖闭包完整、版本匹配、校验通过安装本身反而是最不起眼的环节。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑