sts: allow client-provided intermediate CAs (#20896)

This commit allows clients to provide a set of intermediate CA
certificates (up to `MaxIntermediateCAs`) that the server will
use as intermediate CAs when verifying the trust chain from the
client leaf certificate up to one trusted root CA.

This is required if the client leaf certificate is not issued by
a trusted CA directly but by an intermediate CA. Without this commit,
MinIO rejects such certificates.

Signed-off-by: Andreas Auernhammer <github@aead.dev>
This commit is contained in:
Andreas Auernhammer 2025-02-05 01:29:41 +01:00 committed by GitHub
parent 4df7a3aa8f
commit 7fa3e39f85
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 32 additions and 10 deletions

View File

@ -81,6 +81,7 @@ const (
ErrSTSMalformedPolicyDocument
ErrSTSInsecureConnection
ErrSTSInvalidClientCertificate
ErrSTSTooManyIntermediateCAs
ErrSTSNotInitialized
ErrSTSIAMNotInitialized
ErrSTSUpstreamError
@ -145,6 +146,11 @@ var stsErrCodes = stsErrorCodeMap{
Description: "The provided client certificate is invalid. Retry with a different certificate.",
HTTPStatusCode: http.StatusBadRequest,
},
ErrSTSTooManyIntermediateCAs: {
Code: "TooManyIntermediateCAs",
Description: "The provided client certificate contains too many intermediate CA certificates",
HTTPStatusCode: http.StatusBadRequest,
},
ErrSTSNotInitialized: {
Code: "STSNotInitialized",
Description: "STS API not initialized, please try again.",

View File

@ -780,12 +780,26 @@ func (sts *stsAPIHandlers) AssumeRoleWithCertificate(w http.ResponseWriter, r *h
// policy mapping would be ambiguous.
// However, we can filter all CA certificates and only check
// whether they client has sent exactly one (non-CA) leaf certificate.
peerCertificates := make([]*x509.Certificate, 0, len(r.TLS.PeerCertificates))
const MaxIntermediateCAs = 10
var (
peerCertificates = make([]*x509.Certificate, 0, len(r.TLS.PeerCertificates))
intermediates *x509.CertPool
numIntermediates int
)
for _, cert := range r.TLS.PeerCertificates {
if cert.IsCA {
continue
numIntermediates++
if numIntermediates > MaxIntermediateCAs {
writeSTSErrorResponse(ctx, w, ErrSTSTooManyIntermediateCAs, fmt.Errorf("client certificate contains more than %d intermediate CAs", MaxIntermediateCAs))
return
}
if intermediates == nil {
intermediates = x509.NewCertPool()
}
intermediates.AddCert(cert)
} else {
peerCertificates = append(peerCertificates, cert)
}
peerCertificates = append(peerCertificates, cert)
}
r.TLS.PeerCertificates = peerCertificates
@ -806,7 +820,8 @@ func (sts *stsAPIHandlers) AssumeRoleWithCertificate(w http.ResponseWriter, r *h
KeyUsages: []x509.ExtKeyUsage{
x509.ExtKeyUsageClientAuth,
},
Roots: globalRootCAs,
Intermediates: intermediates,
Roots: globalRootCAs,
})
if err != nil {
writeSTSErrorResponse(ctx, w, ErrSTSInvalidClientCertificate, err)

View File

@ -18,15 +18,16 @@ func _() {
_ = x[ErrSTSMalformedPolicyDocument-7]
_ = x[ErrSTSInsecureConnection-8]
_ = x[ErrSTSInvalidClientCertificate-9]
_ = x[ErrSTSNotInitialized-10]
_ = x[ErrSTSIAMNotInitialized-11]
_ = x[ErrSTSUpstreamError-12]
_ = x[ErrSTSInternalError-13]
_ = x[ErrSTSTooManyIntermediateCAs-10]
_ = x[ErrSTSNotInitialized-11]
_ = x[ErrSTSIAMNotInitialized-12]
_ = x[ErrSTSUpstreamError-13]
_ = x[ErrSTSInternalError-14]
}
const _STSErrorCode_name = "STSNoneSTSAccessDeniedSTSMissingParameterSTSInvalidParameterValueSTSWebIdentityExpiredTokenSTSClientGrantsExpiredTokenSTSInvalidClientGrantsTokenSTSMalformedPolicyDocumentSTSInsecureConnectionSTSInvalidClientCertificateSTSNotInitializedSTSIAMNotInitializedSTSUpstreamErrorSTSInternalError"
const _STSErrorCode_name = "STSNoneSTSAccessDeniedSTSMissingParameterSTSInvalidParameterValueSTSWebIdentityExpiredTokenSTSClientGrantsExpiredTokenSTSInvalidClientGrantsTokenSTSMalformedPolicyDocumentSTSInsecureConnectionSTSInvalidClientCertificateSTSTooManyIntermediateCAsSTSNotInitializedSTSIAMNotInitializedSTSUpstreamErrorSTSInternalError"
var _STSErrorCode_index = [...]uint16{0, 7, 22, 41, 65, 91, 118, 145, 171, 192, 219, 236, 256, 272, 288}
var _STSErrorCode_index = [...]uint16{0, 7, 22, 41, 65, 91, 118, 145, 171, 192, 219, 244, 261, 281, 297, 313}
func (i STSErrorCode) String() string {
if i < 0 || i >= STSErrorCode(len(_STSErrorCode_index)-1) {