☰
Ajenti auth_users 插件 API 深度解析:基于配置文件的用户认证与权限管理
2026/9/27 21:22:55 网站建设 项目流程
  • 后端
  • 运维

【免费下载链接】ajenti

Ajenti Core and stock plugins

项目地址:https://gitcode.com/gh_mirrors/aj/ajenti
点击查看免费下载

Ajenti 的auth_users插件提供了一套完全独立于操作系统账号的认证方案:用户数据存放在 YAML 配置文件(默认/etc/ajenti/users.yml)中,密码使用 scrypt 算法加盐加密存储,并支持细粒度的权限控制。本文以 aj.plugins.auth-users.api 模块文档 为骨架,结合插件源码 api.py、views.py 与核心框架 auth.py、config.py,完整讲解该认证提供者的核心接口、配置格式、HTTP 管理端点、密码重置机制及权限判定流程,帮助你理解并落地"配置文件驱动"的 Ajenti 用户体系。

认证提供者(AuthenticationProvider)接口概述

在 Ajenti 中,认证体系由AuthenticationProvider接口定义,所有认证方式(操作系统账号、自定义用户等)都是该接口的实现。接口定义位于 auth.py,核心抽象方法如下:

方法职责
authenticate(username, password)校验用户名与密码是否匹配
authorize(username, permission)判定用户是否拥有某权限
get_isolation_uid(username)返回该用户登录后 worker 进程降权所用的 UID
get_isolation_gid(username)返回降权所用的 GID(可返回None)
get_profile(username)返回用户资料(不含密码等敏感字段)
check_mail(mail)根据邮箱反查用户名(用于密码重置)
check_password_complexity(password)密码复杂度校验(当前为占位实现)
update_password(username, password)更新用户密码
prepare_environment(username)登录前的环境准备钩子
signout()登出钩子

auth_users插件通过 jadi 组件的@component(AuthenticationProvider)装饰器注册了自己的实现,其id = 'users'、name = _('Custom users')、pw_reset = True(后者表示该提供者支持自助密码重置)。AuthenticationService.get_provider()会根据主配置auth.provider的值('os'或'users')在AuthenticationProvider.all(context)中选取对应实现,见 auth.py。

启用 users 认证提供者

要让 Ajenti 使用自定义用户认证,需要修改主配置文件(示例见 config.yml)中的auth段:

auth: provider: users # 由默认的 os 切换为 users users_file: /etc/ajenti/users.yml # 自定义用户数据文件位置 emails: {} allow_sudo: true

关键参数说明:

  • provider:认证提供者 id,可选os(操作系统账号)或users(自定义用户);默认值为os,见 config.py。
  • users_file:用户数据文件路径,默认/etc/ajenti/users.yml,见 config.py。
  • emails:邮箱相关配置的占位字典。
  • allow_sudo:是否允许在面板内执行需要 sudo 的提权操作(由核心的check_sudo_password使用,见 auth.py)。

需要注意的是:在 Ajenti 2.1.38 之前,用户数据直接存放在config.yml的auth.users段。若检测到这种旧布局,核心的BaseConfig.ensure_structure()会发出警告并把用户迁移到users_file指定的独立文件,然后从主配置中删除auth.users,实现见 config.py。

用户数据文件格式

auth_users认证提供者读写的是AjentiUsers配置类(定义于 config.py)。该文件的标准结构如下:

users: root: email: root@example.com password: 73637279707400100000000800000001f77e545afaeced51... permissions: core:config:read: true core:config:write: true uid: 0 fs_root: / alice: email: alice@example.com password: 73637279707400100000000800000001d9b05b16f3a84e91... permissions: core:config:read: true uid: 1001 fs_root: /home/alice

字段含义:

字段类型说明
email字符串用户邮箱,用于密码重置时反查用户名
password字符串scrypt 加密后的十六进制哈希串
permissions映射权限 id 到布尔值的映射,false表示明确拒绝
uid整数登录后 worker 降权使用的系统 UID
fs_root字符串该用户可见的文件系统根目录(由上层文件管理器等模块使用)

当文件不存在时,AjentiUsers.load()会记录错误并初始化空结构{'users': {}};文件存在时若以 root 运行,会先把文件权限收紧为0o600再读取(config.py),避免哈希与权限信息泄露。config.yml中旧式内联的auth.users.root.password示例哈希即为 scrypt 格式的十六进制表示。

认证流程:authenticate 与 scrypt 密码校验

UsersAuthenticationProvider.authenticate()(api.py)执行如下流程:

  1. 调用self.context.worker.reload_master_config()重新加载主配置,确保读到最新的用户文件路径;
  2. 将明文密码编码为 UTF-8 字节串;
  3. 若username存在于aj.users.data['users']中,取出其password字段(十六进制串);
  4. 通过scrypt.decrypt(bytes.fromhex(user_hash), password, maxtime=15, encoding=None)校验密码:解密失败抛出scrypt.error,此时记录调试日志并返回False;解密成功返回True;
  5. 用户名不存在时直接返回False。

密码写入侧对应hash_password()(api.py):先以os.urandom(256)生成 256 字节随机盐,再用scrypt.encrypt(salt, password, maxtime=1).hex()生成十六进制哈希。scrypt 的maxtime参数控制计算耗时预算:加密时为 1 秒级,解密校验时为 15 秒级(可放宽到更长耗时以抵御暴力破解)。

update_password()(api.py)负责改写用户密码:对存在的用户重新计算哈希并写回aj.users.data['users'][username]['password'],随后调用aj.users.save()落盘,返回True;用户不存在则返回False。

授权判定:authorize 与权限解析

authorize(username, permission)(api.py)的逻辑非常简洁:

return aj.users.data['users'].get(username, {}).get('permissions', {}).get(permission['id'], permission['default'])

即:优先读取用户permissions映射中该权限 id 的显式布尔值;若未配置,则回退到权限定义自带的default默认值。权限 id 与默认值由各插件的PermissionProvider提供,例如核心插件在 main.py 中注册了core:config:read与core:config:write两个权限。

运行时的权限检查由authorize装饰器/上下文管理器完成(auth.py):它收集所有PermissionProvider提供的权限列表,找到与请求匹配的权限后调用当前提供者的authorize()判定,未通过则抛出SecurityError('Forbidden: permission "..." is required')。

HTTP 管理端点

插件通过HttpPlugin暴露了三个 REST 端点,全部定义在 views.py 中,且均要求进程以 root(os.getuid() == 0)运行,否则返回403:

端点方法权限说明
/api/auth-users/password/<username>POST仅 root设置指定用户的密码
/api/auth-users/configGETroot +core:config:read读取整个用户配置文件内容
/api/auth-users/configPOSTroot +core:config:write保存(合并)用户配置并重新加载

各端点的具体行为:

  • 设置密码:handle_api_set_password()(views.py)把请求体(明文密码)经hash_password()哈希后写入aj.users.data,并立即aj.users.save()落盘;写前会先reload_master_config()保证基于最新配置操作。
  • 读取配置:handle_api_users_get_config()(views.py)在core:config:read授权通过后重新加载主配置并返回aj.users.data(即整个users映射)。
  • 保存配置:handle_api_users_set_config()(views.py)在core:config:write授权通过后,把请求体 JSON 解析出来执行aj.users.data.update(data)合并,保存后重新加载主配置并返回最新数据。

这些端点与前端 Angular 资源(resources/js/services/users.service.es、resources/js/controllers/index.controller.es,声明于 plugin.yml)配合,构成管理界面中"用户管理"页面的数据通道。

用户隔离与资料

get_isolation_uid(username)(api.py)返回用户配置中的uid;若该用户未设置uid,则回退到主配置restricted_user(默认nobody)对应的系统 UID。get_isolation_gid()恒返回None,表示不改变 GID。登录时AuthenticationService.login()(auth.py)会先调用prepare_environment()(此处为空实现),然后据 UID/GID 调用worker.demote()降权执行后续操作,从而实现"以受限身份运行面板逻辑、以配置 UID 隔离用户"。

get_profile(username)(api.py)返回该用户的配置副本,并弹出password字段,避免敏感哈希泄露给上层调用方。

密码重置(pw_reset)

由于该提供者声明pw_reset = True,核心的密码重置中间件(pwreset.py)会启用/api/master/send_password_reset、/api/master/check_password_serial、/api/master/update_password三个端点:

  1. 用户提交邮箱后,send_password_reset()调用self.auth_provider.check_mail(mail)(即插件的check_mail(),见 api.py)遍历用户表反查用户名;命中后使用/etc/ajenti/.secret中的密钥生成带时间戳的序列号,并把重置链接发送到对应邮箱。
  2. check_serial()使用URLSafeTimedSerializer.loads(serial, max_age=900)校验链接有效性,序列号 15 分钟(900 秒)后过期。
  3. update_password()校验序列号后调用当前提供者的update_password(user, password),由插件完成哈希更新与落盘(见上文)。

由于该链路依赖邮件发送,使用时需要同时配置主配置中的email段与trusted_domains(用于构造重置链接的 base URL,见 pwreset.py)。

用户级配置存储:UserAuthConfig

除认证提供者外,插件还通过@component(UserConfigProvider)注册了UserAuthConfig(api.py),id = 'users'。它负责每个已认证用户的私有配置存储:

  • 存储目录为~{os_user}/.config/ajenti_auth_users(其中os_user是运行 Ajenti 的系统用户名);
  • 每个用户对应一个userconfig_{username}.yml文件;
  • load()使用 YAML SafeLoader 读取;文件不存在时初始化为{};
  • harden()将文件权限收紧为stat.S_IRWXU(仅属主可读写执行);
  • save()以 UTF-8、allow_unicode=True方式安全序列化后写盘并调用harden()加固权限。

UserConfigService.get_provider()(config.py)会按auth.provider匹配对应的UserConfigProvider,即当认证切换为users时,用户的个人配置也自动走UserAuthConfig这条独立存储路径,与os提供者的~/.config/ajenti.yml(见 config.py)形成对照。

侧边栏入口

插件在 main.py 中通过SidebarItemProvider注册了侧边栏入口:挂在category:general分类下,id 为auth_users,名称"Users",图标users,点击跳转/view/auth-users。管理员从该入口进入用户管理界面,即可通过前文所述的 HTTP 端点完成用户的增删改查与权限分配。

  • 后端
  • 运维

【免费下载链接】ajenti

Ajenti Core and stock plugins

项目地址:https://gitcode.com/gh_mirrors/aj/ajenti
点击查看免费下载
上一篇:OpenMAIC 首页提示持久化不可用(配置了 NEXT_PUBLIC_PERSISTENCE 后)怎么排查?
下一篇:roothide生态系统详解:Bootstrap与Patcher工具如何彻底改变iOS越狱体验

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询