配置 S3 存储
配置 S3 存储
本页介绍如何将 AWS S3 以及 S3 兼容对象存储(MinIO、Apache Ozone S3 网关、Ceph RGW 等)配置为 Polaris 目录的存储后端。在 AWS S3 上,所有读写操作均通过凭证发放(credential vending)完成:Polaris 通过 STS 假定客户的 IAM 角色,并向客户端返回限定作用域的短期凭证。IAM 角色、其信任策略以及存储桶本身都必须在创建目录之前配置好。
本页仅涉及 Polaris 原生认证。Polaris 也支持外部身份提供方,但此处暂不涉及;下面的配置模式在其他方面保持不变。
涉及的 IAM 身份
S3 凭证发放流程涉及三个不同的 IAM 身份。将它们混为一谈是导致创建目录或加载表时出现 AccessDenied 错误的最常见原因。
| # | 身份 | 使用方 | 用途 |
|---|---|---|---|
| 1 | Polaris 服务身份 | Polaris 服务器进程 | 对 Polaris 发出的每个 sts:AssumeRole 请求进行签名 |
| 2 | 目录访问角色 | Polaris(假定该角色) | 持有目录存储桶上实际的 S3 / KMS 权限 |
| 3 | 发放的凭证 | Iceberg 客户端(Spark/Trino/PyIceberg) | Polaris 在加载表时返回的短期会话密钥 |
在运行时,客户端调用 Polaris 加载表,Polaris 以身份 1 的身份、针对身份 2 对 sts:AssumeRole 请求进行签名,AWS STS 返回限定作用域的临时凭证(身份 3),客户端使用这些凭证直接与 S3 和 KMS 通信。
身份 1 在 Polaris 部署时配置一次。身份 2 按目录创建,其 ARN 在创建目录时注册。身份 3 在每次加载表时生成,从不持久化。
Polaris 服务身份
Polaris 服务器本身需要一个 AWS 身份,以便针对目录访问角色调用 STS。该身份在 Polaris 之外配置——通过标准的 AWS SDK 凭证链——并且与任何目录无关。
选择与部署方式相匹配的发现机制:
- EKS / IRSA — 将 IAM 角色 ARN(
eks.amazonaws.com/role-arn)以注解形式添加到 Polaris 的 ServiceAccount 上。Pod 会收到一个投影令牌,并自动用它换取 STS 凭证。 - EC2 — 将 IAM 实例配置文件附加到 EC2 实例上。SDK 会从 IMDS 读取凭证。
- 静态凭证 — 在 Polaris 容器的环境中设置
AWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEY,可选设置AWS_SESSION_TOKEN和AWS_REGION。仅适用于本地开发。
任何其他参与标准 AWS SDK 凭据链的 AWS 计算环境也应该同样可用,尽管上述模式是我们已经验证过的。
无论采用哪种机制,最终的身份只需要一项权限即可与 STS 通信:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::123456789012:role/polaris-warehouse-access"
}
]
}Resource 应列出 Polaris 预期代入的每一个目录访问角色;只有当角色名称遵循由 AWS 账户所有者严格控制的命名约定时,才应使用通配符(例如 arn:aws:iam::123456789012:role/polaris-*)。
该身份的 ARN 就是目录访问角色的信任策略必须信任的 Principal——详见下一节。一个常见的错误是:只更新了目录角色的权限,却没有同时将 Polaris 服务身份加入其信任策略;其表现是即使目录角色拥有正确的 S3 权限,STS:AssumeRole 仍然返回 AccessDenied。
关于在 EC2 上实际使用该身份的端到端部署示例,请参阅 在 AWS 上部署 Polaris。
目录访问角色与信任策略
当客户端请求凭据时,Polaris 会通过 STS 代入一个由客户管理的 IAM 角色。该角色必须:
- 为支撑该目录的存储桶及前缀授予对象访问所需的操作(
s3:GetObject、s3:PutObject、s3:DeleteObject、s3:ListBucket,以及在使用加密时相关的kms:*操作)。 - 信任 Polaris 服务主体——通常是 Polaris 服务器运行所使用的 IAM 角色。如果配置了
externalId,Polaris 会在sts:AssumeRole请求中附带该值,信任策略必须接受相同的外部 ID。
对于跨账户或托管式 Polaris 部署,建议使用 externalId 以缓解混淆代理人(confused deputy)问题。一个最小化的信任策略如下所示:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::123456789012:role/polaris-server" },
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": { "sts:ExternalId": "polaris-prod" }
}
}
]
}目录存储配置
创建目录时,请提供角色 ARN、区域和 externalId。下方 Authorization 头部中的令牌是从 /api/catalog/v1/oauth/tokens 获取的 Polaris 管理员承载令牌(关于如何初始化并签发管理员令牌,请参阅为生产环境配置 Polaris)。
curl -X POST https://<polaris-host>/management/v1/catalogs \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"catalog": {
"type": "INTERNAL",
"name": "warehouse_s3",
"properties": { "default-base-location": "s3://warehouse-bucket/prod/" },
"storageConfigInfo": {
"storageType": "S3",
"roleArn": "arn:aws:iam::123456789012:role/polaris-warehouse-access",
"externalId": "polaris-prod",
"region": "us-east-1"
}
}
}'角色 ARN 会按照 AwsStorageConfigurationInfo 所强制的模式进行校验;格式不正确的 ARN 将在创建 catalog 时被拒绝。
使用 KMS 进行服务端加密
当存储桶使用 SSE-KMS 时,需要同时提供 currentKmsKey(Polaris 写入时应使用的密钥)和 allowedKmsKeys(该 catalog 被允许从中读取的所有密钥)。这两个字段在 AwsCredentialsStorageIntegration 中是独立处理的,因此如果你希望通过凭证分发(vended credentials)读取写入密钥,就必须把该密钥也包含在 allowedKmsKeys 中:
"storageConfigInfo": {
"storageType": "S3",
"roleArn": "...",
"region": "us-east-1",
"currentKmsKey": "arn:aws:kms:us-east-1:123456789012:key/aaaa-bbbb",
"allowedKmsKeys": [
"arn:aws:kms:us-east-1:123456789012:key/aaaa-bbbb",
"arn:aws:kms:us-east-1:123456789012:key/cccc-dddd"
]
}IAM 角色的策略必须包含针对 currentKmsKey 的 kms:GenerateDataKey 和 kms:Decrypt 权限,以及针对 allowedKmsKeys 中列出的每个密钥的 kms:Decrypt 权限;同时,每个密钥策略都必须向该角色主体授予相同的权限。
如果部署不使用 KMS,请将 kmsUnavailable 设置为 true,这样 Polaris 就不会请求与 KMS 相关的会话权限:
"kmsUnavailable": trueS3 兼容端点
Polaris 可以指向 S3 兼容的对象存储(MinIO、Ceph RGW、Apache Ozone S3 网关)。可用字段如下:
endpoint—— Polaris 及其客户端应当调用的 S3 API 端点。endpointInternal—— 可选,当集群内端点与返回给客户端的端点不同时,由 Polaris 服务器使用。pathStyleAccess—— 对于不支持虚拟主机风格寻址的后端,设置为true。stsEndpoint—— STS 端点;未设置时,先回退到endpointInternal,再回退到endpoint。stsUnavailable—— 当后端未实现 STS 时,设置为true。
本页开头所述的凭证颁发保证以后端实现 STS 为前提。对于 AWS S3 以及暴露了 STS API 的 S3 兼容后端(例如 MinIO),请保持 stsUnavailable 未设置(或设为 false),上述凭证颁发流程即可原样使用。
"storageConfigInfo": {
"storageType": "S3",
"endpoint": "https://s3.internal.example.com",
"pathStyleAccess": true,
"region": "us-east-1"
}对于不支持 STS 的 S3 兼容后端(如 Apache Ozone 的 S3 网关,或未启用 STS 的 Ceph RGW),请设置 stsUnavailable: true。此时 Polaris 将完全跳过子作用域凭据发放,客户端必须去掉 X-Iceberg-Access-Delegation: vended-credentials 请求头,并直接向对象存储进行认证。Polaris 关于 Apache Ozone 和 Ceph 的指南展示了这种模式的完整流程。
"storageConfigInfo": {
"storageType": "S3",
"endpoint": "https://s3.internal.example.com",
"pathStyleAccess": true,
"stsUnavailable": true,
"region": "us-east-1"
}客户端配置
引擎通过 Iceberg REST API 进行连接,并让 Polaris 在表加载时动态签发凭据;当 STS 可用时,它们无需静态 AWS 凭据。
Spark 示例,与现有 MinIO / RustFS 指南中使用的属性名称保持一致:
bin/spark-sql \
--packages org.apache.iceberg:iceberg-spark-runtime-3.5_2.12:1.10.1,org.apache.iceberg:iceberg-aws-bundle:1.10.1 \
--conf spark.sql.extensions=org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions \
--conf spark.sql.catalog.polaris=org.apache.iceberg.spark.SparkCatalog \
--conf spark.sql.catalog.polaris.type=rest \
--conf spark.sql.catalog.polaris.uri=https://<polaris-host>/api/catalog \
--conf spark.sql.catalog.polaris.oauth2-server-uri=https://<polaris-host>/api/catalog/v1/oauth/tokens \
--conf spark.sql.catalog.polaris.token-refresh-enabled=false \
--conf spark.sql.catalog.polaris.warehouse=warehouse_s3 \
--conf spark.sql.catalog.polaris.scope=PRINCIPAL_ROLE:ALL \
--conf spark.sql.catalog.polaris.credential=<client-id>:<client-secret> \
--conf spark.sql.catalog.polaris.header.X-Iceberg-Access-Delegation=vended-credentials推荐配置 oauth2-server-uri:如果不配置,Iceberg REST 客户端会回退到硬编码的 /v1/oauth/tokens 路径,并输出一条弃用警告,因为这一自动回退行为计划在未来的 Iceberg 版本中移除。
对于 Trino,请使用 Iceberg 连接器配合 REST catalog。REST/OAuth2 相关属性用于与 Polaris 通信,而 Polaris 会在加载表的响应中一并下发端点、路径风格标志和区域信息以及限定范围的凭据(s3.endpoint、s3.path-style-access、client.region),因此客户端无需重复配置这些内容。不过,Trino 端仍然需要启用原生 S3 文件系统:
connector.name=iceberg
iceberg.catalog.type=rest
iceberg.rest-catalog.uri=https://<polaris-host>/api/catalog
iceberg.rest-catalog.warehouse=warehouse_s3
iceberg.rest-catalog.security=OAUTH2
iceberg.rest-catalog.oauth2.credential=<client-id>:<client-secret>
iceberg.rest-catalog.oauth2.scope=PRINCIPAL_ROLE:ALL
iceberg.rest-catalog.oauth2.server-uri=https://<polaris-host>/api/catalog/v1/oauth/tokens
iceberg.rest-catalog.vended-credentials-enabled=true
fs.native-s3.enabled=true对于 PyIceberg,使用 rest 目录类型。相同的 Polaris 端属性(uri、warehouse、credential、scope、oauth2-server-uri)同样适用,并且代发凭证(vended credential)请求头必须作为 REST 请求头转发:
from pyiceberg.catalog.rest import RestCatalog
cat = RestCatalog(
name="polaris",
**{
"uri": "https://<polaris-host>/api/catalog",
"warehouse": "warehouse_s3",
"credential": "<client-id>:<client-secret>",
"scope": "PRINCIPAL_ROLE:ALL",
"oauth2-server-uri": "https://<polaris-host>/api/catalog/v1/oauth/tokens",
"header.X-Iceberg-Access-Delegation": "vended-credentials",
},
)Polaris 会在加载表时将发放的 S3 属性(s3.access-key-id、s3.secret-access-key、s3.session-token)返回给客户端,因此不应在 PyIceberg 侧配置静态凭据。
验证配置
在不向客户端提供任何长期有效的 AWS 凭据的情况下,应能成功完成端到端测试:
CREATE NAMESPACE warehouse_s3.demo;
CREATE TABLE warehouse_s3.demo.t (id BIGINT, name STRING) USING iceberg;
INSERT INTO warehouse_s3.demo.t VALUES (1, 'hello');
SELECT * FROM warehouse_s3.demo.t;如果 INSERT 或 SELECT 返回 403 失败,最常见的原因有:
- IAM 角色的信任策略与 Polaris 提供的
roleArn/externalId不匹配。 - 该角色授予了 S3 权限,但缺少针对
currentKmsKey所需的 KMS 操作权限。 - 存储桶策略拒绝了来自特定 VPC 端点之外的访问。
Polaris 会在 debug 级别记录担任角色的 STS 请求,这是确认当前提供了哪个身份的最快方式。
评论
登录后参与评论
KnowForge