权限模型
SPC(系统权限控制)概述
TOS 7 引入 SPC(System Permission Control,系统权限控制) 系统,遵循最小权限原则,对应用的系统访问行为进行管控:
- 应用无法直接修改系统文件或获取 root 权限,所有权限请求需通过平台 API 提交
- 开发者需在权限声明中明确应用的权限需求,应用只有经平台审核通过后才能获得对应的访问权限
- 禁止任何绕过 SPC 权限检查的行为,此类应用将无法通过审核或被下架
概述
TOS7 遵循 最小权限原则。应用只能请求运行所必需的最小权限。TOS7 应用与 SPC(系统权限控制) 系统交互。应用必须:
- 在权限声明(第 8 节)中声明权限需求
- 不得绕过 SPC 权限检查
- 使用平台 API 进行权限请求,而不是直接修改系统文件
平台为 Deb 和 Docker 应用提供结构化的权限模型。
用户与用户组模型
严禁应用用户使用 root 权限。所有应用必须以非 root 专属用户运行。Deb 应用:
| 场景 | 用户 | 说明 | 配置要求 |
|---|---|---|---|
| 专属用户 | <appid> | 必须使用。平台在安装时自动创建该用户(非 root 权限)并分配 UID。 | 强制 |
强制要求: 所有 Deb 应用必须将
config.ini中的user字段设置为专属用户名(如<appid>)。平台会在安装时创建对应的系统用户,应用以该用户权限运行,严禁使用 root 权限。重要: 开发者不得在生命周期脚本(如
preinst)中手动创建该用户。开发者在这方面的唯一职责是:在config.ini中设置用户名,并确保它与 systemd 服务文件中的User=字段一致。
Docker 应用:
| 场景 | 用户 | 说明 |
|---|---|---|
| 非 root | UID:GID(如 1000:1000) | 必须使用。在 compose 中通过 user 字段指定。 |
文件系统权限
Deb 应用的标准目录权限:
| 路径 | 归属 | 权限 | 说明 |
|---|---|---|---|
/usr/local/<appid>/ | <appid>:<appid> | 755 | 应用目录(服务只读) |
/usr/local/<appid>/bin/ | <appid>:<appid> | 755 | 可执行文件 |
/usr/local/<appid>/config/ | <appid>:<appid> | 750 | 配置文件(服务只读) |
/usr/local/<appid>/site/ | <appid>:<appid> | 755 | Web UI 文件 |
/usr/local/<appid>/data/ | <appid>:<appid> | 750 | 运行时数据(读写) |
/usr/local/<appid>/logs/ | <appid>:<appid> | 750 | 应用日志(读写) |
注意 1: 上述路径为你的应用所使用的路径。安装后,平台会将
/usr/local/<appid>/映射到用户所选卷上,该同一目录在物理上位于/Volume<N>/@apps/<appid>/。*是代表该卷号的文档记法(例如 Volume1、Volume2)—— 平台不会展开它,因此严禁将/Volume*/…写入 docker-compose.yml、systemd unit、生命周期脚本或任何会被机器读取的配置。
注意 2: 应用二进制和配置文件对服务用户应为只读。只有数据和日志目录应为可写。
注意 3:数据类型
- 运行时数据(
/usr/local/<appid>/data/)—— 应用生成的缓存、临时文件和运行时状态。此数据由应用管理,可安全重新生成。- 用户数据 —— 持久化的业务数据(文档、照片、数据库)。
- Deb 应用:将其存储在通过
ter_share_add创建的共享文件夹中,以便用户通过 SMB/NFS 访问。- Docker 应用:不使用共享文件夹。其持久化业务数据保存在容器的卷挂载中,平台会将其解析到
/Volume<N>/DockerAppData/<appid>/(参见第 6 节)。
网络权限
| 权限 | Deb 应用 | Docker 应用 | 说明 |
|---|---|---|---|
| 绑定端口 | 在服务配置中绑定指定端口 | 在 compose 中映射端口 | 不得与系统端口冲突 |
| 访问本地服务 | 默认允许 | 使用 network_mode: host 或显式链接 | 尽量减少网络暴露 |
| 出站连接 | 允许 | 允许 | 出站无限制 |
共享文件夹访问
适用范围: 本节仅适用于 Deb 应用。Docker 应用不参与 TNAS 共享文件夹机制:平台不会为其创建共享文件夹,它们不得调用
ter_share_add或声明share_folders,其所有持久化业务数据都存储在容器的卷挂载中,平台会将其解析到应用数据根目录/Volume<N>/DockerAppData/<appid>/(参见第 9 章第 3 节与第 12.2 节)。
TNAS 共享文件夹是 Deb 应用主要的数据访问机制。需要访问用户数据的应用必须:
- 创建共享文件夹,通过
ter_share_add:
ter_share_add -name <appid>-data -owner <appid>
- 或请求访问现有共享文件夹,通过加入
allusers组:
usermod -aG allusers <appid>
Docker 应用: 它们不使用共享文件夹。其持久化业务数据存储在容器的卷挂载中,平台会将其解析到
/Volume<N>/DockerAppData/<appid>/(参见第 12.2 节)。Docker 包中不得包含共享文件夹挂载项或ter_share_add调用。
应用不得直接修改共享文件夹权限。使用 TOS 共享文件夹管理 API 或让用户手动配置访问权限。
权限请求流程
当 Deb 应用需要访问共享文件夹时:
-
专用应用文件夹(推荐):
- 在 postinst 中通过
ter_share_add创建 - 应用拥有完整的读写权限
- 无需用户授权
- 在 postinst 中通过
-
用户共享文件夹(需要授权):
- 应用请求
allusers组成员资格 - 用户通过 TOS 共享文件夹设置授权文件夹访问
- 应用在权限声明中注明只读或读写需求
- 应用请求
-
声明格式:共享文件夹及其访问模式在 README 权限表中声明(参见第 8 节),例如
共享文件夹:<文件夹名称>(只读)。Docker 应用始终声明共享文件夹:无—— 它们不使用共享文件夹。
系统资源限制
应用安装路径
第三方应用完整安装在 存储卷(数据盘)上,而不是系统盘(/)。你的应用通过逻辑路径 /usr/local/<appid>/ 使用该位置;平台会将其解析到用户安装时选择的卷上的 /Volume<N>/@apps/<appid>/。
*仅为文档记法,代表用户安装时选择的卷号(例如 Volume1、Volume2)。平台不会展开它 —— 严禁将/Volume*/…写入 docker-compose.yml、systemd unit、生命周期脚本或任何会被机器读取的配置。- 所有应用文件——包括二进制文件、配置文件、日志、脚本和 Web UI 文件——均存放在应用目录中,你的应用看到的是
/usr/local/<appid>/。 - 系统盘上仅保留轻量级的 注册/入口记录(用于 TOS 识别已安装的应用)。此记录占用空间可忽略不计,不构成任何容量问题。
- 只有系统内置应用才驻留在系统盘(
/usr/local/system_app_data/)上。
对第三方开发者而言: 由于整个应用(包括程序文件、配置和日志)安装在数据盘上,系统盘容量对您的应用不是问题。所有业务数据也应存储在数据盘(
/Volume*/)上,数据盘没有容量限制。
按应用类型的默认资源配额:
| 应用类型 | CPU 限制 | 内存限制 | 示例 |
|---|---|---|---|
| 媒体服务器 | 200%(2 核) | 2048M | Jellyfin, Plex, Emby |
| 下载管理器 | 100%(1 核) | 512M | Aria2, qBittorrent |
| 实用工具 | 50% | 256M | 文件管理器, 文本编辑器 |
| Web 服务 | 100%(1 核) | 512M | CMS, 博客, Wiki |
| 数据库 | 200%(2 核) | 2048M | MySQL, PostgreSQL, Redis |
| 安全类 | 50% | 256M | 防火墙, 杀毒软件 |
以上为平台默认值。开发者可在权限声明中附合理理由申请更高的限制。
Deb 应用(通过 systemd):
[Service]
# 内存限制
MemoryMax=512M
# CPU 配额(200% = 2 核)
CPUQuota=200%
# 文件描述符限制
LimitNOFILE=65536
# 进程数限制
LimitNPROC=256
Docker 应用(通过 compose):
services:
myapp:
deploy:
resources:
limits:
cpus: '2.0'
memory: 512M
reservations:
cpus: '0.5'
memory: 128M
权限声明
注意: 下表是一个 示例,展示如何记录应用的权限需求。请将表中的值(端口号、文件路径、用户名)替换为应用实际使用的值。
为保持透明,应用应在 README.md 中记录其权限需求:
| 权限 | 理由 |
|---|---|
网络:端口 <你的端口> | Web UI 访问 |
文件系统:<你的数据路径> | 运行时数据存储 |
用户:<你的 appid>(系统用户) | 隔离的服务执行 |
| 共享文件夹:无 | 不需要用户数据访问 |
Docker 应用始终声明
共享文件夹:无:它们不创建也不挂载任何 TNAS 共享文件夹,其所有持久化数据都存储在应用数据根目录/Volume<N>/DockerAppData/<appid>/中。
权限红线(自动驳回)
以下权限请求将导致 自动驳回:
| 违规行为 | 说明 |
|---|---|
| Root 执行 | 请求 root 用户运行应用(包括在 systemd 服务文件中设置 User=root,以及在 Docker 中不指定 user 字段(默认以 root 运行)两种情况) |
| 特权模式 | 请求 --privileged Docker 模式 |
| 系统目录写入 | 请求写入 /etc/、/usr/、/boot/ 等系统目录 |
| 跨应用数据访问 | 请求访问其他应用的数据目录 |
| 无限制网络访问 | 未提供书面合理理由而申请 network_mode: host(仅系统级网络工具可申请) |
| 过多端口暴露 | 请求超过功能所需的端口数量 |