NAS 的数据备份、权限管理及文件共享
tags: NASNAS 的数据备份、权限管理及文件共享
成品 NAS 系统的这些功能比较完善,因此这篇文章基于 Linux 进行讲解。每个人对于自己存储、权限的规划都有所不同,本文仅供参考。
管理面板 Cockpit
Cockpit 整体体验不算好,但总体来说,Cockpit 的界面比较友好,该有的功能也还算齐全,使用过程中虽然会遇到一些小问题,但整体堪用。
Fedora Server 已经自带了 Cockpit 管理面板

为了使用容器功能、文件管理功能和文件共享功能,需要安装插件:
# podman 插件
sudo dnf install cockpit-podman cockpit-files
文件共享插件需要根据 45Drives/cockpit-file-sharing 这个仓库手动安装
这个插件其实就是 SMB 和 NFS 配置文件的图形化管理,你完全可以选择手动写配置,效果没有区别
Cockpit 是白名单制度,/etc/cockpit/cockpit.conf 中定义了允许访问的域名,需要按照自己的需求更改,例如,你至少需要添加允许内网访问的域名和反向代理后的域名。
系统基本配置
这个 NAS 系统的整体逻辑如下:
- 一切从简
- 仅部署需要与机械硬盘大量交互的服务,其他服务应该交给前面创建的 LXC docker 专用容器
- 需要与显卡交互的应用应单独开启 LXC 容器部署
关闭 SE Linux
编辑 SELinux 配置文件 sudo vim /etc/selinux/config
找到 SELINUX=enforcing 并更改为 SELINUX=disabled,保存并退出文件,重启系统

ddns-go
略
SSH 配置
Ed25519 是 基于 Edwards 曲线的椭圆曲线数字签名算法(ECDSA),由 Daniel J. Bernstein 等人设计,是目前最安全、高效的数字签名算法之一,广泛用于 SSH、TLS、区块链(如比特币、Monero)、密码学工具等场景。
Ed25519 密钥对体积小,但安全性比 RSA 高一个数量级。因此建议使用 Ed25519 密钥对来进行访问
创建 ed25519 的密钥对
ssh-keygen -t ed25519 -C "你的备注(如邮箱)"
生成成功后,查看密钥文件:
# 查看公钥(可复制到服务器 ~/.ssh/authorized_keys)
cat ~/.ssh/id_ed25519.pub
# 查看私钥(切勿泄露给他人)
cat ~/.ssh/id_ed25519
将 .pub 公钥文件上传到服务器,核对一下权限
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pub
chmod 600 ~/.ssh/authorized_keys
简单介绍一下这些权限的逻辑,.ssh 文件夹是完全属于某用户的文件夹,因此所有者对它有完全访问权限,其他人无任何权限,设为 700。~/.ssh/id_ed25519 中保存私钥,被盗走影响被盗者的安全,所以不应被除你以外的任何人看见,因此为 600。~/.ssh/id_ed25519.pub 是公钥,本身就是公开的,这份文件被人盗走,威胁的是偷盗人的安全,因此可以被任何人读取,644。
SMB/NFS 共享
图形界面管理,一目了然,实际上就是更改了 samba 和 NFS 的配置文件。
但有一个小 bug,如果你不是使用 root 用户登录,那么第二次访问 samba 管理页面时可以会出现报错

这是由于普通用户没有权限读取 samba 配置文件导致的,不影响文件共享本身的功能,你可以选择修复权限或者无视。
❯ sudo testparm
Load smb config files from /etc/samba/smb.conf
Loaded services file OK.
Weak crypto is allowed by GnuTLS (e.g. NTLM as a compatibility fallback)
Server role: ROLE_STANDALONE
Press enter to see a dump of your service definitions
# Global parameters
[global]
registry shares = Yes
idmap config * : backend = tdb
[1000]
path = /HUH1/1000
read only = No
[public]
path = /HUH1/public/
read only = No
❯ testparm
Load smb config files from /etc/samba/smb.conf
regdb_init: Failed to open registry /var/lib/samba/registry.tdb (权限不够)
Failed to initialize the registry: WERR_ACCESS_DENIED
error initializing registry configuration: SBC_ERR_BADFILE
Error loading services.
不加 sudo,普通用户也无法执行 testparm 命令。
Webdav 共享
OpenList 不仅可以将网盘挂载为 Webdav,还可以把本地目录挂载为 Webdav
使用一键安装脚本
curl -fsSL https://res.oplist.org/script/v4.sh > install-openlist-v4.sh && sudo bash install-openlist-v4.sh
安装之后手动修改一下 OpenList 的服务文件,添加 User=admin,否则你上传的文件权限所有者为 root:root:
❯ cat /etc/systemd/system/openlist.service
[Unit]
Description=OpenList service
Wants=network.target
After=network.target network.service
[Service]
Type=simple
User=admin
WorkingDirectory=/opt/openlist
ExecStart=/opt/openlist/openlist server
KillMode=process
[Install]
WantedBy=multi-user.target
最后在 OpenList 中添加本机存储,实现 webdav 功能。

存储结构管理与文件备份
存储结构
我自用的存储结构,借鉴了飞牛的存储结构,并进行了简化,几个用户的话完全可以满足需求,但用的人多了还是需要使用更专业的方法管理。
/HUH/ # ext4 本地存储根目录(主磁盘挂载点)
├─ 1000 # 用户1000的私有文件(仅本人访问)
├─ 1001 # 用户1001的私有文件(仅本人访问)
└─ public # 公共共享区(所有用户可访问)
/ST/ # btrfs 备份存储路径
├─ 1000 # 子卷:用户1000的私有文件(仅本人访问)
├─ 1001 # 子卷:用户1001的私有文件(仅本人访问)
└─ public # 子卷:公共共享区(所有用户可访问)
└─ 备份 # 文件备份区
其中,1000、1001 的所有者为本人,权限为 700 或者 755。public 的权限可以设为 1000:100(100 是 Users 组),权限 775,只要加入 Users 组的用户都可以访问这个文件夹。
对于我来说,这样的结构已经够用了。对了,/ST/ 下面还有一个快照文件夹
drwx------ 1 root root 20 11月16日 19:12 snapshots
这个文件夹很重要,因此仅 root。
备份
使用 rsync+crontab 的方式进行备份
备份脚本:
#!/bin/bash
# 备份脚本(默认 dry-run 模式,添加 --run 参数执行正式备份)
# 使用前务必通过默认的 dry-run 测试,确认路径和权限正确
# 添加到 crontab 时建议使用 --run 参数,并输出日志到文件
# 定义统一的备份根路径(所有备份的目标路径都基于此)
BACKUP_ROOT="/ST/public/备份/"
# 定义备份路径数组:每行为 "源路径 目标子路径 [额外参数]"
# 说明:目标子路径会自动拼接在 BACKUP_ROOT 后面
declare -a backup_items=(
# 本地目录备份(格式:源路径 目标子路径)
"/home/admin/.local/share/qBittorrent/ qBittorrent/"
# 远程 PVE 备份(格式:源路径 目标子路径 额外参数)
"[email protected]:/var/lib/vz/dump/ pve_dump/ -z --progress -e ssh"
)
# 初始化 rsync 参数(默认包含 --dry-run)
rsync_args=("-av" "--delete" "--dry-run")
# 检查是否传入 --run 参数,若是则移除 --dry-run
if [[ "$1" == "--run" ]]; then
echo "⚠️ 已启用正式备份模式(无 dry-run)"
rsync_args=("-av" "--delete") # 移除 --dry-run
else
echo "ℹ️ 运行在 dry-run 模式(仅测试,不实际备份)"
echo "ℹ️ 如需正式备份,请添加 --run 参数:$0 --run"
fi
echo
# 遍历数组执行备份
for item in "${backup_items[@]}"; do
# 拆分源路径、目标子路径和额外参数(支持空格路径)
read -r src sub_dest opts <<<"$item"
# 拼接完整目标路径(BACKUP_ROOT + 目标子路径)
dest="${BACKUP_ROOT}${sub_dest}"
echo "===== 开始备份: $src -> $dest ====="
# 执行 rsync(拼接固定参数和额外参数)
rsync "${rsync_args[@]}" $opts "$src" "$dest"
echo "===== 备份结束: $src -> $dest ====="
echo
done
echo "✅ 所有备份任务执行完成"
if [[ "$1" != "--run" ]]; then
echo "ℹ️ 提示:本次为 dry-run 测试,未实际修改文件。正式备份请运行:$0 --run"
fi
保存为 backup.sh,给可执行权限 chmod +x backup.sh,运行 ./backup 为测试运行,./backup --run 为正式运行,每次修改脚本,一定要先测试运行,确保路径配置无误之后,再添加 --run 参数。
快照
快照脚本,我使用的是保留最近三份快照的策略,需要别的策略可以让 AI 帮助修改。
#!/bin/bash
# 需要创建快照的源目录列表(添加新目录直接补充即可)
SOURCE_DIRS=(
"/ST/public/"
"/ST/1000/"
# 示例:添加新目录
# "/path/to/another/dir"
)
# 所有快照的根目录
SNAPSHOTS_ROOT="/ST/snapshots"
# 生成时间戳(同一批次快照共用)
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
# 遍历每个源目录
for src in "${SOURCE_DIRS[@]}"; do
# 提取目录名作为快照子目录
dir_name=$(basename "$src")
# 快照存放路径
snap_path="${SNAPSHOTS_ROOT}/${dir_name}/snapshot_${TIMESTAMP}"
# 创建快照目录
mkdir -p "$(dirname "$snap_path")"
# 创建只读快照
btrfs subvolume snapshot -r "$src" "$snap_path"
# 仅保留最近3份快照,删除更早的
# 列出该目录下所有快照(按名称排序,时间戳升序)
snapshots=($(ls -1 "${SNAPSHOTS_ROOT}/${dir_name}/" | grep '^snapshot_' | sort))
# 计算需要删除的快照数量(总数 - 3)
delete_count=$(( ${#snapshots[@]} - 3 ))
# 如果需要删除(总数超过3)
if [ $delete_count -gt 0 ]; then
# 循环删除最旧的N个快照
for ((i=0; i<delete_count; i++)); do
old_snap="${SNAPSHOTS_ROOT}/${dir_name}/${snapshots[$i]}"
btrfs subvolume delete "$old_snap"
done
fi
done
权限
这个用得比较少,主要用在某些 docker 容器,由于映射的关系把目录权限、文件权限搞乱的情况。如果你懂得如何配置 docker 权限,懂得如何编辑 systemd 服务文件,懂得使用 podman 代替 docker,那么一般不会遇到这种问题。
这两个脚本都不能直接使用,需要仔细阅读修改里面的配置,如果你不懂 Linux 权限,请不要轻易使用。
setup_perms.sh:
#!/bin/bash
# 通用权限配置脚本:支持用户目录(750模板)和公用目录(775模板)
# 使用示例:
# 用户目录:sudo ./setup_perms.sh /path/1001 user1001 group1001 user
# 公用目录:sudo ./setup_perms.sh /path/share admin users public
# 参数检查
if [ $# -ne 4 ]; then
echo "用法: $0 <目录路径> <所有者> <所属组> <类型(user/public)>"
exit 1
fi
DIR="$1"
OWNER="$2"
GROUP="$3"
TYPE="$4"
# 目录存在性检查
[ ! -d "$DIR" ] && echo "目录 $DIR 不存在" && exit 1
# 基础配置:所有者/组
chown -R "$OWNER:$GROUP" "$DIR"
# 按类型设置权限模板
if [ "$TYPE" = "user" ]; then
# 用户目录模板(750)
PERM="750"
DEF_ACL="u::rwx,g::r-x,o::---"
elif [ "$TYPE" = "public" ]; then
# 公用目录模板(775)
PERM="775"
DEF_ACL="u::rwx,g::rwx,o::r-x"
# 确保所有者在所属组内
groups "$OWNER" | grep -q "$GROUP" || usermod -aG "$GROUP" "$OWNER"
else
echo "类型错误,仅支持user或public" && exit 1
fi
# 设置基础权限
chmod -R "$PERM" "$DIR"
# 递归设置setgid(目录)
find "$DIR" -type d -exec chmod g+s {} \;
# ACL配置
setfacl -k "$DIR" # 清除旧默认ACL
setfacl -d -m "$DEF_ACL" "$DIR" # 设置默认ACL
setfacl -m "$DEF_ACL" "$DIR" # 同步当前ACL
# 子目录递归应用ACL
find "$DIR" -type d -exec setfacl -d -m "$DEF_ACL" {} \;
find "$DIR" -type d -exec setfacl -m "$DEF_ACL" {} \;
echo "目录 $DIR 已按 $TYPE 模板配置完成"
reqpair_perms.sh:
#!/bin/bash
# 定义目录路径(可根据实际情况增删)
USER_DIRS=(
"/HUH/1000"
"/ST/1000"
)
PUBLIC_DIRS=(
"/HUH/public"
"/ST/public"
)
# 修复用户目录权限(1000:1000,目录750,文件644)
for dir in "${USER_DIRS[@]}"; do
if [ -d "$dir" ]; then
echo "修复用户目录: $dir"
sudo chown -R 1000:1000 "$dir"
sudo find "$dir" -type d -exec chmod 750 {} \; # 用户目录限制组权限
sudo find "$dir" -type f -exec chmod 644 {} \;
else
echo "警告: 用户目录 $dir 不存在,跳过"
fi
done
# 修复公用目录权限(1000:100,目录775,文件664)
for dir in "${PUBLIC_DIRS[@]}"; do
if [ -d "$dir" ]; then
echo "修复公用目录: $dir"
sudo chown -R 1000:100 "$dir"
sudo find "$dir" -type d -exec chmod 775 {} \; # 公用目录开放组权限
sudo find "$dir" -type f -exec chmod 664 {} \;
# 确保目录setgid位生效(新文件自动继承users组)
sudo find "$dir" -type d -exec chmod g+s {} \;
else
echo "警告: 公用目录 $dir 不存在,跳过"
fi
done
echo "权限修复完成"