首页
/ MinIO X.509 客户端证书 STS 认证(AssumeRoleWithCertificate)完整指南:配置、认证流程与源码解析

MinIO X.509 客户端证书 STS 认证(AssumeRoleWithCertificate)完整指南:配置、认证流程与源码解析

2026-09-07 18:45:42作者:侯霆垣

本文聚焦 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 为空则返回默认的禁用态配置;否则依次解析 EnabledInsecureSkipVerify。这说明配置变更的生效路径是「环境变量 / mc admin config setLookup → 更新全局 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.goAssumeRoleWithCertificateResponse,其 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

请重点观察两点:

  1. Subject: CN = consoleAdmin —— 证书主体 CN 为 consoleAdmin,因此 MinIO 会把该证书关联到同名的内置 consoleAdmin 策略;
  2. 证书必须包含 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.goAssumeRoleWithCertificate 处理逻辑一一对应:

  1. 客户端通过 TLS 连接发送 HTTP POST 请求,命中 MinIO 的 TLS STS API;
  2. MinIO 校验客户端证书的有效性
  3. MinIO 查找与证书 CN 匹配的 S3 策略
  4. MinIO 返回与该策略绑定的临时 S3 凭证

对应到源码,步骤 2 的“校验”包含若干比文档更细的边界条件,这些构成了生产可用的关键细节:

  • 必须基于 TLS 连接r.TLS == nil 时直接返回 ErrSTSInsecureConnectioncmd/sts-handlers.go),防止明文通道上冒用证书;
  • 只接受唯一一张叶子客户端证书:客户端可随请求发送证书链。MinIO 会过滤掉其中的 CA 证书作为中间证书池,仅保留非 CA 的叶子证书;若叶子证书为 0 张(ErrSTSMissingParameter)或多于 1 张(ErrSTSInvalidParameterValue),都会拒绝,因为多证书会让 CN→策略映射产生歧义(cmd/sts-handlers.go);
  • 中间 CA 数量上限:常量 MaxIntermediateCAs = 10,超过会报 ErrSTSTooManyIntermediateCAscmd/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.ExtKeyUsageAnyx509.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 等非浏览器客户端则不受影响。

关联阅读与源码路径

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
docsdocs
暂无描述
Markdown
899
5.83 K
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.76 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
860
1.35 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
925
1.85 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.84 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
533
601
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.37 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
395
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.04 K
525