手头正好有个项目现场是一台离线内网的Ubuntu服务器既不能连外网也没有内部apt源却要给几十台Windows终端开文件共享。apt装samba这条路直接断掉唯一的办法就是把samba源码包和一整套编译依赖打包用.tar方式离线装。折腾了一整天踩了无数个坑之后总算跑通了这里把整个流程和关键教训完整记录下来给后面遇到类似情况的兄弟省点时间。这篇内容适合两种人一是纯离线环境下需要装samba的运维或工控工程师二是对samba源码编译不熟悉、想搞明白configure参数和依赖关系的人。我会从依赖分析、源码准备、编译安装、smb.conf配置、systemd托管到排错实战一步步讲清楚全部基于Ubuntu LTS版本实测验证过。1. 离线安装的整体思路与依赖分析1.1 为什么绕不开.tar源码包很多人第一反应是“离线装samba直接把联网机器上下载好的deb包拷过去不就行了”。确实如果内网有一台可以联网的同版本Ubuntu机器用apt-get download把samba和所有依赖的deb拉下来再离线dpkg安装是最省事的路径。但现实往往没这么理想——很多隔离环境的机器自己就是内网根本没有“同版本联网机”可以用来下载或者下载下来的deb包依赖版本对不上dpkg装到一半就报一串依赖缺失错误。这时候源码编译反而是最稳定的方案因为.tar包里自带了大部分第三方库的版本控制只要你把需要的源码包凑齐在离线机器上逐个编译就没有版本匹配问题。源码编译还有一个好处就是可以自己指定安装路径。默认的samba源码编译支持--prefix参数把它丢到/opt/samba或者/usr/local/samba不污染系统原有的/usr目录。对那种“轻易不动系统自带二进制”的现场环境来说这个隔离性很重要万一出了问题直接删掉整个目录就能回滚比dpkg卸载干净得多。1.2 源码编译samba到底需要哪些东西samba不是那种孤零零一个包就能编译的软件它的核心底层依赖是talloc、tdb、tevent、ldb这一组C库分别负责内存池、轻量数据库、事件循环和轻量目录访问。这几个库都来自Samba团队自己的配套项目编译samba时如果没有系统安装它们源码树的3rdparty里通常有捆绑版本但最稳妥的做法还是单独下载对应版本源码先装好这几个依赖再编译samba本体。除了Samba自家的库还需要popt命令行参数解析、libbsd、libaio、python3-dev、gnutls开发头文件、libreadline-dev、zlib1g-dev这些基础编译依赖。其中gnutls这条链最头疼它自己又依赖nettle和gmp如果离线机器上连这些都没有就得把gmp、nettle、gnutls三个包全部下载好一起编译。看到这里不用慌后面我会给出完整的依赖清单和推荐编译顺序。如果你的场景只是做简单的文件共享不需要加入域控那么可以在configure时显式关掉AD DC相关的组件能省掉大量额外依赖。这一点在实际编译时非常重要很多人失败的根源就是尝试编译了全部组件结果缺了一堆无关紧要的开发库。2. 离线安装准备源码下载与依赖预处理2.1 在一台联网机器上把源码包准备齐先找一台能联网的Ubuntu机器建议系统和目标机保持同一个大版本比如都是Ubuntu 22.04这样编译出来的二进制兼容性最好。然后准备下面这些源码包samba-4.x.x.tar.gz去Samba官网下载选当前stable版本talloc-2.x.x.tar.gztdb-1.x.x.tar.gztevent-0.x.x.tar.gzldb-2.x.x.tar.gzpopt-1.x.tar.gz有些系统自带popt保险起见也下一份gnutls、nettle、gmp的源码包如果目标机器没有libgnutls28-dev的话把这些包全部放到一个目录下然后用一条命令打包tar czvf samba-offline-all.tar.gz samba-*.tar.gz talloc-*.tar.gz tdb-*.tar.gz tevent-*.tar.gz ldb-*.tar.gz popt-*.tar.gz gnutls-*.tar.gz nettle-*.tar.gz gmp-*.tar.gzU盘或者内部传输工具把这个.tar.gz拷到离线目标机上后面所有操作都在目标机上进行。有人可能会问为什么不直接在一台联网机器上编译好samba可执行文件然后整个目录拷过去这当然也可以但涉及动态库路径和系统环境差异在工控现场这种“不知道对方用的什么系统镜像”的场景下风险较大。源码包安装虽然慢一点但是每一步都看得见摸得着出问题也能准确定位。2.2 目标机器的系统环境确认开始编译之前先检查目标机的硬件架构和基础工具链uname -m cat /etc/os-release which gcc make python3如果是x86_64架构下载源码时选对应的generic包即可如果是ARM架构比如国产化机器或嵌入式设备源码编译方式完全一样只是编译时间会长不少。gcc和make是硬性条件Python3也是samba编译脚本必需的如果这三样都没有得先从操作系统安装光盘里把这些基础工具补上。这里有个非常关键的小技巧在编译前先设置一个环境变量固定编译进程数比如export MAKEFLAGS-j4如果你不确定机器CPU核数用nproc查看。设太高的编译并发数在内存较小的机器上会直接OOM我就在一台4G内存的旧机器上被-j8干崩溃过swap都救不回来。2.3 推荐先编译的基础依赖samba本体编译前我建议按“gmp → nettle → gnutls → talloc → tdb → tevent → ldb”的顺序把底层依赖全部编译安装到同一个前缀目录。统一使用/usr/local/samba-deps作为前缀这样所有依赖库都集中在同一个地方samba的configure脚本扫描时也能一次性找到。每个依赖包的编译套路基本一致拿talloc举例tar xzf talloc-2.3.4.tar.gz cd talloc-2.3.4 ./configure --prefix/usr/local/samba-deps make make install注意talloc、tdb、tevent、ldb这四个Samba系库各自还带了一个python绑定模块编译时如果报找不到Python.h之类的错误说明缺少python3-dev。如果离线机器上没有这个包一个临时替代方案是在configure时不要单独编译python绑定但这些库相对独立用默认配置一般不会出大问题。gnutls这条链要稍微多说两句。gmp编译时只需要标准C工具链nettle需要gmp的头文件gnutls需要nettle和gmp两者。如果顺序不对gnutls的configure会报找不到nettle这就是典型的“依赖顺序错误”问题。另外编译完每个库后临时把/usr/local/samba-deps/lib加进LD_LIBRARY_PATH再继续下一个防止链接时找不到上一层的库文件。我记得当时在安装时曾经漏装了libaio-dev然后在编译samba本体阶段报了一个特别隐蔽的错误——一直提示找不到aio.h排查了半天最后才想起来是基础依赖没装全。后来学聪明了把所有可能需要的基础开发库提前检查一遍zlib1g-dev、libreadline-dev、libpam0g-dev、libacl1-dev、libattr1-dev、libblkid-dev、libgnutls28-dev、libpopt-dev。如果你的离线系统里确实没有这些deb包而apt源也访问不了那就在联网机器上把对应deb下载过来dpkg安装或者干脆绕开这些可选依赖用configure参数显式禁用不需要的功能。3. Samba本体编译安装流程3.1 解压与配置参数依赖库全部安装完之后终于可以进入正题。先把samba源码包解压tar xzf samba-4.19.1.tar.gz cd samba-4.19.1然后创建一个单独的构建目录避免源码树里混入大量编译中间文件samba官方官方推荐用build目录mkdir build cd build接下来是重头戏configure参数。针对纯文件共享场景这是我稳定复现过的配置../configure \ --prefix/opt/samba \ --sysconfdir/etc/samba \ --localstatedir/var \ --with-configdir/etc/samba \ --with-privatedir/var/lib/samba/private \ --with-piddir/var/run/samba \ --with-logfilebase/var/log/samba \ --without-ad-dc \ --without-ldap \ --with-system-talloc \ --with-system-tdb \ --with-system-tevent \ --with-system-ldb \ --bundled-librariesNONE逐个解释一下这些参数的含义。--prefix指定samba的安装根目录我习惯放到/opt/samba方便识别和卸载--sysconfdir和--with-configdir都指向/etc/samba这样smb.conf配置文件就放在系统约定俗成的位置别让它散落到/usr/local目录深处--without-ad-dc是关闭域控功能这一项能省掉大量编译工作--with-system-*告诉编译器去用之前编译好的系统级talloc、tdb、tevent、ldb而不是samba自带的第三方捆绑版本。最关键的是--bundled-librariesNONE。这个参数的意思是强制不使用源码包内嵌的第三方库而是去系统路径和/usr/local/samba-deps里寻找依赖。配合前面提前安装好的依赖这个参数能避免samba源码树内部的库版本和系统库版本相互干扰。如果你编译时缺依赖可以临时去掉这个参数让samba自己用捆绑库但那样会导致依赖库重复编译而且后续升级维护很别扭。3.2 编译与安装configure成功后直接make。首次编译samba即使只编译文件共享相关组件通常也要10到20分钟。中途遇到那个经典的Waf编译系统报错不要慌绝大多数情况是某个依赖没找到回到第2节检查一遍即可。make make install安装完成后检查一下关键文件是否齐全ls -l /opt/samba/sbin/smbd ls -l /opt/samba/sbin/nmbd /opt/samba/sbin/smbd --version这步能正常输出版本号说明samba编译安装成功。注意samba的配置文件目录是/etc/samba但二进制工具都在/opt/samba需要把这个bin目录加入PATH否则后续执行smbpasswd、testparm等命令时会提示command not found。建议改一下全局环境变量echo export PATH/opt/samba/sbin:/opt/samba/bin:$PATH /etc/profile.d/samba.sh source /etc/profile.d/samba.sh3.3 安装后的目录规划与用户准备samba跑起来需要两个基础条件存在系统用户、存在可写权限的共享目录。建议在安装完成后立刻把这些备好。先创建系统用户samba的valid users验证要落到系统用户上useradd -M -s /sbin/nologin smbuser-M表示不创建家目录-s /sbin/nologin表示不允许该用户登录Shell这是一个安全建议。以后凡是需要通过samba访问的人都映射到这个系统账户上。如果要给多个Windows用户分配不同权限可以创建多个系统用户通过samba的组来控制。然后建共享目录mkdir -p /data/share chown -R smbuser:smbuser /data/share chmod -R 0750 /data/share这里权限我故意设成0750而不是0777后面讲配置时会解释原因。Windows客户端看到这个共享后能不能读写不完全取决于这个目录权限还取决于smb.conf里掩码与用户映射的配合这一步先把自己的数据目录权限控制好别一上来就图省事对外开放。4. smb.conf配置与权限模型4.1 文件共享基础配置samba编译安装完成后系统不会自动生成/etc/samba/smb.conf需要自己动手创建。先把最基础的一版写出来[global] workgroup WORKGROUP server string Samba Offline Server security user map to guest Bad User log file /var/log/samba/log.%m max log size 1000 socket options TCP_NODELAY server role standalone server disable netbios no load printers no printing bsd printcap name /dev/null [share] path /data/share browseable yes read only no valid users smbgroup create mask 0660 directory mask 0770 force group smbgroup[global]段的含义是全局生效的配置。workgroup对应Windows同一局域网里的工作组名保持默认WORKGROUP最省事。security user是samba最常用的安全模式用户访问共享时需要提供用户名和密码而这个账户由系统用户samba独立密码组成。map to guest Bad User的含义是如果用户名合法但密码错误就当作guest来映射——注意这只是说“该用户不存在或密码错时映射为guest”并没有开放匿名访问。如果不想要任何匿名访问可以将这个值改为Never。还要注意我关闭了打印机共享这能避免一些无谓的CVE暴露面。工控现场根本用不上samba的打印服务器能关就关。[share]段是具体共享目录的定义。path指向真实目录valid users smbgroup限定了只有smbgroup组里的用户才能访问这个共享比单纯依赖系统目录权限更直观。create mask和directory mask决定了Windows客户端新建文件或目录时对应的权限掩码0660和0770的含义是“用户和组可读写其他人没有权限”。4.2 用户密码与权限控制配置文件写完之后最重要的一个环节就是给系统用户设置samba独立密码。先创建组并把系统用户加进去groupadd smbgroup usermod -a -G smbgroup smbuser然后设置samba密码smbpasswd -a smbuser这里我经常遇到有人问“为什么我明明设置了系统用户密码Windows连的时候还是提示密码错误”。原因是samba的用户认证并不使用系统登录密码而是单独存放在samba自己的密码数据库里默认在/var/lib/samba/private/passdb.tdb。smbpasswd -a这条命令的作用正是把用户和samba密码写入这个库。这一步漏掉后面登录必然失败。如果要管理多个用户可以用pdbedit命令查看当前生效的samba账户pdbedit -L设置密码后再次检查共享目录权限和smb.conf里的valid users是否匹配。正确的顺序是系统用户先存在→加入组→设置samba密码→smb.conf引用该用户或组→testparm检查配置→启动服务。4.3 配置检查与基本验证每次改完smb.conf都要先跑一遍testparm检查语法问题testparm -s /etc/samba/smb.conf如果输出里出现“Load smb config files from /etc/samba/smb.conf”后面没有Error字样说明配置合法。如果有Error它会明确告诉你哪一行有问题。配置没问题后可以在本机先用smbclient验证一下smbclient //localhost/share -U smbuser%你的密码如果进入smb: 交互提示符就说明本机认证和共享服务都正常。接下来再通过Windows的\192.168.1.100访问走到这一步基本就通了。5. 服务托管、开机自启与网络放行5.1 用systemd管理smbd/nmbdsamba源码安装完成后不会自动注册systemd服务需要手动编写unit文件。先创建smbd.service[Unit] DescriptionSamba SMB Daemon Afternetwork.target [Service] Typeforking ExecStart/opt/samba/sbin/smbd -D ExecReload/bin/kill -HUP $MAINPID PIDFile/var/run/samba/smbd.pid LimitNOFILE16384 [Install] WantedBymulti-user.target再创建nmbd.service[Unit] DescriptionSamba NetBIOS Daemon Afternetwork.target [Service] Typeforking ExecStart/opt/samba/sbin/nmbd -D ExecReload/bin/kill -HUP $MAINPID PIDFile/var/run/samba/nmbd.pid [Install] WantedBymulti-user.target然后重载并启动systemctl daemon-reload systemctl start smbd nmbd systemctl enable smbd nmbd启动后用smbclient或Windows测试同时用systemctl status smbd查看服务状态。如果服务起不来赶紧去/var/log/samba/目录看日志这个目录下的log.smbd和log.客户端IP记录了大量排错线索。有个容易忽略的坑是samba默认的pid目录/var/run/samba在重启后可能不存在。如果启动报pid写入失败先手动创建并赋权mkdir -p /var/run/samba chmod 1777 /var/run/samba再重启服务。5.2 防火墙与Windows客户端连接Ubuntu默认可能开着ufw防火墙先把samba需要的端口放行ufw allow 137/udp ufw allow 138/udp ufw allow 139/tcp ufw allow 445/tcp如果平时习惯直接关闭防火墙那当我没说但工控环境不建议裸奔按端口精确放行更稳妥。Windows客户端连接时在资源管理器地址栏输入\服务器IP比如\192.168.1.100然后会弹出认证窗口。如果之前用别的凭据登录过可能缓存了旧密码在Windows命令行执行net use * /delete之后重新连接即可。打开“网络”刷不出机器是一种常见反馈。别惊讶Windows 10和11已经默认关掉了SMB1下的NetBIOS发现跨网段时间还能看到算运气。这不影响直接输IP访问所以不用纠结。6. 常见问题排查与避坑记录6.1 总是提示用户名或密码错误这个现象在离线安装后出现的频率最高而且原因有好几种可能。我把实际排查顺序整理成表格按这个顺序查基本一摸一个准排错步骤操作命令或检查项常见结果1确认系统用户存在useradd后的用户才有效2确认设置了samba密码smbpasswd -a smbuser3确认安全模式不是sharesecurity user4确认valid users没有把该用户排除valid users smbgroup5查看日志tail -f /var/log/samba/log.smbd6检查密码库pdbedit -L还有一个很容易踩的细节是当你在客户端连接时填写的用户名“smbuser”必须和服务器上的系统用户名完全一致大小写都要一样。很多Windows弹窗默认会带上“^”或“.\”如果你的机器加入了某个域环境那么要在用户名前加服务器主机名例如UBUNTU-SVR\smbuser否则samba会把“.\smbuser”解析成错误的用户。如果日志里出现“Failed to start service”或者“NT_STATUS_LOGON_FAILURE”别怀疑日志直接看smbpasswd有没有执行成功。这是90%的情况。6.2 共享目录大小限制如何做离线环境里经常有需求要给共享目录设置容量上限。samba本身没有直接在smb.conf里限制目录大小的参数但可以通过系统层“配额”实现。需要先在/etc/fstab里把目录挂载时启用配额选项然后使用setquota命令限制用户或组的磁盘用量。比如共享目录是/data/share而/data是独立分区可以在fstab里加上usrquota,grpquota挂载选项然后quotacheck -cug /data edquota -u smbuser在交互界面里设置soft和hard限制。设置完成后Windows客户端写入超过配额时会出现“磁盘已满”的提示达到限制目的。这个方法比第三方工具靠谱而且完全离线可用。如果你不想动底层配额也可以在samba层面用allow/deny写出限制但那是对目录权限的控制不是空间大小限制两者不是一个维度的东西。6.3 其他我踩过的坑再记录几个不常见但一踩就耽误半天的坑第一系统时间不同步导致认证异常。samba做Kerberos相关校验时对时间敏感虽然standalone文件共享模式影响不大但如果你开了某些高级选项Windows客户机和服务器的系统时间差了太多会报“访问被拒绝”。解决办法是离线环境里手动校正服务器时间或者配置局域网NTP时间源。第二SMB协议版本兼容问题。Windows XP和Win7默认走SMB1而新版samba里SMB1可能默认关闭。如果现场还有老设备在[global]里显式加一行server min protocol NT1注意这是往下兼容老设备不是鼓励开SMB1能用新协议就不用开。可以根据现场设备情况灵活调整最低协议等级。第三LD_LIBRARY_PATH被清空导致smbd起不来。源码编译安装的东西往往会依赖/usr/local/samba-deps/lib下的so库如果重启后自启动时LD_LIBRARY_PATH还没设置好服务就会报“error while loading shared libraries”。解决办法是在systemd unit文件的[Service]段里加一行EnvironmentLD_LIBRARY_PATH/usr/local/samba-deps/lib这样一个服务单元就能拿到正确环境比全局变量可靠。第四跨网段客户端无法访问。samba默认监听所有网卡但如果服务器有多块网卡可能只监听其中一块。检查/etc/hosts里主机名解析到哪块网卡必要时在[global]段用interfaces和bind interfaces only强制绑定内网业务网卡。第五Windows防火墙弹窗拦截。局域网内访问时Windows自带的防御软件可能把入站连接拦掉但这个问题通常在服务器端或客户端的安全策略软件上被放大。遇到客户端能ping通但无法访问共享的情况先在Windows上关闭防火墙尝试一次如果马上好了就是防守策略拦了SMB端口。最后讲讲升级维护的问题。源码编译安装的samba后续如果要对smb.conf调整直接编辑/etc/samba/smb.conf然后systemctl reload smbd即可不需要重新编译。如果samba要升级新版本必须重新走一遍编译安装流程但因为依赖库不变只需要重新准备samba源码包执行configure、make、make install三步安装目录一样的话配置文件和密码库都不会丢存量共享可以无缝衔接。我个人在实际项目里的体会是离线编译samba这件事真正的难点从来不是samba本身的编译而是整个依赖链的梳理和排错耐心。第一次做最好拿个小本子把每一步的configure输出、编译错误和最终命令都记下来形成自己的安装脚本。这样下次在现场机器上从解压源码到启动共享十分钟左右就能搞定不用再走一遍漫长的排错流程。