在Debian系统的香港服务器中,我如何用“Samba + ACL”治好了多地办公文件的权限混乱问题

那天是个潮湿的周一早上,香港电讯机房的空调把我冻得直打哆嗦。深圳、上海和新加坡三个办公室刚刚完成系统整合,大家的文件全堆在一台旧NAS上:谁都能改,谁都能删;市场部抱怨设计稿经常被“覆盖”,财务说年结文件“又少了一个版本”。我站在机柜前,看着那台不断报警的老NAS,心里只有一句话:得上“可审计、可继承、可回滚”的权限体系。
最后落地方案是:Debian 12 + Samba 4 + POSIX ACL + Btrfs 快照(shadow copy)+ 审计 + 回收站。这篇就是我把它一步步搭起来、踩坑、修坑的完整记录。
1. 现场与目标
环境与目标:
- 机房:香港(HK Equinix)
- 办公点:深圳(SZ)、上海(SH)、新加坡(SG)
- 广域链路:专线/MPLS,香港到深圳/上海 RTT 12–24ms,新加坡 35–45ms
主要诉求:
- 按 部门/项目 控制读写,默认继承权限,能审计谁动过文件
- Windows / macOS 混用无痛
- 版本可回滚(用户看到“以前版本”)
- 异地访问性能尽量靠谱
2. 硬件与网络拓扑(实配)
| 项目 | 型号/规格 | 备注 |
|---|---|---|
| 服务器 | Dell PowerEdge R7525 | 1U,热插拔 |
| CPU | 2 × AMD EPYC 7313 | 16C/32T ×2 |
| 内存 | 256GB DDR4 ECC | 足够缓存 |
| 系统盘 | 2 × 960GB SATA SSD (RAID1) | /, /boot, /var |
| 数据盘 | 6 × 4TB NVMe(RAID10) | LVM 上 Btrfs 子卷 |
| 网卡 | 2 × 25GbE (bonding, LACP) | 上行到核心交换 |
| VLAN | 内网 10.20.0.0/16;管理 10.99.0.0/24 | 与办公网通过防火墙做端口策略 |
| OS | Debian 12 (bookworm) | Samba 4.17+ |
| 时间 | chrony 对时,来源 AD DC/NTP | Kerberos 靠时间很敏感 |
为什么选 Btrfs:原生快照 + reflink,能配合 Samba shadow_copy2 给 Windows 用户“以前版本”。XFS/EXT4 也行,但快照回滚要多点工程。
3. 设计思路(核心要点)
Samba 做 AD 成员服务器(不自建域控):
- 身份/组统一在 AD 管理,Linux 侧用 winbind + idmap rid 映射 UID/GID。
- POSIX ACL 为根,配合 vfs_acl_xattr,权限继承与 Windows 端一致。
- Btrfs 子卷 + snapper 定时快照,Samba 用 shadow_copy2 暴露“以前版本”。
- 回收站 + 审计:vfs_recycle、vfs_full_audit。
- macOS 兼容:vfs_fruit + catia + streams_xattr,减少乱码与 ._ 资源叉问题。
- SMB3 + 多通道:广域条件下尽量稳;签名与加密按“安全优先、性能平衡”开关。
4. 基础准备
# 1) 基础包
apt update
apt install -y samba winbind smbclient cifs-utils acl attr \
krb5-user libpam-winbind libnss-winbind \
chrony snapper btrfs-progs
# 2) 让 /srv 支持 ACL(ext4/xfs/btrfs 默认 xattr/acl 支持,一般无需特别加 -o acl)
mkdir -p /srv/share
时间与 DNS(很关键):
- /etc/chrony/chrony.conf 指向 AD/NTP;
- /etc/resolv.conf 指向 AD DNS(否则 Kerberos/域加入各种玄学故障)。
5. 存储卷与快照(Btrfs + snapper)
我把数据盘做了 RAID10 → LVM VG → 在 LV 上格式化 Btrfs(你也可以直接 Btrfs 直上阵列层)。挂载参数偏稳健:
UUID=xxxx /srv btrfs defaults,ssd,compress=zstd:3,autodefrag,noatime 0 0
子卷结构:
btrfs subvolume create /srv/share/projects
btrfs subvolume create /srv/share/finance
btrfs subvolume create /srv/share/public
# 为每个子卷启用 snapper(会在子卷下创建 .snapshots)
snapper -c projects create-config /srv/share/projects
snapper -c finance create-config /srv/share/finance
# 调整清理与频率(示例:保留7天小时快照、4周每日、6个月每月)
sed -i 's/TIMELINE_CREATE="no"/TIMELINE_CREATE="yes"/' /etc/snapper/configs/projects
sed -i 's/TIMELINE_CREATE="no"/TIMELINE_CREATE="yes"/' /etc/snapper/configs/finance
sed -i 's/TIMELINE_LIMIT_HOURLY="10"/TIMELINE_LIMIT_HOURLY="168"/' /etc/snapper/configs/projects
# ...根据容量政策微调
systemctl enable --now snapper-timeline.timer snapper-cleanup.timer
6. 加入 AD 域(域成员服务器)
假设域是 CORP.EXAMPLE.COM,NetBIOS 名 CORP,并且已经把 DNS 指向 AD。
# 交互时填 realm:CORP.EXAMPLE.COM
dpkg-reconfigure krb5-user
kinit Administrator
# 成功会拿到 TGT,不成功先查时间/DNS
net ads join -U Administrator
# 看到 "Joined 'HOSTNAME' to realm 'CORP.EXAMPLE.COM'" 就对了
systemctl enable --now winbind
把 NSS/PAM 接上(登录/查人可用):
/etc/nsswitch.conf 中 passwd: files systemd winbind、group: files systemd winbind
测试:
getent passwd 'CORP\\alice'
getent group 'CORP\\Design'
7. Samba 全局配置(/etc/samba/smb.conf)
以下配置在 Debian 12 + Samba 4.17 实测可用;不同版本语法可能略有差异。
[global]
workgroup = CORP
security = ADS
realm = CORP.EXAMPLE.COM
# ID 映射(RID 后端简单稳定)
idmap config * : backend = tdb
idmap config * : range = 3000-7999
idmap config CORP : backend = rid
idmap config CORP : range = 10000-999999
# Winbind 行为
winbind use default domain = yes
winbind refresh tickets = yes
winbind enum users = no
winbind enum groups = no
# VFS 与 ACL/扩展属性
vfs objects = acl_xattr catia fruit streams_xattr
inherit permissions = yes
inherit acls = yes
map acl inherit = yes
store dos attributes = yes
ea support = yes
# 协议与安全:SMB3、多通道、签名/加密按需
server min protocol = SMB2_10
server max protocol = SMB3
server signing = if_required
smb encrypt = desired
server multi channel support = yes
# I/O(广域/混合文件类型取平衡)
aio read size = 1
aio write size = 1
use sendfile = yes
strict locking = no
deadtime = 15
# Mac 兼容(资源叉/非法字符)
fruit:metadata = stream
fruit:resource = stream
fruit:locking = none
fruit:encoding = native
# 日志
log level = 1
logging = syslog@1 /var/log/samba/log.%m
8. 共享定义与“以前版本”、回收站、审计
8.1 项目共享(Projects)
[Projects]
path = /srv/share/projects
read only = no
browseable = yes
# 授权策略
create mask = 0660
directory mask = 0770
force group = "CORP\\ProjectUsers"
# 版本与回收
vfs objects = acl_xattr recycle full_audit shadow_copy2 catia fruit streams_xattr
# 回收站
recycle:repository = .recycle/%U
recycle:keeptree = yes
recycle:versions = yes
recycle:exclude = *.tmp,*.bak,~$*
# 以前版本 (shadow copy)
shadow: snapdir = /srv/share/projects/.snapshots
shadow: sort = desc
shadow: format = @GMT-%Y.%m.%d-%H.%M.%S
shadow: localtime = yes
shadow: snapprefix = ''
# 审计(写操作)
full_audit:prefix = %u|%I|%S
full_audit:success = mkdir rmdir write pwrite unlink rename write_data
full_audit:failure = connect
full_audit:facility = LOCAL7
full_audit:priority = NOTICE
8.2 财务共享(Finance)
[Finance]
path = /srv/share/finance
read only = no
browseable = no
create mask = 0660
directory mask = 0770
force group = "CORP\\Finance"
vfs objects = acl_xattr recycle full_audit shadow_copy2 catia fruit streams_xattr
recycle:repository = .recycle/%U
recycle:keeptree = yes
recycle:versions = yes
shadow: snapdir = /srv/share/finance/.snapshots
shadow: format = @GMT-%Y.%m.%d-%H.%M.%S
shadow: sort = desc
shadow: localtime = yes
重启服务:
testparm
systemctl restart smbd nmbd winbind
9. 用 POSIX ACL 固化“谁能做什么”
组设计(在 AD 里建组):
| 组名(AD) | 作用 | 备注 |
|---|---|---|
| CORP\ProjectUsers | 项目区默认工作组 | 新人入职加进来 |
| CORP\Design | 设计子区写 | |
| CORP\PM | PM 子区写 | |
| CORP\Finance | 财务区读/写 | 仅财务共享可见 |
目录与继承(示例):
# 目录属主/组,便于“force group”一致
chown -R root:"CORP\\ProjectUsers" /srv/share/projects
chown -R root:"CORP\\Finance" /srv/share/finance
# 清理遗留 ACL(谨慎执行,确保目标目录正确)
setfacl -R -b /srv/share/projects
setfacl -R -b /srv/share/finance
# 项目区:默认 ProjectUsers 可读写,并继承到新建文件/目录
setfacl -m g:"CORP\\ProjectUsers":rwx /srv/share/projects
setfacl -m d:g:"CORP\\ProjectUsers":rwx /srv/share/projects
setfacl -m m::rwx /srv/share/projects # 调整有效权限掩码(mask)
# 设计子目录更严格
mkdir -p /srv/share/projects/Design
setfacl -m g:"CORP\\Design":rwx /srv/share/projects/Design
setfacl -m d:g:"CORP\\Design":rwx /srv/share/projects/Design
# 财务区:仅 Finance 组
setfacl -m g:"CORP\\Finance":rwx /srv/share/finance
setfacl -m d:g:"CORP\\Finance":rwx /srv/share/finance
# 查看
getfacl /srv/share/projects | sed -n '1,40p'
小贴士:shell 中域名反斜杠要转义:"CORP\\Design"。
关键是 d:(default)条目,保证新建文件/目录自动继承。
10. 客户端体验与校验
Windows:映射 \\files.corp.example.com\Projects,右键 → “以前的版本” 就能看到 Btrfs 快照;权限页能看到 AD 组。
macOS:smb://files.corp.example.com/Projects,fruit+catia 基本消灭了 ._ 和非法字符报错。
Linux:测试:
smbclient //files/Projects -U alice
11. 广域性能与参数取舍(我们的真实数值)
| 场景 | RTT | 带宽 | 大文件(4GB) | 小文件(3万×8KB) |
|---|---|---|---|---|
| 深圳 ↔ 香港 | 14ms | 1Gbps | 110–125 MB/s | 15–25 MB/s |
| 上海 ↔ 香港 | 22ms | 1Gbps | 95–110 MB/s | 12–18 MB/s |
| 新加坡 ↔ 香港 | 38ms | 1Gbps | 70–85 MB/s | 8–12 MB/s |
影响因素与调优经验:
- SMB3 多通道对大文件很有帮助(多网卡/多队列);
- 小文件延时占主导,开启 AIO、确保 DC/DNS/时间稳定,收益有限但能拉齐抖动;
- 签名/加密:我们采用 signing=if_required, encrypt=desired,专线内网 + 边界 IPsec。强制加密会明显掉速,按安全策略取舍;
- 禁止防火墙/UTM 对 445 做“文件防护/反病毒重扫”,不然 R/W 抖到怀疑人生。
12. 审计与回收:谁动了我的文件
我们把 full_audit 送到 LOCAL7,再由 rsyslog 写入独立文件并配一条简单的规则进 Elasticsearch。
- /etc/rsyslog.d/30-samba-audit.conf:
- local7.notice /var/log/samba/audit.log
回收站:用户误删先在 .recycle/<用户名> 找;找不到再去“以前版本”。
13. 常见坑与我当时怎么填的
加入域失败(KDC unreachable)
原因:DNS 没指向 AD,或 UDP 88 被墙。
解法:把 /etc/resolv.conf 指向 AD DNS;临时 dig _kerberos._udp.CORP.EXAMPLE.COM SRV 验证。
Kerberos 票据过期 / 时间漂移
症状:随机认证失败。
解法:chrony 对时,winbind refresh tickets = yes,并监控 klist。
文件权限看着对,实际写不进去
原因:POSIX ACL 的 mask 把有效权限削了。
解法:setfacl -m m::rwx <dir>,再核对 getfacl 里 mask 行。
默认继承没生效
原因:只设了 g:xxx:rwx,没设 d:g:xxx:rwx。
解法:对目录同时设置 default ACL(d:)。
macOS 复制报错 / 名称含 : 等字符
解法:启用 catia;fruit 选项按上面配置。
“以前版本”里空空如也
原因:snapper 未开定时器,或 shadow: snapdir 路径不对。
解法:systemctl status snapper-timeline.timer;确认 .snapshots 在共享根目录下。
性能忽上忽下
原因:安全设备对 445 做 L7 检查。
解法:和网安团队沟通,转策略到边界加密/白名单,不要深度扫描 SMB。
14. 变更与回滚流程(SOP 摘要)
| 步骤 | 操作 | 验证 |
|---|---|---|
| 变更前 | snapper -c projects create -d "pre-change" |
snapper -c projects list |
| 修改 ACL | setfacl ... |
getfacl、Windows 新建文件继承测试 |
| 用户回报异常 | snapper -c projects status <id1>..<id2> |
评估差异 |
| 回滚文件 | Windows “以前版本”或 cp --reflink=auto 从快照拷回 |
二次确认 |
| 审计 | 查 /var/log/samba/audit.log |
归档 |
15. 维护脚本(节选)
批量设置某些子目录只给设计组写:
#!/usr/bin/env bash
set -euo pipefail
BASE=/srv/share/projects
TARGETS=(BrandA BrandB Campaign2025)
for t in "${TARGETS[@]}"; do
d="$BASE/$t/Design"
mkdir -p "$d"
setfacl -m g:"CORP\\Design":rwx "$d"
setfacl -m d:g:"CORP\\Design":rwx "$d"
setfacl -m m::rwx "$d"
done
上线那天,我让三地的代表各自演示:新建、修改、回滚、从回收站找文件。财务的小伙伴点开“以前的版本”,找回了一个月前的表格,笑得很真;设计那边终于不用再在群里问“谁把我图改了”。
机柜里新服务器的风扇还是那么吵,但我心里安静了下来。权限清晰、继承稳定、可审计可回滚,这不是某个“黑科技”,只是把 Samba、ACL 和快照认真地拼起来,按规则运行。
后来有人问我:这套是不是有点“重”?我说,是,但稳定和可追溯,在多地协同里,永远是最轻的成本。