释放小七猫的分享欲

NAS 的数据备份、权限管理及文件共享

tags: NAS
@ 27/11/2025

NAS 的数据备份、权限管理及文件共享

成品 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 系统的整体逻辑如下:

  1. 一切从简
  2. 仅部署需要与机械硬盘大量交互的服务,其他服务应该交给前面创建的 LXC docker 专用容器
  3. 需要与显卡交互的应用应单独开启 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 "权限修复完成"