ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Read the Docs 构建器安全之 AWS STS 临时凭证:最小化构建产物存储的访问范围

Read the Docs 构建器安全之 AWS STS 临时凭证:最小化构建产物存储的访问范围 后端文档【免费下载链接】readthedocs.orgThe source code that powers readthedocs.org项目地址https://gitcode.com/gh_mirrors/re/readthedocs.org点击查看免费下载本文基于 Read the Docs 源码仓库中的 aws-temporary-credentials 设计文档 展开解释为什么构建器Builder不应该长期持有可以访问全部 S3 资源的静态凭证以及如何通过 AWS STSSecurity Token Service为每一次构建生成按桶、按路径、按项目、按版本收窄权限的临时凭证。读完本文你将掌握 STS 临时凭证在构建流水线中的完整 IAM 配置方法、本地开发环境的接入步骤以及 readthedocs/aws/security_token_service.py 中三类存储凭证的生成逻辑与调用链。为什么构建器不能直接使用全局 AWS 凭证Read the Docs 的构建器会在 Docker 沙箱中执行由用户提供的任意命令。官方设计文档docs/dev/aws-temporary-credentials.rst明确指出Docker 沙箱不应该成为唯一的防线因为平台在某些操作上仍然需要在容器之外与用户生成的文件打交道。一旦构建器进程持有能访问账号内所有 S3 资源的全局凭证任何沙箱逃逸、命令注入或凭证泄露都可能把影响面扩大到整个账号的存储层。因此平台的策略是不直接向构建器发放全局凭证而是经由 AWS STS 生成临时凭证temporary credentials这些凭证短期有效并且被限定在构建实际需要的资源范围内。从源码结构看这一策略的落地点是 readthedocs/aws/ 模块下的 security_token_service.py其模块文档注释与上述设计文档一一对应并额外说明了 STS 的一个关键性质临时凭证的最终权限是角色策略与AssumeRole 时传入的内联策略的交集——内联策略只能进一步收紧权限不能扩大权限。启用 STS 所需的 IAM 配置要让一套 AWS 账号支持 STS 临时凭证需要按设计文档准备以下三块 IAM 资源创建一个 IAM 角色Role其受信任实体类型trusted entity type设置为将要用于生成临时凭证的 AWS 账号为该角色创建内联策略inline policy策略应允许访问所有将要使用的 S3 桶与路径为将要调用AssumeRole的用户创建内联策略策略应允许sts:AssumeRole与sts:TagSession两个动作且作用对象就是第 1 步创建的角色。其中第 3 步里的sts:TagSession不是可有可无的平台会利用会话标签session tags把哪个项目、哪个版本打进临时凭证进而在策略里用${aws:PrincipalTag/...}变量引用下文会看到具体用法。本地开发环境接入本地开发时通过 docs/dev/settings.rst 中 AWS configuration 一节列出的环境变量配置 AWS 凭证并务必把RTD_S3_PROVIDER设为AWS。该组变量在 readthedocs/settings/docker_compose.py 中被读取环境变量用途RTD_S3_PROVIDER存储后端提供商AWS或默认的rustfsRTD_AWS_ACCESS_KEY_ID/RTD_AWS_SECRET_ACCESS_KEY调用 STS 的基础凭证默认回退为admin/password供 rustfs 使用RTD_AWS_STS_ASSUME_ROLE_ARN要 Assume 的角色 ARN映射到AWS_STS_ASSUME_ROLE_ARN设置RTD_S3_MEDIA_STORAGE_BUCKET构建产物media桶名RTD_S3_BUILD_COMMANDS_STORAGE_BUCKET构建命令日志桶名RTD_S3_BUILD_TOOLS_STORAGE_BUCKET构建工具桶名RTD_S3_BUILD_UPLOADS_STORAGE_BUCKET用户上传产物桶名RTD_S3_STATIC_STORAGE_BUCKET静态资源桶名RTD_AWS_S3_REGION_NAMES3 区域名是否启用 STS在代码中由设置项USING_AWS控制其实现是 readthedocs/settings/base.py 中的一个属性S3_PROVIDER AWS时返回True。设计文档还给出了两条重要的团队约定如果你是开发团队成员可以直接使用storage-dev用户的凭证——它已经配置好可以使用 STS——并配合builder-dev角色的 ARN只有在测试 AWS 集成时才应使用 AWS本地开发请使用默认的 rustfs 提供商。否则多名开发者共用同一套凭证时互相写入的桶文件可能相互覆盖。核心实现readthedocs/aws/security_token_service.py数据结构与 STS 客户端模块用两个 dataclass 承载凭证AWSTemporaryCredentialsaccess_key_id、secret_access_key、session_tokensecurity_token_service.py#L40-L46AWSS3TemporaryCredentials在此基础上追加bucket_name与region_name方便构建器直接用返回体配置 S3 客户端security_token_service.py#L49-L54。get_sts_client()security_token_service.py#L57-L63用settings.AWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEY、AWS_S3_REGION_NAME构造 boto3 的sts客户端。_get_scoped_credentials临时凭证生成的统一入口所有具体凭证类型都汇聚到_get_scoped_credentials(*, session_name, policy, duration, tags)security_token_service.py#L66-L128其中封装了 AWS 的若干硬性限制与一个关键的本地回退逻辑回退全局凭证仅限本地当USING_AWS为False时函数不请求 STS直接返回AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY两个全局设置值session_tokenNone。但这一路径要求DEBUGTrue若非 DEBUG 环境则抛出ValueError(Not returning global credentials, AWS STS should always be used in production.)——即生产环境永远走 STS会话名截断session_name被截取到 64 字符这是 AWS 对RoleSessionName的长度限制策略压缩policy通过json.dumps(policy, separators(,, :))压缩为单行字符串因为内联策略压缩后不能超过 2048 字符会话标签限制tag 的 key 最长 128 字符、value 最长 256 字符且可以在策略条件中引用实际调用assume_role(RoleArnsettings.AWS_STS_ASSUME_ROLE_ARN, RoleSessionName..., Policy..., Tags..., DurationSeconds...)任何异常都会被记录日志并转抛为AWSTemporaryCredentialsErrorsecurity_token_service.py#L36-L37。三类构建存储凭证模块对外暴露三个函数分别对应构建过程中三类不同的 S3 访问需求1. 构建产物桶build media读写删按项目/版本前缀收窄get_s3_build_media_scoped_credentials(*, build, duration60 * 15)security_token_service.py#L131-L217生成对构建产物桶的读、写、删凭证。其策略生成过程有几点值得注意存储前缀来自version.get_storage_paths()即该版本实际会使用的目录如html/project/latest、pdf/project/latest等用会话标签替代 slug函数先把路径中的{project.slug}/{version.slug}替换为${aws:PrincipalTag/Project}/${aws:PrincipalTag/Version}。原因是当项目/版本 slug 很长时逐字写进策略可能撞破 2048 字符上限用标签引用则长度固定security_token_service.py#L153-L163对象级动作允许s3:GetObject、s3:PutObject、s3:DeleteObject资源是arn:aws:s3:::{bucket}/{prefix}/*s3:ListBucket作用于桶级资源arn:aws:s3:::{bucket}因为该动作本身无法限定到路径只能用Condition.StringLike[s3:prefix]条件把列举范围约束在上述前缀内security_token_service.py#L184-L197会话名为rtd-{build.id}-{project.slug}-{version.slug}并附带Project、Version两个标签正好被策略中的PrincipalTag变量引用默认有效期 15 分钟AWS 允许的最小值最大值由角色决定默认 1 小时。测试文件 readthedocs/aws/tests/test_security_token_service.py#L71-L110 固化了最终策略的完整形态对readthedocs-media桶资源为html/、pdf/、epub/、htmlzip/、json/、diff/六种前缀下{Project}/{Version}/*而针对 PR 等外部版本version.type EXTERNAL所有前缀会额外加上external/test_security_token_service.py#L124-L194说明 PR 构建的产物被隔离在独立的external/命名空间下。2. 构建工具桶build tools整桶只读get_s3_build_tools_scoped_credentialssecurity_token_service.py#L220-L267生成对S3_BUILD_TOOLS_STORAGE_BUCKET的只读凭证。由于构建工具桶本身是公开可读的策略不限制到具体路径只授予s3:GetObject与s3:ListBucket资源为bucket_arn和bucket_arn/*。该路径不使用会话标签Tags[]由 test_security_token_service.py#L242-L248 断言确认。3. 上传桶build uploads只读精确到单个对象get_s3_build_uploads_scoped_credentialssecurity_token_service.py#L270-L318从 Django 的storages[build-uploads]后端正向取得桶名策略只允许读取当前构建上传的那个 zip 文件对象资源被精确限制为arn:aws:s3:::{bucket}/{build.uploaded_artifacts_storage_path}。测试断言中的形态为arn:aws:s3:::build-uploads/{project.slug}/{build.id}/artifacts.ziptest_security_token_service.py#L325-L348会话名相应简化为rtd-{build.id}-{project.slug}。构建器如何拿到这些凭证credentials/storageAPI 端点临时凭证并非在构建进程内直接生成而是通过构建器专用的 Build API 下发。BuildViewSet.credentials_for_storage动作readthedocs/api/v2/views/model_views.py#L404-L444定义了完整的调用链端点 URL 为credentials/storage仅接受 POST权限类为HasBuildAPIKey——即必须持有当前构建的构建器 API Key请求体中的type字段选择凭证类型build_media、build_tools、build_uploads分别路由到上文三个get_s3_*_scoped_credentials函数非法类型返回 400{error: Invalid storage type}三种类型的有效期统一设为30 分钟源码注释解释了依据30 minutes should be enough for uploading build artifacts / downloading build tools / downloading the zip with the build artifactsmodel_views.py#L419-L430比_get_scoped_credentials的 15 分钟默认值更宽裕正好落在 STS 允许的最小/最大区间内生成失败捕获AWSTemporaryCredentialsError时返回 500{error: Failed to generate temporary credentials}成功时把 dataclass 经asdict展开为字典响应体形如{s3: {access_key_id: ..., secret_access_key: ..., session_token: ..., region_name: ..., bucket_name: ...}}。集成测试 readthedocs/rtd_tests/tests/test_api.py#L190-L252 验证了该端点确实按type调用对应的 STS 函数并以 30 分钟有效期生成凭证。端到端行为总结把设计文档与源码串起来一次构建的凭证生命周期是构建器用 Build API Key 调用POST .../credentials/storage声明所需类型build_media/build_tools/build_uploads视图按类型选择对应的策略生成函数基于build.project/build.version或uploaded_artifacts_storage_path计算出前缀收窄的 IAM 内联策略并用会话标签引用项目/版本 slug_get_scoped_credentials以服务端全局凭证AWS_ACCESS_KEY_ID等调用sts:AssumeRole策略经压缩后随请求下发最终权限 角色策略 ∩ 内联策略构建器拿到最多 30 分钟有效的临时凭证只能触碰本次构建名下的 S3 前缀过期后凭证自动失效即使构建器进程被攻破泄露的凭证也无法访问其他项目或其他版本的数据本地开发USING_AWSFalse且DEBUGTrue时跳过 STS直接返回全局设置值配合 rustfs 模拟存储即可完整调试除真实 STS 之外的全部逻辑。延伸阅读设计文档原文docs/dev/aws-temporary-credentials.rst环境变量清单AWS configuration 一节docs/dev/settings.rstSTS 凭证生成实现readthedocs/aws/security_token_service.py策略快照与外部版本external/前缀断言readthedocs/aws/tests/test_security_token_service.pyUSING_AWS与AWS_STS_ASSUME_ROLE_ARN设置定义readthedocs/settings/base.py本地 compose 环境对RTD_S3_PROVIDER的读取与 rustfs 回退readthedocs/settings/docker_compose.py。赞分享后端文档【免费下载链接】readthedocs.orgThe source code that powers readthedocs.org项目地址https://gitcode.com/gh_mirrors/re/readthedocs.org点击查看免费下载相关推荐如何为 MinIO STS 配置 etcd 以集中存储临时凭证如何为 MinIO STS 配置 etcd 以集中存储临时凭证 如果你的 MinIO 要对外签发临时凭证Security Token ServiceSTS后端存储对象存储分布式存储云原生Velero 使用 AWS 临时凭证时遇到 S3 存储桶访问问题的解决方案Velero 使用 AWS 临时凭证时遇到 S3 存储桶访问问题的解决方案 问题背景 在使用 Helm 部署 Velero 时配置了 AWS 临时凭证包含云原生灾备存储后端如何通过 STS Assume API 为 MinIO 获取临时访问凭证如何通过 STS Assume API 为 MinIO 获取临时访问凭证 在客户端应用或外部服务需要访问 MinIO 对象资源但不希望把长期凭证写进应用代码后端存储对象存储分布式存储云原生上一篇Files.md 为什么从 ctime 换成 mtime云盘同步背后的硬核教训下一篇如何在Chrome与Safari中运行TermKitWebKit终端应用的终极指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表