MinIO X.509 客户端证书 STS 认证(AssumeRoleWithCertificate)完整指南:配置、认证流程与源码解析
本文聚焦 MinIO 提供的自定义 STS API——AssumeRoleWithCertificate:它允许客户端使用 X.509/TLS 客户端证书完成身份认证,并换取临时的 S3 访问凭证(AccessKey / SecretKey / SessionToken)。文中将给出完整的启用配置、curl 请求与响应示例、证书到 S3 策略的映射规则、凭证有效期约束以及对应的源码实现证据,帮助你直接在项目里落地一套不依赖任何外部组件即可持续可用的证书级认证方案。
概述:为什么选择证书认证
与 MinIO 的其他 STS 认证方式(例如 OpenID Connect、LDAP/AD)不同,基于客户端证书的认证不依赖任何必须持续在线的外部组件。身份校验所需的全部信息都封装在客户端出示的 X.509 证书里,因此从可用性(availability)与运维复杂度(operational complexity)两个维度看,证书认证往往更优:只要 MinIO 服务器与客户端之间能建立 TLS 连接,认证链路就始终可用。
从仓库源码可以看到,该功能在 IAM 子系统中有一个独立的配置子系统 identity_tls(对应配置常量 IdentityTLSSubSys),其核心配置类型定义在 internal/config/identity/tls/config.go。该 API 默认是关闭的,必须显式启用。
启用与配置 TLS STS API
通过环境变量启用
MinIO 的 TLS STS API 可通过标准配置 API(mc admin config set/get)配置,也可以通过以下环境变量开启(二者均可读写该功能):
# 启用 X.509/TLS 证书 SSO 支持(默认关闭,必须显式置为 on)
export MINIO_IDENTITY_TLS_ENABLE=on
# 是否跳过对客户端证书的校验(on|off),默认 off 表示“校验”
export MINIO_IDENTITY_TLS_SKIP_VERIFY=off
这两个环境变量的常量定义位于 internal/config/identity/tls/config.go:
MINIO_IDENTITY_TLS_ENABLE:控制 X.509 TLS STS API 是否启用。未设置时该 API 保持禁用。MINIO_IDENTITY_TLS_SKIP_VERIFY:控制 MinIO 在签发临时凭证前是否校验客户端证书。默认始终校验;该选项只应在调试或测试环境打开——一旦跳过校验,任意客户端都可能凭自造证书拿到任意策略(包括 admin 权限)的临时凭证。
通过 mc 命令查看帮助与配置
对应文档给出了用 mc 查看该项配置全部参数的命令与输出,其中 ARGS 段就是可配置项:
mc admin config set myminio identity_tls --env
输出形如:
KEY:
identity_tls enable X.509 TLS certificate SSO support
ARGS:
MINIO_IDENTITY_TLS_SKIP_VERIFY (on|off) trust client certificates without verification. Defaults to "off" (verify)
在源码层面,该子系统的配置键名为 skip_verify,默认值正是 "off",见 internal/config/identity/tls/config.go 中的 DefaultKVS:
var DefaultKVS = config.KVS{
config.KV{Key: skipVerify, Value: "off"},
}
Lookup() 函数(internal/config/identity/tls/config.go)会先把运行环境变量与 K/V 配置系统合并:若 MINIO_IDENTITY_TLS_ENABLE 为空则返回默认的禁用态配置;否则依次解析 Enabled 与 InsecureSkipVerify。这说明配置变更的生效路径是「环境变量 / mc admin config set → Lookup → 更新全局 IAM 配置」。
配置在启动时的装载与告警
在 MinIO IAM 初始化流程中(cmd/iam.go),系统会调用 xtls.Lookup(...) 装载该配置并写入 globalIAMSys.STSTLSConfig。特别值得注意的是:当检测到 MINIO_IDENTITY_TLS_SKIP_VERIFY=on 时,启动日志会打印一条 warning,明确提示“在生产环境中不建议开启”:
if stsTLSConfig.InsecureSkipVerify {
iamLogIf(ctx, fmt.Errorf("Enabling %s is not recommended in a production environment", xtls.EnvIdentityTLSSkipVerify), logger.WarningKind)
}
调用示例:获取临时 S3 凭证
MinIO 暴露的自定义 S3 STS 端点格式为 Action=AssumeRoleWithCertificate。客户端必须向
https://<host>:<port>?Action=AssumeRoleWithCertificate&Version=2011-06-15
发送 HTTP POST 请求。由于认证与授权完全依托 X.509 证书,请求必须走 TLS,并且必须携带客户端证书(--key 指定私钥,--cert 指定证书公钥)。curl 示例如下:
curl -X POST --key private.key --cert public.crt "https://minio:9000?Action=AssumeRoleWithCertificate&Version=2011-06-15&DurationSeconds=3600"
请求成功后将返回一个与 AWS STS 2011-06-15 命名空间一致的 XML 响应,其中包含临时凭证三元组 AccessKeyId / SecretAccessKey / SessionToken 以及过期时间 Expiration:
<?xml version="1.0" encoding="UTF-8"?>
<AssumeRoleWithCertificateResponse xmlns="https://sts.amazonaws.com/doc/2011-06-15/">
<AssumeRoleWithCertificateResult>
<Credentials>
<AccessKeyId>YC12ZBHUVW588BQAE5BM</AccessKeyId>
<SecretAccessKey>Zgl9+zdE0pZ88+hLqtfh0ocLN+WQTJixHouCkZkW</SecretAccessKey>
<Expiration>2021-07-19T20:10:45Z</Expiration>
<SessionToken>eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhY2Nlc3NLZXkiOiJZQzEyWkJIVVZXNTg4QlFBRTVCTSIsImV4cCI6MTYyNjcyNTQ0NX0.wvMUf3w_x16qpVWgua8WxnV1Sgtv1jOnSu03vbrwOMzV3cI4q3_9WZD9LwlP-34DTsvbsg7gCBGh6YNriMMiQw</SessionToken>
</Credentials>
</AssumeRoleWithCertificateResult>
<ResponseMetadata>
<RequestId>169339CD8B3A6948</RequestId>
</ResponseMetadata>
</AssumeRoleWithCertificateResponse>
上述响应结构的 Go 定义位于 cmd/sts-datatypes.go 的 AssumeRoleWithCertificateResponse,其 XML 命名空间 https://sts.amazonaws.com/doc/2011-06-15/ 与请求参数 Version=2011-06-15 严格对应。
请求参数补充说明(参数名定义见 cmd/sts-handlers.go):
| 参数 | 说明 |
|---|---|
Action |
固定为 AssumeRoleWithCertificate |
Version |
固定为 2011-06-15 |
DurationSeconds |
希望凭证生效的时长(秒),可选 |
TokenRevokeType |
可选,用于将凭证纳入会话吊销管理(写入 JWT claims) |
准备一张可用的客户端证书
文档给出了一张自签证书示例,其关键特征包括:
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 35:ac:60:46:ad:8d:de:18:dc:0b:f6:98:14:ee:89:e8
Signature Algorithm: ED25519
Issuer: CN = consoleAdmin
Validity
Not Before: Jul 19 15:08:44 2021 GMT
Not After : Aug 18 15:08:44 2021 GMT
Subject: CN = consoleAdmin
Subject Public Key Info:
Public Key Algorithm: ED25519
ED25519 Public-Key:
pub:
5a:91:87:b8:77:fe:d4:af:d9:c7:c7:ce:55:ae:74:
aa:f3:f1:fe:04:63:9b:cb:20:97:61:97:90:94:fa:
12:8b
X509v3 extensions:
X509v3 Key Usage: critical
Digital Signature
X509v3 Extended Key Usage:
TLS Web Client Authentication
X509v3 Basic Constraints: critical
CA:FALSE
Signature Algorithm: ED25519
7e:aa:be:ed:47:4d:b9:2f:fc:ed:7f:5a:fc:6b:c0:05:5b:f5:
a0:31:fe:86:e3:8e:3f:49:af:6d:d5:ac:c7:c4:57:47:ce:97:
7d:ab:b8:e9:75:ec:b4:39:fb:c8:cf:53:16:5b:1f:15:b6:7f:
5a:d1:35:2d:fc:31:3a:10:e7:0c
请重点观察两点:
Subject: CN = consoleAdmin—— 证书主体 CN 为consoleAdmin,因此 MinIO 会把该证书关联到同名的内置consoleAdmin策略;- 证书必须包含
Extended Key Usage: TLS Web Client Authentication(即 Go 中的x509.ExtKeyUsageClientAuth)扩展,否则 MinIO 不会将该证书当作合法客户端证书接受;同时CA:FALSE保证它是一张叶子证书而非 CA 证书。
如需用 openssl 现场生成一张结构相似的客户端证书,可参考下面命令(实际用途建议按你的 PKI 流程签发):
openssl req -x509 -newkey ed25519 -keyout consoleAdmin.key -out consoleAdmin.crt -days 30 -nodes \
-subj "/CN=consoleAdmin" \
-addext "keyUsage=critical,digitalSignature" \
-addext "extendedKeyUsage=clientAuth" \
-addext "basicConstraints=critical,CA:FALSE"
证书到策略的映射规则
客户端拿到临时凭证后,这些凭证会与服务器上的某个 S3 策略绑定。那么在证书认证场景中,MinIO 如何把“证书”映射到“策略”?
答案是:通过 X.509 证书主体的 Common Name(CN)字段。MinIO 会把 Subject: CN = foobar 的证书关联到名为 foobar 的 S3 策略。上述 CN = consoleAdmin 的自签证书,正是被映射到 MinIO 预定义的内置策略 consoleAdmin。
源码中这段映射逻辑在 cmd/sts-handlers.go 有明确注释:
// We map the X.509 subject common name to the policy. So, a client
// with the common name "foo" will be associated with the policy "foo".
// Other mapping functions - e.g. public-key hash based mapping - are
// possible but not implemented.
//
// Group mapping is not possible with standard X.509 certificates.
if certificate.Subject.CommonName == "" {
writeSTSErrorResponse(ctx, w, ErrSTSMissingParameter,
errors.New("certificate subject CN cannot be empty"))
return
}
两种含义值得展开:
- 策略映射只支持 CN → 单策略:不同于 LDAP 支持用户组映射,标准 X.509 证书里没有可用的“组”信息,因此证书认证无法做组映射;每张叶子证书只能落到一个以 CN 命名的策略上。
- CN 不允许为空:如果客户端证书没有 CN 字段,MinIO 会直接拒绝该请求。
- 需要预先建好同名策略:证书的 CN 必须对应一个已存在于服务器上的策略名,否则即便签发成功,后续授权也会失败——临时凭证是按
policyName := CN调用SetTempUser关联策略的(见 cmd/sts-handlers.go)。
认证流程(4 个步骤)
文档把证书认证的完整流程归纳为 4 步,与源码中 cmd/sts-handlers.go 的 AssumeRoleWithCertificate 处理逻辑一一对应:
- 客户端通过 TLS 连接发送 HTTP
POST请求,命中 MinIO 的 TLS STS API; - MinIO 校验客户端证书的有效性;
- MinIO 查找与证书 CN 匹配的 S3 策略;
- MinIO 返回与该策略绑定的临时 S3 凭证。
对应到源码,步骤 2 的“校验”包含若干比文档更细的边界条件,这些构成了生产可用的关键细节:
- 必须基于 TLS 连接:
r.TLS == nil时直接返回ErrSTSInsecureConnection(cmd/sts-handlers.go),防止明文通道上冒用证书; - 只接受唯一一张叶子客户端证书:客户端可随请求发送证书链。MinIO 会过滤掉其中的 CA 证书作为中间证书池,仅保留非 CA 的叶子证书;若叶子证书为 0 张(
ErrSTSMissingParameter)或多于 1 张(ErrSTSInvalidParameterValue),都会拒绝,因为多证书会让 CN→策略映射产生歧义(cmd/sts-handlers.go); - 中间 CA 数量上限:常量
MaxIntermediateCAs = 10,超过会报ErrSTSTooManyIntermediateCAs(cmd/sts-handlers.go); - 证书链验签:默认(
InsecureSkipVerify=false)使用x509.VerifyOptions{KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, Intermediates: ..., Roots: globalRootCAs}校验证书是否由 MinIO 进程信任的根 CA(globalRootCAs)签发,且用途必须包含客户端认证(cmd/sts-handlers.go); - 跳过验签时仍校验用途:即使在
InsecureSkipVerify=true的调试模式下,MinIO 仍会检查证书ExtKeyUsage是否包含x509.ExtKeyUsageAny或x509.ExtKeyUsageClientAuth,以保证“测试环境能用、正式环境也能用”的证书一致性体验(cmd/sts-handlers.go)。
校验通过后,MinIO 会组装 JWT claims(exp / sub = CN / aud = 证书 Organization / iss = 签发者 CN / parent = 父用户 tls:<CN>),签发临时凭证,并按 CN 关联策略,最后还会触发站点复制(site replication)的 IAM 变更钩子同步该 STS 账号(cmd/sts-handlers.go)。
临时凭证的有效期约束
- 临时凭证会在一段时间后过期,时长可通过请求参数
&DurationSeconds=3600配置; - 默认有效期为 1 小时,最短为 15 分钟。
上述默认值与边界在 internal/config/identity/tls/config.go 中被固化为常量:
const (
defaultExpiry time.Duration = 1 * time.Hour
minExpiry time.Duration = 15 * time.Minute
maxExpiry time.Duration = 365 * 24 * time.Hour
)
func (l Config) GetExpiryDuration(dsecs string) (time.Duration, error) {
if dsecs == "" {
return defaultExpiry, nil
}
d, err := strconv.Atoi(dsecs)
if err != nil {
return 0, auth.ErrInvalidDuration
}
dur := time.Duration(d) * time.Second
if dur < minExpiry || dur > maxExpiry {
return 0, auth.ErrInvalidDuration
}
return dur, nil
}
即:不传 DurationSeconds 默认 1 小时;传入的值必须在 15 分钟到 365 天之间,超出即返回 ErrInvalidDuration。
凭证永远不会比证书“活得更久”
文档特别强调了一个容易被忽视的约束:临时 S3 凭证绝不允许比签发它的客户端证书更晚过期。
举例来说,即使你请求了一个长达 7 天的有效期(无论通过何种配置或参数给出),但客户端证书本身只剩 3 天有效,MinIO 也只会签发 3 天内有效的凭证。原因是 MinIO 不允许签发比其“认证根”——证书本身——寿命更长的凭证。
这段“取两者较小值”的逻辑在 cmd/sts-handlers.go 中实现:
// We set the expiry of the temp. credentials to the minimum of the
// configured expiry and the duration until the certificate itself
// expires.
// We must not issue credentials that out-live the certificate.
if validUntil := time.Until(certificate.NotAfter); validUntil < expiry {
expiry = validUntil
}
所以在设计证书与凭证生命周期时,应把“证书续期节奏”作为第一约束:证书即将过期前,依赖它的所有 STS 会话都会同步受限。
已知注意点(Caveat)
基于客户端证书的认证在浏览器交互式场景下有一个已知体验问题:当应用向预签名 URL 上传内容时,浏览器会弹出要求提供客户端证书的对话框。普通应用使用者通常需要手动“取消”该弹窗才能继续上传。
这适用于所有使用直接 S3 API 的应用(应用自身工作正常),但在通过浏览器对应用生成的预签名 URL 进行 POST 上传时会出现;目前没有绕过该交互的现成方案。对于 CLI、SDK 等非浏览器客户端则不受影响。
关联阅读与源码路径
- 配置实现与默认值:internal/config/identity/tls/config.go
- 处理器实现(校验、CN 映射、有效期、签发凭证):cmd/sts-handlers.go
- 响应数据结构:cmd/sts-datatypes.go
- 启动装载与 skip-verify 告警:cmd/iam.go
- 同目录的其他 STS 认证方式对比:OpenID Connect 见 docs/sts/web-identity.md,LDAP 见 docs/sts/ldap.md,客户端授权(client grants)见 docs/sts/client-grants.md,通用 STS AssumeRole 见 docs/sts/assume-role.md,自定义令牌插件见 docs/sts/custom-token-identity.md
- MinIO 配置子系统总览:docs/config/README.md
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0631
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
video-shotcraftAI宣传片skill,使用 Remotion 制作电影级产品视频:提供106 张镜头配方卡和可复用的视频魔板。适用于 Claude Code 与 Codex以及所有其他智能体Markdown00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python09
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00