当知识库接入 Amazon Q 后,所有被索引的文档都可能出现在聊天回答里。如果 S3 桶里同时存放了全员可见的规章制度和仅限高管查看的薪酬方案,不加权限过滤就等于把锁拆了。Amazon Q Business 的 S3 知识库支持文档级访问控制列表(ACL),让每条文档自带"谁能看"的标签,聊天和自动化工作流都会自动遵守这套规则。
ACL 的工作原理
Amazon Q 在索引 S3 文档时,会读取每条文档的元数据字段 x-amz-meta-DocumentACL。该字段存储一个 JSON 字符串,列出允许访问该文档的用户或组。检索阶段,Amazon Q 会把当前请求者的身份与文档 ACL 做交集——命中才返回内容,未命中则完全忽略该文档,用户不会看到任何片段或摘要。
关键点:
- ACL 是在索引时写入的,不是查询时动态计算。修改 ACL 后需要重新同步知识库才能生效。
- ACL 同时约束聊天界面和自动化工作流(如插件调用、API 查询),一处配置全局生效。
- 未设置 ACL 的文档默认对所有已授权用户可见——相当于"公开"。
配置步骤与示例
下面用一个最小场景演示:S3 桶 my-kb-docs 里有两份文档,一份全员可见,一份仅限 hr-leaders 组。
1. 为文档打上 ACL 元数据
上传文档时,在 S3 对象元数据中写入 ACL:
# 全员可见的员工手册——不设 ACL,或设为允许所有人
aws s3api put-object \
--bucket my-kb-docs \
--key handbook.pdf \
--body handbook.pdf
# 仅限 hr-leaders 组的薪酬报告
aws s3api put-object \
--bucket my-kb-docs \
--key compensation-2024.pdf \
--body compensation-2024.pdf \
--metadata '{"DocumentACL":"{\"groups\":[\"hr-leaders\"]}"}'
x-amz-meta-DocumentACL是 S3 用户自定义元数据的约定键名。上传时--metadata中的键会自动加上x-amz-meta-前缀,所以你只需写DocumentACL。
ACL JSON 支持的字段:
{
"users": ["arn:aws:identitystore::d-1234567890:user/abc123"],
"groups": ["hr-leaders", "finance-admin"]
}
users:IAM Identity Center 用户的 ARN 或唯一标识。groups:Identity Center 中的组名或组 ID。- 两者可同时使用,任一匹配即允许访问。
2. 创建或更新知识库并同步
在 Amazon Q Business 控制台创建知识库,数据源选择 S3,指向 my-kb-docs 桶。创建完成后触发同步:
aws qbusiness start-data-source-sync-job \
--application-id app-xyz \
--data-source-id ds-abc \
--index-id idx-def
同步过程中,Amazon Q 会读取每个对象的 x-amz-meta-DocumentACL,将权限信息写入索引。
3. 验证 ACL 是否生效
用两个不同身份的用户分别提问,观察返回内容差异:
# hr-leaders 组成员查询
aws qbusiness chat \
--application-id app-xyz \
--user-id "arn:aws:identitystore::d-1234567890:user/hr-admin-001" \
--query-text "2024年薪酬调整方案是什么"
# 普通员工查询
aws qbusiness chat \
--application-id app-xyz \
--user-id "arn:aws:identitystore::d-1234567890:user/eng-002" \
--query-text "2024年薪酬调整方案是什么"
预期结果:前者能拿到薪酬报告中的具体内容,后者只会收到员工手册中的泛化信息,或者被告知"未找到相关信息"。
批量打标签的实用脚本
实际场景中文档数量远不止两份。下面是一个用 boto3 批量回写 ACL 的脚本,按前缀路径区分公开与受限文档:
import boto3
import json
s3 = boto3.client("s3")
BUCKET = "my-kb-docs"
# 定义路径与 ACL 的映射规则
ACL_RULES = {
"public/": None, # 公开文档,不设 ACL
"hr-confidential/": {"groups": ["hr-leaders"]},
"finance-internal/": {"groups": ["finance-admin", "c-suite"]},
}
def apply_acl():
paginator = s3.get_paginator("list_objects_v2")
for page in paginator.paginate(Bucket=BUCKET):
for obj in page.get("Contents", []):
key = obj["Key"]
for prefix, acl in ACL_RULES.items():
if key.startswith(prefix) and acl is not None:
# 复制对象自身以更新元数据(S3 不支持原地改 metadata)
acl_str = json.dumps(acl)
s3.copy_object(
Bucket=BUCKET,
Key=key,
CopySource={"Bucket": BUCKET, "Key": key},
Metadata={"DocumentACL": acl_str},
MetadataDirective="REPLACE",
)
print(f"Applied ACL to {key}: {acl_str}")
break
if __name__ == "__main__":
apply_acl()
运行前确认:
public/前缀下的文档不需要 ACL,脚本会跳过。copy_object使用MetadataDirective="REPLACE",会替换所有现有元数据。如果对象还有其他自定义元数据字段,需要先把它们一并写入Metadata参数,否则会丢失。- 脚本执行后,在 Amazon Q 控制台重新同步数据源。
需要注意的边界与取舍
| 方面 | 说明 |
|---|---|
| 生效延迟 | ACL 在索引时固化,修改后必须重新同步。频繁变动权限的场景要评估同步耗时(通常数分钟到数十分钟)。 |
| 元数据丢失风险 | copy_object + REPLACE 会覆盖原有元数据。务必在脚本中保留非 ACL 的自定义字段。 |
| 未设 ACL = 公开 | 如果某份敏感文档漏打了标签,它对所有用户可见。建议用前缀或标签策略做兜底检查。 |
| 组名 vs 组 ID | ACL 中填组名更易读,但组重命名后需重新同步。填组 ID(如 Identity Center 的 GroupId)更稳定,但可读性差。 |
| 删除文档 | 从 S3 删除对象后同步,索引会移除该文档,ACL 自然失效。但仅修改 ACL 而不删除对象时,旧索引条目仍在——必须同步刷新。 |
上线前的检查清单
- 分类梳理:按业务需求将文档分为公开、组级受限、个人级受限三类,明确每类的 ACL 模板。
- 路径规划:用 S3 前缀(如
public/、hr-only/)组织文档,便于批量脚本匹配。 - 元数据备份:批量回写 ACL 前,导出现有自定义元数据,避免
REPLACE误删。 - 身份对齐:确认 Amazon Q 应用已接入 IAM Identity Center,用户/组的 ARN 与 ACL 中填写的标识一致。
- 同步验证:每次修改 ACL 后触发同步,再用不同身份的测试账号做问答验证,确认受限文档确实不可见。
- 监控告警:在 CloudWatch 中监控同步任务状态,失败时及时通知,避免权限变更悬空。
文档级 ACL 不是一次性配置,而是需要随组织架构和数据分类持续维护的机制。把它当作知识库的"门禁卡系统"来运营,才能让 Amazon Q 在开放问答的同时守住敏感边界。